Фильтр намерений Android для определенного расширения файла?
Я хочу иметь возможность загрузить файл с определенным расширением из сети и передать его в мое приложение для его решения, но я не смог выяснить фильтр намерений. Тип файла не включен в mimetypes, и я попытался использовать
<data android:path="*.ext" />
но я не мог заставить это работать.
13 ответов
Вот как я определил свою активность в своем AndroidManifest.xml, чтобы заставить это работать.
<activity android:name="com.keepassdroid.PasswordActivity">
<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:mimeType="*/*" />
<data android:pathPattern=".*\\.kdb" />
<data android:host="*" />
</intent-filter>
</activity>
scheme
из file
указывает, что это должно происходить при открытии локального файла (а не протокола, такого как HTTP).
mimeType
можно установить на \*/\*
соответствовать любому типу пантомимы.
pathPattern
где вы указываете, какое расширение вы хотите сопоставить (в этом примере .kdb
). .*
в начале соответствует любой последовательности символов. Эти строки требуют двойного экранирования, поэтому \\\\.
соответствует буквальному периоду. Затем вы заканчиваете с расширением вашего файла. Одно предостережение с pathPattern в том, что .*
не жадное совпадение, как вы ожидаете, если бы это было регулярное выражение. Этот шаблон не будет соответствовать путям, которые содержат .
перед .kdb
, Для более подробного обсуждения этого вопроса и обходного пути смотрите здесь
Наконец, согласно документации Android, оба host
а также scheme
атрибуты необходимы для pathPattern
атрибут для работы, так что просто установите его в подстановочный знак, чтобы соответствовать чему-либо.
Теперь, если вы выберете .kdb
файл в приложении, таком как Linda File Manager, мое приложение отображается как опция. Я должен отметить, что это само по себе не позволяет загружать этот тип файла в браузер, так как он регистрируется только в схеме файла. Наличие на телефоне приложения, такого как Linda File Manager, самоустойчиво, что позволяет загружать файлы любого типа.
В этой теме много дезинформации, не в последнюю очередь из собственной документации Google. Лучшая, и, учитывая странную логику, возможно, единственная настоящая документация - это исходный код.
Реализация фильтра намерений имеет логику, которая почти не поддается описанию. Код синтаксического анализатора является другой важной частью головоломки.
Следующие фильтры очень близки к разумному поведению. Паттерны путей действительно применимы для намерений "файловой" схемы.
Глобальное совпадение с типом mime-типов будет соответствовать всем типам, если совпадает расширение файла. Это не идеально, но это единственный способ сопоставить поведение файловых менеджеров, таких как ES File Explorer, и он ограничен намерениями, где совпадает расширение URI/ файла.
Я не включил здесь другие схемы, такие как http, но они, вероятно, будут хорошо работать на всех этих фильтрах.
Нечетной схемой является "контент", для которого расширение недоступно для фильтра. Но пока провайдер указывает ваш тип MIME (например, Gmail передаст тип MIME для вложения беспрепятственно), фильтр будет соответствовать.
Нужно быть в курсе:
- Имейте в виду, что ничто не ведет себя согласованно в фильтрах, это лабиринт случайных случаев и рассматривает нарушение принципа наименьшего удивления как цель проекта. Ни один из алгоритмов сопоставления с образцом не имеет такой же синтаксис или поведение. Отсутствие поля иногда является подстановочным знаком, а иногда нет. Атрибуты внутри элемента данных иногда должны идти вместе, а иногда игнорировать группировку. Это действительно могло быть сделано лучше.
- Схема И хост должны быть указаны для соответствия правилам пути (в отличие от руководства Google API, в настоящее время).
- По крайней мере, ES File Explorer генерирует намерения с типом MIME "", который фильтруется совсем иначе, чем ноль, его невозможно сопоставить явно, и его можно сопоставить только с помощью рискованного фильтра "*/*".
- Фильтр "*/*" НЕ будет сопоставлять Intents с пустым MIME-типом - для этого конкретного случая требуется отдельный фильтр без MIME-типа вообще.
- Схема "содержимого" может соответствовать только типу MIME, поскольку исходное имя файла недоступно в намерении (по крайней мере, в Gmail).
- Группировка атрибутов в отдельных элементах "данных" (почти) не имеет отношения к интерпретации, за исключением конкретного хоста и порта, которые соединяются вместе. Все остальное не имеет конкретной связи внутри элемента "данные" или между элементами "данные".
Учитывая все это, вот пример с комментариями:
<!--
Capture content by MIME type, which is how Gmail broadcasts
attachment open requests. pathPattern and file extensions
are ignored, so the MIME type *MUST* be explicit, otherwise
we will match absolutely every file opened.
-->
<intent-filter
android:icon="@drawable/icon"
android:label="@string/app_name"
android:priority="50" >
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:mimeType="application/vnd.my-type" />
</intent-filter>
<!--
Capture file open requests (pathPattern is honoured) where no
MIME type is provided in the Intent. An Intent with a null
MIME type will never be matched by a filter with a set MIME
type, so we need a second intent-filter if we wish to also
match files with this extension and a non-null MIME type
(even if it is non-null but zero length).
-->
<intent-filter
android:icon="@drawable/icon"
android:label="@string/app_name"
android:priority="50" >
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:host="*" />
<!--
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:pathPattern=".*\\.my-ext" />
<data android:pathPattern=".*\\..*\\.my-ext" />
<data android:pathPattern=".*\\..*\\..*\\.my-ext" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.my-ext" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.my-ext" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.my-ext" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.my-ext" />
</intent-filter>
<!--
Capture file open requests (pathPattern is honoured) where a
(possibly blank) MIME type is provided in the Intent. This
filter may only be necessary for supporting ES File Explorer,
which has the probably buggy behaviour of using an Intent
with a MIME type that is set but zero-length. It's
impossible to match such a type except by using a global
wildcard.
-->
<intent-filter
android:icon="@drawable/icon"
android:label="@string/app_name"
android:priority="50" >
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:host="*" />
<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:pathPattern=".*\\.my-ext" />
<data android:pathPattern=".*\\..*\\.my-ext" />
<data android:pathPattern=".*\\..*\\..*\\.my-ext" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.my-ext" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.my-ext" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.my-ext" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.my-ext" />
</intent-filter>
Я должен признать, что простая задача открытия вложений из электронных писем и файлов из файловой системы на Android была одной из самых безумных в истории. Легко обрабатывать слишком много файлов или слишком мало. Но сделать это правильно очень сложно. Большинство решений, размещенных в stackru, не работали правильно для меня.
Мои требования были:
- мое приложение обрабатывает прикрепленные приложения
- мое приложение обрабатывает файлы в хранилище файлов, которые были сгенерированы моим приложением и имеют определенное расширение
Вероятно, лучший способ выполнить эту задачу - указать пользовательский тип MIME для ваших вложений. И вы, вероятно, также захотите иметь собственное расширение файла. Итак, скажем, что наше приложение называется "Cool App", и мы генерируем вложения, которые имеют ".cool" в конце.
Это самая близкая мне цель, и она работает... удовлетворительно.
<!-- Register to handle email attachments -->
<!-- WARNING: Do NOT use android:host="*" for these as they will not work properly -->
<intent-filter>
<!-- needed for properly formatted email messages -->
<data
android:scheme="content"
android:mimeType="application/vnd.coolapp"
android:pathPattern=".*\\.cool" />
<!-- needed for mangled email messages -->
<data
android:scheme="content"
android:mimeType="application/coolapp"
android:pathPattern=".*\\.cool" />
<!-- needed for mangled email messages -->
<data
android:scheme="content"
android:mimeType="application/octet-stream"
android:pathPattern=".*\\.cool" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
<!-- Register to handle file opening -->
<intent-filter>
<data android:scheme="file"
android:mimeType="*/*"
android:pathPattern=".*\\.cool"
android:host="*"/>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
Заметки:
pathPattern
кажется, более или менее игнорируется для вложений (при использованииandroid:scheme="content"
). Если кто-то заставит pathPattern отвечать только на определенные шаблоны, я был бы рад узнать, как это сделать.- Приложение Gmail отказывалось указывать мое приложение в списке, если я добавила
android:host="*"
приписывать. - Это, вероятно, все еще работает, если эти
intent-filter
блоки объединены, но я не проверял это. - Для обработки запросов из браузера при загрузке файла
android:scheme="http"
может быть использован. Обратите внимание, что некоторые браузеры могут испортитьandroid:mimeType
так что экспериментируйте сandroid:mimeType="*/*"
и проверьте в отладчике, что на самом деле пропущено, а затем ужесточите фильтрацию, чтобы не оказаться тем раздражающим приложением, которое обрабатывает все. - Определенные File Explorer также могут испортить MIME-типы для ваших файлов. Выше
intent-filter
был протестирован с приложением Samsung "Мои файлы" на Galaxy S3. FX Explorer по-прежнему отказывается правильно открывать файл, и я также заметил, что значок приложения не используется для файлов. Снова, если кто-то заставляет это работать, пожалуйста, прокомментируйте ниже.
Я надеюсь, что вы найдете это полезным, и вам не придется тратить дни на изучение всех возможных комбинаций. Есть возможности для улучшения, поэтому комментарии приветствуются.
Обновление 2020
Android перешел на URI контента и MIME-типы для фильтров намерений.
Проблема
URI контента не обязательно должен содержать расширение или имя файла, и он будет отличаться в разных приложениях, которые предоставляют контент / файл.
Вот несколько примеров URI контента из разных почтовых приложений для одного и того же вложения электронной почты:
Gmail -> content://com.google.android.gm.sapi/some_email@gmail.com/message_attachment_external/%23thread-a%3Ar332738858767305663/%23msg-a%3Ar-5439466788231005876/0.1?account_type=com.google&mimeType=application%2Foctet-stream&rendition=1
Outlook -> content://com.microsoft.office.outlook.fileprovider/outlookfile/data/data/com.microsoft.office.outlook/cache/file-download/file--2146063402/filename.customextention
Приложение электронной почты Samsung -> content://com.samsung.android.email.attachmentprovider/1/1/RAW
Как видите, все они разные и не гарантируют, что они содержат что-либо, связанное с вашим фактическим файлом. Таким образом, вы не можете использоватьandroid:pathPattern
как многие предложили.
Обходное решение для вложений электронной почты
<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="content"/>
<data android:host="*"/>
<!-- Required for Gmail and Samsung Email App -->
<data android:mimeType="application/octet-stream"/>
<!-- Required for Outlook -->
<data android:mimeType="application/my-custom-extension"/>
</intent-filter>
В ходе тестирования я обнаружил MIME-типы, которые используют Gmail, Outlook и Samsung Email, и добавил их в свой фильтр намерений.
Предостережения / Проблемы
Я обнаружил, что с помощью приведенного выше решения, если я открою любой файл двоичного типа, он автоматически запустит мое приложение. Я обработал это в своей деятельности, отображая состояние сбоя, если мы не могли проанализировать файл. Я подумал, что это было довольно редкое событие, так что это было бы приемлемо.
Я не мог найти способ запустить свое приложение через файловый браузер без добавления
<data android:mimeType="*/*"/>
к моему фильтру намерений. Я не мог использовать это, потому что оно запускало бы мое приложение всякий раз, когда пользователь щелкал любой файл на своем телефоне (а не только файлы с расширением пользовательского файла). Я бы не рекомендовал добавлять это в ваш фильтр намерений.
Последние мысли
- В настоящее время нет элегантного решения для связывания вашего приложения с определенным типом расширения в Android. Это было лучшее, что я мог сделать в своей ситуации.
Ответ Брайана выше дал мне 90% пути туда. Чтобы закончить это, для типа пантомимы я использовал
android:mimeType="*/*"
Я подозреваю, что предыдущие авторы пытались опубликовать ту же самую деталь, но из-за недостаточного указания звездной косой черты как кода, stackru отображает ее как просто косую черту.
Я пытался заставить это работать целую вечность и попробовал в основном все предложенные решения и все еще не могу заставить Android распознавать определенные расширения файла. У меня есть интент-фильтр с "*/*"
mimetype- единственное, что работает, и файловые браузеры теперь отображают мое приложение в качестве опции для открытия файлов, однако мое приложение теперь отображается как опция для открытия ЛЮБОГО ВИДА файла, даже если я указал определенные расширения файлов, используя тег pathPattern. Это заходит так далеко, что даже когда я пытаюсь просмотреть / изменить контакт в моем списке контактов, Android спрашивает меня, хочу ли я использовать свое приложение для просмотра контакта, и это только одна из многих ситуаций, когда это происходит, ОЧЕНЬ ОЧЕНЬ раздражает.
В конце концов я нашел этот пост группы Google с похожим вопросом, на который ответил настоящий инженер по Android Framework. Она объясняет, что Android просто ничего не знает о расширениях файлов, только о MIME-типах ( https://groups.google.com/forum/).
Так что из того, что я видел, пробовал и читал, Android просто не может отличить расширения файлов, а тег pathPattern представляет собой гигантскую трату времени и энергии. Если вам достаточно повезло, что вам нужны только файлы определенного типа MIME (скажем, текст, видео или аудио), вы можете использовать фильтр намерений с MIME-типом. Если вам нужно конкретное расширение файла или mime-тип, неизвестный Android, то вам не повезло.
Если я ошибаюсь по этому поводу, пожалуйста, скажите мне, пока я прочитал все посты и попробовал каждое предложенное решение, которое смог найти, но ни одно из них не сработало.
Я мог бы написать еще одну или две страницы о том, как часто такие вещи встречаются в Android, и как испортил опыт разработчиков, но я спасу вас от моих злых разглагольствований;). Надеюсь, я спас кого-то от неприятностей.
Скорее, чем android:path
, пытаться android:mimeType
со значением типа MIME этого конкретного фрагмента содержимого. Также, android:path
не принимает подстановочные знаки - используйте android:pathPattern
для этого.
Когда Намерение встречает intent-filter
, эти intent-filter
требования: (представьте себе контрольный список).
- Любое соответствие
<action>
- Любое соответствие
<category>
- Любое соответствие
<data mimeType>
(легко исправить: "/") По выбору:
Любое соответствие
<data scheme>
(легко исправить:<data android:scheme="file" /> <data android:scheme="content" />
)Любое соответствие
<data host>
(легко исправить: "*")- Любое соответствие
<data pathPattern/etc.>
(например.*\\.0cc
)
Определение нескольких <data $type="">
элементы проверяют поле $type, если любое <data $type=>
соответствует Intent
,
Опуская mimeType ломает ваш intent-filter
даже если это кажется излишним. Опуская <data scheme/host/pathPattern>
заставляет ваш фильтр соответствовать всем.
https://f-droid.org/en/packages/de.k3b.android.intentintercept/ - это приложение, предназначенное для получения всех намерений и позволяющее проверить намерение. Я узнал, что нераспознанные расширения файлов, открытые через Simple File Manager, поставляются с типом MIME application/octet-stream
,
/questions/39301865/pathpattern-dlya-sopostavleniya-s-rasshireniem-fajla-ne-rabotaet-esli-v-drugom-meste-imeni-fajla-suschestvuet-tochka/39301872#39301872 сообщает, что <data pathPattern=>
.*xyz
прерывает на первом x
он видит, и сразу же потерпит неудачу, если не последует yz
, Так /sdcard/.hidden/foo.0cc
не пройдет .*\\.0cc
если не попробуешь .*\\..*\\.0cc
вместо.
- Я не проверял, нужен ли этот обходной путь.
Конечный результат:
<activity android:name=".Ft2NsfActivity">
<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:host="*" />
<data android:pathPattern=".*\\.ftm"/>
<data android:pathPattern=".*\\..*\\.ftm"/>
<data android:pathPattern=".*\\..*\\..*\\.ftm"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\.ftm"/>
<data android:pathPattern=".*\\.0cc"/>
<data android:pathPattern=".*\\..*\\.0cc"/>
<data android:pathPattern=".*\\..*\\..*\\.0cc"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\.0cc"/>
<data android:mimeType="*/*" />
</intent-filter>
</activity>
Ничто из вышеперечисленного не работает должным образом для действий VIEW или SEND, если суффикс не зарегистрирован с типом MIME в системной базе данных Android = широкая MIME. Единственные настройки, которые я обнаружил, что огонь для указанного суффикса включают android:mimeType="*/*"
, но затем действие запускается для всех файлов. Ясно НЕ то, что вы хотите!
Я не могу найти правильного решения без добавления MIME и суффикса в базу данных Android 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:mimeType="*/*" />
<data android:pathPattern="*.*\\.kdb" />
</intent-filter>
Я боролся с этим довольно много для собственного расширения файла, сам. После долгих поисков я обнаружил эту веб-страницу, где автор обнаружил, что класс Android PatternMatcher (который используется для сопоставления pathPattern в Intent-Filters) имеет неожиданное поведение, когда ваш путь содержит первый символ вашего шаблона сопоставления в другом месте пути. (например, если вы пытаетесь сопоставить "*.xyz", класс patternMatcher останавливается, если в вашем пути есть "x"). Вот что он нашел для обходного пути, и работал для меня, хотя это немного взломать:
PatternMatcher используется для pathPattern в IntentFilter. Но алгоритм PatternMatcher для меня довольно странный. Вот алгоритм Android PatternMatcher.
Если в середине строки есть "следующий символ" шаблона ". *", PatternMatcher останавливает цикл в этой точке. (См. PatternMatcher.java для платформы Android.)
Ex. Строка: "это моя привязанность" шаблон: ".att.". Android PatternMatcher вводит цикл, чтобы соответствовать '.'pattern, пока не встретится следующий символ pattern (в этом примере' a ') Итак,'.'совпадающий цикл останавливается на индексе 8 - ' между 'является' и 'my'. Поэтому результат этого совпадения возвращает "ложь".
Довольно странно, не правда ли. Чтобы обойти это - фактически уменьшить вероятность - разработчик должен использовать раздражающий тупой pathPattern.
Ex. Цель: соответствие пути URI, который включает в себя "сообщение".
<intent-filter>
...
<data android:pathPattern=".*message.*" />
<data android:pathPattern=".*m.*message.*" />
<data android:pathPattern=".*m.*m.*message.*" />
<data android:pathPattern=".*m.*m.*m.*message.*" />
<data android:pathPattern=".*m.*m.*m.*m.*message.*" />
...
</intent-filter>
Это особенно выдается при сопоставлении с пользовательским расширением файла.
На Android 4 правила стали более строгими, чем раньше. Использование:
<data
android:host=""
android:mimeType="*/*"
android:pathPattern=".*\\.ext"
android:scheme="file"
></data>
Используя фильтр, как показано ниже, откройте браузер, почту и браузер файлов (протестировано). ПРИМЕЧАНИЕ. Пожалуйста, не объединяйте два фильтра, чтобы браузер игнорировал ваше приложение (протестировано).
<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" android:pathPattern=".*\\.ext" android:mimeType="application/*"/>
<data android:scheme="content" android:pathPattern=".*\\.ext" android:mimeType="application/*"/>
</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="http"
android:host="*"
android:pathPattern=".*\\.ext" />
<data android:scheme="https"
android:host="*"
android:pathPattern=".*\\.ext" />
<data android:scheme="ftp"
android:host="*"
android:pathPattern=".*\\.ext" />
</intent-filter>
Если вы хотите, чтобы файлы открывались непосредственно из Gmail, Dropbox или любого из встроенных файловых инструментов Android, используйте следующий код (delete 'android:host="*"', который сделал файл недоступным для 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:scheme="content" android:pathPattern=".*\\.kdb"
android:mimeType="application/octet-stream"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="file" android:mimeType="*/*"
android:pathPattern=".*\\.kdb"/>
</intent-filter>
Фильтр данных должен быть записан в одном операторе согласно Android версии 4.x
Прочтите этот https://developer.android.com/training/sharing/receive, это может помочь начать работу. затем в файле манифеста попробуйте фильтр намерений ниже при регистрации получателя в открываемой активности
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="content"/>
<category android:name="android.intent.category.BROWSABLE"/>
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/your_extension" />
</intent-filter>
убедитесь, что расширение предоставляется без "." в этом