Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Permission denied for the attachment" (on Gmail 5.0) trying to attach file to email intent

This question has been posted before, but there was no clear or accepted answer and all of the solutions provided that were supposed to "work" didn't for me. See here: Gmail 5.0 app fails with "Permission denied for the attachment" when it receives ACTION_SEND intent

I have an app which builds up data in a text file and needs to send the text file along in an email, automatically attaching it. I have tried many ways to get this to attach, and it apparently works for Gmail 4.9 and below but 5.0 has some new permission features disabling it from doing what I wish.

    Intent i = new Intent(Intent.ACTION_SEND);

    String to = emailRecipient.getText().toString();

    i.setType("message/rfc822");
    i.putExtra(Intent.EXTRA_EMAIL, new String[] { to });
    i.putExtra(Intent.EXTRA_SUBJECT, "Pebble Accelerometer Data");
    i.putExtra(Intent.EXTRA_TEXT, "Attached are files containing accelerometer data captured by SmokeBeat Pebble app.");
    String[] dataPieces = fileManager.getListOfData(getApplicationContext());
    for(int i2 = 0; i2 < dataPieces.length; i2++){
        i.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(getApplicationContext().getFilesDir() + File.separator + dataPieces[i2])));
    }
    i.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(getApplicationContext().getFilesDir() + File.separator + fileManager.getCurrentFileName(getApplicationContext()))));
    Log.e("file loc", getApplicationContext().getFilesDir() + File.separator + fileManager.getCurrentFileName(getApplicationContext()));
    try {
        startActivity(Intent.createChooser(i, "Send Email"));
    } catch (android.content.ActivityNotFoundException ex) {
        Toast.makeText(Main.this, "There are no email clients installed.", Toast.LENGTH_SHORT).show();
    }

The datapieces might be empty yes but the current file line below the for loop is always reliable and always attaches something.

I have tried changing

Uri.fromFile()

to

Uri.parse()

When I do that, it attaches, but Gmail then crashes and when I check the logcat it's because of a null pointer. This is most likely because Gmail has no access to the file and therefore results as null.

I've also tried using

getCacheDir()

instead of

getFilesDir()

and it has the same outcome.

What am I doing wrong here, and how should I go about fixing it? Some example code would be really, really handy because I am new to Android development and explaining what I need to do without some sort of push off probably won't end up helping.

Thanks a lot.

like image 675
Edwin Finch Avatar asked Jan 15 '15 04:01

Edwin Finch


Video Answer


2 Answers

Alright guys. Took a break and came back, figured it out.

Here's how it works, you need to have write/read permissions to external storage, so add these permissions to your manifest:

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

Then, your file has to be copied from your app's internal storage directory into the app's external directory. I recommend you use internal storage, and that's what I'm doing here so you can figure out SD cards yourself.

Here is the block of code that does the magic. Logs are included but you can remove them by all means.

public void writeToExternal(Context context, String filename){
    try {
        File file = new File(context.getExternalFilesDir(null), filename); //Get file location from external source
        InputStream is = new FileInputStream(context.getFilesDir() + File.separator + filename); //get file location from internal
        OutputStream os = new FileOutputStream(file); //Open your OutputStream and pass in the file you want to write to
        byte[] toWrite = new byte[is.available()]; //Init a byte array for handing data transfer
        Log.i("Available ", is.available() + "");
        int result = is.read(toWrite); //Read the data from the byte array
        Log.i("Result", result + "");
        os.write(toWrite); //Write it to the output stream
        is.close(); //Close it
        os.close(); //Close it
        Log.i("Copying to", "" + context.getExternalFilesDir(null) + File.separator + filename);
        Log.i("Copying from", context.getFilesDir() + File.separator + filename + "");
    } catch (Exception e) {
        Toast.makeText(context, "File write failed: " + e.getLocalizedMessage(), Toast.LENGTH_LONG).show(); //if there's an error, make a piece of toast and serve it up
    }
}
like image 61
Edwin Finch Avatar answered Sep 25 '22 02:09

Edwin Finch


Encountered the same attachment denied. Permissions in manifest did not have any effect, rather do not have an effect any more since API 23. Finally solved it as follows.

1st need to check and grant permissions on run-time, I did it in my main activity:

public static final int MY_PERMISSIONS_REQUEST_READ_STORAGE=10001;
private void checkPermission(){
    if (this.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
        // Should we show an explanation?
        if (this.shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE)) {
            // Show an explanation to the user asynchronously
        } else {
            // No explanation needed, we can request the permission.
            this.requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                    MY_PERMISSIONS_REQUEST_READ_STORAGE);
        }
    }
}

Now when sending, create a file in PUBLIC directory (tried saving to my app folder - same denial problem)

public File createFile(){
String htmlStr="<!DOCTYPE html>\n<html>\n<body>\n<p>my html file</p></body></html>";
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "aimexplorersummary.html");
try {
      FileWriter writer = new FileWriter(file ,false);
      writer.write(htmlStr); 
      }
      writer.flush();
      writer.close();
   } catch (IOException e) {
     e.printStackTrace();
     return null;
   }
return file;
}

Now compose sending intent and putExtra with uri to your file which is in public storage that user must grant permissions to and that causes no problem now

public void send(){
Intent intentSend = new Intent(android.content.Intent.ACTION_SEND);
intentSend.setType("text/html"); 
File file = createFile();
if(file!=null){
    intentSend.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file));
}
startActivity(Intent.createChooser(intentSend, "Send using:"));
}
like image 39
Boris Gafurov Avatar answered Sep 22 '22 02:09

Boris Gafurov