Электронная почта из внутреннего хранилища
В моем приложении я записываю файл во внутреннее хранилище, как описано в Android developer. Затем позже я хочу отправить по электронной почте файл, который я записал во внутреннее хранилище. Вот мой код и ошибка, которую я получаю, буду признателен за любую помощь.
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.."));
И ошибка
файл:// путь к вложению должен указывать на файл: //mnt / sdcard. Игнорирование файла вложения:// ...
Переведено автоматически
Ответ 1
Я думаю, вы, возможно, обнаружили ошибку (или, по крайней мере, ненужное ограничение) в клиенте Android Gmail. Я смог обойти это, но мне кажется, что это слишком специфично для реализации, и для переносимости потребуется немного больше работы:
First CommonsWare очень правильно говорит о необходимости сделать файловый мир доступным для чтения:
fos = openFileOutput(xmlFilename, MODE_WORLD_READABLE);
Далее, нам нужно обойти требование Gmail указывать путь / mnt / sdcard (или эквивалент для конкретной реализации?):
Uri uri = Uri.fromFile(new File("/mnt/sdcard/../.."+getFilesDir()+"/"+xmlFilename));
По крайней мере, на моем модифицированном устройстве Gingerbread это позволяет мне отправлять в Gmail вложение из личного хранилища самому себе и просматривать содержимое с помощью кнопки предварительного просмотра, когда я его получу. Но я не чувствую себя "хорошо" из-за необходимости делать это, чтобы заставить его работать, и кто знает, что произойдет с другой версией Gmail, или другим почтовым клиентом, или телефоном, который подключает внешнее хранилище в другом месте.
Ответ 2
В последнее время я боролся с этой проблемой, и я хотел бы поделиться решением, которое я нашел, используя FileProvider, из библиотеки поддержки. это расширение контент-провайдера, которое хорошо решает эту проблему без обходных путей, и это не слишком большая работа.
Как описано по ссылке, чтобы активировать контент-провайдера: в вашем манифесте напишите:
<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>
...
метаданные должны указывать на XML-файл в папке res / xml (я назвал его 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>
путь пуст, когда вы используете папку internal files , но если для более общего расположения (сейчас мы говорим о пути внутреннего хранилища) вам следует использовать другие пути. имя, которое вы напишете, будет использоваться для URL-адреса, который контент-провайдер предоставит файлу.
теперь вы можете сгенерировать новый, доступный для чтения во всем мире URL-адрес, просто используя:
Uri contentUri = FileProvider.getUriForFile(context, "com.yourdomain.yourapp.fileprovider", file);
для любого файла из пути в разделе res/xml/file_paths.xml метаданные.
а теперь просто используй:
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();
}
вам не нужно давать разрешение, вы делаете это автоматически, когда прикрепляете URL-адрес к файлу.
и вам не нужно делать ваш файл MODE_WORLD_READABLE, этот режим теперь устарел, сделайте его MODE_PRIVATE, контент-провайдер создает новый URL для того же файла, который доступен другим приложениям.
Должен отметить, что я тестировал ее только на эмуляторе с Gmail.
Ответ 3
Крис Страттон предложил хорошее решение проблемы. Однако оно дает сбой на многих устройствах. Вам не следует жестко кодировать путь / mnt / sdcard. Вам лучше вычислить его:
String sdCard = Environment.getExternalStorageDirectory().getAbsolutePath();
Uri uri = Uri.fromFile(new File(sdCard +
new String(new char[sdCard.replaceAll("[^/]", "").length()])
.replace("\0", "/..") + getFilesDir() + "/" + xmlFilename));
Ответ 4
Принимая во внимание рекомендации отсюда: http://developer.android.com/reference/android/content/Context.html#MODE_WORLD_READABLE, начиная с API 17, нам рекомендуется использовать ContentProviders и т.д. Благодаря этому парню и его сообщению http://stephendnicholas.com/archives/974 у нас есть решение:
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; }
}
Чем создавать файл во внутреннем кэше:
File tempDir = getContext().getCacheDir();
File tempFile = File.createTempFile("your_file", ".txt", tempDir);
fout = new FileOutputStream(tempFile);
fout.write(bytes);
fout.close();
Намерение настройки:
...
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + CachedFileProvider.AUTHORITY + "/" + tempFile.getName()));
И зарегистрируйте контент-провайдера в файле AndroidManifest:
<provider android:name="CachedFileProvider" android:authorities="com.yourpackage.gmailattach.provider"></provider>