Android意图过滤器:将应用程序与文件扩展名关联

Android意图过滤器:将应用程序与文件扩展名关联,android,android-manifest,intentfilter,Android,Android Manifest,Intentfilter,我有一个自定义文件类型/扩展名,我想将我的应用程序与之关联 据我所知,数据元素就是为了这个目的而设计的,但我无法让它工作。 根据文档和许多论坛帖子,它应该是这样工作的: <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.DEFAULT" /> <ca

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

据我所知,数据元素就是为了这个目的而设计的,但我无法让它工作。 根据文档和许多论坛帖子,它应该是这样工作的:

<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>
            <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>
        <intent-filter>
            <action android:name="android.intent.action.VIEW"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <data android:scheme="content"/>
            <data android:scheme="file"/>
            <data android:mimeType="application/pdf"/>
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.VIEW"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <data android:scheme="content"/>
            <data android:scheme="file"/>
            <data android:host="*"/>
            <data android:pathPattern=".*\\.pdf"/>
        </intent-filter>

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

尝试添加

<action android:name="android.intent.action.VIEW"/>

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

示例1,处理不带mimetype的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>

处理与后缀无关的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>

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

  <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>

路径模式

<data android:pathPattern=".*\\.pdf" />

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

这将有助于:

<data android:pathPattern=".*\\.pdf" />
<data android:pathPattern=".*\\..*\\.pdf" />
<data android:pathPattern=".*\\..*\\..*\\.pdf" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.pdf" />


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

在我添加以下内容之前,其他解决方案对我来说并不可靠:

android:mimeType="*/*" 
在此之前,它在一些应用程序中工作,在一些非

为我提供完整的解决方案:

<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>

我的发现:

您需要几个过滤器来处理检索文件的不同方式。例如,通过gmail附件,通过文件浏览器,通过HTTP,通过FTP。。。 它们都发出非常不同的意图

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

对于下面的示例,我创建了一个伪文件类型new.mrz。我从gmail附件和文件浏览器中检索到了它

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

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>

多年来,我一直在努力让它发挥作用,基本上尝试了所有建议的解决方案,但仍然无法让Android识别特定的文件扩展名。我有一个带有
“*/*”
mimetype的意图过滤器,这似乎是唯一有效的东西,文件浏览器现在将我的应用程序列为打开文件的选项,但是我的应用程序现在显示为打开任何类型文件的选项,即使我使用pathPattern标记指定了特定的文件扩展名。甚至当我试图查看/编辑联系人列表中的联系人时,Android也会问我是否想使用我的应用程序查看联系人,这只是出现这种情况的众多情况之一,非常烦人

最终,我发现这篇谷歌集团的帖子中有一个类似的问题,一位真正的安卓框架工程师回答了这个问题。她解释说,Android对文件扩展名一无所知,只知道MIME类型()

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

如果我在这些问题上有任何错误,请告诉我,到目前为止,我已经阅读了每一篇文章,并尝试了每一个我能找到的解决方案,但没有一个有效


我可以再写一两页,谈谈这些东西在安卓系统中有多普遍,开发人员的体验有多糟糕,但我不会再对你发牢骚了。希望我帮了别人一些麻烦。

对于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/pdf" /> <!-- .pdf -->
  <data android:mimeType="application/msword" /> <!-- .doc / .dot -->
  <data android:mimeType="application/vnd.openxmlformats-officedocument.wordprocessingml.document" /> <!-- .docx -->
  <data android:mimeType="application/vnd.ms-excel" />  <!-- .xls / .xlt / .xla -->
  <data android:mimeType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" />  <!-- .xlsx -->
  <data android:mimeType="application/vnd.ms-powerpoint" />  <!-- .ppt / .pps / .pot / .ppa -->
  <data android:mimeType="application/vnd.openxmlformats-officedocument.presentationml.presentation" /> <!-- .pptx -->
  <data android:mimeType="application/vnd.openxmlformats-officedocument.presentationml.slideshow" /> <!-- .ppsx -->
  <data android:mimeType="application/zip" /> <!-- .zip -->
  <data android:mimeType="image/jpeg" /> <!-- .jpeg -->
  <data android:mimeType="image/png" /> <!-- .png -->
  <data android:mimeType="image/gif" /> <!-- .gif -->
  <data android:mimeType="text/plain" /> <!-- .txt / .text / .log / .c / .c++ / ... -->

根据需要添加尽可能多的mime类型。我的项目只需要这些。


         <!--
            Works for Files, Drive and DropBox
        -->
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="file" />
            <data android:mimeType="*/*" />
            <data android:host="*" />
            <data android:pathPattern=".*\\.teamz" />
        </intent-filter>

        <!--
            Works 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:host="gmail-ls" android:scheme="content" android:mimeType="application/octet-stream"/>
        </intent-filter>
请注意,这将使你的应用程序打开所有gmail文件附件,没有办法解决这一问题。

作者给出的答案已经非常有用了

我想补充一点,从Android 7.0牛轧糖开始,应用程序之间文件共享的处理方式发生了变化:

来自官方:

对于针对Android 7.0的应用程序,Android框架强制执行 StrictMode API策略,禁止在外部公开文件://URI 你的应用程序。如果包含文件URI的意图离开你的应用程序,应用程序 由于FileUriExposedException异常而失败

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

