Skip to content

Fix image capture + image upload to work with Android "scoped storage". #4283

@gnprice

Description

@gnprice

Android 10 introduces significant changes in how apps access the device's photos, other media of several types, and files in general. Gone, in that future, are the days where your photos and documents live in a single shared storage area which lots and lots of apps have unrestricted access to, like on a desktop OS. The new model is called "scoped storage".

That future is opt-out for apps targeting Android 10, and we've had to take that opt-out -- see #3665 (comment) and #4282. But it will be mandatory for apps targeting Android 11, and targeting Android 11 will presumably be mandatory for updates on Google Play in about a year. So our opt-out is only a temporary extension of the deadline; we'll need to adapt to scoped storage

The known issues we currently run into with scoped storage are all within the react-native-image-picker library, which we use both for taking a photo to send via Zulip and picking an existing image on the device to send via Zulip. Both of those features encounter trouble:

  • On trying to take a photo (the "camera" icon), an error modal appears before the camera widget even shows up. Detailed report: Update Android targetSdkVersion to 29 (Android 10), by 2020-11 deadline #3665 (comment)
  • On trying to pick an existing image (the "photo" icon), the symptoms are different; I saw them earlier today but have forgotten exactly how it went wrong. ISTR the picker appeared successfully, but then an error occurred upon actually choosing an image.

The basic problem is that r-n-image-picker tries to handle the photos as if it had unrestricted access to them as files, and with scoped storage it no longer does. Several upstream issues seem related (I think some of them are duplicates): at least

The PR is quite simple, and the main reason it isn't already merged seems to be (react-native-image-picker/react-native-image-picker#1439 (comment)) that it gives up entirely on putting the captured image, when you use the camera, into the central "Pictures" folder shared with other apps. That is a useful feature. As a Zulip user, I prefer to keep that feature too.

The Android release notes on this change point to a guide doc on how to handle media files in the new world. I believe these are the key points for us, and for react-native-image-picker:

  • We still need READ_EXTERNAL_STORAGE permission, to read images/etc. added by other apps.
  • We no longer need WRITE_EXTERNAL_STORAGE permission, and shouldn't request it on newer Android versions.
  • Whenever we're creating an image file ourselves -- either with the camera, or by resizing or rotating:
    • We need to do it in our own storage area; in other words storageOptions.privateDirectory should be ignored and effectively treated as true.
    • If we want to make the new image available to other apps (so, if storageOptions.cameraRoll is true), we can add it to the MediaStore.Images "media collection". (Or MediaStore.Video, for videos.) Once we do that, other apps that have READ_EXTERNAL_STORAGE permission will be able to see the image. Here's an example showing how to add it.
  • It might not be possible to go and read the EXIF data, which the existing code does in order to perform auto-rotation. But instead, we should be able to get it with a MediaStore query, using the MediaStore.MediaColumns.ORIENTATION column. That should be more efficient anyway, because it's already there in the database rather than having to parse through the image file. (In Zulip we don't use this feature.)

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions