Фильтр намерений Android для определенного расширения файла? - PullRequest
86 голосов
/ 14 ноября 2009

Я хочу иметь возможность загрузить файл с определенным расширением из сети и передать его в мое приложение для его обработки, но я не смог выяснить фильтр намерений. Тип файла не включен в mimetypes, и я попытался использовать

<data android:path="*.ext" />

но я не мог заставить это работать.

Ответы [ 13 ]

114 голосов
/ 14 января 2010

Вот как я определил свою активность в своем 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, самоустойчиво и позволяет загружать файлы любого типа.

25 голосов
/ 24 июня 2015

В этой теме много дезинформации, не в последнюю очередь из собственной документации Google. Лучшая, и, учитывая странную логику, возможно, единственная настоящая документация - это исходный код.

Реализация фильтра намерений имеет логику, которая почти не поддается описанию. код синтаксического анализатора является другой важной частью головоломки.

Следующие фильтры очень близки к разумному поведению. Шаблоны путей действительно применяются для намерений "файловой" схемы.

Глобальное соответствие шаблону типа mime будет соответствовать всем типам, если совпадает расширение файла. Это не идеально, но это единственный способ сопоставить поведение файловых менеджеров, таких как ES File Explorer, и оно ограничено намерениями, где совпадает расширение URI / файла.

Я не включил здесь другие схемы, такие как «http», но они, вероятно, будут хорошо работать на всех этих фильтрах.

Странная схема out - это «content», расширение которого недоступно для фильтра. Но пока поставщик указывает ваш тип MIME (например, Gmail передаст тип MIME для вложения беспрепятственно), фильтр будет соответствовать.

Нужно быть в курсе:

  1. Имейте в виду, что ничто не ведет себя согласованно в фильтрах, это лабиринт особых случаев и рассматривает нарушение принципа наименьшего удивления как цель проекта. Ни один из алгоритмов сопоставления с образцом не придерживается того же синтаксиса или поведения. Отсутствие поля иногда является подстановочным знаком, а иногда нет. Атрибуты внутри элемента данных иногда должны идти вместе, а иногда игнорировать группировку. Это действительно могло быть сделано лучше.
  2. Схема И хост должны быть указаны для соответствия правилам пути (в отличие от руководства Google API в настоящее время).
  3. По крайней мере ES File Explorer генерирует намерения с типом MIME "", который фильтруется совсем иначе, чем ноль, его невозможно сопоставить явно, и может быть сопоставлен только рискованным фильтром "* / *".
  4. Фильтр "* / *" НЕ будет сопоставлять Intents с нулевым MIME-типом - для этого конкретного случая требуется отдельный фильтр без MIME-типа вообще.
  5. Схема «содержимого» может соответствовать только типу MIME, поскольку исходное имя файла в намерении недоступно (по крайней мере, в Gmail).
  6. Группировка атрибутов в отдельных элементах «данных» (почти) не имеет отношения к интерпретации, за исключением конкретного хоста и порта, которые соединяются вместе. Все остальное не имеет конкретной связи внутри элемента «данные» или между элементами «данные».

Имея все это в виду, вот пример с комментариями:

<!--
     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>
23 голосов
/ 16 октября 2014

Я должен признать, что простая задача открытия вложений из электронных писем и файлов из файловой системы на Android была одной из самых безумных в истории. Легко обрабатывать слишком много файлов или слишком мало. Но сделать это правильно очень сложно. Большинство решений, размещенных в stackoverflow, не работали правильно для меня.

Мои требования были:

  • мои приложения обрабатывают вложенные файлы в моем приложении
  • мои файлы приложения хранят в хранилище файлов, которые были сгенерированы моим приложением, и имеют определенное расширение

Вероятно, лучший способ выполнить эту задачу - указать собственный тип 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="*/*" и проверьте в отладчике, что на самом деле прошло, а затем ужесточите фильтрацию, чтобы не оказаться тем раздражающим приложением, которое обрабатывает все .
  • Некоторые файловые обозреватели также могут испортить MIME-типы для ваших файлов. Выше intent-filter был протестирован с приложением Samsung "Мои файлы" на Galaxy S3. FX Explorer по-прежнему отказывается правильно открывать файл, и я также заметил, что значок приложения не используется для файлов. Снова, если кто-то заставляет это работать, пожалуйста, прокомментируйте ниже.

Я надеюсь, что вы найдете это полезным, и вам не придется тратить дни на изучение всех возможных комбинаций. Есть возможности для улучшения, поэтому комментарии приветствуются.

10 голосов
/ 24 мая 2011

Ответ Брайана выше дал мне 90% пути туда. Чтобы закончить это, для типа пантомимы я использовал

android:mimeType="*/*"

Я подозреваю, что предыдущие авторы пытались опубликовать ту же самую деталь, но из-за недостаточного указания звездной косой черты в качестве кода stackoverflow отображает ее как просто косую черту.

