ViewfinderSurfaceRequest.java

/*
 * Copyright 2022 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.viewfinder;

import static android.hardware.camera2.CameraMetadata.LENS_FACING_BACK;
import static android.hardware.camera2.CameraMetadata.LENS_FACING_EXTERNAL;
import static android.hardware.camera2.CameraMetadata.LENS_FACING_FRONT;

import static androidx.camera.viewfinder.surface.ViewfinderSurfaceRequest.MIRROR_MODE_HORIZONTAL;
import static androidx.camera.viewfinder.surface.ViewfinderSurfaceRequest.MIRROR_MODE_NONE;

import android.annotation.SuppressLint;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraMetadata;
import android.util.Size;
import android.view.Surface;
import android.view.SurfaceView;
import android.view.TextureView;

import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;

import com.google.common.util.concurrent.ListenableFuture;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;

/**
 * The request to get a {@link Surface} to display camera feed.
 *
 * <p> This request contains requirements for the surface resolution and camera
 * device information from {@link CameraCharacteristics}.
 *
 * <p> Calling {@link CameraViewfinder#requestSurfaceAsync(ViewfinderSurfaceRequest)} with this
 * request will send the request to the surface provider, which is either a {@link TextureView} or
 * {@link SurfaceView} and get a {@link ListenableFuture} of {@link Surface}.
 *
 * <p> Calling {@link ViewfinderSurfaceRequest#markSurfaceSafeToRelease()} will notify the
 * surface provider that the surface is not needed and related resources can be released.
 *
 * @deprecated Use {@link androidx.camera.viewfinder.surface.ViewfinderSurfaceRequest} instead.
 */
@Deprecated
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
public class ViewfinderSurfaceRequest {

    private static final String TAG = "ViewfinderSurfaceRequest";

    @NonNull private androidx.camera.viewfinder.surface.ViewfinderSurfaceRequest
            mViewfinderSurfaceRequest;

    /**
     * Creates a new surface request with surface resolution, camera device, lens facing and
     * sensor orientation information.
     *
     * surfaceRequest The {@link androidx.camera.viewfinder.surface.ViewfinderSurfaceRequest}.
     */
    ViewfinderSurfaceRequest(
            @NonNull androidx.camera.viewfinder.surface.ViewfinderSurfaceRequest surfaceRequest) {
        mViewfinderSurfaceRequest = surfaceRequest;
    }

    @Override
    @SuppressWarnings("GenericException") // super.finalize() throws Throwable
    protected void finalize() throws Throwable {
        // TODO(b/323226220): Differentiate between surface being released by consumer vs producer
        mViewfinderSurfaceRequest.markSurfaceSafeToRelease();
        super.finalize();
    }

    /**
     * Returns the resolution of the requested {@link Surface}.
     *
     * <p>The value is set by {@link Builder#Builder(Size)}.
     *
     * The surface which fulfills this request must have the resolution specified here in
     * order to fulfill the resource requirements of the camera.
     *
     * @return The guaranteed supported resolution.
     * @see SurfaceTexture#setDefaultBufferSize(int, int)
     */
    @NonNull
    public Size getResolution() {
        return mViewfinderSurfaceRequest.getResolution();
    }

    /**
     * Returns the sensor orientation.
     *
     * <p>The value is set by {@link Builder#setSensorOrientation(int)}, which can be retrieved from
     * {@link CameraCharacteristics} by key {@link CameraCharacteristics#SENSOR_ORIENTATION}.
     *
     * @return The sensor orientation.
     */
    @SensorOrientationDegreesValue
    public int getSensorOrientation() {
        return mViewfinderSurfaceRequest.getSourceOrientation();
    }

    /**
     * Returns the camera lens facing.
     *
     * <p>The value is set by {@link Builder#setLensFacing(int)}, which can be retrieved from
     * {@link CameraCharacteristics} by key {@link CameraCharacteristics#LENS_FACING}.
     *
     * @return The lens facing.
     */
    @LensFacingValue
    public int getLensFacing() {
        return mViewfinderSurfaceRequest.getOutputMirrorMode() == MIRROR_MODE_HORIZONTAL
                ? LENS_FACING_FRONT : LENS_FACING_BACK;
    }

