Sharing Bitmap via Android Intent

39,246

Solution 1

I found 2 variants of the solution. Both involve saving Bitmap to storage, but the image will not appear in the gallery.

First variant:

Saving to external storage

  • but to private folder of the app.
  • for API <= 18 it requires permission, for newer it does not.

1. Setting permissions

Add into AndroidManifest.xml before tag

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

2. Saving method:

 /**
 * Saves the image as PNG to the app's private external storage folder.
 * @param image Bitmap to save.
 * @return Uri of the saved file or null
 */
private Uri saveImageExternal(Bitmap image) {
    //TODO - Should be processed in another thread
    Uri uri = null;
    try {
        File file = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "to-share.png");
        FileOutputStream stream = new FileOutputStream(file);
        image.compress(Bitmap.CompressFormat.PNG, 90, stream);
        stream.close();
        uri = Uri.fromFile(file);
    } catch (IOException e) {
        Log.d(TAG, "IOException while trying to write file for sharing: " + e.getMessage());
    }
    return uri;
}

3. Checking storage accesibility

External storage might not be accessible, so you should check it before trying to save: https://developer.android.com/training/data-storage/files

/**
 * Checks if the external storage is writable.
 * @return true if storage is writable, false otherwise
 */
public boolean isExternalStorageWritable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state)) {
        return true;
    }
    return false;
}

Second variant

Saving to cacheDir using FileProvider. It does not require any permissions.

1. Setup FileProvider in AndroidManifest.xml

<manifest>
    ...
    <application>
        ...
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.mydomain.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
                <meta-data
                    android:name="android.support.FILE_PROVIDER_PATHS"
                    android:resource="@xml/file_paths" />
        </provider>
        ...
    </application>
</manifest>

2. Add path to the res/xml/file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
         <cache-path name="shared_images" path="images/"/>
    </paths>
</resources>

3. Saving method:

 /**
 * Saves the image as PNG to the app's cache directory.
 * @param image Bitmap to save.
 * @return Uri of the saved file or null
 */
private Uri saveImage(Bitmap image) {
    //TODO - Should be processed in another thread
    File imagesFolder = new File(getCacheDir(), "images");
    Uri uri = null;
    try {
        imagesFolder.mkdirs();
        File file = new File(imagesFolder, "shared_image.png");

        FileOutputStream stream = new FileOutputStream(file);
        image.compress(Bitmap.CompressFormat.PNG, 90, stream);
        stream.flush();
        stream.close();
        uri = FileProvider.getUriForFile(this, "com.mydomain.fileprovider", file);

    } catch (IOException e) {
        Log.d(TAG, "IOException while trying to write file for sharing: " + e.getMessage());
    }
    return uri;
}

More info about file provider - https://developer.android.com/reference/android/support/v4/content/FileProvider

Compressing and saving can be time-consuming, therefore this should be done in a different thread

Actual sharing

/**
 * Shares the PNG image from Uri.
 * @param uri Uri of image to share.
 */
private void shareImageUri(Uri uri){
    Intent intent = new Intent(android.content.Intent.ACTION_SEND);
    intent.putExtra(Intent.EXTRA_STREAM, uri);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    intent.setType("image/png");
    startActivity(intent);
}

Solution 2

As CommonsWare stated you need to get the URI to the bitmap and pass that as your Extra.

String bitmapPath = Images.Media.insertImage(getContentResolver(), bitmap,"title", null);
Uri bitmapUri = Uri.parse(bitmapPath);
...
intent.putExtra(Intent.EXTRA_STREAM, bitmapUri );

Solution 3

Finally, I got the solution:

Step 1 : Share Intent handling Block. This will Pop Up your window with list of Applications in you phone

public void share_bitMap_to_Apps() {
    Intent i = new Intent(Intent.ACTION_SEND);

    i.setType("image/*");
    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    /*compress(Bitmap.CompressFormat.PNG, 100, stream);
    byte[] bytes = stream.toByteArray();*/

    i.putExtra(Intent.EXTRA_STREAM, getImageUri(mContext, getBitmapFromView(relative_me_other)));
    try {
        startActivity(Intent.createChooser(i, "My Profile ..."));
    } catch (android.content.ActivityNotFoundException ex) {
        ex.printStackTrace();
    }
}

