after tinkering with the paths XML and my manifest provider settings I was finally able to get my app to stop crashing when trying to send an attachment in an email intent.
HOWEVER, while everything seems normal in the app, when Gmail or drive opens the file is not attached.
Screenshots:


My code is as follows
MainActivity.java
package com.loopbreakr.filesend;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.FileProvider;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import java.io.File;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
public String reciever;
public String subject;
public String body;
public final String stringPath = "/storage/emulated/0/Android/data/com.loopbreakr.firstpdf/files/PDF_files/Abdile&Name 2021-01-29&15:59:55.pdf";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
reciever = "[email protected]";
subject = "my subject";
body = "blank email";
File file = new File(stringPath);
Button button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
prepareEmail(file);
}
});
}
private void prepareEmail(File report) {
ArrayList<Uri> uris = new ArrayList<>();
uris.add(FileProvider.getUriForFile(getApplicationContext(), "com.loopbreakr.filesend", report));
Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
intent.setType("message/rfc822");
intent.putExtra(Intent.EXTRA_EMAIL, reciever);
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
intent.putExtra(Intent.EXTRA_TEXT, body);
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(intent, "Send email via:").addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION));
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.loopbreakr.filesend">
<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:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Filesend">
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.loopbreakr.filesend"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
<meta-data
android:name="com.google.android.actions"
android:resource="@xml/file_paths" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
file_paths.xml
<?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>
As the program can find my file, I don't think it's a permission error anymore. The file is also on external storage. Is it possible that I am missing something in the intent? Many thanks!
EDITED
Note that I originally manually set the permissions to make my question more readable, however after adding runtime storage reading permissions to my code and simplifying the filename, as well as changing the intent to only send one file I get the couldn't attach file toast message
MainActivity.java:
package com.loopbreakr.filesend;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import android.Manifest;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import java.io.File;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
public String reciever;
public String subject;
public String body;
public final String stringPath = "/storage/emulated/0/Android/data/samplefile.pdf";
private int STORAGE_PERMISSION_CODE = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
reciever = "[email protected]";
subject = "my subject";
body = "blank email";
File file = new File(stringPath);
Button button = findViewById(R.id.button);
if (ContextCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(MainActivity.this, "You have already granted this permission!",
Toast.LENGTH_SHORT).show();
} else {
requestStoragePermission();
}
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
prepareEmail(file);
}
});
}
private void prepareEmail(File report) {
Uri uri = FileProvider.getUriForFile(getApplicationContext(), "com.loopbreakr.filesend", report);
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("message/rfc822");
intent.putExtra(Intent.EXTRA_EMAIL, reciever);
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
intent.putExtra(Intent.EXTRA_TEXT, body);
intent.putExtra(Intent.EXTRA_STREAM, uri);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
Intent chooser = Intent.createChooser(intent, "Share File");
List<ResolveInfo> resInfoList = this.getPackageManager().queryIntentActivities(chooser, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
this.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
startActivity(chooser);
}
private void requestStoragePermission() {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.READ_EXTERNAL_STORAGE)) {
new AlertDialog.Builder(this)
.setTitle("Permission needed")
.setMessage("This permission is needed because of this and that")
.setPositiveButton("ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(MainActivity.this,
new String[] {Manifest.permission.READ_EXTERNAL_STORAGE}, STORAGE_PERMISSION_CODE);
}
})
.setNegativeButton("cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.create().show();
} else {
ActivityCompat.requestPermissions(this,
new String[] {Manifest.permission.READ_EXTERNAL_STORAGE}, STORAGE_PERMISSION_CODE);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == STORAGE_PERMISSION_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "Permission GRANTED", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Permission DENIED", Toast.LENGTH_SHORT).show();
}
}
}
}
Am I missing something in my manifest?
I tried to send PDF attachment with ACTION_SEND_MULTIPLE and I found your problem is that you need to grant the explicit permission to email Intent (not to the chooser Intent as you do). My code:
Intent emailIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);//ACTION_SEND does not support purParcelableArrayListExtra
emailIntent.setType("text/plain");
emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{testBox.getEmail()});
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Covid Test Certificate result");
emailIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);//attaching the pdf file(s) to the email
emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//does not really work right now, I had to give explicit permissions
//GRANTING THE PERMISSIONS EXPLICITLY HERE! to all possible choosers (3rd party apps):
List<ResolveInfo> resolvedInfoActivities =
activity.getPackageManager().queryIntentActivities(emailIntent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo ri : resolvedInfoActivities) {
for (Uri uri : uris) {
Log.d(TAG, "Granting permission to - " + ri.activityInfo.packageName);
activity.grantUriPermission(ri.activityInfo.packageName,uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
}
try {
Intent chooserIntent =Intent.createChooser(emailIntent, "Send mail...").addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
activity.startActivity(
chooserIntent
);
} catch (android.content.ActivityNotFoundException ex) {
Toast.makeText(activity, "There are no email clients installed.", Toast.LENGTH_SHORT).show();
Log.e(TAG, "ERROR, THERE ARE NO EMAIL CLIENTS INSTALLED.");
}
And for others wondering about the permissions, this is a good article: https://medium.com/@benexus/dealing-with-permissions-when-sharing-files-android-m-cee9ecc287bf It says the explicit permissions are needed, because the permissions added via provider in Manifest and via Intent do not work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With