Filtro de intención de Android: asociar la aplicación con la extensión de archivo


92

Tengo un tipo / extensión de archivo personalizado con el que quiero asociar mi aplicación.

Hasta donde yo sé, el elemento de datos está hecho para este propósito, pero no puedo hacerlo funcionar. http://developer.android.com/guide/topics/manifest/data-element.html De acuerdo con los documentos, y muchas publicaciones en el foro, debería funcionar así:

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

Bueno, no funciona. ¿Qué hice mal? Simplemente quiero declarar mi propio tipo de archivo.


Agregue android: label = "@ string / app_name" en el filtro de intención
Prashant

Respuestas:


116

Necesita varios filtros de intención para abordar las diferentes situaciones que desea manejar.

Ejemplo 1, maneje solicitudes http sin tipos MIME:

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

Manejar con mimetypes, donde el sufijo es irrelevante:

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

Manejar la intención desde una aplicación de navegador de archivos:

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

Pensé que el host = "*" podría omitirse, pero comenzó a ser demasiado amplio.
Phyrum Tea

@Phyrum Tea: ¿Hay alguna forma de asociar una extensión de archivo a OTRA aplicación usando mi aplicación? Mi aplicación genera archivos .list que quiero decirle a Android que son archivos de texto y que los abra con un editor de texto (mi aplicación NO es un editor de texto).
Luis A. Florit

@ LuisA.Florit ¿intentaste llamar a tus archivos * .txt? Podría funcionar.
Gavriel

La extensión personalizada no funciona sin MimeType. Si doy mimetype como / mi aplicación se muestra cuando hago clic en la notificación de Gmail también.
madan V

1
No funciona ... Estoy harto de buscar en los últimos 2 días no funcionó. Estoy usando Android 9
Ahmad Arslan

49

Las otras soluciones no funcionaron de manera confiable para mí hasta que agregué:

android:mimeType="*/*" 

Antes de eso funcionaba en algunas aplicaciones, en otras no ...

solución completa para mí:

<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
¡Guauu! esto me ayudó. Parece que la documentación de Android es incorrecta. Todo (esquema, host, ruta [Patrón], mimeType) debe declararse para funcionar
Gavriel

Deberías tanto una norma con mimeTypey otra sin él, si quieres ser completa. Ver developer.android.com/guide/components/…
Andrew Sun

1
¿Qué sucede si desea abrir su aplicación de archivos desde Gmail?
IgorGanapolsky

31

Las respuestas dadas por Phyrum Tea y yuku son muy informativas.

Quiero agregar eso comenzando con Android 7.0 Nougat hay un cambio en la forma en que se maneja el intercambio de archivos entre aplicaciones:

De los cambios oficiales de Android 7.0 :

Para las aplicaciones orientadas a Android 7.0, el marco de Android aplica la política de API StrictMode que prohíbe exponer archivos: // URI fuera de su aplicación. Si una intención que contiene un URI de archivo sale de su aplicación, la aplicación falla con una excepción FileUriExposedException.

Para compartir archivos entre aplicaciones, debe enviar un contenido: // URI y otorgar un permiso de acceso temporal en el URI. La forma más sencilla de conceder este permiso es mediante la clase FileProvider. Para obtener más información sobre permisos y uso compartido de archivos, consulte Compartir archivos.

Si tiene su propio archivo personalizado que termina sin un específico mime-type(o supongo que incluso con uno), es posible que deba agregar un segundo schemevalor para intent-filterque funcione FileProviderstambién.

Ejemplo:

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

Lo importante aquí es la adición de

<data android:scheme="content" />

al filtro.

Me costó mucho descubrir este pequeño cambio que impidió que mi actividad se abriera en dispositivos con Android 7.0 mientras todo estaba bien en versiones anteriores. Espero que esto ayude a alguien.


Sin este consejo e información adicional, no podría lograr que mi aplicación se registre con los exploradores de archivos. Esto es crucial y debe ser votado a favor
u2tall

¡Salvaste un día! Big tnx. ¡Esta debería ser una respuesta aceptada!
Andris

