4

We are building a quiz instant app, where the user can complete the quiz and then share their result. We share some text with a link, and also an image that shows the user's quiz result. There is no problem when we go through this flow in the installed app, however in the instant app, the image fails to share.

Here is how we generate the intent:

val uri = FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", image)
val shareIntent = Intent().apply {
    action = Intent.ACTION_SEND
    putExtra(Intent.EXTRA_TEXT, content)
    putExtra(Intent.EXTRA_STREAM, uri)
    type = "image/*"
    addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
activity?.startActivity(Intent.createChooser(shareIntent, getString(R.string.quiz_share_title)))

Here is the provider in our base application manifest:

<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/fileprovider" />
</provider>

When the user shares the image in the instant app, this error message appears in logcat:

java.lang.SecurityException: Permission Denial: reading androidx.core.content.FileProvider uri content://com.redacted.fileprovider/shared/1563809004297.png from pid=29184, uid=1000 requires the provider be exported, or grantUriPermission()

I have tried setting exported="true", and that crashes the instant app on startup with the following exception:

java.lang.RuntimeException: Unable to get provider androidx.core.content.FileProvider: java.lang.SecurityException: Provider must not be exported

I'm guessing that instant apps can't use the FLAG_GRANT_READ_URI_PERMISSION flag, for the same reason that they can't use the WRITE_EXTERNAL_STORAGE permission.

Is there another way we can share images in instant apps?

  • is that image stored in your mobile directory? – Jawad Malik Jul 23 '19 at 10:17
  • @JawadMalik the image generated by the app and is stored in internal storage using `Context.getFilesDir` – Jamie Adkins Jul 23 '19 at 10:23
  • I don't think you can, Instant Apps do not get to have EXTERNAL_STORAGE_PERMISSION, see this, https://developer.android.com/topic/google-play-instant/getting-started/instant-enabled-app-bundle#configure-permissions – Ashish Kumar Jul 23 '19 at 10:35

3 Answers3

1

An instant app can not have an exported ContentProvider. This is a security restriction and crashing the app here is working as intended.

You could use InstantApps.showInstallPrompt() before firing the Intent in order to get users to install the app before doing this. Please make sure that you display a message containing your rationale or otherwise you might confuse your users.

There are other ways to share images using instant apps. But these depend on where the image is coming from. In case of an external content provider (i.e. the Camera app's) you should be able to forward the URI.

Ben Weiss
  • 17,182
  • 6
  • 67
  • 87
  • Understood. Thanks for your response. The image is generated by the app. In this case we are drawing a View into a bitmap and saving that. Just to 100% confirm, we are not actually looking to export the ContentProvider, it is just what the error message suggested. Ideally we would just use `FLAG_GRANT_READ_URI_PERMISSION` in the intent and just expose that particular image. Is it possible to improve the documentation/error message here? It was not obvious what the problem was. – Jamie Adkins Jul 23 '19 at 15:36
0

use this piece of code to share image from directory:

private void shareImage() {
    Intent share = new Intent(Intent.ACTION_SEND);

    // If you want to share a png image only, you can do:
    // setType("image/png"); OR for jpeg: setType("image/jpeg");
    share.setType("image/*");

    // Make sure you put example png image named myImage.png in your
    // directory
    String imagePath = Environment.getExternalStorageDirectory()
            + "/myImage.png";

    File imageFileToShare = new File(imagePath);

    Uri uri = Uri.fromFile(imageFileToShare);
    share.putExtra(Intent.EXTRA_STREAM, uri);

    startActivity(Intent.createChooser(share, "Share Image!"));
}
Jawad Malik
  • 608
  • 5
  • 21
0

You can share anythings using Intent class

Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, "Image URL");
startActivity(intent);