Android 意图过滤器:将应用程序与文件扩展名关联
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3760276/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Android intent filter: associate app with file extension
提问by Tamas
I have a custom file type/extension that I want to associate my app with.
我有一个自定义文件类型/扩展名,我想将我的应用程序与之关联。
As far as I know, the data element is made for this purpose, but I can't get it working. http://developer.android.com/guide/topics/manifest/data-element.htmlAccording to the docs, and a lot of forum posts, it should work like this:
据我所知,数据元素是为此目的而制作的,但我无法让它工作。 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>
Well, it does not work. What did I do wrong? I simply want to declare my own file type.
好吧,它不起作用。我做错了什么?我只是想声明我自己的文件类型。
回答by Phyrum Tea
You need multiple intent filters to address different situation you want to handle.
您需要多个意图过滤器来解决您想要处理的不同情况。
Example 1, handle http requests without mimetypes:
示例 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>
Handle with mimetypes, where the suffix is irrelevant:
处理 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>
Handle intent from a file browser app:
处理来自文件浏览器应用程序的意图:
<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>
回答by Emmanuel Touzery
The other solutions did not work reliably for me until I added:
在我添加之前,其他解决方案对我来说并不可靠:
android:mimeType="*/*"
Before that it worked in some applications, in some not...
在此之前,它在某些应用程序中有效,而在某些应用程序中则不...
complete solution for me:
对我来说完整的解决方案:
<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>
回答by Markus Ressel
The answeres given by Phyrum Teaand yukuare very informative already.
Phyrum Tea和yuku给出的答案已经非常有用了。
I want to add that starting with Android 7.0 Nougatthere is a change to the way file sharing between apps is handled:
我想补充一点,从Android 7.0 Nougat开始,应用程序之间文件共享的处理方式发生了变化:
From official Android 7.0 Changes:
从官方Android 7.0 变化:
For apps targeting Android 7.0, the Android framework enforces the StrictMode API policy that prohibits exposing file:// URIs outside your app. If an intent containing a file URI leaves your app, the app fails with a FileUriExposedException exception.
To share files between applications, you should send a content:// URI and grant a temporary access permission on the URI. The easiest way to grant this permission is by using the FileProvider class. For more information on permissions and sharing files, see Sharing Files.
对于面向 Android 7.0 的应用,Android 框架强制执行 StrictMode API 政策,禁止在您的应用之外公开 file:// URI。如果包含文件 URI 的 Intent 离开您的应用程序,应用程序将失败并出现 FileUriExposedException 异常。
要在应用程序之间共享文件,您应该发送一个 content:// URI 并授予对该 URI 的临时访问权限。授予此权限的最简单方法是使用 FileProvider 类。有关权限和共享文件的更多信息,请参阅共享文件。
If you have your own custom file ending without a specific mime-type
(or i guess even with one) you may have to add a second scheme
value to your intent-filter
to make it work with FileProviders
too.
如果您有自己的自定义文件,但没有特定的结尾mime-type
(或者我猜甚至有一个),您可能需要为您的文件添加第二个scheme
值intent-filter
以使其也可以使用FileProviders
。
Example:
例子:
<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>
The important thing here is the addition of
这里重要的是添加
<data android:scheme="content" />
to the filter.
到过滤器。
I had a hard time finding out about this little change which kept my activity from opening on Android 7.0 devices while everything was fine on older versions. I hope it helps someone.
我很难找到这个小小的变化,它使我的活动无法在 Android 7.0 设备上打开,而在旧版本上一切正常。我希望它可以帮助某人。
回答by LFOR
My findings:
我的发现:
You need several filters to deal with the different ways of retrieving a file. ie, by gmail attachment, by file explorer, by HTTP, by FTP... They all send very different intents.
您需要多个过滤器来处理检索文件的不同方式。即,通过 gmail 附件、通过文件浏览器、通过 HTTP、通过 FTP……它们都发送非常不同的意图。
And you need to filter out the intent that trigger your activity in your activity code.
并且您需要在您的活动代码中过滤出触发您的活动的意图。
For the example below, I created a fake file type new.mrz. And I retrieved it from gmail attachment and file explorer.
对于下面的示例,我创建了一个伪造的文件类型 new.mrz。我从 gmail 附件和文件浏览器中检索到它。
Activity code added in the onCreate():
在 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 attachement filter:
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>
- LOG: Content intent detected: android.intent.action.VIEW : content://gmail-ls/[email protected]/messages/2950/attachments/0.1/BEST/false : application/octet-stream : new.mrz
- 日志:检测到内容意图:android.intent.action.VIEW:content://gmail-ls/[email protected]/messages/2950/attachments/0.1/BEST/false:application/octet-stream:new。先生
File explorer filter:
文件浏览器过滤器:
<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>
- LOG: File intent detected: android.intent.action.VIEW : file:///storage/sdcard0/My%20Documents/new.mrz : null : new.mrz
- 日志:检测到文件意图:android.intent.action.VIEW:file:///storage/sdcard0/My%20Documents/new.mrz:null:new.mrz
HTTP filter:
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 functions used above:
上面使用的私有函数:
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());
}
}
回答by Randy Sugianto 'Yuku'
The pathPattern
这 pathPattern
<data android:pathPattern=".*\.pdf" />
does not work if the file path contains one or more dots before ".pdf".
如果文件路径在“.pdf”之前包含一个或多个点,则不起作用。
This will work:
这将起作用:
<data android:pathPattern=".*\.pdf" />
<data android:pathPattern=".*\..*\.pdf" />
<data android:pathPattern=".*\..*\..*\.pdf" />
<data android:pathPattern=".*\..*\..*\..*\.pdf" />
Add more if you want to support more dots.
如果您想支持更多点,请添加更多。
回答by PeeGee85
I've been trying to get this to work for ages and have tried basicly all the suggested solutions and still cannot get Android to recognise specific file extensions. I have an intent-filter with a "*/*"
mimetype which is the only thing that seems to work and file-browsers now list my app as an option for opening files, however my app is now shown as an option for opening ANY KIND of file even though I've specified specific file extensions using the pathPattern tag. This goes so far that even when I try to view/edit a contact in my contacts list Android asks me if I want to use my app to view the contact, and that is just one of many situations where this occurs, VERY VERY annoying.
我一直试图让它工作很长时间,并且基本上已经尝试了所有建议的解决方案,但仍然无法让 Android 识别特定的文件扩展名。我有一个带有"*/*"
mimetype的意图过滤器,它似乎是唯一可用的东西,文件浏览器现在将我的应用程序列为打开文件的选项,但是我的应用程序现在显示为打开任何类型文件的选项,即使我已经使用 pathPattern 标记指定了特定的文件扩展名。到目前为止,即使我尝试查看/编辑联系人列表中的联系人,Android 也会询问我是否要使用我的应用程序查看联系人,这只是发生这种情况的众多情况之一,非常非常烦人。
Eventually I found this google groups post with a similar question to which an actual Android framework engineer replied. She explains that Android simply does not know anything about file-extensions, only MIME-types (https://groups.google.com/forum/#!topic/android-developers/a7qsSl3vQq0).
最终我找到了这个 google groups 的帖子,里面有一个类似的问题,一个实际的 Android 框架工程师回答了这个问题。她解释说,Android 根本不了解文件扩展名,只了解 MIME 类型(https://groups.google.com/forum/#!topic/android-developers/a7qsSl3vQq0)。
So from what I've seen, tried and read, Android simply cannot distinguish between file-extensions and the pathPattern tag is basicly a gigantic waste of time and energy. If you are fortunate enough to only need files of a certain mime-type (say text, video or audio), you can use an intent-filter with a mime-type. If you need a specific file-extension or a mime-type not known by Android however then you're out of luck.
因此,从我所看到、尝试和阅读的情况来看,Android 根本无法区分文件扩展名,而 pathPattern 标签基本上是对时间和精力的巨大浪费。如果你足够幸运只需要某种 mime 类型的文件(比如文本、视频或音频),你可以使用带有 mime 类型的意图过滤器。但是,如果您需要特定的文件扩展名或 Android 不知道的 mime 类型,那么您就不走运了。
If I'm wrong about any of this please tell me, so far I've read every post and tried every proposed solution I could find but none have worked.
如果我对此有任何错误,请告诉我,到目前为止,我已经阅读了所有帖子并尝试了我能找到的所有建议解决方案,但都没有奏效。
I could write another page or two about how common these kinds of things seem to be in Android and how screwed up the developer experience is, but I'll save you my angry rantings ;). Hope I saved someone some trouble.
我可以再写一两页关于这类事情在 Android 中似乎有多普遍以及开发者体验是多么糟糕,但我会为你省去我愤怒的咆哮 ;)。希望我为某人省了一些麻烦。
回答by Peter Newman
Markus Ressel is correct. Android 7.0 Nougat no longer permits file sharing between apps using a file URI. A content URI must be used. However, a content URI does not allow a file path to be shared, only a mime type. So you cannot use a content URI to associate your app with your own file extension.
Markus Ressel 是正确的。Android 7.0 Nougat 不再允许使用文件 URI 在应用之间共享文件。必须使用内容 URI。但是,内容 URI 不允许共享文件路径,只能共享 MIME 类型。因此,您不能使用内容 URI 将您的应用与您自己的文件扩展名相关联。
Drobpox has an interesting behavior on Android 7.0. When it meets an unknown file extension it appears to form a file URI intent but instead of launching the intent it calls the operating system to find out which apps can accept the intent. If there is only one app that can accept that file URI it then sends an explicit content URI directly to that app. So to work with Dropbox you do not need to change the intent filters on your app. It does not require a content URI intent filter. Just make sure the app can receive a content URI and your app with your own file extension will work with Dropbox just like it did before Android 7.0.
Drobpox 在 Android 7.0 上有一个有趣的行为。当它遇到未知的文件扩展名时,它似乎会形成一个文件 URI 意图,但它不会启动该意图,而是调用操作系统来找出哪些应用程序可以接受该意图。如果只有一个应用程序可以接受该文件 URI,则它会直接向该应用程序发送显式内容 URI。因此,要使用 Dropbox,您无需更改应用程序上的意图过滤器。它不需要内容 URI 意图过滤器。只需确保该应用程序可以接收内容 URI,并且您的具有您自己的文件扩展名的应用程序可以像 Android 7.0 之前一样在 Dropbox 中使用。
Here is an example of my file loading code modified to accept a content URI:
这是我的文件加载代码修改为接受内容 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);
...
}
}
回答by Sumit
Those who having issue with other File Manager\Explorer apps, as @yuku and @phyrum-tea answered
那些对其他文件管理器\资源管理器应用程序有问题的人,@yuku 和 @phyrum-tea 回答
This works with LG default file manager app
这适用于 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>
but could not work with ES File Explorer and other File Managers so i added
但无法与 ES 文件资源管理器和其他文件管理器一起使用,所以我添加了
android:mimeType="*/*"
then it works with ES Explorer but LG file manager could not detect file type so my solution is
然后它可以与 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>
回答by Sofi Software LLC
Content URI ftw, and with the intent filter in the manifest... if your files have a custom extension .xyz, add a matching mime type:
内容 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>
Some apps such as email seem to convert the extension into a mime type. Now I can click on the attachment in email and have it open in my app.
某些应用程序(例如电子邮件)似乎将扩展程序转换为 mime 类型。现在我可以点击电子邮件中的附件并在我的应用程序中打开它。
回答by Arjun Othayoth
You try this it will help for you.Instead of pdf you can use other extensions also. First you have to add read external storage permission in androidmanifest.xmlfile.
你试试这个它会对你有帮助。你也可以使用其他扩展名而不是 pdf。首先,您必须在androidmanifest.xml文件中添加读取外部存储权限。
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Then in the androidmanifestfile in the Activity tag, you add an intent-filter as shown below.
然后在Activity 标签的androidmanifest文件中,添加一个意图过滤器,如下所示。
<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>
Finally in your code, you get path of the pdf file as shown below:
最后在您的代码中,您将获得 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();
}