java - enviar - Correo electrónico desde el almacenamiento interno
enviar correo java netbeans (7)
Chris Stratton propuso una buena solución. Sin embargo, falla en muchos dispositivos. No debe codificar / mnt / sdcard ruta. Es mejor que lo calcule:
String sdCard = Environment.getExternalStorageDirectory().getAbsolutePath();
Uri uri = Uri.fromFile(new File(sdCard +
new String(new char[sdCard.replaceAll("[^/]", "").length()])
.replace("/0", "/..") + getFilesDir() + "/" + xmlFilename));
En mi aplicación, escribo un archivo en el almacenamiento interno cubierto por el desarrollador de Android . Luego, más tarde, deseo enviar por correo electrónico el archivo que escribí al almacenamiento interno. Aquí está mi código y el error que recibo, cualquier ayuda será apreciada.
FileOutputStream fos = openFileOutput(xmlFilename, MODE_PRIVATE);
fos.write(xml.getBytes());
fos.close();
Intent intent = new Intent(android.content.Intent.ACTION_SEND);
intent.setType("text/plain");
...
Uri uri = Uri.fromFile(new File(xmlFilename));
intent.putExtra(android.content.Intent.EXTRA_STREAM, uri);
startActivity(Intent.createChooser(intent, "Send eMail.."));
Y el error es
file: // la ruta del archivo adjunto debe apuntar a file: // mnt / sdcard. Haciendo caso omiso del archivo adjunto: // ...
Creo que es posible que haya encontrado un error (o al menos una limitación innecesaria) en el cliente de Gmail de Android. Pude evitarlo, pero me parece demasiado específico de la implementación y necesitaría un poco más de trabajo para ser portátil:
First CommonsWare es muy correcto sobre la necesidad de hacer que el archivo sea legible en todo el mundo:
fos = openFileOutput(xmlFilename, MODE_WORLD_READABLE);
A continuación, tenemos que trabajar en torno a la insistencia de Gmail en la ruta / mnt / sdcard (o ¿equivalente en la implementación específica?):
Uri uri = Uri.fromFile(new File("/mnt/sdcard/../.."+getFilesDir()+"/"+xmlFilename));
Al menos en mi dispositivo Gingerbread modificado, esto me permite a Gmail un archivo adjunto de almacenamiento privado para mí, y veo el contenido usando el botón de vista previa cuando lo recibo. Pero no me siento muy "bien" sobre tener que hacer esto para que funcione, y quién sabe qué pasaría con otra versión de Gmail u otro cliente de correo electrónico o un teléfono que monte el almacenamiento externo en otro lugar.
El error es lo suficientemente específico: debe usar el archivo del almacenamiento externo para realizar un archivo adjunto.
He estado luchando con este tema últimamente y me gustaría compartir la solución que encontré, utilizando FileProvider , desde la biblioteca de soporte. es una extensión del proveedor de contenido que resuelve este problema sin problemas, y no es demasiado trabajo.
Como se explica en el enlace, para activar el proveedor de contenido: en su manifiesto, escriba:
<application
....
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.youdomain.yourapp.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
...
los metadatos deben indicar un archivo xml en la carpeta res / xml (lo llamé file_paths.xml):
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path path="" name="document"/>
</paths>
la ruta está vacía cuando usa la carpeta de archivos internos, pero si desea una ubicación más general (ahora estamos hablando de la ruta de almacenamiento interna) debe usar otras rutas. el nombre que escriba se usará para la URL que el proveedor de contenido le dará al archivo.
y ahora, puedes generar una nueva url legible a nivel mundial simplemente usando:
Uri contentUri = FileProvider.getUriForFile(context, "com.yourdomain.yourapp.fileprovider", file);
en cualquier archivo de una ruta en los metadatos res / xml / file_paths.xml.
y ahora solo usa:
Intent mailIntent = new Intent(Intent.ACTION_SEND);
mailIntent.setType("message/rfc822");
mailIntent.putExtra(Intent.EXTRA_EMAIL, recipients);
mailIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
mailIntent.putExtra(Intent.EXTRA_TEXT, body);
mailIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
try {
startActivity(Intent.createChooser(mailIntent, "Send email.."));
} catch (android.content.ActivityNotFoundException ex) {
Toast.makeText(this, R.string.Message_No_Email_Service, Toast.LENGTH_SHORT).show();
}
no necesita dar un permiso, lo hace automáticamente cuando adjunta la url al archivo.
y no necesita hacer su archivo MODE_WORLD_READABLE, este modo ya no está disponible, conviértalo en MODE_PRIVATE, el proveedor de contenido crea una nueva URL para el mismo archivo al que pueden acceder otras aplicaciones.
Debo señalar que solo lo probé en un emulador con Gmail.
Si va a utilizar el almacenamiento interno, intente utilizar la ruta de almacenamiento exacta:
Uri uri = Uri.fromFile(new File(context.getFilesDir() + File.separator + xmlFilename));
o siga cambiando el nombre del archivo en el depurador y simplemente llame a "new File (blah) .exists ()" en cada uno de los archivos para ver rápidamente el nombre exacto de su archivo
también podría ser un problema real de implementación del dispositivo específico de su dispositivo. ¿Has probado usar otros dispositivos / emulador?
Teniendo en cuenta las recomendaciones de aquí: http://developer.android.com/reference/android/content/Context.html#MODE_WORLD_READABLE , desde la API 17 se nos anima a utilizar ContentProviders, etc. Gracias a ese tipo y su publicación http://stephendnicholas.com/archives/974 tenemos una solución:
public class CachedFileProvider extends ContentProvider {
public static final String AUTHORITY = "com.yourpackage.gmailattach.provider";
private UriMatcher uriMatcher;
@Override
public boolean onCreate() {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "*", 1);
return true;
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
switch (uriMatcher.match(uri)) {
case 1:// If it returns 1 - then it matches the Uri defined in onCreate
String fileLocation = AppCore.context().getCacheDir() + File.separator + uri.getLastPathSegment();
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(fileLocation), ParcelFileDescriptor.MODE_READ_ONLY);
return pfd;
default:// Otherwise unrecognised Uri
throw new FileNotFoundException("Unsupported uri: " + uri.toString());
}
}
@Override public int update(Uri uri, ContentValues contentvalues, String s, String[] as) { return 0; }
@Override public int delete(Uri uri, String s, String[] as) { return 0; }
@Override public Uri insert(Uri uri, ContentValues contentvalues) { return null; }
@Override public String getType(Uri uri) { return null; }
@Override public Cursor query(Uri uri, String[] projection, String s, String[] as1, String s1) { return null; }
}
Luego de crear el archivo en la memoria caché interna:
File tempDir = getContext().getCacheDir();
File tempFile = File.createTempFile("your_file", ".txt", tempDir);
fout = new FileOutputStream(tempFile);
fout.write(bytes);
fout.close();
Intento de instalación:
...
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + CachedFileProvider.AUTHORITY + "/" + tempFile.getName()));
Y registrar el proveedor de contenido en el archivo AndroidManifest:
<provider android:name="CachedFileProvider" android:authorities="com.yourpackage.gmailattach.provider"></provider>
File.setReadable(true, false);
trabajó para mi.