androidandroid-intent

Android file attachment included in a SendIntent not being added to email in Gmail


I am trying to attach a file to an SendIntent for Gmail, so that my users can email the file file to me upon request. I am targeting Android 35

I have declared a FileContentProvider in the Application element of my manifest

        <provider
            android:authorities="com.my_app.fileprovider"
            android:name="androidx.core.content.FileProvider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_provider_paths" />
        </provider>

I have declared the file resource (res/xml/file_provider_paths.xml):

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="logs" path="/logs"/>
</paths>

I am constructing the Intent and handing it off as per:

final Intent intent = new Intent(Intent.ACTION_SENDTO);
intent.setData(Uri.parse("mailto:"));
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{ "support@my_app.com"});
intent.putExtra(Intent.EXTRA_SUBJECT, "Some Subject");
intent.putExtra(Intent.EXTRA_TEXT, "Some content");

final File logsFolder = new File(context.getFilesDir(), "logs");
final File logsFile = new File(logsFolder, "Application.log")
final Uri contentUri = FileProvider.getUriForFile(context, "com.my_app.fileprovider", logFile);

Log.w(TAG, "Sending this log content: " + contentUri);
intent.putExtra(Intent.EXTRA_STREAM, contentUri));
intent.addFlags((Intent.FLAG_GRANT_READ_URI_PERMISSION));

startActivity(Intent.createChooser(intent, "Contact Us"));

This opens up a chooser in which Gmail is an option and upon choosing Gmail I get the prepared email with subject, content etc, but no attached file.

Looking at Logcat I see these errors (which appear after starting the ChooserIntent:

E  Failed to find provider info for com.my_app.fileprovider
E  ComposeActivity: Error adding attachment [CONTEXT android_log_tag="ComposeActivity" ]
hea: FileNotFoundException when openAssetFileDescriptor.
at gzz.e(PG:565)
at hsu.L(PG:182)
...
Caused by: java.io.FileNotFoundException: No content provider: content://com.my_app.fileprovider/logs/application.log
at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:2029)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1858)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1773)
at lky.y(PG:10)
at gzz.e(PG:452)
...

I can resolve the content Uri using a ContentResolver within my app and see its and and file size. But Gmail doesn't seem to even be able to find my ContentResolver.

What am I doing wrong? What do I need to change so that GMail can resolve my file and attach it to the email?


Solution

  • What’s going wrong

    1. Wrong intent action for attachments.
      ACTION_SENDTO (with mailto:) is for composing an email without attachments. Most clients (incl. Gmail) ignore EXTRA_STREAM with ACTION_SENDTO. Use ACTION_SEND.

    2. You computed a contentUri but attached a File.
      You must put the content://… Uri from FileProvider in EXTRA_STREAM, not the File.

    3. Authority mismatch.
      Failed to find provider info for com.my_app.fileprovider means the authority string in your code doesn’t match the provider declared in the installed app.

      • Avoid hard-coding: use ${applicationId}.fileprovider in the manifest and context.getPackageName() + ".fileprovider" in code.

      • Also note: package names can’t contain underscores; double-check your real applicationId.

    4. paths entry has a leading slash.
      In file_provider_paths.xml the path must be relative (no leading /). If your file is at context.getFilesDir()/logs/Application.log, the path should be logs/.

    5. Granting read permission.
      Add FLAG_GRANT_READ_URI_PERMISSION. (Optional but sometimes helpful: explicitly call grantUriPermission for Gmail.)

    AndroidManifest.xml

    <application ... >
        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="${applicationId}.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_provider_paths" />
        </provider>
    </application>
    

    res/xml/file_provider_paths.xml

    <?xml version="1.0" encoding="utf-8"?>
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
        <!-- File will live at: context.getFilesDir()/logs/Application.log -->
        <files-path name="logs" path="logs/" />
    </paths>
    

    Kotlin/Java (building the email)

    val logsFolder = File(context.filesDir, "logs")
    val logFile = File(logsFolder, "Application.log")
    
    // Ensure it exists (and is within filesDir/logs)
    require(logFile.exists()) { "Log file doesn't exist: ${logFile.absolutePath}" }
    
    val authority = context.packageName + ".fileprovider"
    val contentUri = FileProvider.getUriForFile(context, authority, logFile)
    
    val email = Intent(Intent.ACTION_SEND).apply {
        // A general type is fine; or detect based on your file
        type = "text/plain" // or "application/octet-stream"
        putExtra(Intent.EXTRA_EMAIL, arrayOf("support@my_app.com"))
        putExtra(Intent.EXTRA_SUBJECT, "Some Subject")
        putExtra(Intent.EXTRA_TEXT, "Some content")
        putExtra(Intent.EXTRA_STREAM, contentUri)
        addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
    
        // Optional: also set ClipData (helps some clients)
        clipData = ClipData.newUri(context.contentResolver, "logs", contentUri)
    }
    
    // Optional: explicitly grant to Gmail (in case chooser picks Gmail)
    try {
        context.grantUriPermission(
            "com.google.android.gm",
            contentUri,
            Intent.FLAG_GRANT_READ_URI_PERMISSION
        )
    } catch (_: Exception) { /* ignore if Gmail not installed */ }
    
    startActivity(Intent.createChooser(email, "Contact Us"))