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’sonActivityResult()
method.
Oh, if it were only this easy.
The theory is sound, but the practice is unrealistic. The problem is three-fold:
-
The rules for what
ACTION_IMAGE_CAPTURE
is supposed to do are fairly limited, at least in terms of what is documented -
I am not aware that the CTS actually tests
ACTION_IMAGE_CAPTURE
-
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, becauseACTION_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 isdata
). 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 forEXTRA_OUTPUT
are. Is that limited only tofile://
Uri
values? Will acontent://
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.