Camera availability tip

Nikola Despotoski
2 min readJan 15, 2017
[Courtesy of gringo4tech]

It’s been a lot of time since I have written something interesting on Android. Following post is on how should we properly listen for camera being released. Duh!

Assume we are have to make our Service aware of camera usage, like Google Photos does it with the option to show Photos logo after you capture new image, for quick navigation to Photos gallery.

Their Service is triggered byJobScheduler that starts job when there are changes in MediaStore.Images.Media.EXTERNAL_CONTENT_URI. But how do they manage to remove fb-chat-head-like-shortcut-icon once you close the camera app? They are waiting for the app to release the camera (≥ Lollipop) and wait for camera app to be removed from foreground (< Lollipop).

There are two concerns here:
- API levels of the given CameraManager API methods
- API levels of the given ActivityManager API methods

Base class:

A centralized way to listen for camera availability.

public class CameraAvailability {
public static CameraAvailability get(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return new CameraAvailabilityLollipop(context);
}
return new CameraAvailabilityPreLolipop(context);
}

void waitForCameraBeingAvailable(OnCameraReleasedListener l) {
throw new UnsupportedOperationException("Don't call super!");
}
interface OnCameraReleasedListener {
void onCameraAvailable();
}
}

Lollipop and above

In camera2 package CameraManager offers really simple way to listen for camera availability, just by using registerAvailabilityCallback() method

Prior Lollipop

After Lollipop there was uproar about how to get current visible activity, since getAppTasks() joined deprecation club. This is really great opportunity to leverage ActivityManager.

Here we are monitoring the current top activity is any of the list of activities that can handle ACTION_IMAGE_CAPTURE intent action. Once the current activity is no longer capable of handling our target intent action, it means that camera has been released. Recursive Runnable checks every 200ms.

On Stackoverflow there is a proposed way to use old Camera API and anticipating a RuntimeException to be thrown if the camera is used when we are attempting to open it.

I’m not fan of this proposed way, because it tries to throw exception and dumping the stacktrace might slow down your device, and it tries to open() and immediately release() the camera just to return boolean.

Usage:

private void registerForCameraAvailability() {
CameraAvailability cameraAvailability = CameraAvailability.get(this);
cameraAvailability.waitForCameraBeingAvailable(this);
}

--

--