Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: How to use grantUriPermission to be able to create and send an email with a bitmap attachment

From within my application, I'm trying to create an email that contains an image contained in a bitmap object.

private void sendEmailWithBitmapAttached(){ 

    final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
    emailIntent.setType("plain/text");
    emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, "Email Subject");
    emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, "Email Body");
    emailIntent.setType("image/png");
    ContentResolver cr = getContentResolver();

    // insert the image and create a path
    String imageBitmapPath = MediaStore.Images.Media.insertImage(cr, bitmapForEmail,"title", "description");

    // create a uri
    Uri imageUri = Uri.parse(imageBitmapPath);
    emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    emailIntent.putExtra(Intent.EXTRA_STREAM, imageUri);

    // send the email
    startActivity(Intent.createChooser(emailIntent, "Send mail..."));
}

This works fine in Android 2.3.

But using later versions, it produces the following error:

07-13 23:01:01.252: E/MediaStore(5194): Failed to insert image
 07-13 23:01:01.252: E/MediaStore(5194): java.lang.SecurityException: 
     Permission Denial: 
         writing com.android.providers.media.MediaProvider 
         uri content://media/external/images/media from 
             pid=5194, uid=10151 requires 
             android.permission.WRITE_EXTERNAL_STORAGE, 
             or grantUriPermission()

So, taking the suggestion of the error message, I tried to grantUriPermission.

grantUriPermission(String toPackage, Uri uri, int modeFlags)

But I am not sure what to put for toPackage or uri

But again, using the error message, I modified the code as follows:

private void sendEmailWithBitmapAttached(){ 

    final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
    emailIntent.setType("plain/text");
    emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, "Email Subject");
    emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, "Email Body");
    emailIntent.setType("image/png");
    ContentResolver cr = getContentResolver();

    // create a Uri for the content provider suggested by the error message
    Uri uri = Uri.parse("content://media/external/images/media");

    // create a package provider string suggested by the error messge.
    String provider = "com.android.providers.media.MediaProvider";


    // grant all three uri permissions!
    grantUriPermission(provider, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
    grantUriPermission(provider, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    grantUriPermission(provider, uri, Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);

    // insert the image and create a path
    String imageBitmapPath = MediaStore.Images.Media.insertImage(cr, bitmapForEmail,"title", "description");

    // create a uri
    Uri imageUri = Uri.parse(imageBitmapPath);
    emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    emailIntent.putExtra(Intent.EXTRA_STREAM, imageUri);

    // send the email
    startActivity(Intent.createChooser(emailIntent, "Send mail..."));
}

And I get the exact same error.

Can a kind soul please give me a hint as how to take care of grantUriPermission's uri and provider items? Is this the correct approach?

Thank you very much for ANY help, hint, guides, or suggestion, you can provide!

like image 541
user3835673 Avatar asked Jul 14 '14 06:07

user3835673


People also ask

How do you use Granturipermission?

Try including this in your AndroidManifest. xml. On Android 6.0+ the permission must be granted by the user to the application otherwise this will throw the SecurityException. To do this go into Settings/Apps/[AppName]/Permissions and allow the Storage Permission.

What is Intent action_ send?

Android uses the action ACTION_SEND to send data from one activity to another, even across process boundaries. You need to specify the data and its type. The system automatically identifies the compatible activities that can receive the data and displays them to the user.

What is Android Intent action view?

An intent allows you to start an activity in another app by describing a simple action you'd like to perform (such as "view a map" or "take a picture") in an Intent object.

What is grant Uri permission?

Data subsets are indicated by the path part of a content: URI. (The authority part of the URI identifies the content provider.) Granting permission is a way of enabling clients of the provider that don't normally have permission to access its data to overcome that restriction on a one-time basis.


2 Answers

I had a similar issue. Below is how I solved the problem for my project. You should be able to adapt this for your solution. This solution also has some Firebase code, which you can ignore. The key points are ActivityCompat.requestPermissions and ActivityCompat.checkSelfPermission:

private void shareViaEmail() {
    int permissionCheck = ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
    if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
        if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
            AlertDialog.Builder alertBuilder = new AlertDialog.Builder(this);
            LayoutInflater inflater = this.getLayoutInflater();
            final ViewGroup nullParent = null;
            final View dialogView = inflater.inflate(R.layout.alert_dialog, nullParent);
            alertBuilder.setView(dialogView);

            alertBuilder.setCancelable(true);
            alertBuilder.setTitle("Permission request");

            String message = "\n\n" + getString(R.string.email_images);
            AppCompatTextView notice = (AppCompatTextView) dialogView.findViewById(R.id.notice);
            if (notice != null) {
                notice.setText(message);
                notice.setTextSize(getResources().getInteger(R.integer.dialog_text_size));
            }
            else {
                alertBuilder.setMessage(message);
            }

            alertBuilder.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {

                @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
                public void onClick(DialogInterface dialog, int which) {
                    mShowingAlert = false;
                    ActivityCompat.requestPermissions(baseActivity(), new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, MY_TARGET_WRITE_PERMISSION_REQUEST);
                }
            });

            mAlertDialog = alertBuilder.create();
            mAlertDialog.show();
            return;
        }
    }
    ActivityCompat.requestPermissions(this,
            new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE},
            MY_TARGET_WRITE_PERMISSION_REQUEST);
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
        if (requestCode == MY_TARGET_WRITE_PERMISSION_REQUEST) {
            doShareViaEmail();
        } else {
            failWriteImage();
        }
    }
}

