Фільтр намірів Android: пов’язати додаток із розширенням файлу


92

У мене є власний тип файлу / розширення, з яким я хочу пов’язати свою програму.

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

Ну, це не працює. Що я зробив неправильно? Я просто хочу оголосити власний тип файлу.


Додайте android: label = "@ string / app_name" у фільтр намірів
Прашант,

Відповіді:


116

Вам потрібно кілька фільтрів намірів, щоб вирішити різну ситуацію, з якою ви хочете впоратися.

Приклад 1, обробка http-запитів без mimeypes:

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

Я думав, що хост = "*" можна пропустити, але він став занадто широким.
Phyrum Tea

@Phyrum Tea: Чи є спосіб пов’язати розширення файлу з ІНШИМ додатком за допомогою мого додатка? Мій додаток генерує файли .list, які я хочу сказати android, що це текстові файли, і відкрити їх за допомогою текстового редактора (мій додаток НЕ є текстовим редактором).
Luis A. Florit

@ LuisA.Florit Ви намагалися викликати свої файли * .txt? Це може спрацювати.
Gavriel

спеціальне розширення не працює з MimeType. Якщо я надаю тип mime, як / мій додаток відображається, коли я натискаю також сповіщення Gmail.
madan V

1
Не працює .. Мені набридло зараз пошуки за останні 2 дні не працювали. я використовую android 9
Ахмад Арслан,

49

Інші рішення не працювали надійно для мене, поки я не додав:

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>

1
Оце Так! це мені допомогло. Здається, документація для android неправильна. Усі (схема, хост, шлях [Шаблон], mimeType) повинні бути оголошені як
працюючі

Ви повинні мати як правило, так mimeTypeі одне, якщо ви хочете бути завершеним. Див. Developer.android.com/guide/components/…
Andrew Sun

1
Що робити, якщо ви хочете відкрити свою файлову програму з Gmail?
Ігор Ганапольський

31

Відповіді, надані Phyrum Tea та yuku, вже дуже інформативні.

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

Від офіційного змін в Android 7.0 :

Для програм, орієнтованих на Android 7.0, фреймворк Android застосовує політику API StrictMode, яка забороняє виставляти 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, тоді як у старих версіях все було добре. Сподіваюся, це комусь допоможе.


Без цієї підказки та додаткової інформації я не міг змусити програму зареєструватися у дослідниках файлів. Це має вирішальне значення, і його слід проголосувати
u2 все

Ви врятували день! Великий tnx. Це повинна бути прийнята відповідь!
Андріс

⚠️ Якщо ви спробуєте отримати файл, наданий за допомогою схеми вмісту, наприклад File(uri.path), він No such file or directoryзазнає аварії через - доведеться обробляти цей сценарій по-іншому під час оновлення для підтримки Nougat +!
Йорданія H

19

Мої висновки:

Вам потрібно кілька фільтрів, щоб мати справу з різними способами отримання файлу. тобто за допомогою вкладення 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>
  • ЖУРНАЛ: Виявлено намір вмісту: 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>
  • ЖУРНАЛ: Виявлено намір файлу: android.intent.action.VIEW: файл: ///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());
    }
}

Діяльність повинна бути запусковою або основним? Тому що я спробував фільтр вкладень Gmail, і він не працює.
Аміт

15

pathPattern

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

не працює, якщо шлях до файлу містить одну або кілька крапок перед ".pdf".

Це буде працювати:

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

Додайте більше, якщо хочете підтримати більше крапок.


1
На даний момент це єдиний варіант переходу до підпапок
LokiDroid

Я використовую той же код , але IDE дати мені попередження, побачити це питання stackoverflow.com/questions/35833776 / ...
AndreaF

5

Я намагався змусити це працювати впродовж століть і в основному спробував усі запропоновані рішення, і все ще не можу змусити Android розпізнавати конкретні розширення файлів. У мене є фільтр намірів із"*/*" mimeype, який єдине, що, здається, працює, і браузери файлів тепер перелічують мою програму як опцію для відкриття файлів, однак моя програма тепер відображається як опція для відкриття БУДЬ-ЯКОГО ВИДУ файлу, хоча Я вказав конкретні розширення файлів, використовуючи тег pathPattern. Це заходить так далеко, що навіть коли я намагаюся переглянути / відредагувати контакт у своєму списку контактів, Android запитує мене, чи хочу я використовувати свою програму для перегляду контакту, і це лише одна з багатьох ситуацій, коли це трапляється, ДУЖЕ ДУЖО дратує.

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