如果您有自己的自定义文件结尾没有特定的
mime类型
(或者我猜甚至有一个),那么您可能需要在
意图过滤器
中添加第二个
方案
值,以使其与
文件提供程序
一起工作

例如:

<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>

这里重要的是增加了

<data android:scheme="content" />

到过滤器


我很难发现这个小小的变化,它阻止我在安卓7.0设备上打开活动,而在旧版本上一切正常。我希望它能帮助别人。

马库斯·雷塞尔是对的。Android 7.0 Nougat不再允许使用文件URI在应用程序之间共享文件。必须使用内容URI。但是,内容URI不允许共享文件路径,只允许共享mime类型。因此,您不能使用内容URI将您的应用程序与您自己的文件扩展名关联

DrobBox在Android 7.0上有一个有趣的行为。当它遇到一个未知的文件扩展名时,它似乎形成了一个文件URI意图,但它没有启动该意图,而是调用操作系统来找出哪些应用程序可以访问
         <!--
            Works for Files, Drive and DropBox
        -->
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="file" />
            <data android:mimeType="*/*" />
            <data android:host="*" />
            <data android:pathPattern=".*\\.teamz" />
        </intent-filter>

        <!--
            Works 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:host="gmail-ls" android:scheme="content" android:mimeType="application/octet-stream"/>
        </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="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>
<data android:scheme="content" />
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);
        ...
    }
}
     <intent-filter android:label="@string/app_name_decrypt">
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="file" />
            <data android:pathPattern=".*\\.lock" />
            <data android:pathPattern=".*\\..*\\.lock" />
            <data android:pathPattern=".*\\..*\\..*\\.lock" />
        </intent-filter>
 android:mimeType="*/*"
     <intent-filter android:label="@string/app_name_decrypt">
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="file" />
            <data android:pathPattern=".*\\.lock" />
            <data android:pathPattern=".*\\..*\\.lock" />
            <data android:pathPattern=".*\\..*\\..*\\.lock" />
        </intent-filter>
        <intent-filter android:label="@string/app_name_decrypt">
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="file"/>
            <data android:scheme="content" />
            <data android:mimeType="*/*" />
            <data android:pathPattern=".*\\.lock" />
            <data android:pathPattern=".*\\..*\\.lock" />
            <data android:pathPattern=".*\\..*\\..*\\.lock" />
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />

            <category android:name="android.intent.category.DEFAULT" />

            <data
                android:host="*"
                android:mimeType="application/xyz"
                android:scheme="content" />
        </intent-filter>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
            <action android:name="android.intent.action.SEND" />

            <action android:name="android.intent.action.VIEW" />

             <category android:name="android.intent.category.DEFAULT" />

            <data android:mimeType= "application/pdf" />

            <data android:host="*" />

        </intent-filter>
Intent intent=getIntent();

if(intent!=null) {          

        String action=intent.getAction();

        String type=intent.getType();

        if(Intent.ACTION_VIEW.equals(action) && type.endsWith("pdf")) {

            // Get the file from the intent object

            Uri file_uri=intent.getData();

            if(file_uri!=null)

                filepath=file_uri.getPath();

            else

                filepath="No file";

        }

        else if(Intent.ACTION_SEND.equals(action) && type.endsWith("pdf")){

            Uri uri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);

            filepath = uri.getPath();

        }
<intent-filter android:priority="999">
    <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:host="*" />
    <data android:mimeType="application/octet-stream" />
    <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.yourextension" />
    <data android:pathPattern=".*\\..*\\..*\\..*\\.yourextension" />
    <data android:pathPattern=".*\\..*\\..*\\.yourextension" />
    <data android:pathPattern=".*\\..*\\.yourextension" />
    <data android:pathPattern=".*\\.yourextension" />
    <data android:scheme="content" />
</intent-filter>
<intent-filter>
   <action android:name="android.intent.action.SEND" />
   <category android:name="android.intent.category.DEFAULT" />
   <data android:mimeType="application/pdf" />
   <data android:pathPattern=".*\\.pdf" />
   <data android:pathPattern=".*\\..*\\.pdf" />
   <data android:pathPattern=".*\\..*\\..*\\.pdf" />
   <data android:pathPattern=".*\\..*\\..*\\..*\\.pdf" />
</intent-filter>
private fun checkFileOpening(intent: Intent) {
    if (intent.action == Intent.ACTION_VIEW && (intent.scheme == ContentResolver.SCHEME_FILE
                    || intent.scheme == ContentResolver.SCHEME_CONTENT)) {

        val text = intent.data?.let {
            contentResolver.openInputStream(it)?.bufferedReader()?.use(BufferedReader::readText) 
        }
    }
}
            <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>
        <intent-filter>
            <action android:name="android.intent.action.VIEW"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <data android:scheme="content"/>
            <data android:scheme="file"/>
            <data android:mimeType="application/pdf"/>
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.VIEW"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <data android:scheme="content"/>
            <data android:scheme="file"/>
            <data android:host="*"/>
            <data android:pathPattern=".*\\.pdf"/>
        </intent-filter>