⚠️ Si intenta obtener el archivo proporcionado a través del esquema de contenido, como File(uri.path), se bloqueará debido a No such file or directory: ¡tendrá que manejar ese escenario de manera diferente al actualizar para admitir Nougat +!
Jordan H

19

Mis hallazgos:

Necesita varios filtros para lidiar con las diferentes formas de recuperar un archivo. es decir, por adjunto de Gmail, por explorador de archivos, por HTTP, por FTP ... Todos envían intenciones muy diferentes.

Y debe filtrar la intención que desencadena su actividad en su código de actividad.

Para el siguiente ejemplo, creé un tipo de archivo falso new.mrz. Y lo recuperé del archivo adjunto de gmail y del explorador de archivos.

Código de actividad agregado en 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!
            }
        }

Filtro de archivos adjuntos de 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>
  • REGISTRO: Intento de contenido detectado: android.intent.action.VIEW: content: //gmail-ls/l.foul@gmail.com/messages/2950/attachments/0.1/BEST/false: application / octet-stream: new. mrz

Filtro del explorador de archivos:

        <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>
  • REGISTRO: Intento de archivo detectado: android.intent.action.VIEW: file: ///storage/sdcard0/My%20Documents/new.mrz: null: new.mrz

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

Funciones privadas utilizadas anteriormente:

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

¿La actividad tiene que ser de tipo lanzador o principal? Porque probé el filtro de archivos adjuntos de Gmail y no funciona.
Amit

15

los pathPattern

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

no funciona si la ruta del archivo contiene uno o más puntos antes de ".pdf".

Esto funcionará:

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

Agregue más si desea admitir más puntos.


1
A partir de ahora, esta es la única opción para
acceder

Estoy usando el mismo código pero el IDE me da una advertencia, vea esta pregunta stackoverflow.com/questions/35833776/…
AndreaF

5

He intentado que esto funcione durante años y he probado básicamente todas las soluciones sugeridas y todavía no puedo hacer que Android reconozca extensiones de archivo específicas. Tengo un filtro de intención con un tipo "*/*"MIME que es lo único que parece funcionar y los navegadores de archivos ahora enumeran mi aplicación como una opción para abrir archivos, sin embargo, mi aplicación ahora se muestra como una opción para abrir CUALQUIER TIPO de archivo aunque He especificado extensiones de archivo específicas usando la etiqueta pathPattern. Esto va tan lejos que incluso cuando intento ver / editar un contacto en mi lista de contactos, Android me pregunta si quiero usar mi aplicación para ver el contacto, y esa es solo una de las muchas situaciones en las que esto ocurre, MUY MUY molesto.

Finalmente encontré esta publicación de grupos de Google con una pregunta similar a la que respondió un ingeniero de marco de Android real. Ella explica que Android simplemente no sabe nada sobre extensiones de archivo, solo tipos MIME ( https://groups.google.com/forum/#!topic/android-developers/a7qsSl3vQq0 ).

Entonces, por lo que he visto, probado y leído, Android simplemente no puede distinguir entre extensiones de archivo y la etiqueta pathPattern es básicamente una pérdida gigantesca de tiempo y energía. Si tiene la suerte de que solo necesite archivos de cierto tipo de mímica (por ejemplo, texto, vídeo o audio), puede utilizar un filtro de intención con un tipo de mímica. Sin embargo, si necesita una extensión de archivo específica o un tipo de mimo que Android no conoce, no tiene suerte.

Si me equivoco en algo de esto, por favor dígame, hasta ahora he leído todas las publicaciones y he probado todas las soluciones propuestas que pude encontrar, pero ninguna ha funcionado.

Podría escribir otra página o dos sobre lo común que parecen ser este tipo de cosas en Android y lo arruinada que es la experiencia del desarrollador, pero te ahorraré mis divagaciones;). Espero haberle ahorrado algunos problemas a alguien.


5

Markus Ressel tiene razón. Android 7.0 Nougat ya no permite compartir archivos entre aplicaciones usando un URI de archivo. Se debe utilizar un URI de contenido. Sin embargo, un URI de contenido no permite compartir una ruta de archivo, solo un tipo mime. Por lo tanto, no puede usar un URI de contenido para asociar su aplicación con su propia extensión de archivo.