Step 2 : Converting your view to Bitmap

public static Bitmap getBitmapFromView(View view) {
    // Define a bitmap with the same size as the view
    Bitmap returnedBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
    // Bind a canvas to it
    Canvas canvas = new Canvas(returnedBitmap);
    // Get the view's background
    Drawable bgDrawable = view.getBackground();
    if (bgDrawable != null)
        // has background drawable, then draw it on the canvas
        bgDrawable.draw(canvas);
    else
        // does not have background drawable, then draw white background on the canvas
        canvas.drawColor(Color.WHITE);
    // draw the view on the canvas
    view.draw(canvas);
    // return the bitmap
    return returnedBitmap;
}

Step 3 : To get The URI from Bitmap Image

public Uri getImageUri(Context inContext, Bitmap inImage) {
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    inImage.compress(Bitmap.CompressFormat.JPEG, 100, bytes);

    String path = MediaStore.Images.Media.insertImage(inContext.getContentResolver(), inImage, "Title", null);
    return Uri.parse(path);
}

Solution 4

ImageButton capture_share = (ImageButton) findViewById(R.id.share);
capture_share.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View view) {

    String bitmapPath = MediaStore.Images.Media.insertImage(getContentResolver(), bitmap,"title", null);
    Uri bitmapUri = Uri.parse(bitmapPath);

        Intent intent = new Intent(Intent.ACTION_SEND);
        intent.setType("image/png");
        intent.putExtra(Intent.EXTRA_STREAM, bitmapUri);
        startActivity(Intent.createChooser(intent, "Share"));
  }
});

Solution 5

Quoting the documentation:

A content: URI holding a stream of data associated with the Intent, used with ACTION_SEND to supply the data being sent.

b, therefore, is not supposed to be a Bitmap, but rather a Uri pointing to a Bitmap, served by a ContentProvider. For example, you could write the Bitmap to a file, then use FileProvider to serve it.

Share:
39,246

Related videos on Youtube

Jhilmil Chatterjee
Author by

Jhilmil Chatterjee

Updated on July 09, 2022

Comments

  • Jhilmil Chatterjee
    Jhilmil Chatterjee almost 2 years

    In my android app, I have a bitmap (say b) and a button. Now when I click on the button, I want to share the bitmap. I am making use of the below code inside my onClick() to achieve this :-

    Intent intent = new Intent(Intent.ACTION_SEND);
    intent.setType("image/png");
    intent.putExtra(Intent.EXTRA_STREAM, b);
    startActivity(Intent.createChooser(intent , "Share"));
    

    I was expecting a list of all application which are able to handle this intent but I get nothing. There is no list of apps nor is there any error in android studio. My application just get hanged for sometime and then quits.

    I have checked the bitmap and it is fine (its not null).

    Where am I a going wrong ?

  • appsthatmatter
    appsthatmatter over 7 years
    don't forget to require android.permission.WRITE_EXTERNAL_STORAGE
  • Rohit Poudel
    Rohit Poudel almost 7 years
    Add some description to answer for clear understanding. @lalit Baghel
  • Jaydip Kalkani
    Jaydip Kalkani about 6 years
    It works but also stores image. so, image is also showing in gallary. How can i avoid saving image or showing this into gallary.
  • Shajeel Afzal
    Shajeel Afzal about 6 years
    Any solution to do it without write external storage?
  • daka
    daka about 6 years
    @JaydipKalkani I need this too, but maybe we cant.
  • Morse
    Morse almost 6 years
    This is the best solution I think , step-by-step detailed explanation. Sharing with images stored in cache is better if security is of concern. I had missed a step of adding xml and provider so it was not working but finally followed the steps and it works now.
  • Pandurang Yachwad
    Pandurang Yachwad over 5 years
    Best solution over internet for sharing over email.
  • Mathijs
    Mathijs over 4 years
    The new package name for the FileProvider since Android X is: androidx.core.content.FileProvider.
  • Raphael C
    Raphael C almost 3 years
    insertImage deprecated in API level 29. inserting of images should be performed using MediaColumns#IS_PENDING, which offers richer control over lifecycle. (which will also be deprecated soon enough lol)