Фильтр намерений Android: связать приложение с расширением файла - PullRequest
88 голосов
/ 21 сентября 2010

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

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

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

Ответы [ 16 ]

112 голосов
/ 24 февраля 2011

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

Пример 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>

Обработка с миметипами, где суффикс не имеет значения:

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

Другие решения не работали надежно для меня, пока я не добавил:

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>
21 голосов
/ 06 октября 2016

Ответы Phyrum Tea и yuku уже очень информативны.

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

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

Для приложений, ориентированных на Android 7.0, платформа Android обеспечивает Политика API StrictMode, которая запрещает выставлять file: // URI вне ваше приложение. Если намерение, содержащее файловый 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, в то время как на старых версиях все было хорошо. Надеюсь, это кому-нибудь поможет.

19 голосов
/ 25 сентября 2014

Мои выводы:

Вам нужно несколько фильтров, чтобы иметь дело с различными способами получения файла. то есть, приложением 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>
  • LOG: обнаружение намерения содержимого: 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>
  • LOG: обнаружена цель файла: 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());
    }
}
15 голосов
/ 28 августа 2012

pathPattern

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

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

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

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

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

4 голосов
/ 12 июня 2017

Маркус Ресел верен.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);
        ...
    }
}
3 голосов
/ 29 октября 2014

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

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

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

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

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

2 голосов
/ 21 сентября 2010

Попробуйте добавить

<action android:name="android.intent.action.VIEW"/>
1 голос
/ 26 ноября 2018

Попробуйте, это поможет вам. Вместо pdf вы можете использовать и другие расширения.Сначала необходимо добавить разрешение на чтение из внешнего хранилища в файл androidmanifest.xml .

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Затем в файле androidmanifest в теге Activity добавьте намерение-фильтр, как показано ниже.

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

Наконец, в вашем коде вы получите путь к файлу 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();

        }
1 голос
/ 26 октября 2017

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

Это работает с приложением 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 File Explorer и другими файловыми менеджерами, поэтому я добавил

 android:mimeType="*/*"

, тогда он работает с 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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...