Camera2ImplConfig.java

/*
 * Copyright 2019 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.impl;

import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.annotation.experimental.UseExperimental;
import androidx.camera.camera2.interop.CaptureRequestOptions;
import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
import androidx.camera.core.ExtendableBuilder;
import androidx.camera.core.impl.Config;
import androidx.camera.core.impl.MutableConfig;
import androidx.camera.core.impl.MutableOptionsBundle;
import androidx.camera.core.impl.OptionsBundle;

/**
 * Internal shared implementation details for camera 2 interop.
 */
@UseExperimental(markerClass = ExperimentalCamera2Interop.class)
public final class Camera2ImplConfig extends CaptureRequestOptions {

    /** @hide */
    @RestrictTo(Scope.LIBRARY)
    public static final String CAPTURE_REQUEST_ID_STEM = "camera2.captureRequest.option.";

    // Option Declarations:
    // *********************************************************************************************

    /** @hide */
    @RestrictTo(Scope.LIBRARY)
    public static final Config.Option<Integer> TEMPLATE_TYPE_OPTION =
            Option.create("camera2.captureRequest.templateType", int.class);
    /** @hide */
    @RestrictTo(Scope.LIBRARY)
    public static final Option<CameraDevice.StateCallback> DEVICE_STATE_CALLBACK_OPTION =
            Option.create("camera2.cameraDevice.stateCallback", CameraDevice.StateCallback.class);
    /** @hide */
    @RestrictTo(Scope.LIBRARY)
    public static final Option<CameraCaptureSession.StateCallback> SESSION_STATE_CALLBACK_OPTION =
            Option.create(
                    "camera2.cameraCaptureSession.stateCallback",
                    CameraCaptureSession.StateCallback.class);
    /** @hide */
    @RestrictTo(Scope.LIBRARY)
    public static final Option<CameraCaptureSession.CaptureCallback>
            SESSION_CAPTURE_CALLBACK_OPTION =
            Option.create("camera2.cameraCaptureSession.captureCallback",
                    CameraCaptureSession.CaptureCallback.class);

    /** @hide */
    @RestrictTo(Scope.LIBRARY)
    public static final Option<CameraEventCallbacks> CAMERA_EVENT_CALLBACK_OPTION =
            Option.create("camera2.cameraEvent.callback", CameraEventCallbacks.class);

    /** @hide */
    @RestrictTo(Scope.LIBRARY)
    public static final Option<Object> CAPTURE_REQUEST_TAG_OPTION = Option.create(
            "camera2.captureRequest.tag", Object.class);

    // *********************************************************************************************

    /**
     * Creates a Camera2ImplConfig for reading Camera2 options from the given config.
     *
     * @param config The config that potentially contains Camera2 options.
     */
    public Camera2ImplConfig(@NonNull Config config) {
        super(config);
    }

    // Unfortunately, we can't get the Class<T> from the CaptureRequest.Key, so we're forced to
    // erase the type. This shouldn't be a problem as long as we are only using these options
    // within the Camera2ImplConfig and Camera2ImplConfig.Builder classes.

    /** @hide */
    @RestrictTo(Scope.LIBRARY)
    @NonNull
    public static Option<Object> createCaptureRequestOption(@NonNull CaptureRequest.Key<?> key) {
        return Option.create(CAPTURE_REQUEST_ID_STEM + key.getName(), Object.class, key);
    }

    /**
     * Returns all capture request options contained in this configuration.
     *
     * @hide
     */
    @RestrictTo(Scope.LIBRARY)
    @NonNull
    public CaptureRequestOptions getCaptureRequestOptions() {
        return CaptureRequestOptions.Builder.from(getConfig()).build();
    }

    /**
     * Returns the CameraDevice template from the given configuration.
     *
     * <p>See {@link CameraDevice} for valid template types. For example, {@link
     * CameraDevice#TEMPLATE_PREVIEW}.
     *
     * @param valueIfMissing The value to return if this configuration option has not been set.
     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
     * configuration.
     */
    public int getCaptureRequestTemplate(int valueIfMissing) {
        return getConfig().retrieveOption(TEMPLATE_TYPE_OPTION, valueIfMissing);
    }

    /**
     * Returns the stored {@link CameraDevice.StateCallback}.
     *
     * @param valueIfMissing The value to return if this configuration option has not been set.
     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
     * configuration.
     */
    @Nullable
    public CameraDevice.StateCallback getDeviceStateCallback(
            @Nullable CameraDevice.StateCallback valueIfMissing) {
        return getConfig().retrieveOption(DEVICE_STATE_CALLBACK_OPTION, valueIfMissing);
    }


    /**
     * Returns the stored {@link CameraCaptureSession.StateCallback}.
     *
     * @param valueIfMissing The value to return if this configuration option has not been set.
     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
     * configuration.
     */
    @Nullable
    public CameraCaptureSession.StateCallback getSessionStateCallback(
            @Nullable CameraCaptureSession.StateCallback valueIfMissing) {
        return getConfig().retrieveOption(SESSION_STATE_CALLBACK_OPTION, valueIfMissing);
    }