Отже, від того, що я бачив, пробував і читав, Android просто не може розрізнити розширення файлів, а тег pathPattern - це в основному гігантська втрата часу та енергії. Якщо вам пощастило, що вам потрібні лише файли певного типу mime (скажімо, текст, відео чи аудіо), ви можете використовувати фільтр намірів із mime-типом. Якщо вам потрібно конкретне розширення файлу або тип mime, не відомий Android, проте вам не пощастило.

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

Я міг би написати ще одну сторінку чи дві про те, наскільки подібні речі, здається, є в Android, і про те, наскільки заплутаний досвід розробників, але я врятую вам свої гнівні сказки;). Сподіваюся, я врятував комусь якусь біду.


5

Маркус Рессель правильний. 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);
        ...
    }
}

2

Спробуйте це, це допоможе вам. Замість 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();

        }

Для цього Intent.ACTION_VIEW, не вдавшись захопити шлях до файлу, він спричиняє помилку: java.io.FileNotFoundException: Дозвіл відмовлено. Це лише з деяких конкретних програм, не кожен раз. будь-які рішення?
Хардік Джоші

@HardikJoshi Додатки можуть не встановлювати дозвіл GRANT_READ_URI_PERMISSION.
Mira_Cole

1

Спробуйте додати

<action android:name="android.intent.action.VIEW"/>

Тепер, коли працюють із типовими фабричними типами файлів, такими як application / pdf, як я можу оголосити свій власний тип файлу? І коли я кажу тип файлу, я маю на увазі mimeType;)
Тамаш

Який файл ви хочете, щоб цей mimeype ловив? Крім того, цей файл відкривається з браузера або файлового менеджера чи надсилається з іншої програми, яку ви створили?
magaio

може бути у браузері, поштовому клієнті, файловому менеджері чи де-небудь ще ... Або власне додаток ofc :) розширення файлу є власним, вказаним клієнтом.
Тамаш

Ну, я все ще застряг ... хтось може допомогти, будь ласка?
Тамаш

@Tamas чи всі ви це сортували. Я на цьому застряг!
StuStirling

1

Для вкладення 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, скільки потрібно. Мені потрібні лише ті, що стосуються мого проекту.


1
         <!--
            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, немає жодної можливості обійти його


Це буде обробляти кожен окремий файл, надісланий користувачеві, а не тільки .teamz
Ігор Ганапольський

1

Відповідали ті, хто має проблеми з іншими програмами 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>

1

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>

Деякі програми, такі як електронна пошта, перетворюють розширення на тип mime. Тепер я можу натиснути на вкладення в електронній пошті та відкрити його у своєму додатку.


1

Прочитайте файл відкриття в kotlin:

private fun checkFileOpening(intent: Intent) {
    if (intent.action == Intent.ACTION_VIEW && (intent.scheme == ContentResolver.SCHEME_FILE
                    || intent.scheme == ContentResolver.SCHEME_CONTENT)) {

        val text = intent.data?.let {
            contentResolver.openInputStream(it)?.bufferedReader()?.use(BufferedReader::readText) 
        }
    }
}

-1

Помістіть цей фільтр намірів всередину тегу активності в маніфесті, який ви хочете відкрити, торкаючись файлу:

<intent-filter android:priority="999">
    <action android:name="android.intent.action.VIEW" />

    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <category android:name="android.intent.category.OPENABLE" />

    <data android:host="*" />
    <data android:mimeType="application/octet-stream" />
    <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.yourextension" />
    <data android:pathPattern=".*\\..*\\..*\\..*\\.yourextension" />
    <data android:pathPattern=".*\\..*\\..*\\.yourextension" />
    <data android:pathPattern=".*\\..*\\.yourextension" />
    <data android:pathPattern=".*\\.yourextension" />
    <data android:scheme="content" />
</intent-filter>

-1

// Я спробував цей код. І це працює добре. Ви можете використовувати цей код для прийняття PDF-файлу.

<intent-filter>
   <action android:name="android.intent.action.SEND" />
   <category android:name="android.intent.category.DEFAULT" />
   <data android:mimeType="application/pdf" />
   <data android:pathPattern=".*\\.pdf" />
   <data android:pathPattern=".*\\..*\\.pdf" />
   <data android:pathPattern=".*\\..*\\..*\\.pdf" />
   <data android:pathPattern=".*\\..*\\..*\\..*\\.pdf" />
</intent-filter>
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.