9

I have an app that shares images from url. Last android update, I got message from instagram "Unable to load image" when I want to share an image in instagram feed.

But i can share image story, direct message, and everywhere... I am having this issue only instagram feed.

public void onShareItemOreo() {
    // Get access to bitmap image from view
    imageView = (ImageView) findViewById(R.id.thumbnail);

    // Get access to the URI for the bitmap
    Uri bmpUri = prepareShareIntent(imageView);
    if (bmpUri != null) {

        //outfile is the path of the image stored in the gallery
        // Construct a ShareIntent with link to image
        Intent shareIntent = new Intent();
        shareIntent.setAction(Intent.ACTION_SEND);
        shareIntent.putExtra(Intent.EXTRA_STREAM, bmpUri);
        shareIntent.setData(bmpUri);
        shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        shareIntent.setType("image/*");
        shareIntent.putExtra(Intent.EXTRA_TEXT,marketLink);
        // Launch sharing dialog for image
        startActivity(Intent.createChooser(shareIntent, "Share Image"));
    } else {
        //
    }
}


public Uri prepareShareIntent(ImageView imageView) {

    // Fetch Bitmap Uri locally
    Drawable drawable = imageView.getDrawable();
    Bitmap bmp = null;
    if (drawable instanceof BitmapDrawable){
        bmp = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
    } else {
        return null;
    }

    Uri bmpUri = getBitmapFromDrawable(bmp);// see previous remote images section and notes for API > 23
    // Construct share intent as described above based on bitmap
    Intent shareIntent = new Intent();
    shareIntent.setAction(Intent.ACTION_SEND);
    shareIntent.putExtra(Intent.EXTRA_STREAM, bmpUri);
    shareIntent.setType("image/*");

    return bmpUri;
}
MMG
  • 3,226
  • 5
  • 16
  • 43
jancooth
  • 555
  • 1
  • 10
  • 25

6 Answers6

5

Update: This issue was fixed in the version of Instagram released earlier this week. Workarounds no longer necessary.


None of the solutions mentioned above worked for me, as it seems that direct sharing via ContentProvider or its derivative FileProvider was broken by a change made within the Instagram app.

I did notice that sharing a MediaStore content Uri still works, as other apps such as Google Photos that write to the MediaStore prior to sharing were still able to share images to feed.

You can insert an image File to the MediaStore as follows:

@SuppressLint("InlinedApi")
fun insertImageToMediaStore(file: File, relativePath: String): Uri? {

    val values = ContentValues().apply {
        put(MediaStore.Images.Media.DISPLAY_NAME, file.name)

        val mimeType = when (file.extension) {
            "jpg", "jpeg" -> "jpeg"
            "png" -> "png"
            else -> return null
        }

        put(MediaStore.Images.Media.MIME_TYPE, "image/$mimeType")

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            put(MediaStore.MediaColumns.RELATIVE_PATH, relativePath)
            put(MediaStore.MediaColumns.IS_PENDING, 1)
        }
    }

    val collection = when (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        true -> MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
        false -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
    }

    val uri = contentResolver.insert(collection, values)

    uri?.let {
        contentResolver.openOutputStream(uri)?.use { outputStream ->
            try {
                outputStream.write(file.readBytes())
                outputStream.close()
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }


        values.clear()

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            values.put(MediaStore.Images.Media.IS_PENDING, 0)
            contentResolver.update(uri, values, null, null)
        }

    } ?: throw RuntimeException("MediaStore failed for some reason")

    return uri
}

Then with that Uri that you're returned, share via Intent as follows:

    val filePath = "/data/data/io.jammy.withintent/files/IMG-20200321_093350_2020-122758.jpg" // this is an example path from an app-internal image file

    val context: Context? = this
    val intent = Intent(Intent.ACTION_SEND)
    intent.type = "image/*"

    insertImageToMediaStore(File(filePath), "Pictures/Your Subdirectory")?.let { uri ->

        val clipData = ClipData.newRawUri("Image", uri)

        intent.clipData = clipData
        intent.putExtra(Intent.EXTRA_STREAM, uri)

        val target = Intent.createChooser(intent, "Share Image")
        target?.let { context?.startActivity(it) }

    } ?: run {
        Log.e(TAG, "Unsupported image file")
        return
    }