    /**
     * Returns the {@link androidx.camera.viewfinder.CameraViewfinder.ImplementationMode}.
     *
     * <p>The value is set by {@link
     * Builder#setImplementationMode(androidx.camera.viewfinder.CameraViewfinder.ImplementationMode)
     * }.
     *
     * @return {@link androidx.camera.viewfinder.CameraViewfinder.ImplementationMode}.
     *         The value will be null if it's not set via
     * {@link Builder#setImplementationMode(
     * androidx.camera.viewfinder.CameraViewfinder.ImplementationMode)}.
     */
    @Nullable
    public androidx.camera.viewfinder.CameraViewfinder.ImplementationMode getImplementationMode() {
        return androidx.camera.viewfinder.CameraViewfinder.ImplementationMode.fromId(
                mViewfinderSurfaceRequest.getImplementationMode().getId());
    }

    @NonNull
    androidx.camera.viewfinder.surface.ViewfinderSurfaceRequest getViewfinderSurfaceRequest() {
        return mViewfinderSurfaceRequest;
    }

    /**
     * Closes the viewfinder surface to mark it as safe to release.
     *
     * <p> This method should be called by the user when the requested surface is not needed and
     * related resources can be released.
     */
    public void markSurfaceSafeToRelease() {
        mViewfinderSurfaceRequest.markSurfaceSafeToRelease();
    }

    @SuppressLint("PairedRegistration")
    void addRequestCancellationListener(@NonNull Executor executor,
            @NonNull Runnable listener) {
        mViewfinderSurfaceRequest.addRequestCancellationListener(executor, listener);
    }

    /**
     * Builder for {@link ViewfinderSurfaceRequest}.
     *
     * @deprecated Use {@link androidx.camera.viewfinder.surface.ViewfinderSurfaceRequest.Builder}
     * instead.
     */
    @Deprecated
    public static final class Builder {
        @NonNull
        private androidx.camera.viewfinder.surface.ViewfinderSurfaceRequest.Builder mBuilder;

        /**
         * Constructor for {@link Builder}.
         *
         * <p>Creates a builder with viewfinder resolution.
         *
         * @param resolution viewfinder resolution.
         */
        public Builder(@NonNull Size resolution) {
            mBuilder = new androidx.camera.viewfinder.surface.ViewfinderSurfaceRequest.Builder(
                    resolution);
        }

        /**
         * Constructor for {@link Builder}.
         *
         * <p>Creates a builder with other builder instance. The returned builder will be
         * pre-populated with the state of the provided builder.
         *
         * @param builder {@link Builder} instance.
         */
        public Builder(@NonNull Builder builder) {
            mBuilder = builder.mBuilder;
        }

        /**
         * Constructor for {@link Builder}.
         *
         * <p>Creates a builder with other {@link ViewfinderSurfaceRequest} instance. The
         * returned builder will be pre-populated with the state of the provided
         * {@link ViewfinderSurfaceRequest} instance.
         *
         * @param surfaceRequest {@link ViewfinderSurfaceRequest} instance.
         */
        public Builder(@NonNull ViewfinderSurfaceRequest surfaceRequest) {
            mBuilder = new androidx.camera.viewfinder.surface.ViewfinderSurfaceRequest.Builder(
                    surfaceRequest.getResolution());
            mBuilder.setSourceOrientation(surfaceRequest.getSensorOrientation());
            mBuilder.setOutputMirrorMode(surfaceRequest.getLensFacing() == LENS_FACING_FRONT
                    ? MIRROR_MODE_HORIZONTAL : MIRROR_MODE_NONE);
            mBuilder.setImplementationMode(
                    androidx.camera.viewfinder.surface.ImplementationMode.fromId(
                            surfaceRequest.getImplementationMode().getId()));
        }

