Email from internal storage

In my application, I write the file to internal memory, as described by the Android developer . Then later I want to email the file that I wrote in the internal storage. Here is my code and the error I get, any help would be appreciated.

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..")); 

And mistake

file: // the attachment path should point to the file: // mnt / sdcard. Ignoring attachment file: // ...

+20
java android email
May 20 '11 at 13:50
source share
7 answers

I think you might have found an error (or at least an unnecessary restriction) in the android gmail client. I managed to get around this, but it leads me to a too specific implementation, and it will take a little more work to work:

First CommonsWare is very correct in making the file world accessible:

 fos = openFileOutput(xmlFilename, MODE_WORLD_READABLE); 

Next, we need to get around Gmail by insisting on the path / mnt / sdcard (or implementing a specific equivalent?):

 Uri uri = Uri.fromFile(new File("/mnt/sdcard/../.."+getFilesDir()+"/"+xmlFilename)); 

At least on my modified Gingerbread device, this allows me to attach Gmail from a private repository for myself and see the contents using the preview button when I receive it. But I don’t feel “very good” in making it work, and who knows what will happen to another version of Gmail or another mail client or phone that mounts external storage in another place.

+24
May 20 '11 at 16:14
source share

Recently, I have been struggling with this problem, and I would like to share the solution that I found using FileProvider from the support library. its Content Provider extension, which effectively solves this problem without work, and doesn't work too much.

As explained in the link, to activate the content provider: in your manifest, write:

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

metadata should indicate the xml file in the res / xml folder (I named it 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> 

The path is empty if you use a folder with internal files, but if for a more general location (now we are talking about the internal storage path), you should use other paths. the name you write will be used for the URL provided by the content provider with the file provided.

and now you can create a new world-readable URL just by using:

 Uri contentUri = FileProvider.getUriForFile(context, "com.yourdomain.yourapp.fileprovider", file); 

in any file from the path in the res / xml / file_paths.xml metadata.

and now just use:

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

you do not need to give permission, you do this automatically when you attach the url to the file.

and you don’t need to make your MODE_WORLD_READABLE file, this mode is now outdated, make it MODE_PRIVATE, the content provider will create a new URL for the same file available to other applications.

I should note that I only tested it on an emulator with Gmail.

+11
Mar 05 '15 at 9:42
source share

Chris Stratton has suggested a good workaround. However, it does not work on many devices. You must not specify the path hardcode / mnt / sdcard. You better figure it out:

 String sdCard = Environment.getExternalStorageDirectory().getAbsolutePath(); Uri uri = Uri.fromFile(new File(sdCard + new String(new char[sdCard.replaceAll("[^/]", "").length()]) .replace("\0", "/..") + getFilesDir() + "/" + xmlFilename)); 
+7
Aug 14 2018-12-12T00:
source share

Given the recommendations from here: http://developer.android.com/reference/android/content/Context.html#MODE_WORLD_READABLE , since API 17 we recommend using ContentProviders, etc. Thanks to this guy and his post http://stephendnicholas.com/archives/974 we have a solution:

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

How to create a file in the internal cache:

  File tempDir = getContext().getCacheDir(); File tempFile = File.createTempFile("your_file", ".txt", tempDir); fout = new FileOutputStream(tempFile); fout.write(bytes); fout.close(); 

Purpose of setting:

 ... emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + CachedFileProvider.AUTHORITY + "/" + tempFile.getName())); 

And register the content provider in the AndroidManifest file:

 <provider android:name="CachedFileProvider" android:authorities="com.yourpackage.gmailattach.provider"></provider> 
+5
Aug 11 '13 at
source share
 File.setReadable(true, false); 

worked for me.

+5
Jul 16 '14 at 15:40
source share

The error is quite specific: you must use a file from external storage to create an attachment.

+1
May 20 '11 at 13:52
source share

If you intend to use internal storage, try using the exact storage path:

 Uri uri = Uri.fromFile(new File(context.getFilesDir() + File.separator + xmlFilename)); 

or optionally change the file name in the debugger and simply call the "new file (blah) .exists ()" in each file to quickly find out what exact name your file is.

it can also be a real device implementation issue specific to your device. Have you tried using other devices / emulator?

+1
Sep 19 '13 at 1:11
source share



All Articles