Rasmus Rajje Josefsson Rasmus Rajje Josefsson - 6 months ago 45
Android Question

Attach a pdf to email, grant permission

If I grant

READ_EXTERNAL_STORAGE
permission all works good, but can I get it to work without asking? It worked without It on my emulator, but on a real device it doesn't work without it.

Output stream

String fileName = "Körjorunal_" + lastUpdateTime + ".PDF";
String path = Environment.getExternalStorageDirectory().toString();

File file = new File(path, fileName)
pdfUri = Uri.fromFile(file);

OutputStream outputStream = new FileOutputStream(file);
document.writeTo(outputStream);
document.close();
outputStream.close();


Intent

// Gets invoked on setup,
mShareIntent = new Intent();
mShareIntent.setAction(Intent.ACTION_SEND);
mShareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
mShareIntent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

// Creates document type.
mShareIntent.setType("application/pdf");

// Going via email:
mShareIntent.putExtra(Intent.EXTRA_SUBJECT, "Körjournal i bifogad PDF");

// Attach the PDf Uri.
mShareIntent.putExtra(Intent.EXTRA_STREAM, pdfUri);

mShareActionProvider.setShareIntent(mShareIntent);


Manifest

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

Answer

FLAG_GRANT_*_URI_PERMISSION (for READ and WRITE) really are for where the Uri is from your own ContentProvider. If you want to avoid the need for the WRITE_EXTERNAL_STORAGE (and superfluous READ_EXTERNAL_STORAGE) permission:

Step #1: Write the file to internal storage, such as getCacheDir()

Step #2: Serve that file from internal storage using FileProvider

Step #3: Use your existing ACTION_SEND logic, substituting the Uri that you get from FileProvider

You will see that pattern used in this sample project. My sample is for ACTION_VIEW, not ACTION_SEND, but otherwise it is a close match.

One key difference, though, will be in how you grant the permissions, if your minSdkVersion is below 21. Uri values in extras were ignored by FLAG_GRANT_*_URI_PERMISSION on Android 4.4 and below. For those versions, the only solution that I know of is to manually grant rights to every possible app (in your case, every PDF viewer).

In this sample app, I use ACTION_IMAGE_CAPTURE to take a picture, and I use a FileProvider Uri for that (since file Uri values are going away). Just as you are using EXTRA_STREAM with a Uri, I am using EXTRA_OUTPUT, and so I run into the same Uri-in-an-extra permission problem. And, so, I go through all this mess:

  i.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);

  if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP) {
    i.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
  }
  else {
    List<ResolveInfo> resInfoList=
      getPackageManager()
        .queryIntentActivities(i, PackageManager.MATCH_DEFAULT_ONLY);

    for (ResolveInfo resolveInfo : resInfoList) {
      String packageName = resolveInfo.activityInfo.packageName;
      grantUriPermission(packageName, outputUri,
        Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
          Intent.FLAG_GRANT_READ_URI_PERMISSION);
    }
  }

  startActivityForResult(i, CONTENT_REQUEST);

(here, i is my ACTION_IMAGE_CAPTURE Intent)

So, I grant any ACTION_IMAGE_CAPTUREIntentthe rights to work with thisUri` on Android 4.4 and older.