Whilst it's not ideal, as the image is then written to the MediaStore, which may not be desired behaviour in many cases, it re-enables the ability to share in the medium term whilst Instagram fixes their whoopsie.

MattMatt
  • 2,242
  • 33
  • 30
  • 1
    Thanks @MattMatt, I will try to do the same with Java, do you know if it possible? I'm not android/java developer há – mauriblint Mar 26 '20 at 17:15
  • 1
    You're welcome @mauriblint. It's definitely possible to do this in Java, in fact the Kotlin compiler creates Java byte code at build time under the hood anyway! This Gist might be helpful to you: https://gist.github.com/benny-shotvibe/1e0d745b7bc68a9c3256 – MattMatt Mar 26 '20 at 22:06
  • 1
    Thanks a lot, @MattMatt! I could write the solution in Java following your Kotlin code, really appreciated, I have been dealing with this issue for almost 7 days – mauriblint Mar 27 '20 at 02:00
  • 1
    hey @MattMatt what about video sharing? Are any key values we need to change? – Choletski Mar 29 '20 at 13:48
  • Hey @Choletski, you can, change the `MediaStore.Images.Media.*` calls to `MediaStore.Video.Media.*`, and ensure that you're setting the MIME type string to a video type that matches the file that you're sending. Here's a Gist that shows how to handle either an image or video file using the above method: https://gist.github.com/noln/584afabed1362fabbc2516275a5f755b – MattMatt Mar 29 '20 at 19:58
  • @MattMatt, it seems to work for me but on Android 10 I get an error: `Caused by: java.lang.IllegalArgumentException: Primary directory Videos not allowed for content://media/external/video/media; allowed directories are [DCIM, Movies]` – Choletski Mar 30 '20 at 20:52
  • @Choletski That's not Android 10 specific, you need to put the media in the DCIM or Movies directory as the error says. "Videos" isn't a valid MediaStore prime directory. Check this line out: https://gist.github.com/noln/584afabed1362fabbc2516275a5f755b#file-mainactivity-kt-L84 It's also mentioned here in the docs: https://developer.android.com/training/data-storage/shared/media – MattMatt Mar 30 '20 at 21:06
4

Facebook is working on this bug, let's wait ! Subscribe to receive notification of updates to this bug report. https://developers.facebook.com/support/bugs/1326888287510350/

1

your uri is "content://packagename/xxx.jpg", it need to be "content://media/external/images/media/..."; it will be worked.

lost000117
  • 71
  • 3
1

Problem solved with latest instagram update

jancooth
  • 555
  • 1
  • 10
  • 25
0

Ok, I searched and found a solution. I do not know this is right way but solved my problem..

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());

Found solution in this answer.

jancooth
  • 555
  • 1
  • 10
  • 25
0

Looks like Facebook already has the bug for this issue: https://developers.facebook.com/support/bugs/1326888287510350/

As a temporary workaround you can save media to MediaStore. This is the method we use to store and then return the uri for Instagram sharing.

    private fun insertImageToMediaStore(file: File): Uri? {

    val fileUri = FileProvider.getUriForFile(
        context,
        "${context.applicationContext.packageName}.provider",
        file
    )
    val mimeType = context.contentResolver.getType(fileUri) ?: "image/*"
    val isImage = mimeType.contains("image")
    val values = ContentValues().apply {

        put(
            if (isImage) {
                MediaStore.Images.Media.DISPLAY_NAME
            } else {
                MediaStore.Video.Media.DISPLAY_NAME
            },
            file.name
        )

        put(
            if (isImage) {
                MediaStore.Images.Media.MIME_TYPE
            } else {
                MediaStore.Video.Media.MIME_TYPE
            },
            mimeType
        )
    }

    val collection = if (isImage) {
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI
    } else {
        MediaStore.Video.Media.EXTERNAL_CONTENT_URI
    }

    val uri = context.contentResolver.insert(collection, values)

    uri?.let {
        context.contentResolver.openOutputStream(uri)?.use { outputStream ->
            try {
                outputStream.write(file.readBytes())
                outputStream.close()
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }


        values.clear()

    } ?: throw RuntimeException("MediaStore failed for some reason")

    return uri
}
nickolay
  • 3,643
  • 3
  • 32
  • 40