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


92

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

据我所知,数据元素是为此目的而制作的,但我无法使其正常工作。 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>

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


在意图过滤器中添加android:label =“ @ string / app_name”
Prashant

Answers:


116

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

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

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

我认为可以忽略host =“ *”,但它开始变得太宽泛。
Phyrum Tea 2011年

@Phyrum Tea:是否可以使用我的应用程序将文件扩展名与OTHER应用程序关联?我的应用程序生成了.list文件,我想告诉android它们是文本文件,并使用文本编辑器打开它们(我的应用程序不是文本编辑器)。
Luis A. Florit 2013年

@ LuisA.Florit您是否尝试过将文件称为* .txt?它可能会起作用。
加夫里耶尔(Gavriel)

自定义扩展不能与MimeType一起使用。如果我给mimetype作为/当我也单击Gmail通知时我的应用程序正在显示。
madan V 2015年

1
无法正常工作..我现在受够了过去两天的搜索工作。即时通讯使用android 9
Ahmad Arslan

48

在添加之前,其他解决方案对我而言并不可靠:

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>

1
哇!这帮助了我。似乎android文档不正确。必须声明所有(方案,主机,路径[模式],mimeType)都可以工作
Gavriel

如果要完成操作,则应该同时使用- mimeType和-而不是。见developer.android.com/guide/components/...
安德鲁太阳

1
如果要从Gmail打开文件应用程序怎么办?
IgorGanapolsky

30

Phyrum Teayuku给出的答案已经非常有用。

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

从官方Android 7.0更改

对于面向Android 7.0的应用程序,Android框架会强制执行StrictMode API策略,该策略禁止在应用程序外部公开file:// URI。如果包含文件URI的意图离开您的应用程序,则该应用程序将失败,并出现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>

这里重要的是增加了

<data android:scheme="content" />

过滤器。

我很难找到这个小的变化,这使得我的活动无法在Android 7.0设备上打开,而在旧版本上一切正常。希望对您有所帮助。


没有这个技巧和额外的信息,我无法让我的应用向文件浏览器注册。这是至关重要的,应该予以反对
u2tall

你节省了一天!大tnx。这应该被接受的答案!
安德里斯·

⚠️如果您尝试通过内容计划(例如)获取文件File(uri.path),则该文件将因No such file or directory-在更新以支持Nougat +时必须以不同的方式处理!
约旦H

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

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.intent.action.VIEW:content://gmail-ls/l.foul@gmail.com/messages/2950/attachments/0.1/BEST/false:application / octet-stream:新。先生

文件浏览器过滤器:

        <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>
  • 日志:检测到文件意图: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>

上面使用的私有函数:

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());
    }
}

活动必须是启动程序还是主要类型?由于我尝试了gmail附件过滤器,因此无法正常工作。
阿米特(Amit)

15

pathPattern

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

如果文件路径在“ .pdf”之前包含一个或多个点,则无法使用。

这将起作用:

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

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


1
到目前为止,这是进入子文件夹的唯一选项
LokiDroid 2014年

我正在使用相同的代码,但IDE会警告我,请参阅此问题stackoverflow.com/questions/35833776/…–
AndreaF

5

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

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

因此,从我所看到,尝试和阅读的内容来看,Android根本无法区分文件扩展名,而pathPattern标签基本上是对时间和精力的巨大浪费。如果您足够幸运,只需要某种mime类型的文件(例如文本,视频或音频),则可以将intent-filter用于mime类型。如果您需要特定的文件扩展名或Android不知道的mime类型,那么您就不走运了。

如果我对此有误,请告诉我,到目前为止,我已经阅读了每篇文章,并尝试了所有可以找到的建议解决方案,但没有一个起作用。

我可以再写一两页关于这类事情在Android中的普遍程度以及开发人员体验的混乱程度,但是我将省去我的愤怒。希望我救了一些麻烦。


5

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

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

这是我的文件加载代码经过修改以接受内容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);
        ...
    }
}

1

尝试添加

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

现在,它适用于工厂设置的文件类型,例如application / pdf,我如何声明自己的文件类型?当我说文件类型时,我的意思是mimeType;)
Tamas 2010年

您希望该模仿类型捕获哪种文件?另外,该文件是从浏览器或文件管理器中打开的,还是从您制作的另一个应用程序发送的?
magaio 2010年

可能来自浏览器,邮件客户端,文件管理器或其他任何位置。或者我自己的app ofc :)文件扩展名是自定义的,由客户端指定。
塔玛斯

好吧,我仍然被困住……有人可以帮忙吗?
塔玛斯

@Tamas你们每个都得到了这种排序。我被卡住了!
StuStirling 2014年

1

对于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类型。我只需要那些用于我的项目。


1
         <!--
            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文件附件,因此无法解决


这将处理通过电子邮件发送给用户的每个文件,而不仅仅是.teamz
IgorGanapolsky

1

那些与其他文件管理器\资源管理器应用程序有问题的用户,例如@yuku和@ phyrum-tea回答了

这适用于LG默认文件管理器应用

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

但无法与ES File Explorer和其他文件管理器一起使用,因此我添加了

 android:mimeType="*/*"

然后它可以与ES Explorer一起使用,但是LG文件管理器无法检测文件类型,所以我的解决方案是

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

1

内容URI ftw,清单中有意图过滤器...如果文件具有自定义扩展名.xyz,请添加匹配的mime类型:

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

某些应用程序(例如电子邮件)似乎会将扩展名转换为mime类型。现在,我可以单击电子邮件中的附件,并在我的应用程序中打开它。


1

您可以尝试使用此扩展名,而不是pdf,也可以使用其他扩展名。首先,您必须在androidmanifest.xml文件中添加读取外部存储权限。

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

然后在Activity标签的androidmanifest文件中,添加一个intent-filter,如下所示。

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

最后,在您的代码中,您将获得pdf文件的路径,如下所示:

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.ACTION_VIEW,无法捕获文件路径会导致错误:java.io.FileNotFoundException:权限被拒绝。它仅来自某些特定应用程序,并非每次都来自。有什么办法吗?
Hardik Joshi

@HardikJoshi应用程序可能未设置权限GRANT_READ_URI_PERMISSION。
Mira_Cole '19

1

阅读Kotlin中的打开文件:

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

-1

将此意图过滤器放入您要打开的清单中的活动标签中,并触摸该文件:

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

-1

//我已经尝试过此代码。而且运作良好。您可以使用此代码接受pdf文件。

<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>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.