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

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

据我所知,数据元素是为了这个目的,但我不能得到它的工作。 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> 

那么,这是行不通的。 我做错了什么? 我只是想声明自己的文件类型。

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

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

其他解决方案不能可靠地工作,直到我补充说:

 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()中:

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

pathPattern

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

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

这将工作:

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

添加更多,如果你想支持更多的点。

Phyrum Tea和yuku提供的答案已经很丰富了。

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

从官方的Android 7.0变化 :

对于面向Android 7.0的应用,Android框架会强制实施StrictMode API策略,禁止在您的应用之外公开file:// URI。 如果包含文件URI的意图离开了您的应用程序,则应用程序将失败,并显示FileUriExposedException异常。

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

如果你有自己的自定义文件没有特定的mime-type结束(或者我猜即使有一个),你可能需要添加第二个scheme值到你的intent-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设备上打开,而在旧版本上一切正常。 我希望它可以帮助别人。

Markus Ressel是正确的。 Android 7.0 Nougat不再允许使用文件URI在应用程序之间共享文件。 必须使用内容URI。 但是,内容URI不允许共享文件路径,只能使用MIME类型。 因此,您不能使用内容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); ... } } 

尝试添加

 <action android:name="android.intent.action.VIEW"/> 
  <!-- 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识别特定的文件扩展名。 我有一个"*/*" mimetype的意图过滤器是唯一的似乎工作和文件浏览器现在列出我的应用程序作为打开文件的选项,但我的应用程序现在显示为一个选项打开任何即使我已经使用pathPattern标签指定了特定的文件扩展名,文件的种类也是如此。 这到目前为止,即使当我尝试查看/编辑我的联系人列表中的联系人Android问我是否要使用我的应用程序来查看联系人,这只是这种情况发生的许多情况之一,非常非常讨厌。

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

所以从我所看到的,尝试和阅读,Android根本无法区分文件扩展名和pathPattern标记基本上是一个巨大的浪费时间和精力。 如果您有幸只需要某种特定MIME类型的文件(如文本,视频或音频),则可以使用MIME类型的意向过滤器。 如果你需要一个特定的文件扩展名或Android不知道的MIME类型然后你运气不好。

如果我对这个问题有任何疑问,请告诉我,到目前为止,我已经阅读了每篇文章,并尝试了所有可以找到的解决方案,但都没有成功。

我可以再写一两页关于这些东西在Android中的普遍程度,以及开发者的经验是如何搞砸的,但是我会为你省去愤怒的咆哮。 希望我救了一些麻烦。

请注意,你可以(也许应该)有多个intent-filter 。 看到我的其他答案:

https://stackoverflow.com/a/8638084/341091

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

那些与其他文件管理器\ Explorer应用程序,如@ yuku和@ phyrum茶有问题的人回答

这适用于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文件资源管理器和其他文件管理器,所以我添加

  android:mimeType="*/*" 

那么它适用于ES浏览器,但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>