1 /*
2  * Copyright 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.camera.core.impl;
18 
19 import android.util.Range;
20 
21 import androidx.camera.core.FocusMeteringAction;
22 import androidx.camera.core.FocusMeteringResult;
23 import androidx.camera.core.impl.utils.SessionProcessorUtil;
24 import androidx.camera.core.impl.utils.futures.Futures;
25 
26 import com.google.common.util.concurrent.ListenableFuture;
27 
28 import org.jspecify.annotations.NonNull;
29 import org.jspecify.annotations.Nullable;
30 
31 /**
32  * A {@link CameraControlInternal} whose capabilities can be restricted by the associated
33  * {@link SessionProcessor}. Only the camera operations that can be retrieved from
34  * {@link SessionProcessor#getSupportedCameraOperations()} can be supported by the
35  * AdapterCameraControl.
36  */
37 public class AdapterCameraControl extends ForwardingCameraControl {
38     private final CameraControlInternal mCameraControl;
39     private final @Nullable SessionProcessor mSessionProcessor;
40 
41     /**
42      * Creates the restricted version of the given {@link CameraControlInternal}.
43      */
AdapterCameraControl(@onNull CameraControlInternal cameraControl, @Nullable SessionProcessor sessionProcessor)44     public AdapterCameraControl(@NonNull CameraControlInternal cameraControl,
45             @Nullable SessionProcessor sessionProcessor) {
46         super(cameraControl);
47         mCameraControl = cameraControl;
48         mSessionProcessor = sessionProcessor;
49     }
50 
51     /**
52      * Returns implementation instance.
53      */
54     @Override
getImplementation()55     public @NonNull CameraControlInternal getImplementation() {
56         return mCameraControl;
57     }
58 
59     /**
60      * Returns the {@link SessionProcessor} associated with the AdapterCameraControl.
61      */
getSessionProcessor()62     public @Nullable SessionProcessor getSessionProcessor() {
63         return mSessionProcessor;
64     }
65 
66     @Override
enableTorch(boolean torch)67     public @NonNull ListenableFuture<Void> enableTorch(boolean torch) {
68         if (!SessionProcessorUtil.isOperationSupported(mSessionProcessor,
69                 AdapterCameraInfo.CAMERA_OPERATION_TORCH)) {
70             return Futures.immediateFailedFuture(
71                     new IllegalStateException("Torch is not supported"));
72         }
73         return mCameraControl.enableTorch(torch);
74     }
75 
76     @Override
startFocusAndMetering( @onNull FocusMeteringAction action)77     public @NonNull ListenableFuture<FocusMeteringResult> startFocusAndMetering(
78             @NonNull FocusMeteringAction action) {
79         FocusMeteringAction modifiedAction =
80                 SessionProcessorUtil.getModifiedFocusMeteringAction(mSessionProcessor, action);
81         if (modifiedAction == null) {
82             return Futures.immediateFailedFuture(
83                     new IllegalStateException("FocusMetering is not supported"));
84         }
85 
86         return mCameraControl.startFocusAndMetering(modifiedAction);
87     }
88 
89     @Override
cancelFocusAndMetering()90     public @NonNull ListenableFuture<Void> cancelFocusAndMetering() {
91         return mCameraControl.cancelFocusAndMetering();
92     }
93 
94     @Override
setZoomRatio(float ratio)95     public @NonNull ListenableFuture<Void> setZoomRatio(float ratio) {
96         if (!SessionProcessorUtil.isOperationSupported(mSessionProcessor,
97                 AdapterCameraInfo.CAMERA_OPERATION_ZOOM)) {
98             return Futures.immediateFailedFuture(
99                     new IllegalStateException("Zoom is not supported"));
100         }
101 
102         if (mSessionProcessor != null) {
103             Range<Float> extensionZoomRange = mSessionProcessor.getExtensionZoomRange();
104             if (extensionZoomRange != null
105                     && (ratio < extensionZoomRange.getLower()
106                     || ratio > extensionZoomRange.getUpper())) {
107                 String outOfRangeDesc = "Requested zoomRatio " + ratio + " is not within valid "
108                         + "range [" + extensionZoomRange.getLower() + " , "
109                         + extensionZoomRange.getUpper() + "]";
110                 return Futures.immediateFailedFuture(new IllegalArgumentException(outOfRangeDesc));
111             }
112         }
113         return mCameraControl.setZoomRatio(ratio);
114     }
115 
116     @Override
setLinearZoom(float linearZoom)117     public @NonNull ListenableFuture<Void> setLinearZoom(float linearZoom) {
118         if (!SessionProcessorUtil.isOperationSupported(mSessionProcessor,
119                 AdapterCameraInfo.CAMERA_OPERATION_ZOOM)) {
120             return Futures.immediateFailedFuture(
121                     new IllegalStateException("Zoom is not supported"));
122         }
123 
124         if (mSessionProcessor != null) {
125             Range<Float> extensionZoomRange = mSessionProcessor.getExtensionZoomRange();
126             if (extensionZoomRange == null) {
127                 return mCameraControl.setLinearZoom(linearZoom);
128             }
129 
130             if (linearZoom > 1.0f || linearZoom < 0f) {
131                 String outOfRangeDesc = "Requested linearZoom " + linearZoom + " is not within"
132                         + " valid range [0..1]";
133                 return Futures.immediateFailedFuture(new IllegalArgumentException(outOfRangeDesc));
134             }
135             float zoomRatio = AdapterCameraInfo.getZoomRatioByPercentage(linearZoom,
136                     extensionZoomRange.getLower(), extensionZoomRange.getUpper());
137             return mCameraControl.setZoomRatio(zoomRatio);
138         }
139 
140         return mCameraControl.setLinearZoom(linearZoom);
141     }
142 
143     @Override
setExposureCompensationIndex(int value)144     public @NonNull ListenableFuture<Integer> setExposureCompensationIndex(int value) {
145         if (!SessionProcessorUtil.isOperationSupported(mSessionProcessor,
146                 AdapterCameraInfo.CAMERA_OPERATION_EXPOSURE_COMPENSATION)) {
147             return Futures.immediateFailedFuture(
148                     new IllegalStateException("ExposureCompensation is not supported"));
149         }
150         return mCameraControl.setExposureCompensationIndex(value);
151     }
152 }
153