android - 通过 FileProvider 和 Intent 将缓存文件附加到 GMail 不起作用
问题描述
所以在过去的一天里,我一直在努力想弄清楚为什么文件不会附加到电子邮件中。每次应用程序运行时,我都会收到一条提示消息,弹出“无法附加文件”。To 和 Subject 字段的填写与我预期的一样。第一个问题是,我如何才能找到此错误背后的更多信息?此消息是从 GMail 应用程序而不是我自己的程序抛出的。它肯定会为我指明正确的方向,因此如果我有此错误的原因,我可以自行进一步调试。我在下面包含了可能相关的信息。文件大小为 78.5kb,我可以验证该文件是否存在并且具有我想要附加的正确内容。根据文件资源管理器,对文件的权限是所有者和组的 rw。
我在输入这篇文章时发现了一些有趣的东西;当使用文件资源管理器在 gmail 应用程序中添加附件时,Android/data 目录不是一个选项!但是,当您在 gmail 应用程序之外使用文件资源管理器时,它会显示出来。所以我认为这可能是权限问题?它无法访问此文件夹。如果是这种情况,存储此文件的推荐位置是什么?理想情况下,在这种情况下,它将是某种缓存位置或临时文件位置。
我尝试在 Android Outlook 应用程序而不是 GMail 中添加附件,并且文件确实被附加了。
尝试使用 Pixel 4 Emulator 在 Android 11 API 30 上运行。
电子邮件意图代码:
protected void sendEmail(File f){
final String[] TO = { "foo@bar.com" };
Intent emailIntent = new Intent(Intent.ACTION_SENDTO);
emailIntent.setData(Uri.parse("mailto:"));
emailIntent.putExtra(Intent.EXTRA_EMAIL, TO);
emailIntent.putExtra(Intent.EXTRA_SUBJECT, f.getName().replaceAll("(?i).pdf", ""));
if (!f.exists() || !f.canRead()) {
Toast.makeText(this, "Attachment Error", Toast.LENGTH_SHORT).show();
finish();
return;
}
emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri uri = FileProvider.getUriForFile(getApplicationContext(), BuildConfig.APPLICATION_ID, f);
emailIntent.putExtra(Intent.EXTRA_STREAM, uri);
try {
startActivity(emailIntent);
finish();
} catch (android.content.ActivityNotFoundException ex) {
Toast.makeText(MainActivity.this,
"There is no email client installed.", Toast.LENGTH_SHORT).show();
}
AndroidManifest.xml:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
...
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
provider_paths.xml:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-cache-path name="external_cache" path="."/>
</paths>
f的值:
/storage/emulated/0/Android/data/com.bar.foo/cache/Form2020-12-27.pdf
uri路径的值:
content://com.bar.foo/external_cache/Form2020-12-27.pdf
解决方案
经过进一步的折腾,我想出了“解决方案”。它在某些情况下可能不适用,但我所追求的结果恰好起作用。它与意图数据和类型有关。我已将意图设置更改为以下内容,而所有其他项目保持不变:
protected void sendEmail(File f){
final String[] TO = { "foo@bar.com" };
Intent emailIntent = new Intent(Intent.ACTION_SEND);
emailIntent.putExtra(Intent.EXTRA_EMAIL, TO);
emailIntent.putExtra(Intent.EXTRA_SUBJECT, f.getName().replaceAll("(?i).pdf", ""));
if (!f.exists() || !f.canRead()) {
Toast.makeText(this, "Attachment Error", Toast.LENGTH_SHORT).show();
finish();
return;
}
Uri uri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID, f);
emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
emailIntent.setDataAndType(uri, getContentResolver().getType(uri));
emailIntent.putExtra(Intent.EXTRA_STREAM, uri);
try {
startActivity(emailIntent);
finish();
} catch (android.content.ActivityNotFoundException ex) {
Toast.makeText(MainActivity.this,
"There is no email client installed.", Toast.LENGTH_SHORT).show();
}
}
我将 Intent 操作从 ACTION_SENDTO 更改为 ACTION_SEND 仅此一项并不能解决问题。
我没有使用“mailto:”手动设置意图数据和类型,而是从 uri 设置数据和类型。
如果未设置默认应用程序,则会弹出一个选择器,询问用户哪些相关应用程序与文件一起使用。但是,这不仅显示了电子邮件客户端,这对于某些应用程序可能并不理想。这并不是我设计的真正初衷,但是考虑到我的用户可能希望将表单附加到非电子邮件客户端(例如 MS Teams 或云存储客户端),它会起作用。单击 GMail 会将文件附加到电子邮件中,并按预期设置主题和收件人。
推荐阅读
- python - Inserting floats into New Dataframe and getting NaN
- location - Mapbox Android - change user location icon
- rust-diesel - why "no method named `execute` found" error have when i use diesel insert_into data to database?
- django - Django中的模型设计
- node.js - 以 nodejs 作为我的后端和 postgres 作为我的数据库来反应本机应用程序
- django - How to delete Django migration files in Dokku?
- android - duplicate value for resource 'attr/checkedButton' with config
- oracle - PLS-000428 error Oracle stored procedure with CTE
- amazon-web-services - 当我运行“eb ssh environment-name”时,我在哪里进行 SSH 连接?
- javascript - 导出异步配置模块 node.js