Camera2CameraFilter.java

/*
 * Copyright 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.camera.camera2.interop;

import android.hardware.camera2.CameraCharacteristics;

import androidx.annotation.NonNull;
import androidx.camera.camera2.internal.Camera2CameraInfoImpl;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraFilter;
import androidx.camera.core.CameraInfo;
import androidx.core.util.Preconditions;

import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;

/**
 * Provides ability to filter cameras with camera IDs and characteristics and create the
 * corresponding {@link CameraFilter}.
 */
@ExperimentalCamera2Interop
public final class Camera2CameraFilter {

    /**
     * Creates a {@link CameraFilter} from a {@link Camera2Filter}.
     */
    @NonNull
    public static CameraFilter createCameraFilter(@NonNull Camera2Filter filter) {
        return (cameras) -> {
            LinkedHashMap<String, Camera> cameraMap = new LinkedHashMap<>();
            LinkedHashMap<String, CameraCharacteristics> characteristicsMap =
                    new LinkedHashMap<>();
            for (Camera camera : cameras) {
                CameraInfo cameraInfo = camera.getCameraInfo();
                Preconditions.checkState(cameraInfo instanceof Camera2CameraInfoImpl,
                        "CameraInfo does not contain any Camera2 information.");
                Camera2CameraInfoImpl camera2CameraInfoImpl =
                        (Camera2CameraInfoImpl) cameraInfo;
                cameraMap.put(camera2CameraInfoImpl.getCameraId(), camera);
                characteristicsMap.put(camera2CameraInfoImpl.getCameraId(),
                        camera2CameraInfoImpl.getCameraCharacteristics());
            }

            LinkedHashMap<String, CameraCharacteristics> resultMap = filter.filter(
                    characteristicsMap);

            LinkedHashSet<Camera> resultCameras = new LinkedHashSet<>();
            for (Map.Entry<String, CameraCharacteristics> entry : resultMap.entrySet()) {
                String cameraId = entry.getKey();
                // The extra camera IDs not contained in the camera map will be ignored.
                if (cameraMap.containsKey(cameraId)) {
                    resultCameras.add(cameraMap.get(cameraId));
                } else {
                    throw new IllegalArgumentException(
                            "There are camera IDs not contained in the original camera map.");
                }
            }

            return resultCameras;
        };
    }

    /**
     * An interface that filters cameras based on camera IDs and characteristics. Applications
     * can implement the filter method for custom camera selection.
     */
    public interface Camera2Filter {
        /**
         * Filters a map of camera IDs and their {@link CameraCharacteristics} then returns those
         * matching the requirements.
         *
         * <p>If the key set of the output map contains camera IDs not in the key set of the
         * input map, when used by a {@link androidx.camera.core.CameraSelector} then it will
         * result in an IllegalArgumentException thrown when calling bindToLifecycle.
         *
         * <p>The camera ID that has lower index in the map has higher priority. When used by
         * {@link androidx.camera.core.CameraSelector.Builder#addCameraFilter(CameraFilter)}, the
         * available cameras will be filtered by the {@link Camera2CameraFilter} and all other
         * {@link CameraFilter}s by the order they were added. The first camera in the result
         * will be selected if there are multiple cameras left.
         *
         * @param idCharacteristicsMap The input map of camera IDs and their
         *                             {@link CameraCharacteristics} of the cameras being
         *                             filtered. It's not expected to be modified.
         * @return The output map of camera IDs and their {@link CameraCharacteristics} that
         * match the requirements. Users are expected to create a new map to return with.
         */
        @NonNull
        LinkedHashMap<String, CameraCharacteristics> filter(
                @NonNull LinkedHashMap<String, CameraCharacteristics> idCharacteristicsMap);
    }

    // Should not be instantiated.
    private Camera2CameraFilter() {}
}