8 голосов
/ 14 ноября 2009

Вместо android:path, попробуйте android:mimeType, со значением типа MIME этого конкретного фрагмента содержимого. Кроме того, android:path не принимает подстановочные знаки - для этого используйте android:pathPattern.

4 голосов
/ 29 октября 2014

Я пытался заставить это работать целую вечность и попробовал в основном все предложенные решения и все еще не могу заставить Android распознавать определенные расширения файла. У меня есть фильтр намерений с типом "*/*" mimetype, который, кажется, работает единственно, и браузеры файлов теперь отображают мое приложение в качестве опции для открытия файлов, однако мое приложение теперь отображается как опция для открытия ЛЮБОГО ВИДА файл, хотя я указал конкретные расширения файлов с помощью тега pathPattern. Это заходит так далеко, что даже когда я пытаюсь просмотреть / изменить контакт в моем списке контактов, Android спрашивает меня, хочу ли я использовать свое приложение для просмотра контакта, и это только одна из многих ситуаций, когда это происходит, ОЧЕНЬ ОЧЕНЬ раздражает.

В конце концов я нашел этот пост группы Google с похожим вопросом, на который ответил настоящий инженер по Android Framework. Она объясняет, что андроид просто не знает ничего о расширениях файлов, только MIME-типы (https://groups.google.com/forum/#!topic/android-developers/a7qsSl3vQq0).

Так что из того, что я видел, пробовал и читал, Android просто не может отличить расширения файлов, а тег pathPattern - это, по сути, гигантская трата времени и энергии. Если вам достаточно повезло, что вам нужны только файлы определенного типа MIME (скажем, текст, видео или аудио), вы можете использовать фильтр намерений с MIME-типом. Если вам нужно конкретное расширение файла или mime-тип, неизвестный Android, то вам не повезло.

Если я ошибаюсь по этому поводу, пожалуйста, сообщите мне, пока я прочитал все посты и попробовал каждое предлагаемое решение, которое смог найти, но ни одно не помогло.

Я мог бы написать еще одну или две страницы о том, как часто встречаются подобные вещи в Android и как испортился опыт разработчика, но я спасу вас от моих злых разглагольствий;). Надеюсь, я спас кого-то от неприятностей.

3 голосов
/ 26 апреля 2014

Я с этим довольно долго боролся за собственное расширение файла. После долгих поисков я обнаружил эту веб-страницу , где автор обнаружил, что класс Android patternMatcher (который используется для сопоставления pathPattern в Intent-Filters) имеет неожиданное поведение, когда ваш путь содержит первый символ вашего сопоставлять шаблон в другом месте пути (например, если вы пытаетесь сопоставить «* .xyz», класс patternMatcher останавливается, если в вашем пути есть «x»). Вот что он нашел для обходного пути и работал для меня, хотя это немного хакерство:

PatternMatcher используется для pathPattern в IntentFilter Но, Алгоритм PatternMatcher для меня довольно странный. Вот алгоритм Android PatternMatcher.

Если в середине строки есть шаблон «следующий символ». * *, PatternMatcher останавливает цикл в этой точке. (См. PatternMatcher.java из Android Framework.)

Ex. строка: шаблон "это моя привязанность": ". att. ". Android PatternMatcher вводит цикл, чтобы соответствовать шаблону. , пока не встретится следующий символ шаблона (в данном примере 'a') Итак, '. ' цикл соответствия останавливается на индексе 8 - «между» и «моим». Поэтому результат этого match возвращает 'false'.

Довольно странно, не правда ли. Чтобы обойти это - на самом деле уменьшить возможность - разработчик должен использовать надоедливую тупую 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>

Это особенно выдается при сопоставлении с пользовательским расширением файла.

3 голосов
/ 18 декабря 2013

В Android 4 правила стали более строгими, чем раньше. Использование:

    <data
      android:host=""
      android:mimeType="*/*"
      android:pathPattern=".*\\.ext"
      android:scheme="file"
    ></data>
3 голосов
/ 20 января 2013

Ничто из вышеперечисленного не работает должным образом для действий VIEW или SEND, если суффикс не зарегистрирован с типом MIME в системной базе данных Android = широкая MIME. Единственные настройки, которые я обнаружил, что запуск для указанного суффикса включает android:mimeType="*/*", но затем действие запускается для ВСЕХ файлов. Ясно, НЕ то, что вы хотите!

Я не могу найти правильного решения, не добавив mime и суффикс в базу данных mime Android, но пока я не нашел способа сделать это. Если кто-нибудь знает, указатель будет потрясающим.

3 голосов
/ 23 июля 2011

Ответ Брайана очень близок, но вот чистый и безошибочный способ вызвать ваше приложение при попытке открыть файл с собственным расширением (без необходимости схемы или хоста):

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