android - startactivity - La aplicación Gmail 5.0 falla con "Permiso denegado para el archivo adjunto" cuando recibe el intento de ACTION_SEND
putextra android (8)
Utilice getExternalCacheDir()
con File.createTempFile
.
Use lo siguiente para crear un archivo temporal en el directorio de caché externo:
String fileName = "my_temp_file";
String fileExtension = ".tmp";
File temporaryFile = File.createTempFile( fileName, fileExtension, context.getExternalCacheDir() );
Mi aplicación crea correos electrónicos con archivos adjuntos y utiliza una intención con Intent.ACTION_SEND
para iniciar una aplicación de correo.
Funciona con todas las aplicaciones de correo electrónico con las que probé, a excepción del nuevo Gmail 5.0 (funciona con Gmail 4.9), donde el correo se abre sin datos adjuntos y muestra el error: "Permiso denegado para el archivo adjunto".
No hay mensajes útiles de Gmail en Logcat. Solo probé Gmail 5.0 en Android KitKat, pero en múltiples dispositivos.
Creo el archivo para el archivo adjunto de esta manera:
String fileName = "file-name_something_like_this";
FileOutputStream output = context.openFileOutput(
fileName, Context.MODE_WORLD_READABLE);
// Write data to output...
output.close();
File fileToSend = new File(context.getFilesDir(), fileName);
Soy consciente de las preocupaciones de seguridad con MODE_WORLD_READABLE
.
Envío la intención de esta manera:
public static void compose(
Context context,
String address,
String subject,
String body,
File attachment) {
Intent emailIntent = new Intent(Intent.ACTION_SEND);
emailIntent.setType("message/rfc822");
emailIntent.putExtra(
Intent.EXTRA_EMAIL, new String[] { address });
emailIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
emailIntent.putExtra(Intent.EXTRA_TEXT, body);
emailIntent.putExtra(
Intent.EXTRA_STREAM,
Uri.fromFile(attachment));
Intent chooser = Intent.createChooser(
emailIntent,
context.getString(R.string.send_mail_chooser));
context.startActivity(chooser);
}
¿Hay algo que hago mal al crear el archivo o enviar el intento? ¿Hay una mejor manera de iniciar una aplicación de correo con archivo adjunto? De forma alternativa, ¿alguien ha encontrado este problema y ha encontrado una solución alternativa?
¡Gracias!
Debe implementar un FileProvider , que puede crear Uris para los archivos internos de su aplicación. A otras aplicaciones se les otorga permiso para leer estos Uris. Entonces, simplemente en lugar de llamar a Uri.fromFile (archivo adjunto), crea una instancia de FileProvider y usa:
fileProvider.getUriForFile(attachment);
Estaba teniendo este problema y finalmente encontré una manera fácil de enviar correos electrónicos con archivos adjuntos. Aquí está el código
public void SendEmail(){
try {
//saving image
String randomNameOfPic = Calendar.DAY_OF_YEAR+DateFormat.getTimeInstance().toString();
File file = new File(ActivityRecharge.this.getCacheDir(), "slip"+ randomNameOfPic+ ".jpg");
FileOutputStream fOut = new FileOutputStream(file);
myPic.compress(Bitmap.CompressFormat.JPEG, 100, fOut);
fOut.flush();
fOut.close();
file.setReadable(true, false);
//sending email
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{"[email protected]"});
intent.putExtra(Intent.EXTRA_SUBJECT, "Recharge Account");
intent.putExtra(Intent.EXTRA_TEXT, "body text");
//Uri uri = Uri.parse("file://" + fileAbsolutePath);
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file));
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(Intent.createChooser(intent, "Send email..."),12);
}catch (Exception e){
Toast.makeText(ActivityRecharge.this,"Unable to open Email intent",Toast.LENGTH_LONG).show();
}
}
En este código, "myPic" es un mapa de bits que fue devuelto por la intención de la cámara
GMail 5.0 agregó algunas verificaciones de seguridad a los archivos adjuntos que recibe de un Intento. Estos no están relacionados con los permisos de Unix, por lo que no importa el hecho de que el archivo sea legible.
Cuando Uri del archivo adjunto es un archivo: //, solo aceptará archivos del almacenamiento externo, el directorio privado de gmail o archivos legibles por todo el mundo desde el directorio de datos privados de la aplicación que realiza la llamada.
El problema con esta comprobación de seguridad es que confía en que gmail pueda encontrar la aplicación de la persona que llama, que solo es confiable cuando la persona que llama ha pedido el resultado. En su código anterior, no solicita el resultado y, por lo tanto, gmail no sabe quién llama y rechaza su archivo.
Como funcionó para usted en 4.9 pero no en 5.0, usted sabe que no es un problema de permiso de Unix, por lo que la razón debe ser las nuevas verificaciones.
Respuesta de TL; DR: reemplace startActivity con startActivityForResult.
O mejor aún, usa un proveedor de contenido.
Google tiene una answer para ese problema:
Almacene los datos en su propio
ContentProvider
, asegurándose de que otras aplicaciones tengan el permiso correcto para acceder a su proveedor. El mecanismo preferido para proporcionar acceso es usar permisos por URI que son temporales y solo otorgan acceso a la aplicación receptora. Una forma sencilla de crear unContentProvider
como este es utilizar la clase de ayudaFileProvider
.Use el sistema
MediaStore
.MediaStore
está principalmente enfocado a los tipos MIME de video, audio e imagen; sin embargo, comenzando con Android 3.0 (API nivel 11) también puede almacenar tipos que no sean de medios (consulteMediaStore.Files
para obtener más información). Los archivos se pueden insertar enMediaStore
usandoscanFile()
después de lo cual un estilo decontent://
Uri
adecuado para compartir se pasa a la devolución de llamadaonScanCompleted()
proporcionada. Tenga en cuenta que una vez agregado al sistemaMediaStore
el contenido es accesible para cualquier aplicación en el dispositivo.
También puedes probar establecer permisos para tu archivo:
emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Y finalmente puede copiar / almacenar sus archivos en el almacenamiento externo, permisos que no son necesarios allí.
Lo probé y descubrí que definitivamente era un problema de acceso al almacenamiento privado. Cuando adjuntas un archivo a Gmail (más de 5.0) no utilices el archivo de almacenamiento privado como / data / data / package /. Intenta usar / storage / sdcard.
Puede adjuntar exitosamente su archivo.
No estoy seguro de por qué a GMail 5.0 no le gustan ciertas rutas de archivos (a las que he confirmado que tiene acceso de lectura), pero una solución aparentemente mejor es implementar su propia clase ContentProvider para servir el archivo. En realidad es algo simple, y encontré un buen ejemplo aquí: http://stephendnicholas.com/archives/974
Asegúrese de agregar la etiqueta al manifiesto de su aplicación, e incluya un "android: grantUriPermissions =" true "" dentro de eso. También querrás implementar getType () y devolver el tipo MIME apropiado para el URI del archivo, de lo contrario algunas aplicaciones no funcionarán con esto ... Hay un ejemplo de eso en la sección de comentarios en el enlace.
Pude pasar un archivo .jpeg de captura de pantalla de mi aplicación a GMail 5.0 a través de un Intento. La clave estaba en esta respuesta .
Todo lo que tengo del código de @natasky es casi idéntico, pero en su lugar, tengo el directorio del archivo como
context.getExternalCacheDir();
Que "representa el directorio de almacenamiento externo donde debe guardar los archivos de caché" (documentación)