        /**
         * Sets the {@link androidx.camera.viewfinder.CameraViewfinder.ImplementationMode}.
         *
         * <p><b>Possible values:</b></p>
         * <ul>
         *   <li>{@link
         *   androidx.camera.viewfinder.CameraViewfinder.ImplementationMode#PERFORMANCE PERFORMANCE}
         *   </li>
         *   <li>{@link
         *   androidx.camera.viewfinder.CameraViewfinder.ImplementationMode#COMPATIBLE COMPATIBLE}
         *   </li>
         * </ul>
         *
         * <p>If not set or setting to null, the
         * {@link androidx.camera.viewfinder.CameraViewfinder.ImplementationMode} set via {@code app
         * :implementationMode} in layout xml will be used for {@link CameraViewfinder}. If not
         * set in the layout xml, the default value
         * {@link androidx.camera.viewfinder.CameraViewfinder.ImplementationMode#PERFORMANCE} will
         * be used in {@link CameraViewfinder}.
         *
         * @param implementationMode The {@link
         * androidx.camera.viewfinder.CameraViewfinder.ImplementationMode}.
         * @return This builder.
         */
        @NonNull
        public Builder setImplementationMode(
                @Nullable
                androidx.camera.viewfinder.CameraViewfinder.ImplementationMode implementationMode) {
            mBuilder.setImplementationMode(
                    androidx.camera.viewfinder.surface.ImplementationMode.fromId(
                            implementationMode.getId()));
            return this;
        }

        /**
         * Sets the lens facing.
         *
         * <p><b>Possible values:</b></p>
         * <ul>
         *   <li>{@link CameraMetadata#LENS_FACING_FRONT FRONT}</li>
         *   <li>{@link CameraMetadata#LENS_FACING_BACK BACK}</li>
         *   <li>{@link CameraMetadata#LENS_FACING_EXTERNAL EXTERNAL}</li>
         * </ul>
         *
         * <p>The value can be retrieved from {@link CameraCharacteristics} by key
         * {@link CameraCharacteristics#LENS_FACING}. If not set,
         * {@link CameraMetadata#LENS_FACING_BACK} will be used by default.
         *
         * @param lensFacing The lens facing.
         * @return This builder.
         */
        @NonNull
        public Builder setLensFacing(@LensFacingValue int lensFacing) {
            mBuilder.setOutputMirrorMode(lensFacing == LENS_FACING_FRONT ? MIRROR_MODE_HORIZONTAL :
                    MIRROR_MODE_NONE);
            return this;
        }

        /**
         * Sets the sensor orientation.
         *
         * <p><b>Range of valid values:</b><br>
         * 0, 90, 180, 270</p>
         *
         * <p>The value can be retrieved from {@link CameraCharacteristics} by key
         * {@link CameraCharacteristics#SENSOR_ORIENTATION}. If it is not
         * set, 0 will be used by default.
         *
         * @param sensorOrientation The camera sensor orientation.
         * @return this builder.
         */
        @NonNull
        public Builder setSensorOrientation(@SensorOrientationDegreesValue int sensorOrientation) {
            mBuilder.setSourceOrientation(sensorOrientation);
            return this;
        }

        /**
         * Builds the {@link ViewfinderSurfaceRequest}.
         * @return the instance of {@link ViewfinderSurfaceRequest}.
         */
        @NonNull
        public ViewfinderSurfaceRequest build() {
            return new ViewfinderSurfaceRequest(mBuilder.build());
        }
    }

    /**
     * Valid integer sensor orientation degrees values.
     */
    @IntDef({0, 90, 180, 270})
    @Retention(RetentionPolicy.SOURCE)
    @interface SensorOrientationDegreesValue {
    }

    /**
     * Valid integer sensor orientation degrees values.
     */
    @IntDef({LENS_FACING_FRONT, LENS_FACING_BACK, LENS_FACING_EXTERNAL})
    @Retention(RetentionPolicy.SOURCE)
    @interface LensFacingValue {
    }
}