Drobpox tiene un comportamiento interesante en Android 7.0. Cuando se encuentra con una extensión de archivo desconocida, parece formar una intención de URI de archivo, pero en lugar de iniciar la intención, llama al sistema operativo para averiguar qué aplicaciones pueden aceptar la intención. Si solo hay una aplicación que puede aceptar ese URI de archivo, envía un URI de contenido explícito directamente a esa aplicación. Entonces, para trabajar con Dropbox, no es necesario cambiar los filtros de intención en su aplicación. No requiere un filtro de intención de URI de contenido. Solo asegúrese de que la aplicación pueda recibir un URI de contenido y que su aplicación con su propia extensión de archivo funcionará con Dropbox como lo hacía antes de Android 7.0.

Aquí hay un ejemplo de mi código de carga de archivos modificado para aceptar un URI de contenido:

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

Si lo intenta, le ayudará. En lugar de pdf, también puede usar otras extensiones. Primero debe agregar el permiso de lectura de almacenamiento externo en el archivo androidmanifest.xml .

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

Luego, en el archivo androidmanifest en la etiqueta de actividad, agrega un filtro de intención como se muestra a continuación.

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

Finalmente, en su código, obtiene la ruta del archivo pdf como se muestra a continuación:

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

        }

Para este Intent.ACTION_VIEW, al no poder tomar la ruta del archivo, causa el error: java.io.FileNotFoundException: Permiso denegado. Es solo de algunas aplicaciones específicas, no siempre. alguna solución?
Hardik Joshi

@HardikJoshi Es posible que las aplicaciones no estén configurando el permiso GRANT_READ_URI_PERMISSION.
Mira_Cole

1

Intenta agregar

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

Ahora que funciona con tipos de archivo configurados de fábrica como application / pdf, ¿cómo declararía mi propio tipo de archivo? Y cuando digo tipo de archivo, me refiero a mimeType;)
Tamas

¿Qué tipo de archivo desea que capture este tipo MIME? Además, ¿este archivo se abre desde el navegador o el administrador de archivos o se envía desde otra aplicación que creó?
magaio

podría ser desde el navegador, cliente de correo, administrador de archivos o en cualquier lugar. O mi propia aplicación ofc :) La extensión de archivo es personalizada, especificada por el cliente.
Tamas

Bueno, todavía estoy atascado ... ¿alguien podría ayudar por favor?
Tamas

@Tamas, ¿todos consiguieron esto? ¡Estoy atrapado en esto!
StuStirling

1

Para el archivo adjunto de Gmail, puede utilizar:

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

Agrega tantos tipos de mímica como necesites. Solo los necesito para mi proyecto.


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>

Tenga en cuenta que esto hará que su aplicación abra todos los archivos adjuntos de gmail, no hay forma de evitarlo


Esto manejará cada archivo enviado por correo electrónico al usuario, no solo .teamz
IgorGanapolsky

1

Aquellos que tienen problemas con otras aplicaciones File Manager \ Explorer, como respondieron @yuku y @ phyrum-tea

Esto funciona con la aplicación de administrador de archivos predeterminada de 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>

pero no pude funcionar con ES File Explorer y otros administradores de archivos, así que agregué

 android:mimeType="*/*"

luego funciona con ES Explorer pero el administrador de archivos LG no pudo detectar el tipo de archivo, por lo que mi solución es

     <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 de contenido ftw, y con el filtro de intención en el manifiesto ... si sus archivos tienen una extensión personalizada .xyz, agregue un tipo de mime coincidente:

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

Algunas aplicaciones, como el correo electrónico, parecen convertir la extensión en un tipo mimo. Ahora puedo hacer clic en el archivo adjunto en el correo electrónico y abrirlo en mi aplicación.


1

Leer el archivo de apertura en 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

Coloque este filtro de intención dentro de la etiqueta de actividad en el manifiesto que le gustaría abrir tocando el archivo:

<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

// He probado este código. Y está funcionando bien. Puede utilizar este código para aceptar archivos 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>
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.