Filtro de intención de Android: asociar aplicación con extensión de archivo
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 Según los documentos y muchas publicaciones del 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.
Necesita múltiples filtros de intención para abordar las diferentes situaciones que desea manejar.
Ejemplo 1, manejar 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 tipos MIME, 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 exploración 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>
Las otras soluciones no me funcionaron de manera confiable 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>
Las respuestas dadas por Phyrum Tea y yuku ya son muy informativas.
Quiero agregar que a partir de 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 trabajo de Android aplica la política de API StrictMode que prohíbe exponer los URI de file:// fuera de su aplicación. Si un intent 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 URI content:// y otorgar un permiso de acceso temporal al URI. La forma más sencilla de otorgar este permiso es mediante la clase FileProvider. Para obtener más información sobre permisos y cómo compartir 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 scheme
valor intent-filter
para que funcione FileProviders
tambié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 enterarme de este pequeño cambio que impedía que mi actividad se abriera en dispositivos con Android 7.0 mientras que todo estaba bien en versiones anteriores. Espero que esto ayude a alguien.
Mis hallazgos:
Necesita varios filtros para abordar las diferentes formas de recuperar un archivo. es decir, por archivo adjunto de Gmail, por explorador de archivos, por HTTP, por FTP... Todos envían intenciones muy diferentes.
Y necesitas filtrar la intención que desencadena tu actividad en tu código de actividad.
Para el siguiente ejemplo, creé un tipo de archivo falso new.mrz. Y lo recuperé del archivo adjunto y del explorador de archivos de Gmail.
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: Intención de contenido detectada: android.intent.action.VIEW: content://gmail-ls/ [email protected] /messages/2950/attachments/0.1/BEST/false: application/octet-stream: nuevo. 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());
}
}
ElpathPattern
<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.