protected void failWriteImage() {
    getHandler().post(new Runnable() {
        @Override

        public void run() {
            String email_failed = getResources().getString(R.string.fail_email_attach);
            @SuppressLint("ShowToast") Toast toast = Toast.makeText(getApplicationContext(), email_failed, Toast.LENGTH_SHORT);
            new CustomToast(toast).invoke();
        }
    });
}

protected void doShareViaEmail() {
    FireUtilities fireUtilities = FireUtilities.getInstance();
    fireUtilities.logEvent(mFirebaseAnalytics, "option_item", "share", "share_string");

    Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    intent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.string_share));

    String appStoreLink = getString(R.string.app_store_link);

    String emailText = getString(R.string.share_string_body1)
            + " " + appStoreLink
            + " " + getString(R.string.share_string_body2);

    emailText = emailText + "<br><br>" + getTitleOfSegment(true, mCurrentSegment, mCurrentSegmentIndex);

    Bitmap targetImage = screenShot(mTargetImageView);
    if (targetImage != null) {
        ArrayList<Uri> files = new ArrayList<Uri>();
        String path = MediaStore.Images.Media.insertImage(getContentResolver(), targetImage, "string", null);
        Uri targetUri = Uri.parse(path);
        files.add(targetUri);
        intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, files);
    }

    intent.setType("message/rfc822");

    Spanned htmlText = fromHtml(getString(R.string.share_score_head1)
            + emailText + getString(R.string.share_score_head2));
    intent.putExtra(Intent.EXTRA_TEXT, htmlText);

    try {
        startActivityForResult(Intent.createChooser(intent, "Email:"), 1234);
    } catch (final android.content.ActivityNotFoundException e) {
        String no_email_client = getResources().getString(R.string.no_email_client);
        @SuppressLint("ShowToast") Toast toast = Toast.makeText(getApplicationContext(), no_email_client, Toast.LENGTH_LONG);
        new CustomToast(toast).invoke();
    }
}

public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == 1234) {
        LogHelper.v(TAG, "e-mail successfully sent");
    }
}

public Bitmap screenShot(View view) {
    if (view != null) {
        Bitmap bitmap = Bitmap.createBitmap(view.getWidth(),
                view.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        view.draw(canvas);
        return bitmap;
    }
    return null;
}
like image 133
Lee Hounshell Avatar answered Nov 15 '22 22:11

Lee Hounshell


Try including this in your AndroidManifest.xml.

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

On Android 6.0+ the permission must be granted by the user to the application otherwise this will throw the SecurityException.

To do this go into Settings/Apps/[AppName]/Permissions and allow the Storage Permission.

like image 43
Badrul Avatar answered Nov 15 '22 22:11

Badrul