5

I am developing an application that sends processing reports via email. When completing the processing data in the application, the user sends an email containing the processing data. However, I am facing a problem on Android 11. When attaching the report to the email, regardless of the email application, a message appears: the file could not be attached. I've been researching a little and saw that it may be related to the permission to access the internal storage of the device on devices with version of android 11. I would like me to help you send email with file attached on android 11.

My code: Manifest

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:networkSecurityConfig="@xml/network_security_config"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme"
    android:requestLegacyExternalStorage="true"
    tools:ignore="AllowBackup"
    tools:targetApi="n">

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

My xml:

enter image description here

<?xml version="1.0" encoding="utf-8"?>
<paths>
   <external-path name="external_files" path="."/>
</paths>

Save file .pdf

val mDocProc = Document()
            val file =
                File(
                    Environment.getExternalStorageDirectory()
                        .toString() + "/" + "Relatório diário" + date + ".pdf"
                )
            if (file.exists()) {
                file.delete()
            }
            //pdf file path
            mFilePathDaily =
                Environment.getExternalStorageDirectory()
                    .toString() + "/" + "Relatório diário " + date + ".pdf"

Class send email:

val report = File(mFilePathDaily)
            val uri = FileProvider.getUriForFile(
                activity.requireContext(),
                activity.requireContext().applicationContext.packageName + ".provider",
                report
            )

            val i = Intent(Intent.ACTION_SENDTO)
            i.data = Uri.parse("mailto:")
            i.putExtra(Intent.EXTRA_EMAIL, arrayOf("testesdissertacao@gmail.com"))
            i.putExtra(Intent.EXTRA_SUBJECT, EMAIL_SUBJECT)
            i.putExtra(Intent.EXTRA_TEXT, "Segue em anexo o relatório de Benchmark")
            i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            i.putExtra(Intent.EXTRA_STREAM, uri)
            //i.type = "image/png"
            activity.requireContext().startActivity(Intent.createChooser(i, null))
            progressDialog.dismiss()

Now the problem is that you can't find the report to attach to the email. I noticed that the report was saved inside that other folder in the emulator, and not in the root folder. I think that is why you are not finding the file to attach to the email.

enter image description here

enter image description here

Do not attach the file to the email

enter image description here

Tecnologia da Net
  • 215
  • 3
  • 7
  • 23
  • `Uri.parse("file://$mFilePathDaily")` You cannot use file uries since Android N/7. They will produce an FileUriExposedException. Use FileProvider.getUriForFile(). – blackapps Mar 15 '21 at 17:10
  • does that solve? How? @blackapps – Tecnologia da Net Mar 15 '21 at 18:23
  • 3
    Using FileProvider to attach and share the file for android 10 and Above version. – Anwar Zahid Mar 18 '21 at 17:03
  • You can store the file at the path given by `context.externalCacheDir` and then access it easily. Else, you'll have to get separate accesses. Read about it [here](https://developer.android.com/about/versions/11/privacy/storage). – Jacob Celestine Mar 19 '21 at 15:45

4 Answers4

13

I have attached the image successfully in email i will give you a code

convert your URI to file

 private void Share(File savepath) {
       
 if (savePath != null) {

            Uri uri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", savePath);

            Intent i = new Intent(Intent.ACTION_SEND);
            i.putExtra(Intent.EXTRA_EMAIL, new String[]{"fake@fake.edu"});
            i.putExtra(Intent.EXTRA_SUBJECT,"On The Job");
            //Log.d("URI@!@#!#!@##!", Uri.fromFile(pic).toString() + "   " + pic.exists());
            i.putExtra(Intent.EXTRA_TEXT,"All Detail of Email are here in message");
            i.putExtra(Intent.EXTRA_STREAM,uri);
            i.setType("image/png");
            context.startActivity(Intent.createChooser(i,"Share you on the jobing"));

        }

how to add provider in XML path first, you have to create an XML resource folder then create an XML resource file after pasting this code

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>

then also add-in manifest

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

file provider in xml

decalre in manifest

axar
  • 539
  • 2
  • 17
  • Axar, I edited the question with your suggestions in the implementation. You do not yet attach the file to the email. I suspected that I wouldn't be saving to storage. However, as seen in the question I attached the print of where it is being saved. Do you have any idea what's wrong? – Tecnologia da Net Mar 20 '21 at 20:17
  • @TecnologiadaNet i.putExtra(Intent.EXTRA_STREAM,uri); check your uri – Nikit Dungrani Jan 05 '22 at 12:38
  • but I want to show only email options. SENDTO shows all options like message, bluetooth. – Shyamaly Lakhadive Feb 28 '22 at 10:41
4

You and email apps won't be able to access files on Android 11. Try to use SAF instead. How to use support FileProvider for sharing content to other apps?

Amin
  • 3,056
  • 4
  • 23
  • 34
1

I was able to send an email with pdf attachment using the code below. screenshot Crucial was granting READ_URI_PERMISSION and WRITE_URI_PERMISSION, using ACTION_SEND instead of ACTION_SENDTO and the Uri being linked to a pdf file in one of the standard directories such as Download or Documents. I did not need to convert Uri to File.:

  //obtaining the uri to the pdf report when creating it
  Uri pathToPdf = null;
  int EmailRequestCode = 200;
  ContentResolver contentresolver = getContentResolver();
  ContentValues contentvalues = new ContentValues();
  contentvalues.put(MediaStore.MediaColumns.DISPLAY_NAME, pdfFileName);
  contentvalues.put(MediaStore.MediaColumns.MIME_TYPE, "application/pdf");
  contentvalues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS + File.separator +"Reports");
  pathToPdf = contentresolver.insert(MediaStore.Files.getContentUri("external"), contentvalues);

  //... writing data to the pdf

  //getting email addresses to include in the email
  String[] mailto = listMailAddressTO.toArray(new String[2]);
  String[] mailCC = listMailAddressCC.toArray(new String[2]);

  Intent emailIntent = new Intent(Intent.ACTION_SEND);
  emailIntent.setType("application/pdf");
  emailIntent.putExtra(Intent.EXTRA_EMAIL, mailto);
  emailIntent.putExtra(Intent.EXTRA_CC, mailCC);
  emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
  emailIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
  emailIntent.putExtra(Intent.EXTRA_SUBJECT, "UllageReport (Pdf)");                                
  emailIntent.putExtra(Intent.EXTRA_TEXT, "UllageReport");
  emailIntent.putExtra(Intent.EXTRA_STREAM, pathToPdf);
  try{
      if (emailIntent.resolveActivity(getPackageManager()) != null) {
          startActivityForResult(emailIntent, EmailRequestCode);
      }
  }
  catch(android.content.ActivityNotFoundException e){
       Toast.makeText(UllageReportPdf.this, "No email client available", Toast.LENGTH_SHORT).show();
  }
  • Your catch block is useless because of your resolveActivity condition. I suggest get rid of the if condition – ruif3r Dec 01 '21 at 19:50
0

The solution for me was I had to replace res/xml/file_paths.xml with:

<?xml version="1.0" encoding="utf-8"?>
  <paths>
    <external-path
         name="external"
         path="." />
    <external-files-path
         name="external_files"
         path="." />
    <cache-path
         name="cache"
         path="." />
    <external-cache-path
         name="external_cache"
         path="." />
    <files-path
         name="files"
         path="." />
</paths>

You should use only the lines you need to make it work.