Android intent filter: связать приложение с расширением файла


У меня есть пользовательский тип файла/расширение, с которым я хочу связать свое приложение.

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

Ну, это не работает. Что я сделал не так? Я просто хочу объявить свой собственный тип файла.

12 80

12 ответов:

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

Пример 1, обрабатывать http-запросы без 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: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.МРЗ. И я извлек его из вложения 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 attachement 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="content" />
            <data android:mimeType="application/octet-stream" />
        </intent-filter>
  • LOG: Content intent detected: android.намерение.действие.Вид : content://gmail-ls/l.foul@gmail.com/messages/2950/attachments/0.1/BEST/false : application / octet-stream: new.mrz

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.намерение.действие.Просмотр: файл:///storage / sdcard0 / My%20Documents / new.mrz: null : новый.mrz

HTTP filter:

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

The pathPattern

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

не работает, если путь к файлу содержит одну или несколько точек до ".документ PDF."

Это будет работать:

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

добавить больше, если вы хотите поддерживать больше точек.

ответы, данные Phyrum Tea и юкку уже очень информативным.

хочу добавить, что начиная с Android 7.0 Нуга есть изменения в способе обмена файлами между приложениями обрабатывается:

официальных Android 7.0 Изменения:

для приложений, ориентированных на Android 7.0, платформа Android обеспечивает Политика API StrictMode, которая запрещает предоставление файла: / / URIs внешний ваше приложение. Если намерение, содержащее URI файла, покидает ваше приложение, приложение сбой с исключением FileUriExposedException.

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

Маркус Ресел прав. 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 спрашивает меня, хочу ли я использовать свое приложение для просмотра контакта, и это только одна из многих ситуаций, когда это происходит, очень очень раздражает.

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

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

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

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

для вложения 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, сколько необходимо. Они мне нужны только для моего проекта.

те, кто имеет проблемы с другими приложениями File Manager\Explorer, как @yuku и @phyrum-tea answered

это работает с приложением LG file manager по умолчанию

     <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 file manager не может определить тип файла, поэтому мое решение

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

content URI ftw, и с фильтром intent в манифесте... если ваши файлы имеют пользовательское расширение .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. Теперь я могу нажать на вложение в электронной почте и открыть его в своем приложении.