Google is committed to advancing racial equity for Black communities. See how.

Camera enumeration

Current Android devices can have two or more cameras in the front, in the back, or both. That can be a lot of lenses to choose from.

To accommodate all device configurations, check the camera list and camera characteristics:

val cameraIdList = cameraManager.cameraIdList // may be empty
val characteristics = cameraManager.getCameraCharacteristics(cameraId)
val cameraLensFacing = characteristics.get(CameraCharacteristics.LENS_FACING)
val cameraCapabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
val cameraCompatible = cameraCapabilities?.contains(
        CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) ?: false

The variable cameraLensFacing will be one of:

For more information about the lens-facing configuration, see CameraCharacteristics.LENS_FACING.

The variable cameraCapabilities from the preceding code sample contains information about miscellaneous capabilities, including whether the camera is able to produce standard frames as an output (as opposed to, for example, only depth sensor data). You can look for whether CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE is one of the camera’s listed capabilities, which is stored as a flag in cameraCompatible.

Choose sensible defaults

Depending on the use case of your app, you may want to open a specific camera lens configuration by default, if it’s available. For example, a selfie app likely opens the front-facing camera, while an augmented reality app might start with the back camera. You can wrap this logic into a function that properly handles the cases mentioned above:

fun getFirstCameraIdFacing(cameraManager: CameraManager,
                           facing: Int = CameraMetadata.LENS_FACING_BACK): String? {
    // Get list of all compatible cameras
    val cameraIds = cameraManager.cameraIdList.filter {
        val characteristics = cameraManager.getCameraCharacteristics(it)
        val capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
        capabilities?.contains(
                CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) ?: false
    }

    // Iterate over the list of cameras and return the first one matching desired
    // lens-facing configuration
    cameraIds.forEach {
        val characteristics = cameraManager.getCameraCharacteristics(it)
        if (characteristics.get(CameraCharacteristics.LENS_FACING) == facing) {
            return it
        }
    }
    // If no camera matched desired orientation, return the first one from the list
    return cameraIds.firstOrNull()
}

Enable switching cameras

Many camera apps give users the option to switch between cameras:

Figure 1. Switch camera button in the Google Camera app

There are many devices with multiple cameras facing the same way, or that have external cameras connected via USB. To provide the user with a UI that lets them switch between different facing cameras, choose the first available camera for each possible lens-facing configuration.

For information on how to do this, see CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA.

Although there is no universal logic for selecting the next camera, the following code works for most use cases:

fun filterCompatibleCameras(cameraIds: Array<String>,
                            cameraManager: CameraManager): List<String> {
    return cameraIds.filter {
        val characteristics = cameraManager.getCameraCharacteristics(it)
        characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)?.contains(
                CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) ?: false
    }
}

fun filterCameraIdsFacing(cameraIds: List<String>, cameraManager: CameraManager,
                          facing: Int): List<String> {
    return cameraIds.filter {
        val characteristics = cameraManager.getCameraCharacteristics(it)
        characteristics.get(CameraCharacteristics.LENS_FACING) == facing
    }
}

fun getNextCameraId(cameraManager: CameraManager, currCameraId: String? = null): String? {
    // Get all front, back and external cameras in 3 separate lists
    val cameraIds = filterCompatibleCameras(cameraManager.cameraIdList, cameraManager)
    val backCameras = filterCameraIdsFacing(
            cameraIds, cameraManager, CameraMetadata.LENS_FACING_BACK)
    val frontCameras = filterCameraIdsFacing(
            cameraIds, cameraManager, CameraMetadata.LENS_FACING_FRONT)
    val externalCameras = filterCameraIdsFacing(
            cameraIds, cameraManager, CameraMetadata.LENS_FACING_EXTERNAL)

    // The recommended order of iteration is: all external, first back, first front
    val allCameras = (externalCameras + listOf(
            backCameras.firstOrNull(), frontCameras.firstOrNull())).filterNotNull()

    // Get the index of the currently selected camera in the list
    val cameraIndex = allCameras.indexOf(currCameraId)

    // The selected camera may not be on the list, for example it could be an
    // external camera that has been removed by the user
    return if (cameraIndex == -1) {
        // Return the first camera from the list
        allCameras.getOrNull(0)
    } else {
        // Return the next camera from the list, wrap around if necessary
        allCameras.getOrNull((cameraIndex + 1) % allCameras.size)
    }
}

This code accounts for a large set of devices with many different configurations.

Create compatible apps

For apps still using the deprecated Camera API, the number of cameras that Camera.getNumberOfCameras() returns depends on OEM implementation. If there is a logical multi-camera in the system, to maintain app backward compatibility, this method will only expose one camera for every logical camera and underlying physical cameras group. Use camera2 API to see all cameras.

For more background information on camera orientations, see Camera.CameraInfo.orientation.

In general, use the Camera.getCameraInfo() API to query all camera orientations, and expose only one camera for each available orientation to users that are switching between cameras.

Accommodate all device types

Android runs on many different devices. You should not assume that your app will always run on a traditional handheld device with one or two cameras. Pick the most appropriate cameras for the app. If you don't need a specific camera, select the first camera with the desired lens-facing configuration. If there are external cameras connected, it may be reasonable to assume that the user would prefer to see those first.