1 /*
2  * Copyright 2020 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.view;
18 
19 import static androidx.camera.core.impl.utils.Threads.checkMainThread;
20 import static androidx.camera.core.impl.utils.executor.CameraXExecutors.directExecutor;
21 import static androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor;
22 import static androidx.camera.core.impl.utils.futures.Futures.transform;
23 import static androidx.camera.view.internal.ScreenFlashUiInfo.ProviderType.PREVIEW_VIEW;
24 import static androidx.camera.view.internal.ScreenFlashUiInfo.ProviderType.SCREEN_FLASH_VIEW;
25 import static androidx.core.content.ContextCompat.getMainExecutor;
26 
27 import android.Manifest;
28 import android.annotation.SuppressLint;
29 import android.content.Context;
30 import android.graphics.Matrix;
31 import android.graphics.PointF;
32 import android.hardware.camera2.CaptureResult;
33 import android.os.Build;
34 import android.os.Handler;
35 import android.os.Looper;
36 import android.util.Range;
37 import android.util.Rational;
38 import android.util.Size;
39 import android.view.Window;
40 
41 import androidx.annotation.FloatRange;
42 import androidx.annotation.IntDef;
43 import androidx.annotation.IntRange;
44 import androidx.annotation.MainThread;
45 import androidx.annotation.OptIn;
46 import androidx.annotation.RequiresApi;
47 import androidx.annotation.RequiresPermission;
48 import androidx.annotation.RestrictTo;
49 import androidx.annotation.VisibleForTesting;
50 import androidx.camera.core.AspectRatio;
51 import androidx.camera.core.Camera;
52 import androidx.camera.core.CameraControl;
53 import androidx.camera.core.CameraEffect;
54 import androidx.camera.core.CameraInfo;
55 import androidx.camera.core.CameraInfoUnavailableException;
56 import androidx.camera.core.CameraSelector;
57 import androidx.camera.core.CameraUnavailableException;
58 import androidx.camera.core.DynamicRange;
59 import androidx.camera.core.FocusMeteringAction;
60 import androidx.camera.core.FocusMeteringResult;
61 import androidx.camera.core.ImageAnalysis;
62 import androidx.camera.core.ImageCapture;
63 import androidx.camera.core.ImageCapture.ScreenFlash;
64 import androidx.camera.core.ImageProxy;
65 import androidx.camera.core.InitializationException;
66 import androidx.camera.core.Logger;
67 import androidx.camera.core.MeteringPoint;
68 import androidx.camera.core.MeteringPointFactory;
69 import androidx.camera.core.MirrorMode;
70 import androidx.camera.core.Preview;
71 import androidx.camera.core.TorchState;
72 import androidx.camera.core.UseCase;
73 import androidx.camera.core.UseCaseGroup;
74 import androidx.camera.core.ViewPort;
75 import androidx.camera.core.ZoomState;
76 import androidx.camera.core.impl.ImageOutputConfig;
77 import androidx.camera.core.impl.StreamSpec;
78 import androidx.camera.core.impl.utils.CameraOrientationUtil;
79 import androidx.camera.core.impl.utils.ContextUtil;
80 import androidx.camera.core.impl.utils.LiveDataUtil;
81 import androidx.camera.core.impl.utils.Threads;
82 import androidx.camera.core.impl.utils.futures.FutureCallback;
83 import androidx.camera.core.impl.utils.futures.Futures;
84 import androidx.camera.core.resolutionselector.AspectRatioStrategy;
85 import androidx.camera.core.resolutionselector.ResolutionSelector;
86 import androidx.camera.core.resolutionselector.ResolutionStrategy;
87 import androidx.camera.lifecycle.ProcessCameraProvider;
88 import androidx.camera.video.FileDescriptorOutputOptions;
89 import androidx.camera.video.FileOutputOptions;
90 import androidx.camera.video.MediaStoreOutputOptions;
91 import androidx.camera.video.OutputOptions;
92 import androidx.camera.video.PendingRecording;
93 import androidx.camera.video.QualitySelector;
94 import androidx.camera.video.Recorder;
95 import androidx.camera.video.Recording;
96 import androidx.camera.video.VideoCapture;
97 import androidx.camera.video.VideoRecordEvent;
98 import androidx.camera.view.internal.ScreenFlashUiInfo;
99 import androidx.camera.view.video.AudioConfig;
100 import androidx.core.content.PermissionChecker;
101 import androidx.core.util.Consumer;
102 import androidx.core.util.Preconditions;
103 import androidx.lifecycle.LiveData;
104 import androidx.lifecycle.MutableLiveData;
105 
106 import com.google.common.util.concurrent.ListenableFuture;
107 
108 import org.jspecify.annotations.NonNull;
109 import org.jspecify.annotations.Nullable;
110 
111 import java.lang.annotation.Retention;
112 import java.lang.annotation.RetentionPolicy;
113 import java.util.HashMap;
114 import java.util.HashSet;
115 import java.util.Map;
116 import java.util.Objects;
117 import java.util.Set;
118 import java.util.concurrent.Executor;
119 import java.util.concurrent.TimeUnit;
120 
121 /**
122  * The abstract base camera controller class.
123  *
124  * <p> This a high level controller that provides most of the CameraX core features
125  * in a single class. It handles camera initialization, creates and configures {@link UseCase}s.
126  * It also listens to device motion sensor and set the target rotation for the use cases.
127  *
128  * <p> The controller is required to be used with a {@link PreviewView}. {@link PreviewView}
129  * provides the UI elements to display camera preview. The layout of the {@link PreviewView} is
130  * used to set the crop rect so the output from other use cases matches the preview display in a
131  * WYSIWYG way. The controller also listens to {@link PreviewView}'s touch events to handle
132  * tap-to-focus and pinch-to-zoom features.
133  *
134  * <p> This class provides features of 4 {@link UseCase}s: {@link Preview}, {@link ImageCapture},
135  * {@link ImageAnalysis} and {@link VideoCapture}. {@link Preview} is required and always enabled.
136  * {@link ImageCapture} and {@link ImageAnalysis} are enabled by default. The video capture is
137  * disabled by default because it might affect other use cases, especially on lower end devices.
138  * It might be necessary to disable {@link ImageCapture} and/or {@link ImageAnalysis} before the
139  * video capture feature can be enabled. Disabling/enabling {@link UseCase}s freezes the preview for
140  * a short period of time. To avoid the glitch, the {@link UseCase}s need to be enabled/disabled
141  * before the controller is set on {@link PreviewView}.
142  */
143 public abstract class CameraController {
144 
145     private static final String TAG = "CameraController";
146 
147     // Externally visible error messages.
148     private static final String CAMERA_NOT_INITIALIZED = "Camera not initialized.";
149     private static final String PREVIEW_VIEW_NOT_ATTACHED =
150             "PreviewView not attached to CameraController.";
151     private static final String CAMERA_NOT_ATTACHED = "Use cases not attached to camera.";
152     private static final String IMAGE_CAPTURE_DISABLED = "ImageCapture disabled.";
153     private static final String VIDEO_CAPTURE_DISABLED = "VideoCapture disabled.";
154     private static final String VIDEO_RECORDING_UNFINISHED = "Recording video. Only one recording"
155             + " can be active at a time.";
156 
157     // Auto focus is 1/6 of the area.
158     private static final float AF_SIZE = 1.0f / 6.0f;
159     private static final float AE_SIZE = AF_SIZE * 1.5f;
160 
161     /**
162      * {@link ImageAnalysis.Analyzer} option for returning {@link PreviewView} coordinates.
163      *
164      * <p>When the {@link ImageAnalysis.Analyzer} is configured with this option, it will receive a
165      * {@link Matrix} that will receive a value that represents the transformation from camera
166      * sensor to the {@link PreviewView}, which can be used for highlighting detected result in
167      * {@link PreviewView}. For example, laying over a bounding box on top of the detected face.
168      *
169      * <p>Note this option only works if the {@link ImageAnalysis.Analyzer} is set via
170      * {@link CameraController#setImageAnalysisAnalyzer}. It will not be effective when used with
171      * camera-core directly.
172      *
173      * @see ImageAnalysis.Analyzer
174      * @deprecated Use {@link ImageAnalysis#COORDINATE_SYSTEM_VIEW_REFERENCED} instead.
175      */
176     @Deprecated
177     public static final int COORDINATE_SYSTEM_VIEW_REFERENCED = 1;
178 
179     /**
180      * No tap-to-focus action has been started by the end user.
181      */
182     public static final int TAP_TO_FOCUS_NOT_STARTED = 0;
183 
184     /**
185      * A tap-to-focus action has started but not completed. The app also gets notified with this
186      * state if a new action happens before the previous one could finish.
187      */
188     public static final int TAP_TO_FOCUS_STARTED = 1;
189 
190     /**
191      * The previous tap-to-focus action was completed successfully and the camera is focused.
192      */
193     public static final int TAP_TO_FOCUS_FOCUSED = 2;
194 
195     /**
196      * The previous tap-to-focus action was completed successfully but the camera is still
197      * unfocused, similar to the {@link CaptureResult#CONTROL_AF_STATE_NOT_FOCUSED_LOCKED} state.
198      * The end user might be able to get a better result by trying again with different camera
199      * distances and/or lighting.
200      */
201     public static final int TAP_TO_FOCUS_NOT_FOCUSED = 3;
202 
203     /**
204      * The previous tap-to-focus action was failed to complete. This is usually due to device
205      * limitations.
206      */
207     public static final int TAP_TO_FOCUS_FAILED = 4;
208 
209     /**
210      * Bitmask options to enable/disable use cases.
211      */
212     @Retention(RetentionPolicy.SOURCE)
213     @RestrictTo(RestrictTo.Scope.LIBRARY)
214     @IntDef(flag = true, value = {IMAGE_CAPTURE, IMAGE_ANALYSIS, VIDEO_CAPTURE})
215     public @interface UseCases {
216     }
217 
218     /**
219      * Bitmask option to enable {@link ImageCapture}. In {@link #setEnabledUseCases}, if
220      * {@code (enabledUseCases & IMAGE_CAPTURE) != 0}, then controller will enable image capture
221      * features.
222      */
223     public static final int IMAGE_CAPTURE = 1;
224     /**
225      * Bitmask option to enable {@link ImageAnalysis}. In {@link #setEnabledUseCases}, if
226      * {@code (enabledUseCases & IMAGE_ANALYSIS) != 0}, then controller will enable image
227      * analysis features.
228      */
229     public static final int IMAGE_ANALYSIS = 1 << 1;
230     /**
231      * Bitmask option to enable video capture use case. In {@link #setEnabledUseCases}, if
232      * {@code (enabledUseCases & VIDEO_CAPTURE) != 0}, then controller will enable video capture
233      * features.
234      */
235     public static final int VIDEO_CAPTURE = 1 << 2;
236 
237     private static final ScreenFlash NO_OP_SCREEN_FLASH =
238             new ScreenFlash() {
239                 @Override
240                 public void apply(long expirationTimeMillis,
241                         ImageCapture.@NonNull ScreenFlashListener screenFlashListener) {
242                     screenFlashListener.onCompleted();
243                 }
244 
245                 @Override
246                 public void clear() {
247 
248                 }
249             };
250 
251     CameraSelector mCameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;
252 
253     // By default, ImageCapture and ImageAnalysis are enabled. VideoCapture is disabled.
254     private int mEnabledUseCases = IMAGE_CAPTURE | IMAGE_ANALYSIS;
255 
256     // CameraController and PreviewView hold reference to each other. The 2-way link is managed
257     // by PreviewView.
258     // Synthetic access
259     @SuppressWarnings("WeakerAccess")
260     @NonNull Preview mPreview;
261 
262     @Nullable OutputSize mPreviewTargetSize;
263     @Nullable ResolutionSelector mPreviewResolutionSelector;
264 
265     // Synthetic access
266     @SuppressWarnings("WeakerAccess")
267     @NonNull ImageCapture mImageCapture;
268 
269     @Nullable OutputSize mImageCaptureTargetSize;
270     @Nullable ResolutionSelector mImageCaptureResolutionSelector;
271 
272     @Nullable Executor mImageCaptureIoExecutor;
273 
274     private @Nullable Executor mAnalysisExecutor;
275 
276     private @Nullable Executor mAnalysisBackgroundExecutor;
277 
278     private ImageAnalysis.@Nullable Analyzer mAnalysisAnalyzer;
279 
280     @NonNull ImageAnalysis mImageAnalysis;
281 
282     @Nullable OutputSize mImageAnalysisTargetSize;
283     @Nullable ResolutionSelector mImageAnalysisResolutionSelector;
284 
285     @NonNull VideoCapture<Recorder> mVideoCapture;
286 
287     @Nullable Recording mActiveRecording = null;
288 
289     @NonNull Map<Consumer<VideoRecordEvent>, Recording> mRecordingMap = new HashMap<>();
290 
291     @NonNull QualitySelector mVideoCaptureQualitySelector = Recorder.DEFAULT_QUALITY_SELECTOR;
292 
293     @MirrorMode.Mirror
294     private int mVideoCaptureMirrorMode = MirrorMode.MIRROR_MODE_OFF;
295 
296     private @NonNull DynamicRange mVideoCaptureDynamicRange = DynamicRange.UNSPECIFIED;
297 
298     private @NonNull DynamicRange mPreviewDynamicRange = DynamicRange.UNSPECIFIED;
299 
300     private @NonNull Range<Integer> mVideoCaptureTargetFrameRate =
301             StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED;
302 
303     // The latest bound camera.
304     // Synthetic access
305     @SuppressWarnings("WeakerAccess")
306     @Nullable Camera mCamera;
307 
308     // Synthetic access
309     @SuppressWarnings("WeakerAccess")
310     @Nullable ProcessCameraProviderWrapper mCameraProvider;
311 
312     // Synthetic access
313     @SuppressWarnings("WeakerAccess")
314     @Nullable ViewPort mViewPort;
315 
316     // Synthetic access
317     @SuppressWarnings("WeakerAccess")
318     Preview.@Nullable SurfaceProvider mSurfaceProvider;
319 
320     private final RotationProvider mRotationProvider;
321 
322     @VisibleForTesting
323     final RotationProvider.@NonNull Listener mDeviceRotationListener;
324 
325     private boolean mPinchToZoomEnabled = true;
326     private boolean mTapToFocusEnabled = true;
327     private FocusMeteringResultCallback mFocusMeteringResultCallback;
328 
329     private final ForwardingLiveData<ZoomState> mZoomState = new ForwardingLiveData<>();
330     private final ForwardingLiveData<Integer> mTorchState = new ForwardingLiveData<>();
331     final MutableLiveData<TapToFocusInfo> mTapToFocusInfoState = new MutableLiveData<>(
332             new TapToFocusInfo(TAP_TO_FOCUS_NOT_STARTED, /* tapPoint = */ null));
333     final LiveData<Integer> mTapToFocusState = LiveDataUtil.map(mTapToFocusInfoState,
334             TapToFocusInfo::getFocusState);
335 
336     private final @NonNull PendingValue<Boolean> mPendingEnableTorch = new PendingValue<>();
337 
338     private final @NonNull PendingValue<Float> mPendingLinearZoom = new PendingValue<>();
339 
340     private final @NonNull PendingValue<Float> mPendingZoomRatio = new PendingValue<>();
341 
342     private final @NonNull Set<CameraEffect> mEffects = new HashSet<>();
343 
344     private final Context mAppContext;
345 
346     private final @NonNull ListenableFuture<Void> mInitializationFuture;
347 
348     private final Map<ScreenFlashUiInfo.ProviderType, ScreenFlashUiInfo>
349             mScreenFlashUiInfoMap = new HashMap<>();
350 
351     private long mTapToFocusAutoCancelDurationNanos = TimeUnit.MILLISECONDS.toNanos(
352             FocusMeteringAction.DEFAULT_AUTO_CANCEL_DURATION_MILLIS);
353 
CameraController(@onNull Context context)354     CameraController(@NonNull Context context) {
355         this(context, transform(ProcessCameraProvider.getInstance(context),
356                 ProcessCameraProviderWrapperImpl::new, directExecutor()));
357     }
358 
CameraController(@onNull Context context, @NonNull ListenableFuture<ProcessCameraProviderWrapper> cameraProviderFuture)359     CameraController(@NonNull Context context,
360             @NonNull ListenableFuture<ProcessCameraProviderWrapper> cameraProviderFuture) {
361         mAppContext = ContextUtil.getApplicationContext(context);
362         mPreview = createPreview();
363         mImageCapture = createImageCapture(null);
364         mImageAnalysis = createImageAnalysis(null, null, null);
365         mVideoCapture = createVideoCapture();
366 
367         // Wait for camera to be initialized before binding use cases.
368         mInitializationFuture = transform(
369                 cameraProviderFuture,
370                 provider -> {
371                     mCameraProvider = provider;
372                     unbindAllAndRecreate();
373                     startCameraAndTrackStates();
374                     return null;
375                 }, mainThreadExecutor());
376 
377         // Listen for device rotation changes and set target rotation for non-preview use cases.
378         // The output of non-preview use cases need to be corrected in fixed landscape/portrait
379         // mode.
380         mRotationProvider = new RotationProvider(mAppContext);
381         mDeviceRotationListener = rotation -> {
382             mImageAnalysis.setTargetRotation(rotation);
383             mImageCapture.setTargetRotation(rotation);
384             mVideoCapture.setTargetRotation(rotation);
385         };
386     }
387 
388     /**
389      * Gets a {@link ListenableFuture} that completes when camera initialization completes.
390      *
391      * <p>This future may fail with an {@link InitializationException} and associated cause that
392      * can be retrieved by {@link Throwable#getCause()}. The cause will be a
393      * {@link CameraUnavailableException} if it fails to access any camera during initialization.
394      *
395      * <p>In the rare case that the future fails with {@link CameraUnavailableException}, the
396      * camera will become unusable. This could happen for various reasons, for example hardware
397      * failure or the camera being held by another process. If the failure is temporary, killing
398      * and restarting the app might fix the issue.
399      *
400      * <p>The initialization also try to bind use cases before completing the
401      * {@link ListenableFuture}. The {@link ListenableFuture} will complete successfully
402      * regardless of whether the use cases are ready to be bound, e.g. it will complete
403      * successfully even if the controller is not set on a {@link PreviewView}. However the
404      * {@link ListenableFuture} will fail if the enabled use cases are not supported by the
405      * current camera.
406      *
407      * @see ProcessCameraProvider#getInstance
408      */
getInitializationFuture()409     public @NonNull ListenableFuture<Void> getInitializationFuture() {
410         return mInitializationFuture;
411     }
412 
413     /**
414      * Implemented by children to refresh after {@link UseCase} is changed.
415      *
416      * @throws IllegalStateException For invalid {@link UseCase} combinations.
417      * @throws RuntimeException For invalid {@link CameraEffect} combinations.
418      */
startCamera()419     abstract @Nullable Camera startCamera();
420 
isCameraInitialized()421     private boolean isCameraInitialized() {
422         return mCameraProvider != null;
423     }
424 
isPreviewViewAttached()425     private boolean isPreviewViewAttached() {
426         return mSurfaceProvider != null && mViewPort != null;
427     }
428 
isCameraAttached()429     private boolean isCameraAttached() {
430         return mCamera != null;
431     }
432 
433     /**
434      * Enables or disables use cases.
435      *
436      * <p>Use cases need to be enabled before they can be used. By default, {@link #IMAGE_CAPTURE}
437      * and {@link #IMAGE_ANALYSIS} are enabled, and {@link #VIDEO_CAPTURE} is disabled. This is
438      * necessary because {@link #VIDEO_CAPTURE} may affect the available resolutions for other use
439      * cases, especially on lower end devices.
440      *
441      * <p>To make sure getting the maximum resolution, only enable {@link #VIDEO_CAPTURE} when
442      * shooting video. For example:
443      *
444      * <pre><code>
445      * // By default, image capture is enabled. Taking picture works.
446      * controller.takePicture(...);
447      *
448      * // Switch to video capture to shoot video.
449      * controller.setEnabledUseCases(VIDEO_CAPTURE);
450      * controller.startRecording(...);
451      *
452      * // Switch back to image capture and image analysis before taking another picture.
453      * controller.setEnabledUseCases(IMAGE_CAPTURE|IMAGE_ANALYSIS);
454      * controller.takePicture(...);
455      *
456      * </code></pre>
457      *
458      * @param enabledUseCases one or more of the following use cases, bitwise-OR-ed together:
459      * {@link #IMAGE_CAPTURE}, {@link #IMAGE_ANALYSIS} and/or {@link #VIDEO_CAPTURE}.
460      * @throws IllegalStateException If the current camera selector is unable to resolve a camera
461      * to be used for the enabled use cases.
462      * @see UseCase
463      * @see ImageCapture
464      * @see ImageAnalysis
465      */
466     @MainThread
setEnabledUseCases(@seCases int enabledUseCases)467     public void setEnabledUseCases(@UseCases int enabledUseCases) {
468         checkMainThread();
469         if (enabledUseCases == mEnabledUseCases) {
470             return;
471         }
472         int oldEnabledUseCases = mEnabledUseCases;
473         mEnabledUseCases = enabledUseCases;
474         if (!isVideoCaptureEnabled() && isRecording()) {
475             stopRecording();
476         }
477         startCameraAndTrackStates(() -> {
478             mEnabledUseCases = oldEnabledUseCases;
479             Logger.w(TAG,
480                     "setEnabledUseCases: failed to enable use cases properly for enabledUseCases = "
481                             + Integer.toBinaryString(enabledUseCases)
482                             + ", restoring back previous values " + Integer.toBinaryString(
483                             oldEnabledUseCases));
484         });
485     }
486 
487     /**
488      * Checks if the given use case mask is enabled.
489      *
490      * @param useCaseMask One of the {@link #IMAGE_CAPTURE}, {@link #IMAGE_ANALYSIS} or
491      * {@link #VIDEO_CAPTURE}.
492      * @return {@code true} if the use case is enabled.
493      */
isUseCaseEnabled(int useCaseMask)494     private boolean isUseCaseEnabled(int useCaseMask) {
495         return (mEnabledUseCases & useCaseMask) != 0;
496     }
497 
498     /**
499      * Configures the resolution on the config.
500      *
501      * <p>If the {@link ResolutionSelector} and the {@link OutputSize} are set at the same time,
502      * the {@link ResolutionSelector} takes precedence.
503      *
504      * <p>If the given {@link ResolutionSelector} is {@code null} and {@link OutputSize}, the
505      * {@link AspectRatioStrategy} will be override to match the {@link ViewPort}.
506      */
configureResolution(ImageOutputConfig.@onNull Builder<?> builder, @Nullable ResolutionSelector resolutionSelector, @Nullable OutputSize outputSize)507     private void configureResolution(ImageOutputConfig.@NonNull Builder<?> builder,
508             @Nullable ResolutionSelector resolutionSelector, @Nullable OutputSize outputSize) {
509         if (resolutionSelector != null) {
510             builder.setResolutionSelector(resolutionSelector);
511         } else if (outputSize != null) {
512             setTargetOutputSize(builder, outputSize);
513         } else if (mViewPort != null) {
514             // Override the aspect ratio strategy if viewport is set and there's no resolution
515             // selector explicitly set by the user.
516             AspectRatioStrategy aspectRatioStrategy = getViewportAspectRatioStrategy(mViewPort);
517             if (aspectRatioStrategy != null) {
518                 builder.setResolutionSelector(
519                         new ResolutionSelector.Builder().setAspectRatioStrategy(
520                                 aspectRatioStrategy).build());
521             }
522         }
523     }
524 
525     /**
526      * Sets the target aspect ratio or target resolution based on {@link OutputSize}.
527      */
setTargetOutputSize(ImageOutputConfig.@onNull Builder<?> builder, @Nullable OutputSize outputSize)528     private void setTargetOutputSize(ImageOutputConfig.@NonNull Builder<?> builder,
529             @Nullable OutputSize outputSize) {
530         if (outputSize == null) {
531             return;
532         }
533         if (outputSize.getResolution() != null) {
534             builder.setTargetResolution(outputSize.getResolution());
535         } else if (outputSize.getAspectRatio() != OutputSize.UNASSIGNED_ASPECT_RATIO) {
536             builder.setTargetAspectRatio(outputSize.getAspectRatio());
537         } else {
538             Logger.e(TAG, "Invalid target surface size. " + outputSize);
539         }
540     }
541 
542     /**
543      * Checks if two {@link OutputSize} are equal.
544      */
isOutputSizeEqual( @ullable OutputSize currentSize, @Nullable OutputSize newSize)545     private boolean isOutputSizeEqual(
546             @Nullable OutputSize currentSize,
547             @Nullable OutputSize newSize) {
548         if (currentSize == newSize) {
549             return true;
550         }
551         return currentSize != null && currentSize.equals(newSize);
552     }
553 
554     // ------------------
555     // Preview use case.
556     // ------------------
557 
558     /**
559      * Internal API used by {@link PreviewView} to notify changes.
560      */
561     @SuppressLint({"MissingPermission", "WrongConstant"})
562     @MainThread
attachPreviewSurface(Preview.@onNull SurfaceProvider surfaceProvider, @NonNull ViewPort viewPort)563     void attachPreviewSurface(Preview.@NonNull SurfaceProvider surfaceProvider,
564             @NonNull ViewPort viewPort) {
565         checkMainThread();
566         if (mSurfaceProvider != surfaceProvider) {
567             mSurfaceProvider = surfaceProvider;
568             mPreview.setSurfaceProvider(surfaceProvider);
569         }
570         boolean shouldUpdateAspectRatio = mViewPort == null || getViewportAspectRatioStrategy(
571                 viewPort) != getViewportAspectRatioStrategy(mViewPort);
572         mViewPort = viewPort;
573         startListeningToRotationEvents();
574         if (shouldUpdateAspectRatio) {
575             unbindAllAndRecreate();
576         }
577         startCameraAndTrackStates();
578     }
579 
580     /**
581      * Clears {@link PreviewView} to remove the UI reference.
582      */
583     @MainThread
clearPreviewSurface()584     void clearPreviewSurface() {
585         checkMainThread();
586         if (mCameraProvider != null) {
587             // Preview is required. Unbind everything if Preview is down.
588             mCameraProvider.unbind(mPreview, mImageCapture, mImageAnalysis, mVideoCapture);
589         }
590         mPreview.setSurfaceProvider(null);
591         mCamera = null;
592         mSurfaceProvider = null;
593         mViewPort = null;
594         stopListeningToRotationEvents();
595     }
596 
startListeningToRotationEvents()597     private void startListeningToRotationEvents() {
598         mRotationProvider.addListener(mainThreadExecutor(),
599                 mDeviceRotationListener);
600     }
601 
stopListeningToRotationEvents()602     private void stopListeningToRotationEvents() {
603         mRotationProvider.removeListener(mDeviceRotationListener);
604     }
605 
606     /**
607      * Sets the intended output size for {@link Preview}.
608      *
609      * <p>The value is used as a hint when determining the resolution and aspect ratio of the
610      * preview. The actual output may differ from the requested value due to device constraints.
611      *
612      * <p>When set to {@code null}, the output will be based on the default config of
613      * {@link Preview}.
614      *
615      * <p>Changing the value will reconfigure the camera which will cause additional latency. To
616      * avoid this, set the value before controller is bound to lifecycle.
617      *
618      * @param targetSize the intended output size for {@link Preview}.
619      * @see Preview.Builder#setTargetAspectRatio(int)
620      * @see Preview.Builder#setTargetResolution(Size)
621      * @deprecated Use {@link #setPreviewResolutionSelector(ResolutionSelector)} instead.
622      */
623     @MainThread
624     @Deprecated
setPreviewTargetSize(@ullable OutputSize targetSize)625     public void setPreviewTargetSize(@Nullable OutputSize targetSize) {
626         checkMainThread();
627         if (isOutputSizeEqual(mPreviewTargetSize, targetSize)) {
628             return;
629         }
630         mPreviewTargetSize = targetSize;
631         unbindPreviewAndRecreate();
632         startCameraAndTrackStates();
633     }
634 
635     /**
636      * Returns the intended output size for {@link Preview} set by
637      * {@link #setPreviewTargetSize(OutputSize)}, or {@code null} if not set.
638      *
639      * @deprecated Use {@link #getPreviewResolutionSelector()} instead.
640      */
641     @MainThread
642     @Deprecated
getPreviewTargetSize()643     public @Nullable OutputSize getPreviewTargetSize() {
644         checkMainThread();
645         return mPreviewTargetSize;
646     }
647 
648     /**
649      * Sets the {@link ResolutionSelector} for {@link Preview}.
650      *
651      * <p>CameraX uses this value as a hint to select the resolution for preview. The actual
652      * output may differ from the requested value due to device constraints. When set to
653      * {@code null}, CameraX will use the default config of {@link Preview}. By default, the
654      * selected resolution will be limited by the {@code PREVIEW} size which is defined as the best
655      * size match to the device's screen resolution, or to 1080p (1920x1080), whichever is smaller.
656      *
657      * <p>Changing the value will reconfigure the camera which will cause additional latency. To
658      * avoid this, set the value before controller is bound to lifecycle.
659      *
660      * @see Preview.Builder#setResolutionSelector(ResolutionSelector)
661      */
662     @MainThread
setPreviewResolutionSelector(@ullable ResolutionSelector resolutionSelector)663     public void setPreviewResolutionSelector(@Nullable ResolutionSelector resolutionSelector) {
664         checkMainThread();
665         if (mPreviewResolutionSelector == resolutionSelector) {
666             return;
667         }
668         mPreviewResolutionSelector = resolutionSelector;
669         unbindPreviewAndRecreate();
670         startCameraAndTrackStates();
671     }
672 
673     /**
674      * Returns the {@link ResolutionSelector} for {@link Preview}.
675      *
676      * <p>This method returns the value set by
677      * {@link #setPreviewResolutionSelector(ResolutionSelector)}. It returns {@code null} if
678      * the value has not been set.
679      */
680     @MainThread
getPreviewResolutionSelector()681     public @Nullable ResolutionSelector getPreviewResolutionSelector() {
682         checkMainThread();
683         return mPreviewResolutionSelector;
684     }
685 
686     /**
687      * Sets the {@link DynamicRange} for preview.
688      *
689      * <p>The dynamic range specifies how the range of colors, highlights and shadows that
690      * are displayed on a display. Some dynamic ranges will allow the preview to make full use of
691      * the extended range of brightness of a display.
692      *
693      * <p>The supported dynamic ranges of the camera can be queried using
694      * {@link CameraInfo#querySupportedDynamicRanges(Set)}.
695      *
696      * <p>It is possible to choose a high dynamic range (HDR) with unspecified encoding by providing
697      * {@link DynamicRange#HDR_UNSPECIFIED_10_BIT}. If the dynamic range is not provided, the
698      * default value is {@link DynamicRange#SDR}.
699      *
700      * @see Preview.Builder#setDynamicRange(DynamicRange)
701      *
702      * TODO: make this public in the next release.
703      */
704     @MainThread
705     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
setPreviewDynamicRange(@onNull DynamicRange dynamicRange)706     public void setPreviewDynamicRange(@NonNull DynamicRange dynamicRange) {
707         checkMainThread();
708         mPreviewDynamicRange = dynamicRange;
709         unbindPreviewAndRecreate();
710         startCameraAndTrackStates();
711     }
712 
713     /**
714      * Gets the {@link DynamicRange} for preview.
715      *
716      * TODO: make this public in the next release.
717      */
718     @MainThread
719     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
getPreviewDynamicRange()720     public @NonNull DynamicRange getPreviewDynamicRange() {
721         checkMainThread();
722         return mPreviewDynamicRange;
723     }
724 
725     /**
726      * Unbinds {@link Preview} and recreates with the latest parameters.
727      */
728     @MainThread
unbindPreviewAndRecreate()729     private void unbindPreviewAndRecreate() {
730         if (isCameraInitialized()) {
731             mCameraProvider.unbind(mPreview);
732         }
733         mPreview = createPreview();
734         if (mSurfaceProvider != null) {
735             mPreview.setSurfaceProvider(mSurfaceProvider);
736         }
737     }
738 
createPreview()739     private Preview createPreview() {
740         Preview.Builder builder = new Preview.Builder();
741         configureResolution(builder, mPreviewResolutionSelector, mPreviewTargetSize);
742         builder.setDynamicRange(mPreviewDynamicRange);
743         return builder.build();
744     }
745 
746     // ----------------------
747     // ImageCapture UseCase.
748     // ----------------------
749 
750     /**
751      * Checks if {@link ImageCapture} is enabled.
752      *
753      * <p>{@link ImageCapture} is enabled by default. It has to be enabled before
754      * {@link #takePicture} can be called.
755      *
756      * @see ImageCapture
757      */
758     @MainThread
isImageCaptureEnabled()759     public boolean isImageCaptureEnabled() {
760         checkMainThread();
761         return isUseCaseEnabled(IMAGE_CAPTURE);
762     }
763 
764     /**
765      * Gets the flash mode for {@link ImageCapture}.
766      *
767      * @return the flashMode. Value is {@link ImageCapture#FLASH_MODE_AUTO},
768      * {@link ImageCapture#FLASH_MODE_ON}, or {@link ImageCapture#FLASH_MODE_OFF}.
769      * @see ImageCapture
770      */
771     @MainThread
772     @ImageCapture.FlashMode
getImageCaptureFlashMode()773     public int getImageCaptureFlashMode() {
774         checkMainThread();
775         return mImageCapture.getFlashMode();
776     }
777 
778     /**
779      * Sets the flash mode for {@link ImageCapture}.
780      *
781      * <p>If not set, the flash mode will default to {@link ImageCapture#FLASH_MODE_OFF}.
782      *
783      * <p>If {@link ImageCapture#FLASH_MODE_SCREEN} is set, a valid {@link android.view.Window}
784      * instance must be set to a {@link PreviewView} or {@link ScreenFlashView} which this
785      * controller is set to. Trying to use {@link ImageCapture#FLASH_MODE_SCREEN} with a
786      * non-front camera or without setting a non-null window will be no-op. While switching the
787      * camera, it is the application's responsibility to change flash mode to the desired one if
788      * it leads to a no-op case (e.g. switching to rear camera while {@code FLASH_MODE_SCREEN} is
789      * still set). Otherwise, {@code FLASH_MODE_OFF} will be set.
790      *
791      * @param flashMode the flash mode for {@link ImageCapture}.
792      * @throws IllegalArgumentException If flash mode is invalid or
793      * {@link ImageCapture#FLASH_MODE_SCREEN} is used without a front camera.
794      * @see PreviewView#setScreenFlashWindow(Window)
795      * @see ScreenFlashView#setScreenFlashWindow(Window)
796      */
797     @MainThread
setImageCaptureFlashMode(@mageCapture.FlashMode int flashMode)798     public void setImageCaptureFlashMode(@ImageCapture.FlashMode int flashMode) {
799         checkMainThread();
800 
801         if (flashMode == ImageCapture.FLASH_MODE_SCREEN) {
802             Integer lensFacing = mCameraSelector.getLensFacing();
803             if (lensFacing != null && lensFacing != CameraSelector.LENS_FACING_FRONT) {
804                 throw new IllegalArgumentException(
805                         "Not a front camera despite setting FLASH_MODE_SCREEN");
806             }
807 
808             updateScreenFlashToImageCapture();
809         }
810 
811         mImageCapture.setFlashMode(flashMode);
812     }
813 
814     /**
815      * Internal API used by {@link PreviewView} and {@link ScreenFlashView} to provide a
816      * {@link ScreenFlash}.
817      */
818     @RestrictTo(RestrictTo.Scope.LIBRARY)
setScreenFlashUiInfo(@onNull ScreenFlashUiInfo screenFlashUiInfo)819     public void setScreenFlashUiInfo(@NonNull ScreenFlashUiInfo screenFlashUiInfo) {
820         ScreenFlashUiInfo previousInfo = getScreenFlashUiInfoByPriority();
821         mScreenFlashUiInfoMap.put(screenFlashUiInfo.getProviderType(), screenFlashUiInfo);
822         ScreenFlashUiInfo prioritizedInfo = getScreenFlashUiInfoByPriority();
823         if (prioritizedInfo != null && !prioritizedInfo.equals(previousInfo)) {
824             updateScreenFlashToImageCapture();
825         }
826     }
827 
828     /**
829      * Internal API used by {@link PreviewView} and {@link ScreenFlashView} to update screen
830      * flash mode to ImageCapture in case it's pending.
831      */
832     @RestrictTo(RestrictTo.Scope.LIBRARY)
updateScreenFlashToImageCapture()833     public void updateScreenFlashToImageCapture() {
834         ScreenFlashUiInfo screenFlashUiInfo = getScreenFlashUiInfoByPriority();
835 
836         if (screenFlashUiInfo == null) {
837             // PreviewView/ScreenFlashView may have not been attached yet, so setting a NO-OP
838             // ScreenFlash until one of the views is attached
839             Logger.d(TAG, "No ScreenFlash instance set yet, need to wait for "
840                     + "controller to be set to either ScreenFlashView or PreviewView");
841             mImageCapture.setScreenFlash(NO_OP_SCREEN_FLASH);
842             return;
843         }
844 
845         mImageCapture.setScreenFlash(screenFlashUiInfo.getScreenFlash());
846         Logger.d(TAG, "Set ScreenFlash instance to ImageCapture, provided by "
847                 + screenFlashUiInfo.getProviderType().name());
848     }
849 
850     /**
851      * Returns a {@link ScreenFlashUiInfo} by prioritizing {@link ScreenFlashView} over
852      * {@link PreviewView}.
853      *
854      * <p>{@link PreviewView} always has a {@link ScreenFlashView} internally and does not know
855      * if user is using another {@link ScreenFlashView} themselves. This API prioritizes user's
856      * {@link ScreenFlashView} over the internal one in {@link PreviewView} and provides the
857      * {@link ScreenFlashUiInfo} accordingly.
858      */
859     @RestrictTo(RestrictTo.Scope.LIBRARY)
getScreenFlashUiInfoByPriority()860     public @Nullable ScreenFlashUiInfo getScreenFlashUiInfoByPriority() {
861         if (mScreenFlashUiInfoMap.get(SCREEN_FLASH_VIEW) != null) {
862             return mScreenFlashUiInfoMap.get(SCREEN_FLASH_VIEW);
863         }
864         if (mScreenFlashUiInfoMap.get(PREVIEW_VIEW) != null) {
865             return mScreenFlashUiInfoMap.get(PREVIEW_VIEW);
866         }
867         return null;
868     }
869 
870     /**
871      * Captures a new still image and saves to a file along with application specified metadata.
872      *
873      * <p>The callback will be called only once for every invocation of this method.
874      *
875      * <p>By default, the saved image is mirrored to match the output of the preview if front
876      * camera is used. To override this behavior, the app needs to explicitly set the flag to
877      * {@code false} using {@link ImageCapture.Metadata#setReversedHorizontal} and
878      * {@link ImageCapture.OutputFileOptions.Builder#setMetadata}.
879      *
880      * <p>The saved image is cropped to match the aspect ratio of the {@link PreviewView}. To
881      * take a picture with the maximum available resolution, make sure that the
882      * {@link PreviewView}'s aspect ratio matches the max JPEG resolution supported by the camera.
883      *
884      * @param outputFileOptions  Options to store the newly captured image.
885      * @param executor The executor in which the callback methods will be run.
886      * @param imageSavedCallback Callback to be called for the newly captured image.
887      * @throws IllegalStateException If {@link ImageCapture#FLASH_MODE_SCREEN} is set to the
888      * {@link CameraController}, but a non-null {@link Window} instance has not been set with
889      * {@link PreviewView#setScreenFlashWindow}.
890      * @see ImageCapture#takePicture(
891      * ImageCapture.OutputFileOptions, Executor, ImageCapture.OnImageSavedCallback)
892      */
893     @MainThread
takePicture( ImageCapture.@onNull OutputFileOptions outputFileOptions, @NonNull Executor executor, ImageCapture.@NonNull OnImageSavedCallback imageSavedCallback)894     public void takePicture(
895             ImageCapture.@NonNull OutputFileOptions outputFileOptions,
896             @NonNull Executor executor,
897             ImageCapture.@NonNull OnImageSavedCallback imageSavedCallback) {
898         checkMainThread();
899         Preconditions.checkState(isCameraInitialized(), CAMERA_NOT_INITIALIZED);
900         Preconditions.checkState(isImageCaptureEnabled(), IMAGE_CAPTURE_DISABLED);
901 
902         throwExceptionForInvalidScreenFlashCapture();
903 
904         updateMirroringFlagInOutputFileOptions(outputFileOptions);
905         mImageCapture.takePicture(outputFileOptions, executor, imageSavedCallback);
906     }
907 
908     /**
909      * Updates {@link ImageCapture.OutputFileOptions} based on config.
910      *
911      * <p>Mirror the output image if front camera is used and if the flag is not set explicitly by
912      * the app.
913      */
914     @VisibleForTesting
915     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
updateMirroringFlagInOutputFileOptions( ImageCapture.@onNull OutputFileOptions outputFileOptions)916     void updateMirroringFlagInOutputFileOptions(
917             ImageCapture.@NonNull OutputFileOptions outputFileOptions) {
918         if (mCameraSelector.getLensFacing() != null
919                 && !outputFileOptions.getMetadata().isReversedHorizontalSet()) {
920             outputFileOptions.getMetadata().setReversedHorizontal(
921                     mCameraSelector.getLensFacing() == CameraSelector.LENS_FACING_FRONT);
922         }
923     }
924 
925     /**
926      * Captures a new still image for in memory access.
927      *
928      * <p>The listener is responsible for calling {@link ImageProxy#close()} on the returned image.
929      *
930      * @param executor The executor in which the callback methods will be run.
931      * @param callback Callback to be invoked for the newly captured image
932      * @throws IllegalStateException If {@link ImageCapture#FLASH_MODE_SCREEN} is set to the
933      * {@link CameraController}, but a non-null {@link Window} instance has not been set with
934      * {@link PreviewView#setScreenFlashWindow}.
935      * @see ImageCapture#takePicture(Executor, ImageCapture.OnImageCapturedCallback)
936      */
937     @MainThread
takePicture( @onNull Executor executor, ImageCapture.@NonNull OnImageCapturedCallback callback)938     public void takePicture(
939             @NonNull Executor executor,
940             ImageCapture.@NonNull OnImageCapturedCallback callback) {
941         checkMainThread();
942         Preconditions.checkState(isCameraInitialized(), CAMERA_NOT_INITIALIZED);
943         Preconditions.checkState(isImageCaptureEnabled(), IMAGE_CAPTURE_DISABLED);
944 
945         throwExceptionForInvalidScreenFlashCapture();
946 
947         mImageCapture.takePicture(executor, callback);
948     }
949 
throwExceptionForInvalidScreenFlashCapture()950     private void throwExceptionForInvalidScreenFlashCapture() {
951         if (getImageCaptureFlashMode() == ImageCapture.FLASH_MODE_SCREEN && (
952                 getScreenFlashUiInfoByPriority() == null
953                         || getScreenFlashUiInfoByPriority().getScreenFlash() == null)) {
954             // ScreenFlash instance won't be found at this point only if a non-null window was not
955             // set to PreviewView.
956             throw new IllegalStateException(
957                     "No window set in PreviewView despite setting FLASH_MODE_SCREEN");
958         }
959     }
960 
961     /**
962      * Sets the image capture mode.
963      *
964      * <p>Valid capture modes are {@link ImageCapture.CaptureMode#CAPTURE_MODE_MINIMIZE_LATENCY},
965      * which prioritizes latency over image quality, or
966      * {@link ImageCapture.CaptureMode#CAPTURE_MODE_MAXIMIZE_QUALITY},
967      * which prioritizes image quality over latency.
968      *
969      * <p>Changing the value will reconfigure the camera which will cause additional latency. To
970      * avoid this, set the value before controller is bound to lifecycle.
971      *
972      * @param captureMode The requested image capture mode.
973      */
974     @MainThread
setImageCaptureMode(@mageCapture.CaptureMode int captureMode)975     public void setImageCaptureMode(@ImageCapture.CaptureMode int captureMode) {
976         checkMainThread();
977         if (mImageCapture.getCaptureMode() == captureMode) {
978             return;
979         }
980         unbindImageCaptureAndRecreate(captureMode);
981         startCameraAndTrackStates();
982     }
983 
984     /**
985      * Returns the image capture mode.
986      *
987      * @see ImageCapture#getCaptureMode()
988      */
989     @MainThread
getImageCaptureMode()990     public int getImageCaptureMode() {
991         checkMainThread();
992         return mImageCapture.getCaptureMode();
993     }
994 
995     /**
996      * Sets the intended image size for {@link ImageCapture}.
997      *
998      * <p>The value is used as a hint when determining the resolution and aspect ratio of the
999      * captured image. The actual output may differ from the requested value due to device
1000      * constraints.
1001      *
1002      * <p>When set to {@code null}, the output will be based on the default config of
1003      * {@link ImageCapture}.
1004      *
1005      * <p>Changing the value will reconfigure the camera which will cause additional latency. To
1006      * avoid this, set the value before controller is bound to lifecycle.
1007      *
1008      * @param targetSize The intended image size for {@link ImageCapture}.
1009      * @deprecated Use {@link #setImageCaptureResolutionSelector(ResolutionSelector)} instead.
1010      */
1011     @MainThread
1012     @Deprecated
setImageCaptureTargetSize(@ullable OutputSize targetSize)1013     public void setImageCaptureTargetSize(@Nullable OutputSize targetSize) {
1014         checkMainThread();
1015         if (isOutputSizeEqual(mImageCaptureTargetSize, targetSize)) {
1016             return;
1017         }
1018         mImageCaptureTargetSize = targetSize;
1019         unbindImageCaptureAndRecreate(getImageCaptureMode());
1020         startCameraAndTrackStates();
1021     }
1022 
1023     /**
1024      * Returns the intended output size for {@link ImageCapture} set by
1025      * {@link #setImageCaptureTargetSize(OutputSize)}, or {@code null} if not set.
1026      *
1027      * @deprecated Use {@link #getImageCaptureResolutionSelector()} instead.
1028      */
1029     @Deprecated
1030     @MainThread
getImageCaptureTargetSize()1031     public @Nullable OutputSize getImageCaptureTargetSize() {
1032         checkMainThread();
1033         return mImageCaptureTargetSize;
1034     }
1035 
1036     /**
1037      * Sets the {@link ResolutionSelector} for {@link ImageCapture}.
1038      *
1039      * <p>CameraX uses this value as a hint to select the resolution for captured images. The actual
1040      * output may differ from the requested value due to device constraints. When set to
1041      * {@code null}, CameraX will use the default config of {@link ImageCapture}. The default
1042      * resolution strategy for ImageCapture is
1043      * {@link ResolutionStrategy#HIGHEST_AVAILABLE_STRATEGY}, which will select the largest
1044      * available resolution to use.
1045      *
1046      * <p>Changing the value will reconfigure the camera which will cause additional latency. To
1047      * avoid this, set the value before controller is bound to lifecycle.
1048      *
1049      * @see ImageCapture.Builder#setResolutionSelector(ResolutionSelector)
1050      */
1051     @MainThread
setImageCaptureResolutionSelector(@ullable ResolutionSelector resolutionSelector)1052     public void setImageCaptureResolutionSelector(@Nullable ResolutionSelector resolutionSelector) {
1053         checkMainThread();
1054         if (mImageCaptureResolutionSelector == resolutionSelector) {
1055             return;
1056         }
1057         mImageCaptureResolutionSelector = resolutionSelector;
1058         unbindImageCaptureAndRecreate(getImageCaptureMode());
1059         startCameraAndTrackStates();
1060     }
1061 
1062     /**
1063      * Returns the {@link ResolutionSelector} for {@link ImageCapture}.
1064      *
1065      * <p>This method returns the value set by
1066      * {@link #setImageCaptureResolutionSelector} (ResolutionSelector)}. It returns {@code null} if
1067      * the value has not been set.
1068      */
1069     @MainThread
getImageCaptureResolutionSelector()1070     public @Nullable ResolutionSelector getImageCaptureResolutionSelector() {
1071         checkMainThread();
1072         return mImageCaptureResolutionSelector;
1073     }
1074 
1075     /**
1076      * Sets the default executor that will be used for {@link ImageCapture} IO tasks.
1077      *
1078      * <p>This executor will be used for any IO tasks specifically for {@link ImageCapture},
1079      * such as {@link #takePicture(ImageCapture.OutputFileOptions, Executor,
1080      * ImageCapture.OnImageSavedCallback)}. If no executor is set, then a default Executor
1081      * specifically for IO will be used instead.
1082      *
1083      * <p>Changing the value will reconfigure the camera which will cause additional latency.
1084      * To avoid this, set the value before controller is bound to lifecycle.
1085      *
1086      * @param executor The executor which will be used for IO tasks.
1087      */
1088     @MainThread
setImageCaptureIoExecutor(@ullable Executor executor)1089     public void setImageCaptureIoExecutor(@Nullable Executor executor) {
1090         checkMainThread();
1091         if (mImageCaptureIoExecutor == executor) {
1092             return;
1093         }
1094         mImageCaptureIoExecutor = executor;
1095         unbindImageCaptureAndRecreate(getImageCaptureMode());
1096         startCameraAndTrackStates();
1097     }
1098 
1099     /**
1100      * Gets the default executor for {@link ImageCapture} IO tasks.
1101      */
1102     @MainThread
getImageCaptureIoExecutor()1103     public @Nullable Executor getImageCaptureIoExecutor() {
1104         checkMainThread();
1105         return mImageCaptureIoExecutor;
1106     }
1107 
1108     /**
1109      * Unbinds {@link ImageCapture} and recreates with the latest parameters.
1110      */
1111     @MainThread
unbindImageCaptureAndRecreate(Integer imageCaptureMode)1112     private void unbindImageCaptureAndRecreate(Integer imageCaptureMode) {
1113         if (isCameraInitialized()) {
1114             mCameraProvider.unbind(mImageCapture);
1115         }
1116         int flashMode = mImageCapture.getFlashMode();
1117         mImageCapture = createImageCapture(imageCaptureMode);
1118         setImageCaptureFlashMode(flashMode);
1119     }
1120 
createImageCapture(Integer imageCaptureMode)1121     private ImageCapture createImageCapture(Integer imageCaptureMode) {
1122         ImageCapture.Builder builder = new ImageCapture.Builder();
1123         if (imageCaptureMode != null) {
1124             builder.setCaptureMode(imageCaptureMode);
1125         }
1126         configureResolution(builder, mImageCaptureResolutionSelector, mImageCaptureTargetSize);
1127         if (mImageCaptureIoExecutor != null) {
1128             builder.setIoExecutor(mImageCaptureIoExecutor);
1129         }
1130 
1131         return builder.build();
1132     }
1133 
1134     // -----------------
1135     // Image analysis
1136     // -----------------
1137 
1138     /**
1139      * Checks if {@link ImageAnalysis} is enabled.
1140      *
1141      * @see ImageAnalysis
1142      */
1143     @MainThread
isImageAnalysisEnabled()1144     public boolean isImageAnalysisEnabled() {
1145         checkMainThread();
1146         return isUseCaseEnabled(IMAGE_ANALYSIS);
1147     }
1148 
1149     /**
1150      * Sets an analyzer to receive and analyze images.
1151      *
1152      * <p>Applications can process or copy the image by implementing the
1153      * {@link ImageAnalysis.Analyzer}. The image needs to be closed by calling
1154      * {@link ImageProxy#close()} when the analyzing is done.
1155      *
1156      * <p>Setting an analyzer function replaces any previous analyzer. Only one analyzer can be
1157      * set at any time.
1158      *
1159      * <p>If the {@link ImageAnalysis.Analyzer#getTargetCoordinateSystem()} returns
1160      * {@link ImageAnalysis#COORDINATE_SYSTEM_VIEW_REFERENCED}, the analyzer will receive a
1161      * transformation via {@link ImageAnalysis.Analyzer#updateTransform} that converts
1162      * coordinates from the {@link ImageAnalysis}'s coordinate system to the {@link PreviewView}'s
1163      * coordinate system.
1164      *
1165      * <p>If the {@link ImageAnalysis.Analyzer#getDefaultTargetResolution()} returns a non-null
1166      * value, calling this method will reconfigure the camera which might cause additional
1167      * latency. To avoid this, set the value before controller is bound to the lifecycle.
1168      *
1169      * @param executor The executor in which the
1170      * {@link ImageAnalysis.Analyzer#analyze(ImageProxy)} will be run.
1171      * @param analyzer of the images.
1172      * @see ImageAnalysis#setAnalyzer(Executor, ImageAnalysis.Analyzer)
1173      */
1174     @MainThread
setImageAnalysisAnalyzer(@onNull Executor executor, ImageAnalysis.@NonNull Analyzer analyzer)1175     public void setImageAnalysisAnalyzer(@NonNull Executor executor,
1176             ImageAnalysis.@NonNull Analyzer analyzer) {
1177         checkMainThread();
1178         if (mAnalysisAnalyzer == analyzer && mAnalysisExecutor == executor) {
1179             return;
1180         }
1181         ImageAnalysis.Analyzer oldAnalyzer = mAnalysisAnalyzer;
1182         mAnalysisExecutor = executor;
1183         mAnalysisAnalyzer = analyzer;
1184         mImageAnalysis.setAnalyzer(executor, analyzer);
1185         restartCameraIfAnalyzerResolutionChanged(oldAnalyzer, analyzer);
1186     }
1187 
1188     /**
1189      * Removes a previously set analyzer.
1190      *
1191      * <p>This will stop data from streaming to the {@link ImageAnalysis}.
1192      *
1193      * <p>If the current {@link ImageAnalysis.Analyzer#getDefaultTargetResolution()} returns
1194      * non-null value, calling this method will reconfigure the camera which might cause additional
1195      * latency. To avoid this, call this method when the lifecycle is not active.
1196      *
1197      * @see ImageAnalysis#clearAnalyzer().
1198      */
1199     @MainThread
clearImageAnalysisAnalyzer()1200     public void clearImageAnalysisAnalyzer() {
1201         checkMainThread();
1202         ImageAnalysis.Analyzer oldAnalyzer = mAnalysisAnalyzer;
1203         mAnalysisExecutor = null;
1204         mAnalysisAnalyzer = null;
1205         mImageAnalysis.clearAnalyzer();
1206         restartCameraIfAnalyzerResolutionChanged(oldAnalyzer, null);
1207     }
1208 
restartCameraIfAnalyzerResolutionChanged( ImageAnalysis.@ullable Analyzer oldAnalyzer, ImageAnalysis.@Nullable Analyzer newAnalyzer)1209     private void restartCameraIfAnalyzerResolutionChanged(
1210             ImageAnalysis.@Nullable Analyzer oldAnalyzer,
1211             ImageAnalysis.@Nullable Analyzer newAnalyzer) {
1212         Size oldResolution = oldAnalyzer == null ? null :
1213                 oldAnalyzer.getDefaultTargetResolution();
1214         Size newResolution = newAnalyzer == null ? null :
1215                 newAnalyzer.getDefaultTargetResolution();
1216         if (!Objects.equals(oldResolution, newResolution)) {
1217             // Rebind ImageAnalysis to reconfigure target resolution.
1218             unbindImageAnalysisAndRecreate(mImageAnalysis.getBackpressureStrategy(),
1219                     mImageAnalysis.getImageQueueDepth(), mImageAnalysis.getOutputImageFormat());
1220             startCameraAndTrackStates();
1221         }
1222     }
1223 
1224     /**
1225      * Returns the mode with which images are acquired.
1226      *
1227      * <p>If not set, it defaults to {@link ImageAnalysis#STRATEGY_KEEP_ONLY_LATEST}.
1228      *
1229      * @return The backpressure strategy applied to the image producer.
1230      * @see ImageAnalysis.Builder#getBackpressureStrategy()
1231      */
1232     @MainThread
1233     @ImageAnalysis.BackpressureStrategy
getImageAnalysisBackpressureStrategy()1234     public int getImageAnalysisBackpressureStrategy() {
1235         checkMainThread();
1236         return mImageAnalysis.getBackpressureStrategy();
1237     }
1238 
1239     /**
1240      * Sets the backpressure strategy to apply to the image producer to deal with scenarios
1241      * where images may be produced faster than they can be analyzed.
1242      *
1243      * <p>The available values are {@link ImageAnalysis#STRATEGY_BLOCK_PRODUCER} and
1244      * {@link ImageAnalysis#STRATEGY_KEEP_ONLY_LATEST}. If not set, the backpressure strategy
1245      * will default to {@link ImageAnalysis#STRATEGY_KEEP_ONLY_LATEST}.
1246      *
1247      * <p>Changing the value will reconfigure the camera which will cause additional latency. To
1248      * avoid this, set the value before controller is bound to lifecycle.
1249      *
1250      * @param strategy The strategy to use.
1251      * @see ImageAnalysis.Builder#setBackpressureStrategy(int)
1252      */
1253     @MainThread
setImageAnalysisBackpressureStrategy( @mageAnalysis.BackpressureStrategy int strategy)1254     public void setImageAnalysisBackpressureStrategy(
1255             @ImageAnalysis.BackpressureStrategy int strategy) {
1256         checkMainThread();
1257         if (mImageAnalysis.getBackpressureStrategy() == strategy) {
1258             return;
1259         }
1260 
1261         unbindImageAnalysisAndRecreate(strategy, mImageAnalysis.getImageQueueDepth(),
1262                 mImageAnalysis.getOutputImageFormat());
1263         startCameraAndTrackStates();
1264     }
1265 
1266     /**
1267      * Sets the image queue depth of {@link ImageAnalysis}.
1268      *
1269      * <p>This sets the number of images available in parallel to {@link ImageAnalysis.Analyzer}.
1270      * The value is only used if the backpressure strategy is
1271      * {@link ImageAnalysis.BackpressureStrategy#STRATEGY_BLOCK_PRODUCER}.
1272      *
1273      * <p>Changing the value will reconfigure the camera which will cause additional latency. To
1274      * avoid this, set the value before controller is bound to lifecycle.
1275      *
1276      * @param depth The total number of images available.
1277      * @see ImageAnalysis.Builder#setImageQueueDepth(int)
1278      */
1279     @MainThread
setImageAnalysisImageQueueDepth(int depth)1280     public void setImageAnalysisImageQueueDepth(int depth) {
1281         checkMainThread();
1282         if (mImageAnalysis.getImageQueueDepth() == depth) {
1283             return;
1284         }
1285         unbindImageAnalysisAndRecreate(mImageAnalysis.getBackpressureStrategy(), depth,
1286                 mImageAnalysis.getOutputImageFormat());
1287         startCameraAndTrackStates();
1288     }
1289 
1290     /**
1291      * Gets the image queue depth of {@link ImageAnalysis}.
1292      *
1293      * @see ImageAnalysis#getImageQueueDepth()
1294      */
1295     @MainThread
getImageAnalysisImageQueueDepth()1296     public int getImageAnalysisImageQueueDepth() {
1297         checkMainThread();
1298         return mImageAnalysis.getImageQueueDepth();
1299     }
1300 
1301     /**
1302      * Sets the intended output size for {@link ImageAnalysis}.
1303      *
1304      * <p>The value is used as a hint when determining the resolution and aspect ratio of
1305      * the output buffer. The actual output may differ from the requested value due to device
1306      * constraints.
1307      *
1308      * <p>When set to {@code null}, the output will be based on the default config of
1309      * {@link ImageAnalysis}.
1310      *
1311      * <p>Changing the value will reconfigure the camera which will cause additional latency.
1312      * To avoid this, set the value before controller is bound to lifecycle.
1313      *
1314      * @param targetSize The intended output size for {@link ImageAnalysis}.
1315      * @see ImageAnalysis.Builder#setTargetAspectRatio(int)
1316      * @see ImageAnalysis.Builder#setTargetResolution(Size)
1317      * @deprecated Use {@link #setImageAnalysisResolutionSelector(ResolutionSelector)} instead.
1318      */
1319     @MainThread
1320     @Deprecated
setImageAnalysisTargetSize(@ullable OutputSize targetSize)1321     public void setImageAnalysisTargetSize(@Nullable OutputSize targetSize) {
1322         checkMainThread();
1323         if (isOutputSizeEqual(mImageAnalysisTargetSize, targetSize)) {
1324             return;
1325         }
1326         mImageAnalysisTargetSize = targetSize;
1327         unbindImageAnalysisAndRecreate(
1328                 mImageAnalysis.getBackpressureStrategy(),
1329                 mImageAnalysis.getImageQueueDepth(),
1330                 mImageAnalysis.getOutputImageFormat());
1331         startCameraAndTrackStates();
1332     }
1333 
1334     /**
1335      * Returns the intended output size for {@link ImageAnalysis} set by
1336      * {@link #setImageAnalysisTargetSize(OutputSize)}, or {@code null} if not set.
1337      *
1338      * @deprecated Use {@link #getImageAnalysisResolutionSelector()} instead.
1339      */
1340     @MainThread
1341     @Deprecated
getImageAnalysisTargetSize()1342     public @Nullable OutputSize getImageAnalysisTargetSize() {
1343         checkMainThread();
1344         return mImageAnalysisTargetSize;
1345     }
1346 
1347     /**
1348      * Sets the {@link ResolutionSelector} for {@link ImageAnalysis}.
1349      *
1350      * <p>CameraX uses this value as a hint to select the resolution for images. The actual
1351      * output may differ from the requested value due to device constraints. When set to
1352      * {@code null}, CameraX will use the default config of {@link ImageAnalysis}. ImageAnalysis has
1353      * a default {@link ResolutionStrategy} with bound size as 640x480 and fallback rule of
1354      * {@link ResolutionStrategy#FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER}.
1355      *
1356      * <p>Changing the value will reconfigure the camera which will cause additional latency. To
1357      * avoid this, set the value before controller is bound to lifecycle.
1358      *
1359      * @see ImageAnalysis.Builder#setResolutionSelector(ResolutionSelector)
1360      */
1361     @MainThread
setImageAnalysisResolutionSelector( @ullable ResolutionSelector resolutionSelector)1362     public void setImageAnalysisResolutionSelector(
1363             @Nullable ResolutionSelector resolutionSelector) {
1364         checkMainThread();
1365         if (mImageAnalysisResolutionSelector == resolutionSelector) {
1366             return;
1367         }
1368         mImageAnalysisResolutionSelector = resolutionSelector;
1369         unbindImageAnalysisAndRecreate(
1370                 mImageAnalysis.getBackpressureStrategy(),
1371                 mImageAnalysis.getImageQueueDepth(),
1372                 mImageAnalysis.getOutputImageFormat());
1373         startCameraAndTrackStates();
1374     }
1375 
1376     /**
1377      * Returns the {@link ResolutionSelector} for {@link ImageAnalysis}.
1378      *
1379      * <p>This method returns the value set by
1380      * {@link #setImageAnalysisResolutionSelector(ResolutionSelector)}. It returns {@code null} if
1381      * the value has not been set.
1382      */
1383     @MainThread
getImageAnalysisResolutionSelector()1384     public @Nullable ResolutionSelector getImageAnalysisResolutionSelector() {
1385         checkMainThread();
1386         return mImageAnalysisResolutionSelector;
1387     }
1388 
1389     /**
1390      * Sets the executor that will be used for {@link ImageAnalysis} background tasks.
1391      *
1392      * <p>If not set, the background executor will default to an automatically generated
1393      * {@link Executor}.
1394      *
1395      * <p>Changing the value will reconfigure the camera, which will cause additional latency. To
1396      * avoid this, set the value before controller is bound to lifecycle.
1397      *
1398      * @param executor The executor for {@link ImageAnalysis} background tasks.
1399      * @see ImageAnalysis.Builder#setBackgroundExecutor(Executor)
1400      */
1401     @MainThread
setImageAnalysisBackgroundExecutor(@ullable Executor executor)1402     public void setImageAnalysisBackgroundExecutor(@Nullable Executor executor) {
1403         checkMainThread();
1404         if (mAnalysisBackgroundExecutor == executor) {
1405             return;
1406         }
1407         mAnalysisBackgroundExecutor = executor;
1408         unbindImageAnalysisAndRecreate(mImageAnalysis.getBackpressureStrategy(),
1409                 mImageAnalysis.getImageQueueDepth(), mImageAnalysis.getOutputImageFormat());
1410         startCameraAndTrackStates();
1411     }
1412 
1413     /**
1414      * Gets the default executor for {@link ImageAnalysis} background tasks.
1415      *
1416      * @see ImageAnalysis.Builder#setBackgroundExecutor(Executor)
1417      */
1418     @MainThread
getImageAnalysisBackgroundExecutor()1419     public @Nullable Executor getImageAnalysisBackgroundExecutor() {
1420         checkMainThread();
1421         return mAnalysisBackgroundExecutor;
1422     }
1423 
1424     /**
1425      * Sets the output image format for {@link ImageAnalysis}.
1426      *
1427      * <p>The supported output image format
1428      * are {@link ImageAnalysis.OutputImageFormat#OUTPUT_IMAGE_FORMAT_YUV_420_888} and
1429      * {@link ImageAnalysis.OutputImageFormat#OUTPUT_IMAGE_FORMAT_RGBA_8888}.
1430      *
1431      * <p>If not set, {@link ImageAnalysis.OutputImageFormat#OUTPUT_IMAGE_FORMAT_YUV_420_888}
1432      * will be used.
1433      *
1434      * <p>Requesting {@link ImageAnalysis.OutputImageFormat#OUTPUT_IMAGE_FORMAT_RGBA_8888} or
1435      * {@link ImageAnalysis.OutputImageFormat#OUTPUT_IMAGE_FORMAT_NV21} causes extra overhead
1436      * because format conversion takes time.
1437      *
1438      * <p>Changing the value will reconfigure the camera, which may cause additional latency. To
1439      * avoid this, set the value before controller is bound to lifecycle. If the value is changed
1440      * when the camera is active, check the {@link ImageProxy#getFormat()} value to determine
1441      * when the new format takes effect.
1442      *
1443      * @see ImageAnalysis.Builder#setOutputImageFormat(int)
1444      * @see ImageAnalysis.Builder#getOutputImageFormat()
1445      * @see ImageAnalysis#getOutputImageFormat()
1446      */
1447     @MainThread
setImageAnalysisOutputImageFormat( @mageAnalysis.OutputImageFormat int imageAnalysisOutputImageFormat)1448     public void setImageAnalysisOutputImageFormat(
1449             @ImageAnalysis.OutputImageFormat int imageAnalysisOutputImageFormat) {
1450         checkMainThread();
1451         if (imageAnalysisOutputImageFormat == mImageAnalysis.getOutputImageFormat()) {
1452             // No-op if the value is not changed.
1453             return;
1454         }
1455         unbindImageAnalysisAndRecreate(mImageAnalysis.getBackpressureStrategy(),
1456                 mImageAnalysis.getImageQueueDepth(), imageAnalysisOutputImageFormat);
1457     }
1458 
1459     /**
1460      * Gets the output image format for {@link ImageAnalysis}.
1461      *
1462      * <p>The returned image format can be either
1463      * {@link ImageAnalysis#OUTPUT_IMAGE_FORMAT_YUV_420_888},
1464      * {@link ImageAnalysis#OUTPUT_IMAGE_FORMAT_RGBA_8888} or
1465      * {@link ImageAnalysis#OUTPUT_IMAGE_FORMAT_NV21}.
1466      *
1467      * @see ImageAnalysis.Builder#setOutputImageFormat(int)
1468      * @see ImageAnalysis.Builder#getOutputImageFormat()
1469      * @see ImageAnalysis#getOutputImageFormat()
1470      */
1471     @MainThread
1472     @ImageAnalysis.OutputImageFormat
getImageAnalysisOutputImageFormat()1473     public int getImageAnalysisOutputImageFormat() {
1474         checkMainThread();
1475         return mImageAnalysis.getOutputImageFormat();
1476     }
1477 
1478     /**
1479      * Unbinds {@link ImageAnalysis} and recreates with the latest parameters.
1480      */
1481     @MainThread
unbindImageAnalysisAndRecreate(Integer strategy, Integer imageQueueDepth, Integer outputFormat)1482     private void unbindImageAnalysisAndRecreate(Integer strategy, Integer imageQueueDepth,
1483             Integer outputFormat) {
1484         checkMainThread();
1485         if (isCameraInitialized()) {
1486             mCameraProvider.unbind(mImageAnalysis);
1487         }
1488         mImageAnalysis = createImageAnalysis(strategy, imageQueueDepth, outputFormat);
1489         if (mAnalysisExecutor != null && mAnalysisAnalyzer != null) {
1490             mImageAnalysis.setAnalyzer(mAnalysisExecutor, mAnalysisAnalyzer);
1491         }
1492     }
1493 
createImageAnalysis(Integer strategy, Integer imageQueueDepth, Integer outputFormat)1494     private ImageAnalysis createImageAnalysis(Integer strategy, Integer imageQueueDepth,
1495             Integer outputFormat) {
1496         ImageAnalysis.Builder builder = new ImageAnalysis.Builder();
1497         if (strategy != null) {
1498             builder.setBackpressureStrategy(strategy);
1499         }
1500         if (imageQueueDepth != null) {
1501             builder.setImageQueueDepth(imageQueueDepth);
1502         }
1503         if (outputFormat != null) {
1504             builder.setOutputImageFormat(outputFormat);
1505         }
1506         configureResolution(builder, mImageAnalysisResolutionSelector, mImageAnalysisTargetSize);
1507         if (mAnalysisBackgroundExecutor != null) {
1508             builder.setBackgroundExecutor(mAnalysisBackgroundExecutor);
1509         }
1510 
1511         return builder.build();
1512     }
1513 
1514     @OptIn(markerClass = {TransformExperimental.class})
1515     @MainThread
updatePreviewViewTransform(@ullable Matrix matrix)1516     void updatePreviewViewTransform(@Nullable Matrix matrix) {
1517         checkMainThread();
1518         if (mAnalysisAnalyzer == null) {
1519             return;
1520         }
1521         if (mAnalysisAnalyzer.getTargetCoordinateSystem()
1522                 == ImageAnalysis.COORDINATE_SYSTEM_VIEW_REFERENCED) {
1523             mAnalysisAnalyzer.updateTransform(matrix);
1524         }
1525     }
1526 
1527     // -----------------
1528     // Video capture
1529     // -----------------
1530 
1531     /**
1532      * Checks if video capture is enabled.
1533      *
1534      * <p>Video capture is disabled by default. It has to be enabled before {@link #startRecording}
1535      * can be called.
1536      */
1537     @MainThread
isVideoCaptureEnabled()1538     public boolean isVideoCaptureEnabled() {
1539         checkMainThread();
1540         return isUseCaseEnabled(VIDEO_CAPTURE);
1541     }
1542 
1543     /**
1544      * Takes a video to a given file.
1545      *
1546      * <p>Only a single recording can be active at a time, so if {@link #isRecording()} is
1547      * {@code true}, this will throw an {@link IllegalStateException}.
1548      *
1549      * <p>Upon successfully starting the recording, a {@link VideoRecordEvent.Start} event will
1550      * be the first event sent to the provided event listener.
1551      *
1552      * <p>If errors occur while starting the recording, a {@link VideoRecordEvent.Finalize} event
1553      * will be the first event sent to the provided listener, and information about the error can
1554      * be found in that event's {@link VideoRecordEvent.Finalize#getError()} method.
1555      *
1556      * <p>Recording with audio requires the {@link android.Manifest.permission#RECORD_AUDIO}
1557      * permission; without it, starting a recording will fail with a {@link SecurityException}.
1558      *
1559      * @param outputOptions The options to store the newly captured video.
1560      * @param audioConfig The configuration of audio.
1561      * @param executor The executor that the event listener will be run on.
1562      * @param listener The event listener to handle video record events.
1563      * @return a {@link Recording} that provides controls for new active recordings.
1564      * @throws IllegalStateException if there is an unfinished active recording.
1565      * @throws SecurityException if the audio config specifies audio should be enabled but the
1566      * {@link android.Manifest.permission#RECORD_AUDIO} permission is denied.
1567      */
1568     @SuppressLint("MissingPermission")
1569     @MainThread
startRecording( @onNull FileOutputOptions outputOptions, @NonNull AudioConfig audioConfig, @NonNull Executor executor, @NonNull Consumer<VideoRecordEvent> listener)1570     public @NonNull Recording startRecording(
1571             @NonNull FileOutputOptions outputOptions,
1572             @NonNull AudioConfig audioConfig,
1573             @NonNull Executor executor,
1574             @NonNull Consumer<VideoRecordEvent> listener) {
1575         return startRecordingInternal(outputOptions, audioConfig, executor, listener);
1576     }
1577 
1578     /**
1579      * Takes a video to a given file descriptor.
1580      *
1581      * <p>Currently, file descriptors as output destinations are not supported on pre-Android O
1582      * (API 26) devices.
1583      *
1584      * <p>Only a single recording can be active at a time, so if {@link #isRecording()} is true,
1585      * this will throw an {@link IllegalStateException}.
1586      *
1587      * <p>Upon successfully starting the recording, a {@link VideoRecordEvent.Start} event will
1588      * be the first event sent to the provided event listener.
1589      *
1590      * <p>If errors occur while starting the recording, a {@link VideoRecordEvent.Finalize} event
1591      * will be the first event sent to the provided listener, and information about the error can
1592      * be found in that event's {@link VideoRecordEvent.Finalize#getError()} method.
1593      *
1594      * <p>Recording with audio requires the {@link android.Manifest.permission#RECORD_AUDIO}
1595      * permission; without it, starting a recording will fail with a {@link SecurityException}.
1596      *
1597      * @param outputOptions The options to store the newly captured video.
1598      * @param audioConfig The configuration of audio.
1599      * @param executor The executor that the event listener will be run on.
1600      * @param listener The event listener to handle video record events.
1601      * @return a {@link Recording} that provides controls for new active recordings.
1602      * @throws IllegalStateException if there is an unfinished active recording.
1603      * @throws SecurityException if the audio config specifies audio should be enabled but the
1604      * {@link android.Manifest.permission#RECORD_AUDIO} permission is denied.
1605      */
1606     @SuppressLint("MissingPermission")
1607     @RequiresApi(26)
1608     @MainThread
startRecording( @onNull FileDescriptorOutputOptions outputOptions, @NonNull AudioConfig audioConfig, @NonNull Executor executor, @NonNull Consumer<VideoRecordEvent> listener)1609     public @NonNull Recording startRecording(
1610             @NonNull FileDescriptorOutputOptions outputOptions,
1611             @NonNull AudioConfig audioConfig,
1612             @NonNull Executor executor,
1613             @NonNull Consumer<VideoRecordEvent> listener) {
1614         return startRecordingInternal(outputOptions, audioConfig, executor, listener);
1615     }
1616 
1617     /**
1618      * Takes a video to MediaStore.
1619      *
1620      * <p>Only a single recording can be active at a time, so if {@link #isRecording()} is
1621      * {@code true}, this will throw an {@link IllegalStateException}.
1622      *
1623      * <p>Upon successfully starting the recording, a {@link VideoRecordEvent.Start} event will
1624      * be the first event sent to the provided event listener.
1625      *
1626      * <p>If errors occur while starting the recording, a {@link VideoRecordEvent.Finalize} event
1627      * will be the first event sent to the provided listener, and information about the error can
1628      * be found in that event's {@link VideoRecordEvent.Finalize#getError()} method.
1629      *
1630      * <p>Recording with audio requires the {@link android.Manifest.permission#RECORD_AUDIO}
1631      * permission; without it, starting a recording will fail with a {@link SecurityException}.
1632      *
1633      * @param outputOptions The options to store the newly captured video.
1634      * @param audioConfig The configuration of audio.
1635      * @param executor The executor that the event listener will be run on.
1636      * @param listener The event listener to handle video record events.
1637      * @return a {@link Recording} that provides controls for new active recordings.
1638      * @throws IllegalStateException if there is an unfinished active recording.
1639      * @throws SecurityException if the audio config specifies audio should be enabled but the
1640      * {@link android.Manifest.permission#RECORD_AUDIO} permission is denied.
1641      */
1642     @SuppressLint("MissingPermission")
1643     @MainThread
startRecording( @onNull MediaStoreOutputOptions outputOptions, @NonNull AudioConfig audioConfig, @NonNull Executor executor, @NonNull Consumer<VideoRecordEvent> listener)1644     public @NonNull Recording startRecording(
1645             @NonNull MediaStoreOutputOptions outputOptions,
1646             @NonNull AudioConfig audioConfig,
1647             @NonNull Executor executor,
1648             @NonNull Consumer<VideoRecordEvent> listener) {
1649         return startRecordingInternal(outputOptions, audioConfig, executor, listener);
1650     }
1651 
1652     @RequiresPermission(Manifest.permission.RECORD_AUDIO)
1653     @MainThread
startRecordingInternal( @onNull OutputOptions outputOptions, @NonNull AudioConfig audioConfig, @NonNull Executor executor, @NonNull Consumer<VideoRecordEvent> listener)1654     private Recording startRecordingInternal(
1655             @NonNull OutputOptions outputOptions,
1656             @NonNull AudioConfig audioConfig,
1657             @NonNull Executor executor,
1658             @NonNull Consumer<VideoRecordEvent> listener) {
1659         checkMainThread();
1660         Preconditions.checkState(isCameraInitialized(), CAMERA_NOT_INITIALIZED);
1661         Preconditions.checkState(isVideoCaptureEnabled(), VIDEO_CAPTURE_DISABLED);
1662         Preconditions.checkState(!isRecording(), VIDEO_RECORDING_UNFINISHED);
1663 
1664         Consumer<VideoRecordEvent> wrappedListener =
1665                 wrapListenerToDeactivateRecordingOnFinalized(listener);
1666         PendingRecording pendingRecording = prepareRecording(outputOptions);
1667         boolean isAudioEnabled = audioConfig.getAudioEnabled();
1668         if (isAudioEnabled) {
1669             checkAudioPermissionGranted();
1670             pendingRecording.withAudioEnabled();
1671         }
1672         Recording recording = pendingRecording.start(executor, wrappedListener);
1673         setActiveRecording(recording, wrappedListener);
1674 
1675         return recording;
1676     }
1677 
checkAudioPermissionGranted()1678     private void checkAudioPermissionGranted() {
1679         int permissionState = PermissionChecker.checkSelfPermission(mAppContext,
1680                 Manifest.permission.RECORD_AUDIO);
1681         if (permissionState == PermissionChecker.PERMISSION_DENIED) {
1682             throw new SecurityException("Attempted to start recording with audio, but "
1683                     + "application does not have RECORD_AUDIO permission granted.");
1684         }
1685     }
1686 
1687     /**
1688      * Generates a {@link PendingRecording} instance for starting a recording.
1689      *
1690      * <p>This method handles {@code prepareRecording()} methods for different output formats,
1691      * and makes {@link #startRecordingInternal(OutputOptions, AudioConfig, Executor, Consumer)}
1692      * only handle the general flow.
1693      *
1694      * <p>This method uses the parent class {@link OutputOptions} as the parameter. On the other
1695      * hand, the public {@code startRecording()} is overloaded with subclasses. The reason is to
1696      * enforce compile-time check for API levels.
1697      */
1698     @MainThread
prepareRecording(@onNull OutputOptions options)1699     private PendingRecording prepareRecording(@NonNull OutputOptions options) {
1700         Recorder recorder = mVideoCapture.getOutput();
1701         if (options instanceof FileOutputOptions) {
1702             return recorder.prepareRecording(mAppContext, (FileOutputOptions) options);
1703         } else if (options instanceof FileDescriptorOutputOptions) {
1704             if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
1705                 throw new UnsupportedOperationException(
1706                         "File descriptors are not supported on pre-Android O (API 26) devices."
1707                 );
1708             }
1709             return recorder.prepareRecording(mAppContext, (FileDescriptorOutputOptions) options);
1710         } else if (options instanceof MediaStoreOutputOptions) {
1711             return recorder.prepareRecording(mAppContext, (MediaStoreOutputOptions) options);
1712         } else {
1713             throw new IllegalArgumentException("Unsupported OutputOptions type.");
1714         }
1715     }
1716 
wrapListenerToDeactivateRecordingOnFinalized( final @NonNull Consumer<VideoRecordEvent> listener)1717     private Consumer<VideoRecordEvent> wrapListenerToDeactivateRecordingOnFinalized(
1718             final @NonNull Consumer<VideoRecordEvent> listener) {
1719         final Executor mainExecutor = getMainExecutor(mAppContext);
1720 
1721         return new Consumer<VideoRecordEvent>() {
1722             @Override
1723             public void accept(VideoRecordEvent videoRecordEvent) {
1724                 if (videoRecordEvent instanceof VideoRecordEvent.Finalize) {
1725                     if (!Threads.isMainThread()) {
1726                         // Post on main thread to ensure thread safety.
1727                         mainExecutor.execute(() -> deactivateRecordingByListener(this));
1728                     } else {
1729                         deactivateRecordingByListener(this);
1730                     }
1731                 }
1732                 listener.accept(videoRecordEvent);
1733             }
1734         };
1735     }
1736 
1737     @MainThread
1738     void deactivateRecordingByListener(@NonNull Consumer<VideoRecordEvent> listener) {
1739         Recording recording = mRecordingMap.remove(listener);
1740         if (recording != null) {
1741             deactivateRecording(recording);
1742         }
1743     }
1744 
1745     /**
1746      * Clears the active video recording reference if the recording to be deactivated matches.
1747      */
1748     @MainThread
1749     private void deactivateRecording(@NonNull Recording recording) {
1750         if (mActiveRecording == recording) {
1751             mActiveRecording = null;
1752         }
1753     }
1754 
1755     @MainThread
1756     private void setActiveRecording(
1757             @NonNull Recording recording,
1758             @NonNull Consumer<VideoRecordEvent> listener) {
1759         mRecordingMap.put(listener, recording);
1760         mActiveRecording = recording;
1761     }
1762 
1763     /**
1764      * Stops an in-progress video recording.
1765      *
1766      * <p>Once the current recording has been stopped, the next recording can be started.
1767      *
1768      * <p>If the recording completes successfully, a {@link VideoRecordEvent.Finalize} event with
1769      * {@link VideoRecordEvent.Finalize#ERROR_NONE} will be sent to the provided listener.
1770      */
1771     @MainThread
1772     private void stopRecording() {
1773         checkMainThread();
1774 
1775         if (mActiveRecording != null) {
1776             mActiveRecording.stop();
1777             deactivateRecording(mActiveRecording);
1778         }
1779     }
1780 
1781     /**
1782      * Returns whether there is an in-progress video recording.
1783      */
1784     @MainThread
1785     public boolean isRecording() {
1786         checkMainThread();
1787         return mActiveRecording != null && !mActiveRecording.isClosed();
1788     }
1789 
1790     /**
1791      * Sets the {@link QualitySelector} for {@link #VIDEO_CAPTURE}.
1792      *
1793      * <p>The provided quality selector is used to select the resolution of the recording
1794      * depending on the resolutions supported by the camera and codec capabilities.
1795      *
1796      * <p>If no quality selector is provided, the default is
1797      * {@link Recorder#DEFAULT_QUALITY_SELECTOR}.
1798      *
1799      * <p>Changing the value will reconfigure the camera which will cause video capture to stop. To
1800      * avoid this, set the value before controller is bound to lifecycle.
1801      *
1802      * @param qualitySelector The quality selector for {@link #VIDEO_CAPTURE}.
1803      * @see QualitySelector
1804      */
1805     @MainThread
1806     public void setVideoCaptureQualitySelector(@NonNull QualitySelector qualitySelector) {
1807         checkMainThread();
1808         mVideoCaptureQualitySelector = qualitySelector;
1809         unbindVideoAndRecreate();
1810         startCameraAndTrackStates();
1811     }
1812 
1813     /**
1814      * Returns the {@link QualitySelector} for {@link #VIDEO_CAPTURE}.
1815      *
1816      * @return the {@link QualitySelector} provided to
1817      * {@link #setVideoCaptureQualitySelector(QualitySelector)} or the default value of
1818      * {@link Recorder#DEFAULT_QUALITY_SELECTOR} if no quality selector was provided.
1819      */
1820     @MainThread
1821     public @NonNull QualitySelector getVideoCaptureQualitySelector() {
1822         checkMainThread();
1823         return mVideoCaptureQualitySelector;
1824     }
1825 
1826     /**
1827      * Sets the mirror mode for video capture.
1828      *
1829      * <p>Valid values include: {@link MirrorMode#MIRROR_MODE_OFF},
1830      * {@link MirrorMode#MIRROR_MODE_ON} and {@link MirrorMode#MIRROR_MODE_ON_FRONT_ONLY}.
1831      * If not set, it defaults to {@link MirrorMode#MIRROR_MODE_OFF}.
1832      *
1833      * @see VideoCapture.Builder#setMirrorMode(int)
1834      */
1835     @MainThread
1836     public void setVideoCaptureMirrorMode(@MirrorMode.Mirror int mirrorMode) {
1837         checkMainThread();
1838         mVideoCaptureMirrorMode = mirrorMode;
1839         unbindVideoAndRecreate();
1840         startCameraAndTrackStates();
1841     }
1842 
1843     /**
1844      * Gets the mirror mode for video capture.
1845      */
1846     @MainThread
1847     @MirrorMode.Mirror
1848     public int getVideoCaptureMirrorMode() {
1849         checkMainThread();
1850         return mVideoCaptureMirrorMode;
1851     }
1852 
1853     /**
1854      * Sets the {@link DynamicRange} for video capture.
1855      *
1856      * <p>The dynamic range specifies how the range of colors, highlights and shadows that
1857      * are captured by the video producer are displayed on a display. Some dynamic ranges will
1858      * allow the video to make full use of the extended range of brightness of a display when
1859      * the video is played back.
1860      *
1861      * <p>The supported dynamic ranges for video capture can be queried through the
1862      * {@link androidx.camera.video.VideoCapabilities} returned by
1863      * {@link Recorder#getVideoCapabilities(CameraInfo)} via
1864      * {@link androidx.camera.video.VideoCapabilities#getSupportedDynamicRanges()}.
1865      *
1866      * <p>It is possible to choose a high dynamic range (HDR) with unspecified encoding by providing
1867      * {@link DynamicRange#HDR_UNSPECIFIED_10_BIT}.
1868      *
1869      * <p>If the dynamic range is not provided, the default value is {@link DynamicRange#SDR}.
1870      *
1871      * @see VideoCapture.Builder#setDynamicRange(DynamicRange)
1872      */
1873     @MainThread
1874     public void setVideoCaptureDynamicRange(@NonNull DynamicRange dynamicRange) {
1875         checkMainThread();
1876         mVideoCaptureDynamicRange = dynamicRange;
1877         unbindVideoAndRecreate();
1878         startCameraAndTrackStates();
1879     }
1880 
1881     /**
1882      * Gets the {@link DynamicRange} for video capture.
1883      */
1884     @MainThread
1885     public @NonNull DynamicRange getVideoCaptureDynamicRange() {
1886         checkMainThread();
1887         return mVideoCaptureDynamicRange;
1888     }
1889 
1890     /**
1891      * Sets the target frame rate range in frames per second for video capture.
1892      *
1893      * <p>This target will be used as a part of the heuristics for the algorithm that determines
1894      * the final frame rate range and resolution of all concurrently bound use cases.
1895      *
1896      * <p>It is not guaranteed that this target frame rate will be the final range,
1897      * as other use cases as well as frame rate restrictions of the device may affect the
1898      * outcome of the algorithm that chooses the actual frame rate.
1899      *
1900      * <p>By default, the value is {@link StreamSpec#FRAME_RATE_RANGE_UNSPECIFIED}. For supported
1901      * frame rates, see {@link CameraInfo#getSupportedFrameRateRanges()}.
1902      *
1903      * @see VideoCapture.Builder#setTargetFrameRate(Range)
1904      */
1905     @MainThread
1906     public void setVideoCaptureTargetFrameRate(@NonNull Range<Integer> targetFrameRate) {
1907         checkMainThread();
1908         mVideoCaptureTargetFrameRate = targetFrameRate;
1909         unbindVideoAndRecreate();
1910         startCameraAndTrackStates();
1911     }
1912 
1913     /**
1914      * Gets the target frame rate in frames per second for video capture.
1915      */
1916     @MainThread
1917     public @NonNull Range<Integer> getVideoCaptureTargetFrameRate() {
1918         checkMainThread();
1919         return mVideoCaptureTargetFrameRate;
1920     }
1921 
1922     /**
1923      * Unbinds VideoCapture and recreate with the latest parameters.
1924      */
1925     @MainThread
1926     private void unbindVideoAndRecreate() {
1927         if (isCameraInitialized()) {
1928             mCameraProvider.unbind(mVideoCapture);
1929         }
1930         mVideoCapture = createVideoCapture();
1931     }
1932 
1933     private VideoCapture<Recorder> createVideoCapture() {
1934         Recorder.Builder videoRecorderBuilder = new Recorder.Builder().setQualitySelector(
1935                 mVideoCaptureQualitySelector);
1936         if (mViewPort != null
1937                 && mVideoCaptureQualitySelector == Recorder.DEFAULT_QUALITY_SELECTOR) {
1938             int aspectRatioInt = getViewportAspectRatioInt(mViewPort);
1939             if (aspectRatioInt != AspectRatio.RATIO_DEFAULT) {
1940                 videoRecorderBuilder.setAspectRatio(aspectRatioInt);
1941             }
1942         }
1943 
1944         return new VideoCapture.Builder<>(videoRecorderBuilder.build())
1945                 .setTargetFrameRate(mVideoCaptureTargetFrameRate)
1946                 .setMirrorMode(mVideoCaptureMirrorMode)
1947                 .setDynamicRange(mVideoCaptureDynamicRange)
1948                 .build();
1949     }
1950 
1951     private @Nullable AspectRatioStrategy getViewportAspectRatioStrategy(
1952             @NonNull ViewPort viewPort) {
1953         int aspectRatioInt = getViewportAspectRatioInt(viewPort);
1954         if (aspectRatioInt != AspectRatio.RATIO_DEFAULT) {
1955             return new AspectRatioStrategy(aspectRatioInt, AspectRatioStrategy.FALLBACK_RULE_AUTO);
1956         } else {
1957             return null;
1958         }
1959     }
1960 
1961     /**
1962      * If the aspect ratio of the viewport is one of the {@link AspectRatio.Ratio}, returns it,
1963      * otherwise returns {@link AspectRatio.Ratio#RATIO_DEFAULT}.
1964      */
1965     @AspectRatio.Ratio
1966     private int getViewportAspectRatioInt(@NonNull ViewPort viewPort) {
1967         int surfaceRotationDegrees =
1968                 viewPort == null ? 0 : CameraOrientationUtil.surfaceRotationToDegrees(
1969                         viewPort.getRotation());
1970         int sensorRotationDegrees =
1971                 mCameraProvider == null ? 0 : mCameraProvider.getCameraInfo(
1972                         mCameraSelector).getSensorRotationDegrees();
1973         boolean isOppositeFacing =
1974                 mCameraProvider == null ? true : mCameraProvider.getCameraInfo(
1975                         mCameraSelector).getLensFacing() == CameraSelector.LENS_FACING_BACK;
1976         int relativeRotation = CameraOrientationUtil.getRelativeImageRotation(
1977                 surfaceRotationDegrees, sensorRotationDegrees, isOppositeFacing);
1978         Rational aspectRatio = viewPort.getAspectRatio();
1979         if (relativeRotation == 90 || relativeRotation == 270) {
1980             aspectRatio = new Rational(/* numerator= */ aspectRatio.getDenominator(),
1981                     /* denominator= */ aspectRatio.getNumerator());
1982         }
1983 
1984         if (aspectRatio.equals(new Rational(4, 3))) {
1985             return AspectRatio.RATIO_4_3;
1986         } else if (aspectRatio.equals(new Rational(16, 9))) {
1987             return AspectRatio.RATIO_16_9;
1988         } else {
1989             return AspectRatio.RATIO_DEFAULT;
1990         }
1991     }
1992 
1993     /**
1994      * Unbinds all the use cases and recreate with the latest parameters.
1995      */
1996     @MainThread
1997     private void unbindAllAndRecreate() {
1998         unbindPreviewAndRecreate();
1999         unbindImageCaptureAndRecreate(getImageCaptureMode());
2000         unbindImageAnalysisAndRecreate(mImageAnalysis.getBackpressureStrategy(),
2001                 mImageAnalysis.getImageQueueDepth(), mImageAnalysis.getOutputImageFormat());
2002         unbindVideoAndRecreate();
2003     }
2004 
2005     // -----------------
2006     // Camera control
2007     // -----------------
2008 
2009     /**
2010      * Sets the {@link CameraSelector}.
2011      *
2012      * <p>Calling this method with a {@link CameraSelector} that resolves to a different camera
2013      * will change the camera being used by the controller. If camera initialization is complete,
2014      * the controller will immediately rebind use cases with the new {@link CameraSelector};
2015      * otherwise, the new {@link CameraSelector} will be used when the camera becomes ready.
2016      *
2017      * <p>The default value is {@link CameraSelector#DEFAULT_BACK_CAMERA}.
2018      *
2019      * @throws IllegalStateException If the provided camera selector is unable to resolve a camera
2020      * to be used for the enabled use cases.
2021      * @see CameraSelector
2022      */
2023     @MainThread
2024     public void setCameraSelector(@NonNull CameraSelector cameraSelector) {
2025         checkMainThread();
2026         if (mCameraSelector == cameraSelector) {
2027             return;
2028         }
2029 
2030         Integer lensFacing = cameraSelector.getLensFacing();
2031         if (mImageCapture.getFlashMode() == ImageCapture.FLASH_MODE_SCREEN && lensFacing != null
2032                 && lensFacing != CameraSelector.LENS_FACING_FRONT) {
2033             throw new IllegalStateException("Not a front camera despite setting FLASH_MODE_SCREEN");
2034         }
2035 
2036         CameraSelector oldCameraSelector = mCameraSelector;
2037         mCameraSelector = cameraSelector;
2038 
2039         if (mCameraProvider == null) {
2040             return;
2041         }
2042         mCameraProvider.unbind(mPreview, mImageCapture, mImageAnalysis, mVideoCapture);
2043         startCameraAndTrackStates(() -> mCameraSelector = oldCameraSelector);
2044     }
2045 
2046     /**
2047      * Checks if the given {@link CameraSelector} can be resolved to a camera.
2048      *
2049      * <p>Use this method to check if the device has the given camera.
2050      *
2051      * <p>Only call this method after camera is initialized. e.g. after the {@link ListenableFuture}
2052      * from {@link #getInitializationFuture()} is finished. Calling it prematurely throws
2053      * {@link IllegalStateException}. Example:
2054      *
2055      * <pre><code>
2056      * controller.getInitializationFuture().addListener(() -> {
2057      *     if (controller.hasCamera(cameraSelector)) {
2058      *         controller.setCameraSelector(cameraSelector);
2059      *     } else {
2060      *         // Update UI if the camera is not available.
2061      *     }
2062      *     // Attach PreviewView after we know the camera is available.
2063      *     previewView.setController(controller);
2064      * }, ContextCompat.getMainExecutor(requireContext()));
2065      * </code></pre>
2066      *
2067      * @return {@code true} if the {@link CameraSelector} can be resolved to a camera.
2068      * @throws IllegalStateException if the camera is not initialized.
2069      */
2070     @MainThread
2071     public boolean hasCamera(@NonNull CameraSelector cameraSelector) {
2072         checkMainThread();
2073         Preconditions.checkNotNull(cameraSelector);
2074 
2075         if (mCameraProvider == null) {
2076             throw new IllegalStateException("Camera not initialized. Please wait for "
2077                     + "the initialization future to finish. See #getInitializationFuture().");
2078         }
2079 
2080         try {
2081             return mCameraProvider.hasCamera(cameraSelector);
2082         } catch (CameraInfoUnavailableException e) {
2083             Logger.w(TAG, "Failed to check camera availability", e);
2084             return false;
2085         }
2086     }
2087 
2088     /**
2089      * Gets the {@link CameraSelector}.
2090      *
2091      * <p>The default value is{@link CameraSelector#DEFAULT_BACK_CAMERA}.
2092      *
2093      * @see CameraSelector
2094      */
2095     @MainThread
2096     public @NonNull CameraSelector getCameraSelector() {
2097         checkMainThread();
2098         return mCameraSelector;
2099     }
2100 
2101     /**
2102      * Returns whether pinch-to-zoom is enabled.
2103      *
2104      * <p>By default pinch-to-zoom is enabled.
2105      *
2106      * @return {@code true} if pinch-to-zoom is enabled.
2107      */
2108     @MainThread
2109     public boolean isPinchToZoomEnabled() {
2110         checkMainThread();
2111         return mPinchToZoomEnabled;
2112     }
2113 
2114     /**
2115      * Enables/disables pinch-to-zoom.
2116      *
2117      * <p>Once enabled, end user can pinch on the {@link PreviewView} to zoom in/out if the bound
2118      * camera supports zooming.
2119      *
2120      * @param enabled {@code true} to enable pinch-to-zoom.
2121      */
2122     @MainThread
2123     public void setPinchToZoomEnabled(boolean enabled) {
2124         checkMainThread();
2125         mPinchToZoomEnabled = enabled;
2126     }
2127 
2128     /**
2129      * Called by {@link PreviewView} for a pinch-to-zoom event.
2130      */
2131     @SuppressWarnings("FutureReturnValueIgnored")
2132     void onPinchToZoom(float pinchToZoomScale) {
2133         if (!isCameraAttached()) {
2134             Logger.w(TAG, CAMERA_NOT_ATTACHED);
2135             return;
2136         }
2137         if (!mPinchToZoomEnabled) {
2138             Logger.d(TAG, "Pinch to zoom disabled.");
2139             return;
2140         }
2141         Logger.d(TAG, "Pinch to zoom with scale: " + pinchToZoomScale);
2142 
2143         ZoomState zoomState = getZoomState().getValue();
2144         if (zoomState == null) {
2145             return;
2146         }
2147         float clampedRatio = zoomState.getZoomRatio() * speedUpZoomBy2X(pinchToZoomScale);
2148         // Clamp the ratio with the zoom range.
2149         clampedRatio = Math.min(Math.max(clampedRatio, zoomState.getMinZoomRatio()),
2150                 zoomState.getMaxZoomRatio());
2151         setZoomRatio(clampedRatio);
2152     }
2153 
2154     private float speedUpZoomBy2X(float scaleFactor) {
2155         if (scaleFactor > 1f) {
2156             return 1.0f + (scaleFactor - 1.0f) * 2;
2157         } else {
2158             return 1.0f - (1.0f - scaleFactor) * 2;
2159         }
2160     }
2161 
2162     /**
2163      * Called by {@link PreviewView} for a tap-to-focus event.
2164      */
2165     @SuppressWarnings("FutureReturnValueIgnored")
2166     void onTapToFocus(MeteringPointFactory meteringPointFactory, float x, float y) {
2167         if (!isCameraAttached()) {
2168             Logger.w(TAG, CAMERA_NOT_ATTACHED);
2169             return;
2170         }
2171         if (!mTapToFocusEnabled) {
2172             Logger.d(TAG, "Tap to focus disabled. ");
2173             return;
2174         }
2175 
2176         PointF tapPoint = new PointF(x, y);
2177         FocusMeteringAction focusMeteringAction = createFocusMeteringAction(meteringPointFactory,
2178                 tapPoint);
2179 
2180         Logger.d(TAG, "Tap to focus started: " + x + ", " + y);
2181 
2182         // Previous callback closed first so that TAP_TO_FOCUS_STARTED of this operation is not
2183         // overwritten by any event from previous operations.
2184         if (mFocusMeteringResultCallback != null) {
2185             mFocusMeteringResultCallback.close();
2186         }
2187 
2188         mTapToFocusInfoState.postValue(new TapToFocusInfo(TAP_TO_FOCUS_STARTED, tapPoint));
2189 
2190         FocusMeteringResultCallback focusMeteringResultCallback = new FocusMeteringResultCallback(
2191                 tapPoint, mTapToFocusInfoState);
2192         mFocusMeteringResultCallback = focusMeteringResultCallback;
2193         Futures.addCallback(mCamera.getCameraControl().startFocusAndMetering(focusMeteringAction),
2194                 focusMeteringResultCallback, directExecutor());
2195 
2196         long cancelDuration = TimeUnit.NANOSECONDS.toMillis(mTapToFocusAutoCancelDurationNanos);
2197         Logger.d(TAG, "Tap to focus auto cancel duration: " + cancelDuration + " ms");
2198 
2199         if (cancelDuration > 0L) {
2200             new Handler(Looper.getMainLooper()).postDelayed(
2201                     focusMeteringResultCallback::resetStateAndClose, cancelDuration);
2202         }
2203     }
2204 
2205     private FocusMeteringAction createFocusMeteringAction(MeteringPointFactory meteringPointFactory,
2206             PointF tapPoint) {
2207         MeteringPoint afPoint = meteringPointFactory.createPoint(tapPoint.x, tapPoint.y, AF_SIZE);
2208         MeteringPoint aePoint = meteringPointFactory.createPoint(tapPoint.x, tapPoint.y, AE_SIZE);
2209         FocusMeteringAction.Builder focusMeteringActionBuilder = new FocusMeteringAction
2210                 .Builder(afPoint, FocusMeteringAction.FLAG_AF)
2211                 .addPoint(aePoint, FocusMeteringAction.FLAG_AE);
2212         if (mTapToFocusAutoCancelDurationNanos > 0L) {
2213             focusMeteringActionBuilder = focusMeteringActionBuilder.setAutoCancelDuration(
2214                     mTapToFocusAutoCancelDurationNanos, TimeUnit.NANOSECONDS);
2215         } else {
2216             focusMeteringActionBuilder = focusMeteringActionBuilder.disableAutoCancel();
2217         }
2218         return focusMeteringActionBuilder.build();
2219     }
2220 
2221     /**
2222      * Returns whether tap-to-focus is enabled.
2223      *
2224      * <p>By default tap-to-focus is enabled.
2225      *
2226      * @return {@code true} if tap-to-focus is enabled.
2227      */
2228     @MainThread
2229     public boolean isTapToFocusEnabled() {
2230         checkMainThread();
2231         return mTapToFocusEnabled;
2232     }
2233 
2234     /**
2235      * Enables/disables tap-to-focus.
2236      *
2237      * <p>Once enabled, end user can tap on the {@link PreviewView} to set focus point.
2238      *
2239      * @param enabled {@code true} to enable tap-to-focus.
2240      */
2241     @MainThread
2242     public void setTapToFocusEnabled(boolean enabled) {
2243         checkMainThread();
2244         mTapToFocusEnabled = enabled;
2245     }
2246 
2247     /**
2248      * Returns a {@link LiveData} with the latest tap-to-focus state.
2249      *
2250      * <p>When tap-to-focus feature is enabled, the {@link LiveData} will receive updates of
2251      * focusing states. This happens when the end user taps on {@link PreviewView}, and then again
2252      * when focusing is finished either successfully or unsuccessfully. The following table
2253      * displays the states the {@link LiveData} can be in, and the possible transitions between
2254      * them.
2255      *
2256      * <table>
2257      * <tr>
2258      *     <th>State</th>
2259      *     <th>Transition cause</th>
2260      *     <th>New State</th>
2261      * </tr>
2262      * <tr>
2263      *     <td>TAP_TO_FOCUS_NOT_STARTED</td>
2264      *     <td>User taps on {@link PreviewView}</td>
2265      *     <td>TAP_TO_FOCUS_STARTED</td>
2266      * </tr>
2267      * <tr>
2268      *     <td>TAP_TO_FOCUS_FOCUSED</td>
2269      *     <td>User taps on {@link PreviewView}</td>
2270      *     <td>TAP_TO_FOCUS_STARTED</td>
2271      * </tr>
2272      * <tr>
2273      *     <td>TAP_TO_FOCUS_NOT_FOCUSED</td>
2274      *     <td>User taps on {@link PreviewView}</td>
2275      *     <td>TAP_TO_FOCUS_STARTED</td>
2276      * </tr>
2277      * <tr>
2278      *     <td>TAP_TO_FOCUS_FAILED</td>
2279      *     <td>User taps on {@link PreviewView}</td>
2280      *     <td>TAP_TO_FOCUS_STARTED</td>
2281      * </tr>
2282      * <tr>
2283      *     <td rowspan="3">TAP_TO_FOCUS_STARTED</td>
2284      *     <td>Focusing succeeded</td>
2285      *     <td>TAP_TO_FOCUS_FOCUSED</td>
2286      * </tr>
2287      * <tr>
2288      *     <td>Focusing failed due to lighting and/or camera distance</td>
2289      *     <td>TAP_TO_FOCUS_NOT_FOCUSED</td>
2290      * </tr>
2291      * <tr>
2292      *     <td>Focusing failed due to device constraints</td>
2293      *     <td>TAP_TO_FOCUS_FAILED</td>
2294      * </tr>
2295      * <tr>
2296      *     <td>TAP_TO_FOCUS_FOCUSED</td>
2297      *     <td>Auto-cancel duration elapses</td>
2298      *     <td>TAP_TO_FOCUS_NOT_STARTED</td>
2299      * </tr>
2300      * <tr>
2301      *     <td>TAP_TO_FOCUS_NOT_FOCUSED</td>
2302      *     <td>Auto-cancel duration elapses</td>
2303      *     <td>TAP_TO_FOCUS_NOT_STARTED</td>
2304      * </tr>
2305      * <tr>
2306      *     <td>TAP_TO_FOCUS_FAILED</td>
2307      *     <td>Auto-cancel duration elapses</td>
2308      *     <td>TAP_TO_FOCUS_NOT_STARTED</td>
2309      * </tr>
2310      * </table>
2311      *
2312      * @see #setTapToFocusEnabled(boolean)
2313      * @see CameraControl#startFocusAndMetering(FocusMeteringAction)
2314      *
2315      * @deprecated Use {@link #getTapToFocusInfoState()} instead.
2316      */
2317     @Deprecated
2318     @MainThread
2319     public @NonNull LiveData<Integer> getTapToFocusState() {
2320         checkMainThread();
2321         return mTapToFocusState;
2322     }
2323 
2324     /**
2325      * Returns a {@link LiveData} with a {@link TapToFocusInfo} containing the latest focus state
2326      * and corresponding tap position.
2327      *
2328      * <p>When tap-to-focus feature is enabled, the {@link LiveData} will receive updates of
2329      * focusing states. This usually happens when the end user taps on {@link PreviewView}, and then
2330      * again when focusing is finished either successfully or unsuccessfully. The following table
2331      * displays the states the {@link LiveData} can be in, and the possible transitions between
2332      * them.
2333      *
2334      * <table>
2335      * <tr>
2336      *     <th>State</th>
2337      *     <th>Transition cause</th>
2338      *     <th>New State</th>
2339      * </tr>
2340      * <tr>
2341      *     <td>TAP_TO_FOCUS_NOT_STARTED</td>
2342      *     <td>User taps on {@link PreviewView}</td>
2343      *     <td>TAP_TO_FOCUS_STARTED</td>
2344      * </tr>
2345      * <tr>
2346      *     <td>TAP_TO_FOCUS_FOCUSED</td>
2347      *     <td>User taps on {@link PreviewView}</td>
2348      *     <td>TAP_TO_FOCUS_STARTED</td>
2349      * </tr>
2350      * <tr>
2351      *     <td>TAP_TO_FOCUS_NOT_FOCUSED</td>
2352      *     <td>User taps on {@link PreviewView}</td>
2353      *     <td>TAP_TO_FOCUS_STARTED</td>
2354      * </tr>
2355      * <tr>
2356      *     <td>TAP_TO_FOCUS_FAILED</td>
2357      *     <td>User taps on {@link PreviewView}</td>
2358      *     <td>TAP_TO_FOCUS_STARTED</td>
2359      * </tr>
2360      * <tr>
2361      *     <td rowspan="3">TAP_TO_FOCUS_STARTED</td>
2362      *     <td>Focusing succeeded</td>
2363      *     <td>TAP_TO_FOCUS_FOCUSED</td>
2364      * </tr>
2365      * <tr>
2366      *     <td>Focusing failed due to lighting and/or camera distance</td>
2367      *     <td>TAP_TO_FOCUS_NOT_FOCUSED</td>
2368      * </tr>
2369      * <tr>
2370      *     <td>Focusing failed due to device constraints</td>
2371      *     <td>TAP_TO_FOCUS_FAILED</td>
2372      * </tr>
2373      * <tr>
2374      *     <td>TAP_TO_FOCUS_FOCUSED</td>
2375      *     <td>Auto-cancel duration elapses</td>
2376      *     <td>TAP_TO_FOCUS_NOT_STARTED</td>
2377      * </tr>
2378      * <tr>
2379      *     <td>TAP_TO_FOCUS_NOT_FOCUSED</td>
2380      *     <td>Auto-cancel duration elapses</td>
2381      *     <td>TAP_TO_FOCUS_NOT_STARTED</td>
2382      * </tr>
2383      * <tr>
2384      *     <td>TAP_TO_FOCUS_FAILED</td>
2385      *     <td>Auto-cancel duration elapses</td>
2386      *     <td>TAP_TO_FOCUS_NOT_STARTED</td>
2387      * </tr>
2388      * </table>
2389      *
2390      * @see #setTapToFocusEnabled(boolean)
2391      * @see CameraControl#startFocusAndMetering(FocusMeteringAction)
2392      */
2393     @MainThread
2394     public @NonNull LiveData<TapToFocusInfo> getTapToFocusInfoState() {
2395         checkMainThread();
2396         return mTapToFocusInfoState;
2397     }
2398 
2399     /**
2400      * Sets the auto-cancel duration for tap-to-focus events.
2401      *
2402      * <p> By default, CameraX uses a value of 5 seconds.
2403      *
2404      * @param duration The duration after which CameraX automatically cancels a tap-to-focus event.
2405      *   A value of 0 will disable the auto-cancellation behavior.
2406      * @param timeUnit The {@link TimeUnit} for the {@code duration} parameter.
2407      *
2408      * @see #getTapToFocusInfoState()
2409      */
2410     @MainThread
2411     public void setTapToFocusAutoCancelDuration(
2412             @IntRange(from = 0) long duration,
2413             @NonNull TimeUnit timeUnit
2414     ) {
2415         Preconditions.checkArgument(
2416                 duration >= 0,
2417                 "Tap-to-focus auto-cancellation duration must be at least 0"
2418         );
2419         mTapToFocusAutoCancelDurationNanos = timeUnit.toNanos(duration);
2420         Logger.d(TAG, "setTapToFocusAutoCancelDuration: " + mTapToFocusAutoCancelDurationNanos
2421                 + " ns set!");
2422     }
2423 
2424     /**
2425      * Returns a {@link LiveData} of {@link ZoomState}.
2426      *
2427      * <p>The LiveData will be updated whenever the set zoom state has been changed. This can
2428      * occur when the application updates the zoom via {@link #setZoomRatio(float)}
2429      * or {@link #setLinearZoom(float)}. The zoom state can also change anytime a
2430      * camera starts up, for example when {@link #setCameraSelector} is called.
2431      *
2432      * @see CameraInfo#getZoomState()
2433      */
2434     @MainThread
2435     public @NonNull LiveData<ZoomState> getZoomState() {
2436         checkMainThread();
2437         return mZoomState;
2438     }
2439 
2440     /**
2441      * Gets the {@link CameraInfo} of the currently attached camera.
2442      *
2443      * <p>For info available directly through CameraController as well as {@link CameraInfo}, it's
2444      * recommended to use the ones with CameraController, e.g. {@link #getTorchState()} v.s.
2445      * {@link CameraInfo#getTorchState()}. {@link CameraInfo} is a lower-layer API and may require
2446      * more steps to achieve the same effect, and will not maintain values when switching between
2447      * cameras.
2448      *
2449      * @return The {@link CameraInfo} of the current camera. Returns {@code null} if camera is not
2450      * ready.
2451      * @see Camera#getCameraInfo()
2452      */
2453     @MainThread
2454     public @Nullable CameraInfo getCameraInfo() {
2455         checkMainThread();
2456         return mCamera == null ? null : mCamera.getCameraInfo();
2457     }
2458 
2459     /**
2460      * Gets the {@link CameraControl} of the currently attached camera.
2461      *
2462      * <p>For controls available directly through CameraController as well as
2463      * {@link CameraControl}, it's recommended to use the ones with CameraController, e.g.
2464      * {@link #setLinearZoom(float)} v.s. {@link CameraControl#setLinearZoom(float)}.
2465      * CameraControl is a lower-layer API and may require more steps to achieve the same effect,
2466      * and will not maintain control values when switching between cameras.
2467      *
2468      * @return The {@link CameraControl} of the current camera. Returns {@code null} if camera is
2469      * not ready.
2470      * @see Camera#getCameraControl()
2471      */
2472     @MainThread
2473     public @Nullable CameraControl getCameraControl() {
2474         checkMainThread();
2475         return mCamera == null ? null : mCamera.getCameraControl();
2476     }
2477 
2478     /**
2479      * Sets current zoom by ratio.
2480      *
2481      * <p>Valid zoom values range from {@link ZoomState#getMinZoomRatio()} to
2482      * {@link ZoomState#getMaxZoomRatio()}.
2483      *
2484      * <p>If the value is set before the camera is ready, {@link CameraController} waits for the
2485      * camera to be ready and then sets the zoom ratio.
2486      *
2487      * @param zoomRatio The requested zoom ratio.
2488      * @return A {@link ListenableFuture} which is finished when camera is set to the given ratio.
2489      * It fails with {@link CameraControl.OperationCanceledException} if there is newer value
2490      * being set or camera is closed. If the ratio is out of range, it fails with
2491      * {@link IllegalArgumentException}. Cancellation of this future is a no-op.
2492      * @see #getZoomState()
2493      * @see CameraControl#setZoomRatio(float)
2494      */
2495     @MainThread
2496     public @NonNull ListenableFuture<Void> setZoomRatio(float zoomRatio) {
2497         checkMainThread();
2498         if (!isCameraAttached()) {
2499             return mPendingZoomRatio.setValue(zoomRatio);
2500         }
2501         return mCamera.getCameraControl().setZoomRatio(zoomRatio);
2502     }
2503 
2504     /**
2505      * Sets current zoom by a linear zoom value ranging from 0f to 1.0f.
2506      *
2507      * <p>LinearZoom 0f represents the minimum zoom while linearZoom 1.0f represents the maximum
2508      * zoom. The advantage of linearZoom is that it ensures the field of view (FOV) varies
2509      * linearly with the linearZoom value, for use with slider UI elements (while
2510      * {@link #setZoomRatio(float)} works well for pinch-zoom gestures).
2511      *
2512      * <p>If the value is set before the camera is ready, {@link CameraController} waits for the
2513      * camera to be ready and then sets the linear zoom.
2514      *
2515      * @return A {@link ListenableFuture} which is finished when camera is set to the given ratio.
2516      * It fails with {@link CameraControl.OperationCanceledException} if there is newer value
2517      * being set or camera is closed. If the ratio is out of range, it fails with
2518      * {@link IllegalArgumentException}. Cancellation of this future is a no-op.
2519      * @see CameraControl#setLinearZoom(float)
2520      */
2521     @MainThread
2522     public @NonNull ListenableFuture<Void> setLinearZoom(
2523             @FloatRange(from = 0f, to = 1f) float linearZoom) {
2524         checkMainThread();
2525         if (!isCameraAttached()) {
2526             return mPendingLinearZoom.setValue(linearZoom);
2527         }
2528         return mCamera.getCameraControl().setLinearZoom(linearZoom);
2529     }
2530 
2531     /**
2532      * Returns a {@link LiveData} of current {@link TorchState}.
2533      *
2534      * <p>The torch can be turned on and off via {@link #enableTorch(boolean)} which will trigger
2535      * the change event to the returned {@link LiveData}.
2536      *
2537      * @return A {@link LiveData} containing current torch state.
2538      * @see CameraInfo#getTorchState()
2539      */
2540     @MainThread
2541     public @NonNull LiveData<Integer> getTorchState() {
2542         checkMainThread();
2543         return mTorchState;
2544     }
2545 
2546     /**
2547      * Enable the torch or disable the torch.
2548      *
2549      * <p>If the value is set before the camera is ready, {@link CameraController} waits for the
2550      * camera to be ready and then enables the torch.
2551      *
2552      * @param torchEnabled {@code true} to turn on the torch, {@code false} to turn it off.
2553      * @return A {@link ListenableFuture} which is successful when the torch was changed to the
2554      * value specified. It fails when it is unable to change the torch state. Cancellation of
2555      * this future is a no-op.
2556      * @see CameraControl#enableTorch(boolean)
2557      */
2558     @MainThread
2559     public @NonNull ListenableFuture<Void> enableTorch(boolean torchEnabled) {
2560         checkMainThread();
2561         if (!isCameraAttached()) {
2562             return mPendingEnableTorch.setValue(torchEnabled);
2563         }
2564         return mCamera.getCameraControl().enableTorch(torchEnabled);
2565     }
2566 
2567     // ------------------------
2568     // Effects and extensions
2569     // ------------------------
2570 
2571     /**
2572      * Sets {@link CameraEffect}.
2573      *
2574      * <p>Call this method to set a list of active effects. There is maximum one effect per
2575      * {@link UseCase}. Adding effects with duplicate or invalid targets throws
2576      * {@link IllegalArgumentException}. Once called, CameraX will rebind the {@link UseCase}
2577      * with the effects applied. Effects not in the list are automatically removed.
2578      *
2579      * <p>The method throws {@link IllegalArgumentException} if the effects combination is not
2580      * supported by CameraX. Please see the Javadoc of {@link UseCaseGroup.Builder#addEffect} to
2581      * see the supported effects combinations.
2582      *
2583      * @param effects The effects applied to camera output.
2584      * @throws IllegalArgumentException if the combination of effects is not supported by CameraX.
2585      * @see UseCaseGroup.Builder#addEffect
2586      */
2587     @MainThread
2588     public void setEffects(@NonNull Set<CameraEffect> effects) {
2589         checkMainThread();
2590         if (Objects.equals(mEffects, effects)) {
2591             // Same effect. No change needed.
2592             return;
2593         }
2594         if (mCameraProvider != null) {
2595             // Unbind to make sure the pipelines will be recreated.
2596             mCameraProvider.unbindAll();
2597         }
2598         mEffects.clear();
2599         mEffects.addAll(effects);
2600         startCameraAndTrackStates();
2601     }
2602 
2603     /**
2604      * Removes all effects.
2605      *
2606      * <p>Once called, CameraX will remove all the effects and rebind the {@link UseCase}.
2607      */
2608     @MainThread
2609     public void clearEffects() {
2610         checkMainThread();
2611         if (mCameraProvider != null) {
2612             // Unbind to make sure the pipelines will be recreated.
2613             mCameraProvider.unbindAll();
2614         }
2615         mEffects.clear();
2616         startCameraAndTrackStates();
2617     }
2618 
2619     // ------------------------------
2620     // Binding to lifecycle
2621     // ------------------------------
2622 
2623     /**
2624      * Binds use cases, gets a new {@link Camera} instance and tracks the state of the camera.
2625      */
2626     void startCameraAndTrackStates() {
2627         startCameraAndTrackStates(null);
2628     }
2629 
2630     /**
2631      * @param restoreStateRunnable {@link Runnable} to restore the controller to the previous good
2632      *                                            state if the binding fails.
2633      * @throws IllegalStateException for invalid {@link UseCase} combinations.
2634      * @throws RuntimeException for invalid {@link CameraEffect} combinations.
2635      */
2636     void startCameraAndTrackStates(@Nullable Runnable restoreStateRunnable) {
2637         try {
2638             mCamera = startCamera();
2639         } catch (RuntimeException exception) {
2640             // Restore the previous state before re-throwing the exception.
2641             if (restoreStateRunnable != null) {
2642                 restoreStateRunnable.run();
2643             }
2644             throw exception;
2645         }
2646         if (!isCameraAttached()) {
2647             Logger.d(TAG, CAMERA_NOT_ATTACHED);
2648             return;
2649         }
2650         mZoomState.setSource(mCamera.getCameraInfo().getZoomState());
2651         mTorchState.setSource(mCamera.getCameraInfo().getTorchState());
2652         mPendingEnableTorch.propagateIfHasValue(this::enableTorch);
2653         mPendingLinearZoom.propagateIfHasValue(this::setLinearZoom);
2654         mPendingZoomRatio.propagateIfHasValue(this::setZoomRatio);
2655     }
2656 
2657     /**
2658      * Creates {@link UseCaseGroup} from all the use cases.
2659      *
2660      * <p>Preview is required. If it is {@code null}, then controller is not ready. Return
2661      * {@code null} and ignore other use cases.
2662      */
2663     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
2664     protected @Nullable UseCaseGroup createUseCaseGroup() {
2665         if (!isCameraInitialized()) {
2666             Logger.d(TAG, CAMERA_NOT_INITIALIZED);
2667             return null;
2668         }
2669         if (!isPreviewViewAttached()) {
2670             // Preview is required. Return early if preview Surface is not ready.
2671             Logger.d(TAG, PREVIEW_VIEW_NOT_ATTACHED);
2672             return null;
2673         }
2674 
2675         UseCaseGroup.Builder builder = new UseCaseGroup.Builder().addUseCase(mPreview);
2676 
2677         if (isImageCaptureEnabled()) {
2678             builder.addUseCase(mImageCapture);
2679         } else {
2680             mCameraProvider.unbind(mImageCapture);
2681         }
2682 
2683         if (isImageAnalysisEnabled()) {
2684             builder.addUseCase(mImageAnalysis);
2685         } else {
2686             mCameraProvider.unbind(mImageAnalysis);
2687         }
2688 
2689         if (isVideoCaptureEnabled()) {
2690             builder.addUseCase(mVideoCapture);
2691         } else {
2692             mCameraProvider.unbind(mVideoCapture);
2693         }
2694 
2695         builder.setViewPort(mViewPort);
2696         for (CameraEffect effect : mEffects) {
2697             builder.addEffect(effect);
2698         }
2699         return builder.build();
2700     }
2701 
2702     /**
2703      * Represents the output size of a {@link UseCase}.
2704      *
2705      * <p>This class is a preferred output size to be used with {@link CameraController}. The
2706      * preferred output size can be based on either resolution or aspect ratio, but not both.
2707      *
2708      * @see #setImageAnalysisTargetSize(OutputSize)
2709      * @see #setPreviewTargetSize(OutputSize)
2710      * @see #setImageCaptureTargetSize(OutputSize)
2711      * @deprecated Use {@link ResolutionSelector} instead.
2712      */
2713     @Deprecated
2714     public static final class OutputSize {
2715 
2716         /**
2717          * A value that represents the aspect ratio is not assigned.
2718          */
2719         public static final int UNASSIGNED_ASPECT_RATIO = -1;
2720 
2721         /**
2722          * Possible value for {@link #getAspectRatio()}
2723          */
2724         @RestrictTo(RestrictTo.Scope.LIBRARY)
2725         @Retention(RetentionPolicy.SOURCE)
2726         @IntDef(value = {UNASSIGNED_ASPECT_RATIO, AspectRatio.RATIO_4_3, AspectRatio.RATIO_16_9})
2727         public @interface OutputAspectRatio {
2728         }
2729 
2730         @OutputAspectRatio
2731         private final int mAspectRatio;
2732 
2733         private final @Nullable Size mResolution;
2734 
2735         /**
2736          * Creates a {@link OutputSize} that is based on aspect ratio.
2737          *
2738          * @see Preview.Builder#setTargetAspectRatio(int)
2739          * @see ImageAnalysis.Builder#setTargetAspectRatio(int)
2740          */
2741         public OutputSize(@AspectRatio.Ratio int aspectRatio) {
2742             Preconditions.checkArgument(aspectRatio != UNASSIGNED_ASPECT_RATIO);
2743             mAspectRatio = aspectRatio;
2744             mResolution = null;
2745         }
2746 
2747         /**
2748          * Creates a {@link OutputSize} that is based on resolution.
2749          *
2750          * @see Preview.Builder#setTargetResolution(Size)
2751          * @see ImageAnalysis.Builder#setTargetResolution(Size)
2752          */
2753         public OutputSize(@NonNull Size resolution) {
2754             Preconditions.checkNotNull(resolution);
2755             mAspectRatio = UNASSIGNED_ASPECT_RATIO;
2756             mResolution = resolution;
2757         }
2758 
2759         /**
2760          * Gets the value of aspect ratio.
2761          *
2762          * @return {@link #UNASSIGNED_ASPECT_RATIO} if the size is not based on aspect ratio.
2763          */
2764         @OutputAspectRatio
2765         public int getAspectRatio() {
2766             return mAspectRatio;
2767         }
2768 
2769         /**
2770          * Gets the value of resolution.
2771          *
2772          * @return {@code null} if the size is not based on resolution.
2773          */
2774         public @Nullable Size getResolution() {
2775             return mResolution;
2776         }
2777 
2778         @Override
2779         public @NonNull String toString() {
2780             return "aspect ratio: " + mAspectRatio + " resolution: " + mResolution;
2781         }
2782     }
2783 
2784     static class FocusMeteringResultCallback implements FutureCallback<FocusMeteringResult> {
2785         private boolean mIsCanceled = false;
2786         private final PointF mTapPoint;
2787         private final MutableLiveData<TapToFocusInfo> mTapToFocusInfoState;
2788 
2789         private final Object mLock = new Object();
2790 
2791         FocusMeteringResultCallback(PointF tapPoint,
2792                 MutableLiveData<TapToFocusInfo> tapToFocusInfoState) {
2793             mTapPoint = tapPoint;
2794             mTapToFocusInfoState = tapToFocusInfoState;
2795         }
2796 
2797         @Override
2798         public void onSuccess(@Nullable FocusMeteringResult result) {
2799             synchronized (mLock) {
2800                 if (mIsCanceled) return;
2801 
2802                 if (result == null) {
2803                     return;
2804                 }
2805 
2806                 Logger.d(TAG, "Tap-to-focus onSuccess: " + result.isFocusSuccessful());
2807                 mTapToFocusInfoState.postValue(new TapToFocusInfo(
2808                         result.isFocusSuccessful() ? TAP_TO_FOCUS_FOCUSED
2809                                 : TAP_TO_FOCUS_NOT_FOCUSED, mTapPoint));
2810             }
2811         }
2812 
2813         @Override
2814         public void onFailure(@NonNull Throwable t) {
2815             synchronized (mLock) {
2816                 if (mIsCanceled) return;
2817 
2818                 if (t instanceof CameraControl.OperationCanceledException) {
2819                     Logger.d(TAG, "Tap-to-focus canceled", t);
2820 
2821                     // Resetting focus state, also closing earlier to avoid multiple reset updates.
2822                     mTapToFocusInfoState.postValue(
2823                             new TapToFocusInfo(TAP_TO_FOCUS_NOT_STARTED, null));
2824                     close();
2825 
2826                     return;
2827                 }
2828 
2829                 Logger.d(TAG, "Tap-to-focus failed.", t);
2830                 mTapToFocusInfoState.postValue(
2831                         new TapToFocusInfo(TAP_TO_FOCUS_FAILED, mTapPoint));
2832             }
2833         }
2834 
2835         /**
2836          * Resets the tap-to-focus state and closes this callback class.
2837          *
2838          * <P> This method, like the other callback methods in this class, is no-op if the class has
2839          * already been closed once.
2840          *
2841          * @see #close
2842          */
2843         void resetStateAndClose() {
2844             synchronized (mLock) {
2845                 if (mIsCanceled) return;
2846 
2847                 Logger.d(TAG, "Tap-to-focus reset.");
2848                 mTapToFocusInfoState.postValue(new TapToFocusInfo(TAP_TO_FOCUS_NOT_STARTED, null));
2849                 mIsCanceled = true;
2850             }
2851         }
2852 
2853         /** Closes the callback class to ignore all future callback invocations. */
2854         void close() {
2855             synchronized (mLock) {
2856                 mIsCanceled = true;
2857             }
2858         }
2859     }
2860 }
2861