    /**
     * Returns the stored {@link CameraCaptureSession.CaptureCallback}.
     *
     * @param valueIfMissing The value to return if this configuration option has not been set.
     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
     * configuration.
     */
    @Nullable
    public CameraCaptureSession.CaptureCallback getSessionCaptureCallback(
            @Nullable CameraCaptureSession.CaptureCallback valueIfMissing) {
        return getConfig().retrieveOption(SESSION_CAPTURE_CALLBACK_OPTION, valueIfMissing);
    }

    /**
     * Returns the stored CameraEventCallbacks instance.
     *
     * @param valueIfMissing The value to return if this configuration option has not been set.
     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
     * configuration.
     */
    @Nullable
    public CameraEventCallbacks getCameraEventCallback(
            @Nullable CameraEventCallbacks valueIfMissing) {
        return getConfig().retrieveOption(CAMERA_EVENT_CALLBACK_OPTION, valueIfMissing);
    }

    /**
     * Returns the capture request tag.
     *
     * @param valueIfMissing The value to return if this configuration option has not been set.
     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
     * configuration.
     */
    @Nullable
    public Object getCaptureRequestTag(@Nullable Object valueIfMissing) {
        return getConfig().retrieveOption(CAPTURE_REQUEST_TAG_OPTION, valueIfMissing);
    }

    /**
     * Builder for creating {@link Camera2ImplConfig} instance.
     *
     * <p>Use {@link Camera2ImplConfig.Builder} for creating {@link Config} which contains
     * camera2 options only. And use
     * {@link androidx.camera.camera2.interop.Camera2Interop.Extender} to add Camera2 options on
     * existing other {@link
     * ExtendableBuilder}.
     */
    public static final class Builder implements ExtendableBuilder<Camera2ImplConfig> {

        private final MutableOptionsBundle mMutableOptionsBundle = MutableOptionsBundle.create();

        @Override
        @NonNull
        public MutableConfig getMutableConfig() {
            return mMutableOptionsBundle;
        }

        /**
         * Inserts new capture request option with specific {@link CaptureRequest.Key} setting.
         */
        @NonNull
        public <ValueT> Camera2ImplConfig.Builder setCaptureRequestOption(
                @NonNull CaptureRequest.Key<ValueT> key, @NonNull ValueT value) {
            Option<Object> opt = Camera2ImplConfig.createCaptureRequestOption(key);
            mMutableOptionsBundle.insertOption(opt, value);
            return this;
        }

        /**
         * Inserts new capture request option with specific {@link CaptureRequest.Key} setting and
         * {@link OptionPriority}.
         */
        @NonNull
        public <ValueT> Builder setCaptureRequestOptionWithPriority(
                @NonNull CaptureRequest.Key<ValueT> key, @NonNull ValueT value,
                @NonNull OptionPriority priority) {
            Option<Object> opt = Camera2ImplConfig.createCaptureRequestOption(key);
            mMutableOptionsBundle.insertOption(opt, priority, value);
            return this;
        }

        /** Inserts options from other {@link Config} object. */
        @NonNull
        public Camera2ImplConfig.Builder insertAllOptions(@NonNull Config config) {
            for (Option<?> option : config.listOptions()) {
                @SuppressWarnings("unchecked") // Options/values are being copied directly
                        Option<Object> objectOpt = (Option<Object>) option;
                mMutableOptionsBundle.insertOption(objectOpt, config.retrieveOption(objectOpt));
            }
            return this;
        }

        /**
         * Builds an immutable {@link Camera2ImplConfig} from the current state.
         *
         * @return A {@link Camera2ImplConfig} populated with the current state.
         */
        @Override
        @NonNull
        public Camera2ImplConfig build() {
            return new Camera2ImplConfig(OptionsBundle.from(mMutableOptionsBundle));
        }
    }

    /**
     * Extends a {@link ExtendableBuilder} to add Camera2 implementation options.
     *
     * @param <T> the type being built by the extendable builder.
     */
    public static final class Extender<T> {

        ExtendableBuilder<T> mBaseBuilder;

        /**
         * Creates an Extender that can be used to add Camera2 implementation options to another
         * Builder.
         *
         * @param baseBuilder The builder being extended.
         */
        public Extender(@NonNull ExtendableBuilder<T> baseBuilder) {
            mBaseBuilder = baseBuilder;
        }

        /**
         * Sets a CameraEventCallbacks instance.
         *
         * @param cameraEventCallbacks The CameraEventCallbacks.
         * @return The current Extender.
         */
        @NonNull
        public Extender<T> setCameraEventCallback(
                @NonNull CameraEventCallbacks cameraEventCallbacks) {
            mBaseBuilder.getMutableConfig().insertOption(CAMERA_EVENT_CALLBACK_OPTION,
                    cameraEventCallbacks);
            return this;
        }
    }
}