I am writing an Android application that dynamically generates a simple PDF document and then fires an intent to open an email client (like Gmail) with the recipient, subject, body, and the PDF attached.
The Problem:
The intent successfully opens the app chooser, and when I select Gmail, the compose screen opens perfectly. The "To", "Subject", "Body", and the PDF attachment are all visible in the compose UI.
However, when I tap "Send", it does not actually send. It just goes straight to the drafts folder, and the PDF attachment is missing. Only the plain text remains.
What I've Tried (and didn't work):
To fix this, I have tried the following based on modern Android (API 30+) best practices:
FileProvider to correctly serve a content:// URI instead of a file:// URI.Intent.FLAG_GRANT_READ_URI_PERMISSION to the intent.intent.setClipData(ClipData.newRawUri("PDF", uri)) which is required by some modern apps to see the attachment.ShareCompat.IntentBuilder to ensure the intent is constructed perfectly.getCacheDir(), getExternalCacheDir(), and getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS).fos.getFD().sync() to the FileOutputStream to guarantee the PDF is fully flushed to physical disk before the intent fires.<queries> for <action android:name="android.intent.action.SEND" /> in the Manifest for Android 11+ package visibility.System.currentTimeMillis() for every PDF to ensure Gmail isn't caching an old/broken draft.> Despite all of this, the attachment still drops silently in the background. > > My Code:
`MainActivity.java`
public class MainActivity extends AppCompatActivity {
private static final String RECIPIENT = "mgauthier@duck.com";
private static final String SUBJECT = "Test Email";
private static final String BODY = "Test Email";
private static final String FILE_PROVIDER_AUTHORITY = "com.mike.email.fileprovider";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btnEmailPDF).setOnClickListener(v -> sendEmailWithPdf());
}
private void sendEmailWithPdf() {
File pdfFile = createPdf();
if (pdfFile == null || !pdfFile.exists()) return;
Uri contentUri = FileProvider.getUriForFile(this, FILE_PROVIDER_AUTHORITY, pdfFile);
Intent intent = ShareCompat.IntentBuilder.from(this)
.setType("application/pdf")
.setEmailTo(new String[]{RECIPIENT})
.setSubject(SUBJECT)
.setText(BODY)
.setStream(contentUri)
.getIntent();
// Granting permissions
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setClipData(ClipData.newRawUri("PDF Attachment", contentUri));
startActivity(Intent.createChooser(intent, "Send Email with PDF..."));
}
private File createPdf() {
PdfDocument document = new PdfDocument();
PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(595, 842, 1).create();
PdfDocument.Page page = document.startPage(pageInfo);
Canvas canvas = page.getCanvas();
Paint paint = new Paint();
paint.setTextSize(18);
canvas.drawText("Hello, this is a test PDF document.", 50, 50, paint);
document.finishPage(page);
File baseDir = getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);
File directory = new File(baseDir, "EmailTests");
if (!directory.exists()) directory.mkdirs();
// Unique filename
File file = new File(directory, "EmailTest_" + System.currentTimeMillis() + ".pdf");
try (FileOutputStream fos = new FileOutputStream(file)) {
document.writeTo(fos);
fos.flush();
fos.getFD().sync(); // Ensure physical write
} catch (IOException e) {
return null;
} finally {
document.close();
}
return file;
}
}
`AndroidManifest.xml`
<queries>
<intent>
<action android:name="android.intent.action.SEND" />
<data android:mimeType="application/pdf" />
</intent>
</queries>
<application ...>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.mike.email.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
`res/xml/file_paths.xml`
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path name="my_pdfs" path="Documents/EmailTests" />
</paths>
Is there a known issue with Gmail dropping FileProvider attachments in recent Android versions, or is there an edge case in intent delegation that I am missing? Any help is greatly appreciated!
You used setStream() as part of the ShareCompat.IntentBuilder work to build the Intent. You then modified the Intent by calling setClipData()... which is what ShareCompat.IntentBuilder does. My guess is that your setClipData() call messed up what ShareCompat.IntentBuilder was doing, and so removing it gave you more consistent results.
CommonsWare