The ACTION_IMAGE_CAPTURE Fallacy

The documentation for the new permission system in the M Developer Preview has:

In many cases, you can choose between two ways for your app to perform a task. You can have your app ask for permission to perform the operation itself. Alternatively, you can have the app use an intent to have another app perform the task.

For example, suppose your app needs to be able to take pictures with the device camera. Your app can request the android.permission.CAMERA permission, which allows your app to access the camera directly. Your app would then use the camera APIs to control the camera and take a picture. This approach gives your app full control over the photography process, and lets you incorporate the camera UI into your app.

However, if you don’t need such control, you can just use an ACTION_IMAGE_CAPTURE intent to request an image. When you start the intent, the user is prompted to choose a camera app (if there isn’t already a default camera app), and that app takes the picture. The camera app returns the picture to your app’s onActivityResult() method.

Oh, if it were only this easy.

The theory is sound, but the practice is unrealistic. The problem is three-fold:

  1. The rules for what ACTION_IMAGE_CAPTURE is supposed to do are fairly limited, at least in terms of what is documented

  2. I am not aware that the CTS actually tests ACTION_IMAGE_CAPTURE

  3. There is no test suite that I am aware of to test arbitrary camera apps’ implementation of ACTION_IMAGE_CAPTURE, let alone anyone actually using it to test their camera apps

As a result, ACTION_IMAGE_CAPTURE is not reliable or consistent.

For example:

  • Some cameras will store photos taken in portrait mode as portrait images. Some cameras will store photos taken in portrait mode as landscape, setting an EXIF header to say “hey, image viewer, could you rotate this for me please, kthxbye”. Since BitmapFactory ignores this header, Android apps frequently load these images as landscape, ignoring the header. And either behavior is perfectly legitimate, because ACTION_IMAGE_CAPTURE does not specify one way or another.

  • Some cameras will record images taken with the front-facing camera as-is. Some will mirror the images, to make them appear more like what the user saw in the camera preview. And either behavior is perfectly legitimate, because ACTION_IMAGE_CAPTURE does not specify one way or another.

  • The documentation says that by default the camera should return “a small sized image” in an unfortunately undocumented Intent extra (hint: the key is data). But there are no standards as to what “a small sized image” means. It has to be less than 1MB due to IPC constraints, but whether it is a 120x80 thumbnail or a 512x512 image is up to the camera app.

  • The documentation says that you can use EXTRA_OUTPUT to indicate where the image should be written as a full-size image. But the documentation does not state what valid values for EXTRA_OUTPUT are. Is that limited only to file:// Uri values? Will a content:// Uri work, if the provider supports write access? Again, any behavior of the camera is perfectly fine, insofar as there is no right or wrong answer, even if the behavior difference is important to developers.

Stack Overflow is littered with people trying to use ACTION_IMAGE_CAPTURE and having difficulty dealing with the wide range of results from camera apps. It’s the primary reason I wrote a library to try to make it easier for apps to have consistent behavior, and why I am rewriting that library (since my first attempt gots issues, yo). I sincerely wish I didn’t feel the need to write these things, as there are lots of other things that I would like to do with my time.

Google, in the interests of granting implementors flexibility, has constrained the practicality of using stuff like ACTION_IMAGE_CAPTURE. Lots of implicit Intent actions tend to work OK, because they do not return any results, and so are simply one-way communications channels, like Web page links. But any implicit Intent that is supposed to return results — basically, anything using startActivityForResult() — has the potential to be unreliable, if there are many implementations that offer to handle that Intent. ACTION_IMAGE_CAPTURE is one of the worst in that regard.

Google’s recommendation of relying on third-party apps for things like basic picture-taking still hold true, but only in cases where the app needing the picture does not really care about the picture, and so whatever the other app happens to hand back will be just fine, thanks. If Google wants developers to rely on third-party apps for more than really trivial stuff, Google needs to tighten up the contract between both sides, so everybody knows what everybody is expecting.