Android意图过滤器:将app与文件扩展名相关联

Tam*_*mas 90 android intentfilter android-manifest

我有一个自定义文件类型/扩展名,我想与我的应用程序关联.

据我所知,数据元素是为此目的而制作的,但我无法使其正常工作. http://developer.android.com/guide/topics/manifest/data-element.html 根据文档和很多论坛帖子,它应该像这样工作:

<intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:mimeType="application/pdf" />
</intent-filter>
Run Code Online (Sandbox Code Playgroud)

好吧,它不起作用.我做错了什么?我只想声明自己的文件类型.

Phy*_*Tea 113

您需要多个意图过滤器来解决您想要处理的不同情况.

例1,处理没有mimetypes的http请求:

  <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="http" />
    <data android:host="*" />
    <data android:pathPattern=".*\\.pdf" />
  </intent-filter>
Run Code Online (Sandbox Code Playgroud)

处理mimetypes,后缀无关紧要:

  <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="http" />
    <data android:host="*" />
    <data android:mimeType="application/pdf" />
  </intent-filter>
Run Code Online (Sandbox Code Playgroud)

处理文件浏览器应用程序中的意图:

  <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:scheme="file" />
    <data android:host="*" />
    <data android:pathPattern=".*\\.pdf" />
  </intent-filter>
Run Code Online (Sandbox Code Playgroud)

  • 你不能计划吗? (2认同)
  • 虽然此答案适用于 .pdf(或其他常见扩展名)的特殊情况,但它不适用于无法识别 mime 类型的文件类型的一般情况下的内容方案意图。或者,需要明确的是,它将接受任何类型的内容,并且您的应用程序将被列为所有文件类型的替代方案。 (2认同)
  • 不工作..我现在厌倦了过去两天的搜索,但没有工作。我使用的是安卓9 (2认同)

Emm*_*ery 48

在我添加之前,其他解决方案对我来说无法可靠地运行:

android:mimeType="*/*" 
Run Code Online (Sandbox Code Playgroud)

之前它在某些应用程序中有效,有些则不...

完整的解决方案:

<intent-filter>
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <data android:scheme="file"  android:host="*" android:pathPattern=".*\\.EXT" android:mimeType="*/*"  />
</intent-filter>
Run Code Online (Sandbox Code Playgroud)


小智 24

Phyrum Teayuku给出的答案已经非常丰富.

我想补充一点,从Android 7.0 Nougat开始,对应用程序之间文件共享的处理方式有所改变:

从官方Android 7.0更改:

对于定位到Android 7.0的应用,Android框架会强制执行StrictMode API策略,该策略禁止在应用外部公开file:// URI.如果包含文件URI的intent离开了您的应用程序,则该应用程序将失败并出现FileUriExposedException异常.

要在应用程序之间共享文件,您应发送content:// URI并授予对URI的临时访问权限.授予此权限的最简单方法是使用FileProvider类.有关权限和共享文件的详细信息,请参阅共享文件.

如果你有自己的自定义文件没有特定的结尾mime-type(或者我猜甚至有一个)你可能需要添加第二个schemeintent-filter,使其也可以使用FileProviders.

例:

<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: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:host="*" />
    <data android:pathPattern=".*\\.sfx" />
    <data android:pathPattern=".*\\..*\\.sfx" />
    <data android:pathPattern=".*\\..*\\..*\\.sfx" />
    <data android:pathPattern=".*\\..*\\..*\\..*\\.sfx" />
    <!-- keep going if you need more -->

</intent-filter>
Run Code Online (Sandbox Code Playgroud)

这里重要的是增加

<data android:scheme="content" />
Run Code Online (Sandbox Code Playgroud)

过滤器.

我很难发现这个小小的变化让我的活动在Android 7.0设备上打开,而旧版本的一切都很好.我希望它对某人有帮助.


LFO*_*FOR 19

我的发现:

您需要多个过滤器来处理检索文件的不同方法.即,通过gmail附件,通过文件浏览器,通过HTTP,通过FTP ...它们都发送非常不同的意图.

您需要过滤掉在活动代码中触发活动的意图.

对于下面的示例,我创建了一个假文件类型new.mrz.我从gmail附件和文件资源管理器中检索它.

onCreate()中添加的活动代码:

        Intent intent = getIntent();
        String action = intent.getAction();

        if (action.compareTo(Intent.ACTION_VIEW) == 0) {
            String scheme = intent.getScheme();
            ContentResolver resolver = getContentResolver();

            if (scheme.compareTo(ContentResolver.SCHEME_CONTENT) == 0) {
                Uri uri = intent.getData();
                String name = getContentName(resolver, uri);

                Log.v("tag" , "Content intent detected: " + action + " : " + intent.getDataString() + " : " + intent.getType() + " : " + name);
                InputStream input = resolver.openInputStream(uri);
                String importfilepath = "/sdcard/My Documents/" + name; 
                InputStreamToFile(input, importfilepath);
            }
            else if (scheme.compareTo(ContentResolver.SCHEME_FILE) == 0) {
                Uri uri = intent.getData();
                String name = uri.getLastPathSegment();

                Log.v("tag" , "File intent detected: " + action + " : " + intent.getDataString() + " : " + intent.getType() + " : " + name);
                InputStream input = resolver.openInputStream(uri);
                String importfilepath = "/sdcard/My Documents/" + name; 
                InputStreamToFile(input, importfilepath);
            }
            else if (scheme.compareTo("http") == 0) {
                // TODO Import from HTTP!
            }
            else if (scheme.compareTo("ftp") == 0) {
                // TODO Import from FTP!
            }
        }
Run Code Online (Sandbox Code Playgroud)

Gmail附加过滤器:

        <intent-filter android:label="@string/app_name">
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="content" />
            <data android:mimeType="application/octet-stream" />
        </intent-filter>
Run Code Online (Sandbox Code Playgroud)
  • 日志:检测到内容意图:android.intent.action.VIEW:content://gmail-ls/l.foul@gmail.com/messages/2950/attachments/0.1/BEST/false:application/octet-stream:new. MRZ

文件浏览器过滤器:

        <intent-filter android:label="@string/app_name">
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="file" />
            <data android:pathPattern=".*\\.mrz" />
        </intent-filter>
Run Code Online (Sandbox Code Playgroud)
  • 日志:检测到文件意图:android.intent.action.VIEW:file:///storage/sdcard0/My%20Documents/new.mrz:null:new.mrz

HTTP过滤器:

        <intent-filter android:label="@string/rbook_viewer">
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="http" />
            <data android:pathPattern=".*\\.mrz" />
        </intent-filter>
Run Code Online (Sandbox Code Playgroud)

上面使用的私有函数:

private String getContentName(ContentResolver resolver, Uri uri){
    Cursor cursor = resolver.query(uri, null, null, null, null);
    cursor.moveToFirst();
    int nameIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME);
    if (nameIndex >= 0) {
        return cursor.getString(nameIndex);
    } else {
        return null;
    }
}

private void InputStreamToFile(InputStream in, String file) {
    try {
        OutputStream out = new FileOutputStream(new File(file));

        int size = 0;
        byte[] buffer = new byte[1024];

        while ((size = in.read(buffer)) != -1) {
            out.write(buffer, 0, size);
        }

        out.close();
    }
    catch (Exception e) {
        Log.e("MainActivity", "InputStreamToFile exception: " + e.getMessage());
    }
}
Run Code Online (Sandbox Code Playgroud)


Ran*_*ku' 15

pathPattern

<data android:pathPattern=".*\\.pdf" />
Run Code Online (Sandbox Code Playgroud)

如果文件路径在".pdf"之前包含一个或多个点,则不起作用.

这将有效:

<data android:pathPattern=".*\\.pdf" />
<data android:pathPattern=".*\\..*\\.pdf" />
<data android:pathPattern=".*\\..*\\..*\\.pdf" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.pdf" />
Run Code Online (Sandbox Code Playgroud)

如果要支持更多点,请添加更多.


小智 5

我一直试图让它工作很长时间,并且基本上已经尝试了所有建议的解决方案,但仍然无法让 Android 识别特定的文件扩展名。我有一个带有"*/*"mimetype的意图过滤器,它似乎是唯一可用的东西,文件浏览器现在将我的应用程序列为打开文件的选项,但是我的应用程序现在显示为打开任何类型文件的选项,即使我已经使用 pathPattern 标记指定了特定的文件扩展名。到目前为止,即使我尝试查看/编辑联系人列表中的联系人,Android 也会询问我是否要使用我的应用程序查看联系人,这只是发生这种情况的许多情况之一,非常非常烦人。

最终我发现这个 google groups 帖子有一个类似的问题,一个实际的 Android 框架工程师回答了这个问题。她解释说,Android 根本不了解文件扩展名,只了解 MIME 类型(https://groups.google.com/forum/#!topic/android-developers/a7qsSl3vQq0)。

因此,从我所看到、尝试和阅读的情况来看,Android 根本无法区分文件扩展名,而 pathPattern 标签基本上是对时间和精力的巨大浪费。如果你足够幸运只需要某种 mime 类型的文件(比如文本、视频或音频),你可以使用带有 mime 类型的意图过滤器。但是,如果您需要特定的文件扩展名或 Android 不知道的 mime 类型,那么您就不走运了。

如果我对此有任何错误,请告诉我,到目前为止,我已经阅读了所有帖子并尝试了我能找到的所有建议解决方案,但都没有奏效。

我可以再写一两页关于这类事情在 Android 中似乎是多么普遍以及开发者体验是多么糟糕,但我会为你省去我愤怒的咆哮;)。希望我为某人省了一些麻烦。


小智 5

Markus Ressel 是正确的。Android 7.0 Nougat 不再允许使用文件 URI 在应用之间共享文件。必须使用内容 URI。但是,内容 URI 不允许共享文件路径,只能共享 MIME 类型。因此,您不能使用内容 URI 将您的应用与您自己的文件扩展名相关联。

Drobpox 在 Android 7.0 上有一个有趣的行为。当它遇到未知的文件扩展名时,它似乎会形成一个文件 URI 意图,但它不会启动该意图,而是调用操作系统来找出哪些应用程序可以接受该意图。如果只有一个应用程序可以接受该文件 URI,则它会直接向该应用程序发送显式内容 URI。因此,要使用 Dropbox,您无需更改应用程序上的意图过滤器。它不需要内容 URI 意图过滤器。只需确保该应用程序可以接收内容 URI,并且您的具有您自己的文件扩展名的应用程序可以像 Android 7.0 之前一样在 Dropbox 中使用。

这是我的文件加载代码修改为接受内容 URI 的示例:

Uri uri = getIntent().getData();
if (uri != null) {
    File myFile = null;
    String scheme = uri.getScheme();
    if (scheme.equals("file")) {
        String fileName = uri.getEncodedPath();
        myFile = new File(filename);
    }
    else if (!scheme.equals("content")) {
        //error
        return;
    }
    try {
        InputStream inStream;
        if (myFile != null) inStream = new FileInputStream(myFile);
        else inStream = getContentResolver().openInputStream(uri);
        InputStreamReader rdr = new InputStreamReader(inStream);
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)