1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.camera.core;
18 
19 import static android.graphics.ImageFormat.JPEG;
20 import static android.graphics.ImageFormat.JPEG_R;
21 import static android.graphics.ImageFormat.RAW_SENSOR;
22 
23 import static androidx.camera.core.CameraEffect.IMAGE_CAPTURE;
24 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_BUFFER_FORMAT;
25 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_CAPTURE_CONFIG_UNPACKER;
26 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_DEFAULT_CAPTURE_CONFIG;
27 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_DEFAULT_SESSION_CONFIG;
28 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_FLASH_MODE;
29 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_FLASH_TYPE;
30 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_IMAGE_CAPTURE_MODE;
31 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_IMAGE_READER_PROXY_PROVIDER;
32 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_IO_EXECUTOR;
33 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_JPEG_COMPRESSION_QUALITY;
34 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_MAX_RESOLUTION;
35 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_OUTPUT_FORMAT;
36 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_POSTVIEW_ENABLED;
37 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_POSTVIEW_RESOLUTION_SELECTOR;
38 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_SCREEN_FLASH;
39 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_SESSION_CONFIG_UNPACKER;
40 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_SUPPORTED_RESOLUTIONS;
41 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_SURFACE_OCCUPANCY_PRIORITY;
42 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_TARGET_ASPECT_RATIO;
43 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_TARGET_CLASS;
44 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_TARGET_NAME;
45 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_TARGET_RESOLUTION;
46 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_TARGET_ROTATION;
47 import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_USE_SOFTWARE_JPEG_ENCODER;
48 import static androidx.camera.core.impl.ImageInputConfig.OPTION_INPUT_DYNAMIC_RANGE;
49 import static androidx.camera.core.impl.ImageInputConfig.OPTION_INPUT_FORMAT;
50 import static androidx.camera.core.impl.ImageInputConfig.OPTION_SECONDARY_INPUT_FORMAT;
51 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_CUSTOM_ORDERED_RESOLUTIONS;
52 import static androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR;
53 import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_TYPE;
54 import static androidx.camera.core.impl.UseCaseConfig.OPTION_HIGH_RESOLUTION_DISABLED;
55 import static androidx.camera.core.impl.UseCaseConfig.OPTION_ZSL_DISABLED;
56 import static androidx.camera.core.impl.utils.Threads.checkMainThread;
57 import static androidx.camera.core.impl.utils.TransformUtils.is90or270;
58 import static androidx.camera.core.internal.utils.ImageUtil.computeCropRectFromAspectRatio;
59 import static androidx.camera.core.internal.utils.ImageUtil.isAspectRatioValid;
60 import static androidx.core.util.Preconditions.checkNotNull;
61 import static androidx.core.util.Preconditions.checkState;
62 
63 import static java.util.Objects.requireNonNull;
64 
65 import android.content.ContentResolver;
66 import android.content.ContentValues;
67 import android.graphics.Bitmap;
68 import android.graphics.ImageFormat;
69 import android.graphics.Rect;
70 import android.hardware.camera2.CameraCharacteristics;
71 import android.location.Location;
72 import android.media.Image;
73 import android.media.ImageReader;
74 import android.net.Uri;
75 import android.os.Build;
76 import android.os.Looper;
77 import android.provider.MediaStore;
78 import android.util.Log;
79 import android.util.Pair;
80 import android.util.Rational;
81 import android.util.Size;
82 import android.view.Display;
83 import android.view.Surface;
84 
85 import androidx.annotation.GuardedBy;
86 import androidx.annotation.IntDef;
87 import androidx.annotation.IntRange;
88 import androidx.annotation.MainThread;
89 import androidx.annotation.OptIn;
90 import androidx.annotation.RestrictTo;
91 import androidx.annotation.RestrictTo.Scope;
92 import androidx.annotation.UiThread;
93 import androidx.annotation.VisibleForTesting;
94 import androidx.camera.core.imagecapture.ImageCaptureControl;
95 import androidx.camera.core.imagecapture.ImagePipeline;
96 import androidx.camera.core.imagecapture.PostviewSettings;
97 import androidx.camera.core.imagecapture.TakePictureManager;
98 import androidx.camera.core.imagecapture.TakePictureRequest;
99 import androidx.camera.core.impl.AdapterCameraInfo;
100 import androidx.camera.core.impl.CameraConfig;
101 import androidx.camera.core.impl.CameraInfoInternal;
102 import androidx.camera.core.impl.CameraInternal;
103 import androidx.camera.core.impl.CaptureConfig;
104 import androidx.camera.core.impl.Config;
105 import androidx.camera.core.impl.ConfigProvider;
106 import androidx.camera.core.impl.ImageCaptureConfig;
107 import androidx.camera.core.impl.ImageInputConfig;
108 import androidx.camera.core.impl.ImageOutputConfig;
109 import androidx.camera.core.impl.ImageOutputConfig.RotationValue;
110 import androidx.camera.core.impl.ImageReaderProxy;
111 import androidx.camera.core.impl.MutableConfig;
112 import androidx.camera.core.impl.MutableOptionsBundle;
113 import androidx.camera.core.impl.OptionsBundle;
114 import androidx.camera.core.impl.SessionConfig;
115 import androidx.camera.core.impl.SessionProcessor;
116 import androidx.camera.core.impl.StreamSpec;
117 import androidx.camera.core.impl.UseCaseConfig;
118 import androidx.camera.core.impl.UseCaseConfigFactory;
119 import androidx.camera.core.impl.utils.CameraOrientationUtil;
120 import androidx.camera.core.impl.utils.CompareSizesByArea;
121 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
122 import androidx.camera.core.impl.utils.futures.Futures;
123 import androidx.camera.core.internal.IoConfig;
124 import androidx.camera.core.internal.ScreenFlashWrapper;
125 import androidx.camera.core.internal.SupportedOutputSizesSorter;
126 import androidx.camera.core.internal.TargetConfig;
127 import androidx.camera.core.internal.compat.quirk.SoftwareJpegEncodingPreferredQuirk;
128 import androidx.camera.core.internal.compat.workaround.ExifRotationAvailability;
129 import androidx.camera.core.internal.utils.ImageUtil;
130 import androidx.camera.core.resolutionselector.AspectRatioStrategy;
131 import androidx.camera.core.resolutionselector.ResolutionSelector;
132 import androidx.camera.core.resolutionselector.ResolutionStrategy;
133 import androidx.core.util.Preconditions;
134 import androidx.lifecycle.LifecycleOwner;
135 
136 import com.google.common.util.concurrent.ListenableFuture;
137 
138 import org.jspecify.annotations.NonNull;
139 import org.jspecify.annotations.Nullable;
140 
141 import java.io.File;
142 import java.io.OutputStream;
143 import java.lang.annotation.ElementType;
144 import java.lang.annotation.Retention;
145 import java.lang.annotation.RetentionPolicy;
146 import java.lang.annotation.Target;
147 import java.util.Collections;
148 import java.util.HashSet;
149 import java.util.List;
150 import java.util.Map;
151 import java.util.Objects;
152 import java.util.Set;
153 import java.util.UUID;
154 import java.util.concurrent.Executor;
155 import java.util.concurrent.atomic.AtomicReference;
156 
157 /**
158  * A use case for taking a picture.
159  *
160  * <p>This class is designed for basic picture taking. It provides takePicture() functions to take
161  * a picture to memory or save to a file, and provides image metadata.  Pictures are taken in
162  * automatic mode after focus has converged. The flash mode can additionally be set by the
163  * application.
164  *
165  * <p>TakePicture returns immediately and a listener is called to provide the results after the
166  * capture completes. Multiple calls to takePicture will take pictures sequentially starting
167  * after the previous picture is captured.
168  *
169  * <p>Note that focus and exposure metering regions can be controlled via {@link Preview}.
170  *
171  * <p>When capturing to memory, the captured image is made available through an {@link ImageProxy}
172  * via an {@link ImageCapture.OnImageCapturedCallback}.
173  */
174 @SuppressWarnings("unused")
175 public final class ImageCapture extends UseCase {
176 
177     ////////////////////////////////////////////////////////////////////////////////////////////
178     // [UseCase lifetime constant] - Stays constant for the lifetime of the UseCase. Which means
179     // they could be created in the constructor.
180     ////////////////////////////////////////////////////////////////////////////////////////////
181 
182     /**
183      * An unknown error occurred.
184      *
185      * <p>See message parameter in onError callback or log for more details.
186      */
187     public static final int ERROR_UNKNOWN = 0;
188     /**
189      * An error occurred while attempting to read or write a file, such as when saving an image
190      * to a File.
191      */
192     public static final int ERROR_FILE_IO = 1;
193 
194     /**
195      * An error reported by camera framework indicating the capture request is failed.
196      */
197     public static final int ERROR_CAPTURE_FAILED = 2;
198 
199     /**
200      * An error indicating the request cannot be done due to camera is closed.
201      */
202     public static final int ERROR_CAMERA_CLOSED = 3;
203 
204     /**
205      * An error indicating this ImageCapture is not bound to a valid camera.
206      */
207     public static final int ERROR_INVALID_CAMERA = 4;
208 
209     /**
210      * Optimizes capture pipeline to prioritize image quality over latency. When the capture
211      * mode is set to MAX_QUALITY, images may take longer to capture.
212      */
213     public static final int CAPTURE_MODE_MAXIMIZE_QUALITY = 0;
214     /**
215      * Optimizes capture pipeline to prioritize latency over image quality. When the capture
216      * mode is set to MIN_LATENCY, images may capture faster but the image quality may be
217      * reduced.
218      */
219     public static final int CAPTURE_MODE_MINIMIZE_LATENCY = 1;
220     /**
221      * Optimizes capture pipeline to have better latency while keeping good image quality. When
222      * the capture mode is set to ZERO_SHUTTER_LAG, the latency between the shutter button is
223      * clicked and the picture is taken is expected to be minimized, compared with other capture
224      * modes.
225      *
226      * <p> ZERO_SHUTTER_LAG mode is aiming to provide the minimum latency for instant capture. It
227      * caches intermediate results and deliver the one with the closest timestamp when
228      * {@link ImageCapture#takePicture(OutputFileOptions, Executor, OnImageSavedCallback)}
229      * is invoked.
230      *
231      * <p> {@link CameraInfo#isZslSupported()} can be used to query the device capability to
232      * support this mode or not. However, this mode also depends on use cases configuration and
233      * flash mode settings. If VideoCapture is bound or flash mode is not OFF or
234      * OEM Extension is ON, this mode will be disabled automatically.
235      */
236     @ExperimentalZeroShutterLag
237     public static final int CAPTURE_MODE_ZERO_SHUTTER_LAG = 2;
238 
239     private static final int FLASH_MODE_UNKNOWN = -1;
240     /**
241      * Auto flash. The flash will be used according to the camera system's determination when taking
242      * a picture.
243      */
244     public static final int FLASH_MODE_AUTO = 0;
245     /** Always flash. The flash will always be used when taking a picture. */
246     public static final int FLASH_MODE_ON = 1;
247     /** No flash. The flash will never be used when taking a picture. */
248     public static final int FLASH_MODE_OFF = 2;
249     /**
250      * Screen flash. Display screen brightness will be used as alternative to flash when taking
251      * a picture with front camera.
252      *
253      * <p> This flash mode can be set via {@link #setFlashMode(int)} after setting a non-null
254      * {@link ScreenFlash} instance with {@link #setScreenFlash(ScreenFlash)}.
255      * This mode will always invoke all the necessary operations for a screen flash image capture,
256      * i.e. it is similar to {@link #FLASH_MODE_ON}, not {@link #FLASH_MODE_AUTO}.
257      *
258      * <p> The following code snippet shows an example implementation of how this flash mode can be
259      * set to an {@link ImageCapture} instance.
260      * <pre>{@code
261      * imageCapture.setScreenFlash(new ImageCapture.ScreenFlash() {
262      *     @Override
263      *     public void apply(long expirationTimeMillis,
264      *             @NonNull ScreenFlashListener screenFlashListener) {
265      *         whiteColorOverlayView.setVisibility(View.VISIBLE);
266      *         maximizeScreenBrightness();
267      *         screenFlashListener.onCompleted();
268      *     }
269      *
270      *     @Override
271      *     public void clear() {
272      *         restoreScreenBrightness();
273      *         whiteColorOverlayView.setVisibility(View.INVISIBLE);
274      *     }
275      * });
276      *
277      * imageCapture.setFlashMode(ImageCapture.FLASH_MODE_SCREEN);
278      * }</pre>
279      *
280      * @see #setFlashMode(int)
281      */
282     public static final int FLASH_MODE_SCREEN = 3;
283 
284     /** The timeout in seconds within which screen flash UI changes have to be completed. */
285     @RestrictTo(Scope.LIBRARY_GROUP)
286     public static final long SCREEN_FLASH_UI_APPLY_TIMEOUT_SECONDS = 3;
287 
288     /**
289      * When flash is required for taking a picture, a normal one shot flash will be used.
290      */
291     @RestrictTo(Scope.LIBRARY_GROUP)
292     public static final int FLASH_TYPE_ONE_SHOT_FLASH = 0;
293     /**
294      * When flash is required for taking a picture, torch will be used as flash.
295      */
296     @RestrictTo(Scope.LIBRARY_GROUP)
297     public static final int FLASH_TYPE_USE_TORCH_AS_FLASH = 1;
298 
299     /**
300      * Captures 8-bit standard dynamic range (SDR) images using the {@link ImageFormat#JPEG}
301      * image format.
302      */
303     public static final int OUTPUT_FORMAT_JPEG = 0;
304 
305     /**
306      * Captures Ultra HDR compressed images using the {@link ImageFormat#JPEG_R} image format.
307      *
308      * <p>This format is backward compatible with SDR JPEG images and supports HDR rendering of
309      * content. This means that on older apps or devices, images appear seamlessly as regular JPEG;
310      * on apps and devices that have been updated to fully support the format, images appear as HDR.
311      *
312      * <p>For more information see
313      * <a href="https://developer.android.com/media/grow/ultra-hdr">Support Ultra HDR</a>.
314      */
315     public static final int OUTPUT_FORMAT_JPEG_ULTRA_HDR = 1;
316 
317     /**
318      * Captures raw images in the {@link ImageFormat#RAW_SENSOR} image format.
319      */
320     public static final int OUTPUT_FORMAT_RAW = 2;
321 
322     /**
323      * Captures raw images in the {@link ImageFormat#RAW_SENSOR} and {@link ImageFormat#JPEG}
324      * image formats.
325      */
326     public static final int OUTPUT_FORMAT_RAW_JPEG = 3;
327 
328     /**
329      * Provides a static configuration with implementation-agnostic options.
330      */
331     @RestrictTo(Scope.LIBRARY_GROUP)
332     public static final Defaults DEFAULT_CONFIG = new Defaults();
333     private static final String TAG = "ImageCapture";
334     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
335     private static final int MAX_IMAGES = 2;
336     // TODO(b/149336664) Move the quality to a compatibility class when there is a per device case.
337     private static final byte JPEG_QUALITY_MAXIMIZE_QUALITY_MODE = 100;
338     private static final byte JPEG_QUALITY_MINIMIZE_LATENCY_MODE = 95;
339     @CaptureMode
340     private static final int DEFAULT_CAPTURE_MODE = CAPTURE_MODE_MINIMIZE_LATENCY;
341     @FlashMode
342     private static final int DEFAULT_FLASH_MODE = FLASH_MODE_OFF;
343 
344     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
345     static final ExifRotationAvailability EXIF_ROTATION_AVAILABILITY =
346             new ExifRotationAvailability();
347 
348     private final ImageReaderProxy.OnImageAvailableListener mClosingListener = imageReader -> {
349         try (ImageProxy image = imageReader.acquireLatestImage()) {
350             Log.d(TAG, "Discarding ImageProxy which was inadvertently acquired: " + image);
351         } catch (IllegalStateException e) {
352             Log.e(TAG, "Failed to acquire latest image.", e);
353         }
354     };
355 
356     @CaptureMode
357     private final int mCaptureMode;
358 
359     @GuardedBy("mLockedFlashMode")
360     private final AtomicReference<Integer> mLockedFlashMode = new AtomicReference<>(null);
361 
362     @FlashType
363     private final int mFlashType;
364 
365     ////////////////////////////////////////////////////////////////////////////////////////////
366     // [UseCase lifetime dynamic] - Dynamic variables which could change during anytime during
367     // the UseCase lifetime.
368     ////////////////////////////////////////////////////////////////////////////////////////////
369 
370     /** Current flash mode. */
371     @GuardedBy("mLockedFlashMode")
372     @FlashMode
373     private int mFlashMode = FLASH_MODE_UNKNOWN;
374     private Rational mCropAspectRatio = null;
375     private @NonNull ScreenFlashWrapper mScreenFlashWrapper;
376 
377     ////////////////////////////////////////////////////////////////////////////////////////////
378     // [UseCase attached dynamic] - Can change but is only available when the UseCase is attached.
379     ////////////////////////////////////////////////////////////////////////////////////////////
380 
381     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
382     SessionConfig.Builder mSessionConfigBuilder;
383 
384     private @Nullable ImagePipeline mImagePipeline;
385     private @Nullable TakePictureManager mTakePictureManager;
386     private SessionConfig.@Nullable CloseableErrorListener mCloseableErrorListener;
387 
388     /**
389      * Creates a new image capture use case from the given configuration.
390      *
391      * @param userConfig for this use case instance
392      * @throws IllegalArgumentException if the configuration is invalid.
393      */
ImageCapture(@onNull ImageCaptureConfig userConfig)394     ImageCapture(@NonNull ImageCaptureConfig userConfig) {
395         super(userConfig);
396 
397         ImageCaptureConfig useCaseConfig = (ImageCaptureConfig) getCurrentConfig();
398 
399         if (useCaseConfig.containsOption(OPTION_IMAGE_CAPTURE_MODE)) {
400             mCaptureMode = useCaseConfig.getCaptureMode();
401         } else {
402             mCaptureMode = DEFAULT_CAPTURE_MODE;
403         }
404 
405         mFlashType = useCaseConfig.getFlashType(FLASH_TYPE_ONE_SHOT_FLASH);
406         mScreenFlashWrapper = ScreenFlashWrapper.from(useCaseConfig.getScreenFlash());
407     }
408 
isSessionProcessorEnabledInCurrentCamera()409     private boolean isSessionProcessorEnabledInCurrentCamera() {
410         if (getCamera() == null) {
411             return false;
412         }
413 
414         CameraConfig cameraConfig = getCamera().getExtendedConfig();
415         return cameraConfig.getSessionProcessor(null) != null;
416     }
417 
418     /**
419      * {@inheritDoc}
420      */
421     @RestrictTo(Scope.LIBRARY_GROUP)
422     @Override
getDefaultConfig(boolean applyDefaultConfig, @NonNull UseCaseConfigFactory factory)423     public @Nullable UseCaseConfig<?> getDefaultConfig(boolean applyDefaultConfig,
424             @NonNull UseCaseConfigFactory factory) {
425         Config captureConfig = factory.getConfig(
426                 DEFAULT_CONFIG.getConfig().getCaptureType(),
427                 getCaptureMode());
428 
429         if (applyDefaultConfig) {
430             captureConfig = Config.mergeConfigs(captureConfig, DEFAULT_CONFIG.getConfig());
431         }
432 
433         return captureConfig == null ? null :
434                 getUseCaseConfigBuilder(captureConfig).getUseCaseConfig();
435     }
436 
437     /**
438      * {@inheritDoc}
439      */
440     @RestrictTo(Scope.LIBRARY_GROUP)
441     @Override
getUseCaseConfigBuilder(@onNull Config config)442     public UseCaseConfig.@NonNull Builder<?, ?, ?> getUseCaseConfigBuilder(@NonNull Config config) {
443         return Builder.fromConfig(config);
444     }
445 
446     /**
447      * {@inheritDoc}
448      */
449     @RestrictTo(Scope.LIBRARY_GROUP)
450     @Override
onMergeConfig(@onNull CameraInfoInternal cameraInfo, UseCaseConfig.@NonNull Builder<?, ?, ?> builder)451     protected @NonNull UseCaseConfig<?> onMergeConfig(@NonNull CameraInfoInternal cameraInfo,
452             UseCaseConfig.@NonNull Builder<?, ?, ?> builder) {
453         if (cameraInfo.getCameraQuirks().contains(SoftwareJpegEncodingPreferredQuirk.class)) {
454             // Request software JPEG encoder if quirk exists on this device, and the software JPEG
455             // option has not already been explicitly set.
456             if (Boolean.FALSE.equals(builder.getMutableConfig().retrieveOption(
457                     OPTION_USE_SOFTWARE_JPEG_ENCODER, true))) {
458                 Logger.w(TAG, "Device quirk suggests software JPEG encoder, but it has been "
459                         + "explicitly disabled.");
460             } else {
461                 Logger.i(TAG, "Requesting software JPEG due to device quirk.");
462                 builder.getMutableConfig().insertOption(OPTION_USE_SOFTWARE_JPEG_ENCODER, true);
463             }
464         }
465 
466         // If software JPEG is requested, disable if it is incompatible.
467         boolean useSoftwareJpeg = enforceSoftwareJpegConstraints(builder.getMutableConfig());
468 
469         // Update the input format base on the other options set (mainly whether processing
470         // is done)
471         Integer bufferFormat = builder.getMutableConfig().retrieveOption(OPTION_BUFFER_FORMAT,
472                 null);
473         if (bufferFormat != null) {
474             Preconditions.checkArgument(!(isSessionProcessorEnabledInCurrentCamera()
475                             && bufferFormat != JPEG),
476                     "Cannot set non-JPEG buffer format with Extensions enabled.");
477             builder.getMutableConfig().insertOption(OPTION_INPUT_FORMAT,
478                     useSoftwareJpeg ? ImageFormat.YUV_420_888 : bufferFormat);
479         } else {
480             if (isOutputFormatRaw(builder.getMutableConfig())) {
481                 builder.getMutableConfig().insertOption(OPTION_INPUT_FORMAT, RAW_SENSOR);
482             } else if (isOutputFormatRawJpeg(builder.getMutableConfig())) {
483                 builder.getMutableConfig().insertOption(OPTION_INPUT_FORMAT, RAW_SENSOR);
484                 builder.getMutableConfig().insertOption(OPTION_SECONDARY_INPUT_FORMAT, JPEG);
485             } else if (isOutputFormatUltraHdr(builder.getMutableConfig())) {
486                 builder.getMutableConfig().insertOption(OPTION_INPUT_FORMAT, JPEG_R);
487                 builder.getMutableConfig().insertOption(OPTION_INPUT_DYNAMIC_RANGE,
488                         DynamicRange.UNSPECIFIED);
489             } else if (useSoftwareJpeg) {
490                 builder.getMutableConfig().insertOption(OPTION_INPUT_FORMAT,
491                         ImageFormat.YUV_420_888);
492             } else {
493                 List<Pair<Integer, Size[]>> supportedSizes =
494                         builder.getMutableConfig().retrieveOption(OPTION_SUPPORTED_RESOLUTIONS,
495                                 null);
496                 if (supportedSizes == null) {
497                     builder.getMutableConfig().insertOption(OPTION_INPUT_FORMAT, JPEG);
498                 } else {
499                     // Use Jpeg first if supported.
500                     if (isImageFormatSupported(supportedSizes, JPEG)) {
501                         builder.getMutableConfig().insertOption(OPTION_INPUT_FORMAT,
502                                 JPEG);
503                     } else if (isImageFormatSupported(supportedSizes, ImageFormat.YUV_420_888)) {
504                         builder.getMutableConfig().insertOption(OPTION_INPUT_FORMAT,
505                                 ImageFormat.YUV_420_888);
506                     }
507                 }
508             }
509         }
510         return builder.getUseCaseConfig();
511     }
512 
isImageFormatSupported(List<Pair<Integer, Size[]>> supportedSizes, int imageFormat)513     private static boolean isImageFormatSupported(List<Pair<Integer, Size[]>> supportedSizes,
514             int imageFormat) {
515         if (supportedSizes == null) {
516             return false;
517         }
518         for (Pair<Integer, Size[]> supportedSize : supportedSizes) {
519             if (supportedSize.first.equals(imageFormat)) {
520                 return true;
521             }
522         }
523         return false;
524     }
525 
isOutputFormatUltraHdr(@onNull MutableConfig config)526     private static boolean isOutputFormatUltraHdr(@NonNull MutableConfig config) {
527         return Objects.equals(config.retrieveOption(OPTION_OUTPUT_FORMAT, null),
528                 OUTPUT_FORMAT_JPEG_ULTRA_HDR);
529     }
530 
isOutputFormatRaw(@onNull MutableConfig config)531     private static boolean isOutputFormatRaw(@NonNull MutableConfig config) {
532         return Objects.equals(config.retrieveOption(OPTION_OUTPUT_FORMAT, null),
533                 OUTPUT_FORMAT_RAW);
534     }
535 
isOutputFormatRawJpeg(@onNull MutableConfig config)536     private static boolean isOutputFormatRawJpeg(@NonNull MutableConfig config) {
537         return Objects.equals(config.retrieveOption(OPTION_OUTPUT_FORMAT, null),
538                 OUTPUT_FORMAT_RAW_JPEG);
539     }
540 
541     /**
542      * Configures flash mode to CameraControlInternal once it is ready.
543      */
544     @RestrictTo(Scope.LIBRARY_GROUP)
545     @Override
onCameraControlReady()546     public void onCameraControlReady() {
547         Logger.d(TAG, "onCameraControlReady");
548         trySetFlashModeToCameraControl();
549         setScreenFlashToCameraControl();
550     }
551 
getCameraLens()552     private @CameraSelector.LensFacing int getCameraLens() {
553         Camera camera = getCamera();
554         if (camera != null) {
555             return camera.getCameraInfo().getLensFacing();
556         }
557         return CameraSelector.LENS_FACING_UNKNOWN;
558     }
559 
560     /**
561      * Get the flash mode.
562      *
563      * @return the flashMode. Value is {@link #FLASH_MODE_AUTO}, {@link #FLASH_MODE_ON},
564      * {@link #FLASH_MODE_SCREEN}, or {@link #FLASH_MODE_OFF}.
565      */
566     @FlashMode
getFlashMode()567     public int getFlashMode() {
568         synchronized (mLockedFlashMode) {
569             return mFlashMode != FLASH_MODE_UNKNOWN ? mFlashMode
570                     : ((ImageCaptureConfig) getCurrentConfig()).getFlashMode(DEFAULT_FLASH_MODE);
571         }
572     }
573 
574     /**
575      * Set the flash mode.
576      *
577      * <p>The flash control for the subsequent photo capture requests. Applications can check if
578      * there is a flash unit via {@link CameraInfo#hasFlashUnit()} and update UI component if
579      * necessary. If there is no flash unit and {@code flashMode} is not {@link #FLASH_MODE_SCREEN},
580      * then calling this API will take no effect for the subsequent photo capture requests and
581      * they will act like {@link #FLASH_MODE_OFF}.
582      *
583      * <p>When the torch is enabled via {@link CameraControl#enableTorch(boolean)}, the torch
584      * will remain enabled during photo capture regardless of flashMode setting. When
585      * the torch is disabled, flash will function as specified by {@code setFlashMode(int)}.
586      *
587      * <p>On some LEGACY devices like Samsung A3, taking pictures with {@link #FLASH_MODE_AUTO}
588      * mode could cause a crash. To workaround this CameraX will disable the auto flash behavior
589      * internally on devices that have this issue.
590      *
591      * <p>If {@link #FLASH_MODE_SCREEN} is set, a {@link ScreenFlash} implementation
592      * must be set via {@link #setScreenFlash(ScreenFlash)} before calling this
593      * API. Trying to use {@link #FLASH_MODE_SCREEN} without a {@code ScreenFlash} instance set or
594      * with a non-front camera will result in an {@link IllegalArgumentException}. It is the
595      * application's responsibility to change flashMode while switching the camera in case it
596      * leads to a non-supported case (e.g. switching to rear camera while FLASH_MODE_SCREEN is
597      * still on).
598      *
599      * @param flashMode the flash mode. Value is {@link #FLASH_MODE_AUTO}, {@link #FLASH_MODE_ON},
600      *                  {@link #FLASH_MODE_SCREEN} or {@link #FLASH_MODE_OFF}.
601      *
602      * @throws IllegalArgumentException If flash mode is invalid or FLASH_MODE_SCREEN is used
603      *                                  without a {@code ScreenFlash} instance or front camera.
604      */
setFlashMode(@lashMode int flashMode)605     public void setFlashMode(@FlashMode int flashMode) {
606         Logger.d(TAG, "setFlashMode: flashMode = " + flashMode);
607 
608         if (flashMode != FLASH_MODE_AUTO && flashMode != FLASH_MODE_ON
609                 && flashMode != FLASH_MODE_OFF) {
610             if (flashMode == FLASH_MODE_SCREEN) {
611                 if (mScreenFlashWrapper.getBaseScreenFlash() == null) {
612                     throw new IllegalArgumentException("ScreenFlash not set for FLASH_MODE_SCREEN");
613                 }
614 
615                 if (getCamera() != null && getCameraLens() != CameraSelector.LENS_FACING_FRONT) {
616                     throw new IllegalArgumentException(
617                             "Not a front camera despite setting FLASH_MODE_SCREEN");
618                 }
619             } else {
620                 throw new IllegalArgumentException("Invalid flash mode: " + flashMode);
621             }
622         }
623 
624         synchronized (mLockedFlashMode) {
625             mFlashMode = flashMode;
626             trySetFlashModeToCameraControl();
627         }
628     }
629 
630     /**
631      * Sets {@link ScreenFlash} for subsequent photo capture requests.
632      *
633      * <p>The calling of this API will take effect for {@link #FLASH_MODE_SCREEN} only
634      * and the {@code screenFlash} instance will be ignored for other flash modes.
635      *
636      * <p>If the implementation provided by the user is no longer valid (e.g. due to any
637      * {@link android.app.Activity} or {@link android.view.View} reference used in the
638      * implementation becoming invalid), user needs to re-set a new valid {@code ScreenFlash} or
639      * clear the previous one with {@code setScreenFlash(null)}, whichever appropriate.
640      *
641      * @param screenFlash A {@link ScreenFlash} implementation that is used to
642      *                             notify API users when app side changes need to be done. This
643      *                             will replace the previous {@code ScreenFlash} instance set
644      *                             with this method.
645      */
setScreenFlash(@ullable ScreenFlash screenFlash)646     public void setScreenFlash(@Nullable ScreenFlash screenFlash) {
647         mScreenFlashWrapper = ScreenFlashWrapper.from(screenFlash);
648         setScreenFlashToCameraControl();
649     }
650 
651     /**
652      * Returns the {@link ScreenFlash} instance currently set, null if none.
653      */
getScreenFlash()654     public @Nullable ScreenFlash getScreenFlash() {
655         return mScreenFlashWrapper.getBaseScreenFlash();
656     }
657 
setScreenFlashToCameraControl()658     private void setScreenFlashToCameraControl() {
659         setScreenFlashToCameraControl(mScreenFlashWrapper);
660     }
661 
setScreenFlashToCameraControl(ImageCapture.@ullable ScreenFlash screenFlash)662     private void setScreenFlashToCameraControl(ImageCapture.@Nullable ScreenFlash screenFlash) {
663         getCameraControl().setScreenFlash(screenFlash);
664     }
665 
666     /**
667      * Sets target cropping aspect ratio for output image.
668      *
669      * <p>This aspect ratio is orientation-dependent. It should be expressed in the coordinate
670      * frame after rotating the image by the target rotation.
671      *
672      * <p>This sets the cropping rectangle returned by {@link ImageProxy#getCropRect()} returned
673      * from {@link ImageCapture#takePicture(Executor, OnImageCapturedCallback)}.
674      *
675      * <p>For example, assume the {@code aspectRatio} of 3x4. If an image has a resolution of
676      * 480x640 after applying the target rotation, then the output {@link ImageProxy} of
677      * {@link ImageCapture#takePicture(Executor, OnImageCapturedCallback)} would have a cropping
678      * rectangle of 480x640 after applying the rotation degrees. However, if an image has a
679      * resolution of 640x480 after applying the target rotation, then the cropping rectangle
680      * of the output {@link ImageProxy} would be 360x480 after applying the rotation degrees.
681      *
682      * <p>This crops the saved image when calling
683      * {@link ImageCapture#takePicture(OutputFileOptions, Executor, OnImageSavedCallback)}. Note
684      * that the cropping will introduce an additional latency.
685      *
686      * <p>Cropping occurs around the center of the image and as though it were in the target
687      * rotation. For example, assume the {@code aspectRatio} of 3x4. If an image has a resolution
688      * of 480x640 after applying the target rotation, then the saved output image would be
689      * 480x640 after applying the EXIF orientation value. However, if an image has a resolution
690      * of 640x480 after applying the target rotation, then the saved output image would be
691      * 360x480 after applying the EXIF orientation value.
692      *
693      * <p>This setting value will be automatically updated to match the new target rotation value
694      * when {@link ImageCapture#setTargetRotation(int)} is called.
695      *
696      * @param aspectRatio New target aspect ratio.
697      */
setCropAspectRatio(@onNull Rational aspectRatio)698     public void setCropAspectRatio(@NonNull Rational aspectRatio) {
699         mCropAspectRatio = aspectRatio;
700     }
701 
702     /**
703      * Returns the desired rotation of the output image.
704      *
705      * <p>The rotation can be set prior to constructing an ImageCapture using
706      * {@link ImageCapture.Builder#setTargetRotation(int)} or dynamically by calling
707      * {@link ImageCapture#setTargetRotation(int)}. The rotation of an image taken is
708      * determined by the rotation value set at the time image capture is initiated, such as when
709      * calling {@link #takePicture(Executor, OnImageCapturedCallback)}.
710      *
711      * <p>If no target rotation is set by the application, it is set to the value of
712      * {@link Display#getRotation()} of the default display at the time the use case is
713      * created. The use case is fully created once it has been attached to a camera.
714      *
715      * @return The rotation of the intended target.
716      */
717     @RotationValue
getTargetRotation()718     public int getTargetRotation() {
719         return getTargetRotationInternal();
720     }
721 
722     /**
723      * Sets the desired rotation of the output image.
724      *
725      * <p>This will affect the EXIF rotation metadata in images saved by takePicture calls and the
726      * {@link ImageInfo#getRotationDegrees()} value of the {@link ImageProxy} returned by
727      * {@link OnImageCapturedCallback}. These will be set to be the rotation, which if applied to
728      * the output image data, will make the image match target rotation specified here.
729      *
730      * <p>While rotation can also be set via {@link Builder#setTargetRotation(int)}, using
731      * {@code setTargetRotation(int)} allows the target rotation to be set dynamically.
732      *
733      * <p>In general, it is best to use an {@link android.view.OrientationEventListener} to
734      * set the target rotation.  This way, the rotation output will indicate which way is down for
735      * a given image.  This is important since display orientation may be locked by device
736      * default, user setting, or app configuration, and some devices may not transition to a
737      * reverse-portrait display orientation. In these cases, set target rotation dynamically
738      * according to the {@link android.view.OrientationEventListener}, without re-creating the
739      * use case. {@link UseCase#snapToSurfaceRotation(int)} is a helper function to convert the
740      * orientation of the {@link android.view.OrientationEventListener} to a rotation value.
741      * See {@link UseCase#snapToSurfaceRotation(int)} for more information and sample code.
742      *
743      * <p>When this function is called, value set by
744      * {@link ImageCapture.Builder#setTargetResolution(Size)} will be updated automatically to make
745      * sure the suitable resolution can be selected when the use case is bound. Value set by
746      * {@link ImageCapture#setCropAspectRatio(Rational)} will also be updated automatically to
747      * make sure the output image is cropped into expected aspect ratio.
748      *
749      * <p>If no target rotation is set by the application, it is set to the value of
750      * {@link Display#getRotation()} of the default display at the time the use case is bound. To
751      * return to the default value, set the value to
752      * <pre>{@code
753      * context.getSystemService(WindowManager.class).getDefaultDisplay().getRotation();
754      * }</pre>
755      *
756      * <p>takePicture uses the target rotation at the time it begins executing (which may be delayed
757      * waiting on a previous takePicture call to complete).
758      *
759      * @param rotation Target rotation of the output image, expressed as one of
760      *                 {@link Surface#ROTATION_0}, {@link Surface#ROTATION_90},
761      *                 {@link Surface#ROTATION_180}, or {@link Surface#ROTATION_270}.
762      */
setTargetRotation(@otationValue int rotation)763     public void setTargetRotation(@RotationValue int rotation) {
764         int oldRotation = getTargetRotation();
765 
766         if (setTargetRotationInternal(rotation)) {
767             // For the crop aspect ratio value, the numerator and denominator of original setting
768             // value will be swapped then set back. It is an orientation-dependent value that will
769             // be used to crop ImageCapture's output image.
770             if (mCropAspectRatio != null) {
771                 int oldRotationDegrees = CameraOrientationUtil.surfaceRotationToDegrees(
772                         oldRotation);
773                 int newRotationDegrees = CameraOrientationUtil.surfaceRotationToDegrees(rotation);
774                 mCropAspectRatio = ImageUtil.getRotatedAspectRatio(
775                         Math.abs(newRotationDegrees - oldRotationDegrees), mCropAspectRatio);
776             }
777 
778             // TODO(b/122846516): Update session configuration and possibly reconfigure session.
779         }
780     }
781 
782     /**
783      * Returns the set capture mode.
784      *
785      * <p>This is set when constructing an ImageCapture using
786      * {@link ImageCapture.Builder#setCaptureMode(int)}. This is static for an instance of
787      * ImageCapture.
788      */
789     @CaptureMode
getCaptureMode()790     public int getCaptureMode() {
791         return mCaptureMode;
792     }
793 
794     /**
795      * Returns the JPEG quality setting.
796      *
797      * <p>This is set when constructing an ImageCapture using
798      * {@link ImageCapture.Builder#setJpegQuality(int)}. If not set, a default value will be set
799      * according to the capture mode setting. JPEG compression quality 95 is set for
800      * {@link #CAPTURE_MODE_MINIMIZE_LATENCY} and 100 is set for
801      * {@link #CAPTURE_MODE_MAXIMIZE_QUALITY}. This is static for an instance of ImageCapture.
802      */
803     @IntRange(from = 1, to = 100)
getJpegQuality()804     public int getJpegQuality() {
805         return getJpegQualityInternal();
806     }
807 
808     /**
809      * Gets selected resolution information of the {@link ImageCapture}.
810      *
811      * <p>The returned {@link ResolutionInfo} will be expressed in the coordinates of the camera
812      * sensor. Note that the resolution might not be the same as the resolution of the received
813      * image by calling {@link #takePicture} because the received image might have been rotated
814      * to the upright orientation using the target rotation setting by the device.
815      *
816      * <p>The resolution information might change if the use case is unbound and then rebound,
817      * {@link #setTargetRotation(int)} is called to change the target rotation setting, or
818      * {@link #setCropAspectRatio(Rational)} is called to change the crop aspect ratio setting.
819      * The application needs to call {@code getResolutionInfo()} again to get the latest
820      * {@link ResolutionInfo} for the changes.
821      *
822      * @return the resolution information if the use case has been bound by the
823      * {@link androidx.camera.lifecycle.ProcessCameraProvider#bindToLifecycle(LifecycleOwner
824      *, CameraSelector, UseCase...)} API, or null if the use case is not bound yet.
825      */
getResolutionInfo()826     public @Nullable ResolutionInfo getResolutionInfo() {
827         return getResolutionInfoInternal();
828     }
829 
830     /**
831      * {@inheritDoc}
832      */
833     @RestrictTo(Scope.LIBRARY_GROUP)
834     @Override
getResolutionInfoInternal()835     protected @Nullable ResolutionInfo getResolutionInfoInternal() {
836         CameraInternal camera = getCamera();
837         Size resolution = getAttachedSurfaceResolution();
838 
839         if (camera == null || resolution == null) {
840             return null;
841         }
842 
843         Rect cropRect = getViewPortCropRect();
844 
845         Rational cropAspectRatio = mCropAspectRatio;
846 
847         if (cropRect == null) {
848             if (cropAspectRatio != null) {
849                 cropRect = ImageUtil.computeCropRectFromAspectRatio(resolution, cropAspectRatio);
850             } else {
851                 cropRect = new Rect(0, 0, resolution.getWidth(), resolution.getHeight());
852             }
853         }
854 
855         int rotationDegrees = getRelativeRotation(camera);
856 
857         return new ResolutionInfo(resolution, requireNonNull(cropRect), rotationDegrees);
858     }
859 
860     /**
861      * Returns the resolution selector setting.
862      *
863      * <p>This setting is set when constructing an ImageCapture using
864      * {@link Builder#setResolutionSelector(ResolutionSelector)}.
865      */
getResolutionSelector()866     public @Nullable ResolutionSelector getResolutionSelector() {
867         return ((ImageOutputConfig) getCurrentConfig()).getResolutionSelector(null);
868     }
869 
870     /**
871      * Returns the output format setting.
872      *
873      * <p>If the output format was not provided to
874      * {@link ImageCapture.Builder#setOutputFormat(int)}, this will return the default of
875      * {@link #OUTPUT_FORMAT_JPEG}.
876      *
877      * @return the output format set for this {@code ImageCapture} use case.
878      *
879      * @see ImageCapture.Builder#setOutputFormat(int)
880      */
881     @OutputFormat
getOutputFormat()882     public int getOutputFormat() {
883         return checkNotNull(getCurrentConfig().retrieveOption(OPTION_OUTPUT_FORMAT,
884                 Defaults.DEFAULT_OUTPUT_FORMAT));
885     }
886 
887     /**
888      * Captures a new still image for in memory access.
889      *
890      * <p>The listener is responsible for calling {@link Image#close()} on the returned image.
891      *
892      * <p>For simultaneous image capture with {@link #OUTPUT_FORMAT_RAW_JPEG}, the
893      * {@link OnImageCapturedCallback#onCaptureSuccess(ImageProxy)} will be triggered twice, one for
894      * {@link ImageFormat#RAW_SENSOR} and the other for {@link ImageFormat#JPEG}. The order of the
895      * callbacks for which image format is triggered first is not guaranteed.
896      *
897      * @param executor The executor in which the callback methods will be run.
898      * @param callback Callback to be invoked for the newly captured image.
899      *
900      * @throws IllegalArgumentException If {@link ImageCapture#FLASH_MODE_SCREEN} is used without a
901      *                                  non-null {@code ScreenFlash} instance set.
902      */
takePicture(@onNull Executor executor, final @NonNull OnImageCapturedCallback callback)903     public void takePicture(@NonNull Executor executor,
904             final @NonNull OnImageCapturedCallback callback) {
905         if (Looper.getMainLooper() != Looper.myLooper()) {
906             CameraXExecutors.mainThreadExecutor().execute(() -> takePicture(executor, callback));
907             return;
908         }
909 
910         takePictureInternal(executor, callback, /*onDiskCallback=*/null,
911                 /*outputFileOptions=*/null, /*secondaryOutputFileOptions=*/null);
912     }
913 
914     /**
915      * Captures a new still image and saves to a file along with application specified metadata.
916      *
917      * <p> If the {@link ImageCapture} is in a {@link UseCaseGroup} where {@link ViewPort} is
918      * set, or {@link #setCropAspectRatio} is used, the image may be cropped before saving to
919      * disk which causes an additional latency.
920      *
921      * @param outputFileOptions  Options to store the newly captured image.
922      * @param executor           The executor in which the callback methods will be run.
923      * @param imageSavedCallback Callback to be called for the newly captured image.
924      *
925      * @throws IllegalArgumentException If {@link ImageCapture#FLASH_MODE_SCREEN} is used without a
926      *                                  a non-null {@code ScreenFlash} instance set. Also if
927      *                                  {@link ImageCapture#OUTPUT_FORMAT_RAW_JPEG} is used.
928      * @see ViewPort
929      */
takePicture( final @NonNull OutputFileOptions outputFileOptions, final @NonNull Executor executor, final @NonNull OnImageSavedCallback imageSavedCallback)930     public void takePicture(
931             final @NonNull OutputFileOptions outputFileOptions,
932             final @NonNull Executor executor,
933             final @NonNull OnImageSavedCallback imageSavedCallback) {
934         if (Looper.getMainLooper() != Looper.myLooper()) {
935             CameraXExecutors.mainThreadExecutor().execute(() -> takePicture(
936                     outputFileOptions, executor, imageSavedCallback));
937             return;
938         }
939 
940         takePictureInternal(executor, /*inMemoryCallback=*/null, imageSavedCallback,
941                 outputFileOptions, /*secondaryOutputFileOptions=*/null);
942     }
943 
944     /**
945      * Captures two still images simultaneously and saves to a file along with application
946      * specified metadata.
947      *
948      * <p>Currently only {@link #OUTPUT_FORMAT_RAW_JPEG} is supporting simultaneous image capture.
949      * It needs two {@link OutputFileOptions}, the first one is used for
950      * {@link ImageFormat#RAW_SENSOR} image and the second one is for {@link ImageFormat#JPEG}. The
951      * order of the callbacks for which image format is triggered first is not guaranteed. Check
952      * with {@link OutputFileResults#getImageFormat()} in
953      * {@link OnImageSavedCallback#onImageSaved(OutputFileResults)} for the image format.
954      *
955      * @param rawOutputFileOptions  Options to store the newly captured raw image.
956      * @param jpegOutputFileOptions Options to store the newly captured jpeg image.
957      * @param executor           The executor in which the callback methods will be run.
958      * @param imageSavedCallback Callback to be called for the newly captured image.
959      *
960      * @throws IllegalArgumentException If {@link ImageCapture#FLASH_MODE_SCREEN} is used without a
961      *                                  a non-null {@code ScreenFlash} instance set. Also if
962      *                                  non-{@link ImageCapture#OUTPUT_FORMAT_RAW_JPEG} format is
963      *                                  used.
964      */
takePicture( final @NonNull OutputFileOptions rawOutputFileOptions, final @NonNull OutputFileOptions jpegOutputFileOptions, final @NonNull Executor executor, final @NonNull OnImageSavedCallback imageSavedCallback)965     public void takePicture(
966             final @NonNull OutputFileOptions rawOutputFileOptions,
967             final @NonNull OutputFileOptions jpegOutputFileOptions,
968             final @NonNull Executor executor,
969             final @NonNull OnImageSavedCallback imageSavedCallback) {
970         if (Looper.getMainLooper() != Looper.myLooper()) {
971             CameraXExecutors.mainThreadExecutor().execute(
972                     () -> takePicture(rawOutputFileOptions, jpegOutputFileOptions,
973                             executor, imageSavedCallback));
974             return;
975         }
976         takePictureInternal(executor, /*inMemoryCallback=*/null, imageSavedCallback,
977                 rawOutputFileOptions, jpegOutputFileOptions);
978     }
979 
980     /**
981      * Returns {@link ImageCaptureCapabilities} to query ImageCapture capability of the given
982      * {@link CameraInfo}.
983      *
984      * <p>Some capabilities are only exposed on Extensions-enabled cameras. To get the correct
985      * capabilities when Extensions are enabled, you need to pass the {@link CameraInfo} from the
986      * Extensions-enabled {@link Camera} instance. To do this, use the {@link CameraSelector}
987      * instance retrieved from
988      * {@link androidx.camera.extensions.ExtensionsManager#getExtensionEnabledCameraSelector(CameraSelector, int)}
989      * to invoke {@link androidx.camera.lifecycle.ProcessCameraProvider#bindToLifecycle} where
990      * you can skip use cases arguments if you'd like to query it before opening the camera. Then,
991      * use the returned {@link Camera} to get the {@link CameraInfo} instance.
992      *
993      * <p>>The following code snippet demonstrates how to enable postview:
994      *
995      * <pre>{@code
996      * CameraSelector extensionCameraSelector =
997      *     extensionsManager.getExtensionEnabledCameraSelector(cameraSelector, ExtensionMode.NIGHT);
998      * Camera camera = cameraProvider.bindToLifecycle(activity, extensionCameraSelector);
999      * ImageCaptureCapabilities capabilities =
1000      *     ImageCapture.getImageCaptureCapabilities(camera.getCameraInfo());
1001      * ImageCapture imageCapture = new ImageCapture.Builder()
1002      *     .setPostviewEnabled(capabilities.isPostviewSupported())
1003      *     .build();
1004      * }}</pre>
1005      *
1006      * @return {@link ImageCaptureCapabilities}
1007      */
getImageCaptureCapabilities( @onNull CameraInfo cameraInfo)1008     public static @NonNull ImageCaptureCapabilities getImageCaptureCapabilities(
1009             @NonNull CameraInfo cameraInfo) {
1010         return new ImageCaptureCapabilitiesImpl(cameraInfo);
1011     }
1012 
1013     private static class ImageCaptureCapabilitiesImpl implements ImageCaptureCapabilities {
1014         private final CameraInfo mCameraInfo;
ImageCaptureCapabilitiesImpl(@onNull CameraInfo cameraInfo)1015         ImageCaptureCapabilitiesImpl(@NonNull CameraInfo cameraInfo) {
1016             mCameraInfo = cameraInfo;
1017         }
1018 
1019         @Override
isPostviewSupported()1020         public boolean isPostviewSupported() {
1021             if (mCameraInfo instanceof CameraInfoInternal) {
1022                 return ((CameraInfoInternal) mCameraInfo).isPostviewSupported();
1023             }
1024             return false;
1025         }
1026 
1027         @Override
isCaptureProcessProgressSupported()1028         public boolean isCaptureProcessProgressSupported() {
1029             if (mCameraInfo instanceof CameraInfoInternal) {
1030                 return ((CameraInfoInternal) mCameraInfo).isCaptureProcessProgressSupported();
1031             }
1032             return false;
1033         }
1034 
1035         @Override
getSupportedOutputFormats()1036         public @NonNull Set<@OutputFormat Integer> getSupportedOutputFormats() {
1037             Set<Integer> outputFormatsFromAdapterCameraInfo =
1038                     getSupportedOutputFormatsFromAdapterCameraInfo();
1039 
1040             if (outputFormatsFromAdapterCameraInfo != null) {
1041                 return outputFormatsFromAdapterCameraInfo;
1042             }
1043 
1044             Set<Integer> formats = new HashSet<>();
1045             formats.add(OUTPUT_FORMAT_JPEG);
1046             if (isUltraHdrSupported()) {
1047                 formats.add(OUTPUT_FORMAT_JPEG_ULTRA_HDR);
1048             }
1049 
1050             if (isRawSupported()) {
1051                 formats.add(OUTPUT_FORMAT_RAW);
1052                 formats.add(OUTPUT_FORMAT_RAW_JPEG);
1053             }
1054 
1055             return formats;
1056         }
1057 
isUltraHdrSupported()1058         private boolean isUltraHdrSupported() {
1059             if (mCameraInfo instanceof CameraInfoInternal) {
1060                 CameraInfoInternal cameraInfoInternal = (CameraInfoInternal) mCameraInfo;
1061                 return cameraInfoInternal.getSupportedOutputFormats().contains(JPEG_R);
1062             }
1063 
1064             return false;
1065         }
1066 
isRawSupported()1067         private boolean isRawSupported() {
1068             if (mCameraInfo instanceof CameraInfoInternal) {
1069                 CameraInfoInternal cameraInfoInternal = (CameraInfoInternal) mCameraInfo;
1070                 return cameraInfoInternal.getSupportedOutputFormats().contains(RAW_SENSOR);
1071             }
1072 
1073             return false;
1074         }
1075 
getSupportedOutputFormatsFromAdapterCameraInfo()1076         private @Nullable Set<Integer> getSupportedOutputFormatsFromAdapterCameraInfo() {
1077             if (!(mCameraInfo instanceof AdapterCameraInfo)) {
1078                 return null;
1079             }
1080 
1081             // If AdapterCameraInfo is used to query the capabilities, retrieves the supported
1082             // output formats from the CameraConfig associated with it.
1083             CameraConfig cameraConfig = ((AdapterCameraInfo) mCameraInfo).getCameraConfig();
1084             UseCaseConfigFactory useCaseConfigFactory = cameraConfig.getUseCaseConfigFactory();
1085             Config useCaseConfig = useCaseConfigFactory.getConfig(
1086                     UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE, DEFAULT_CAPTURE_MODE);
1087 
1088             if (useCaseConfig == null || !useCaseConfig.containsOption(
1089                     OPTION_SUPPORTED_RESOLUTIONS)) {
1090                 return null;
1091             }
1092 
1093             Set<Integer> formats = new HashSet<>();
1094             // JPEG output format should always be supported for ImageCapture.
1095             formats.add(OUTPUT_FORMAT_JPEG);
1096             for (Pair<Integer, Size[]> formatSizesPair : useCaseConfig.retrieveOption(
1097                     OPTION_SUPPORTED_RESOLUTIONS)) {
1098                 if (formatSizesPair.first == ImageFormat.JPEG_R) {
1099                     formats.add(OUTPUT_FORMAT_JPEG_ULTRA_HDR);
1100                     // Besides JPEG, only JPEG_ULTRA_HDR can be supported for now from the
1101                     // adapter camera info. Breaks the for-loop when it has been confirmed that
1102                     // JPEG_ULTRA_HDR can be supported.
1103                     break;
1104                 }
1105             }
1106 
1107             return formats;
1108         }
1109     }
1110 
computeDispatchCropRect(@ullable Rect viewPortCropRect, @Nullable Rational cropAspectRatio, int rotationDegrees, @NonNull Size dispatchResolution, int dispatchRotationDegrees)1111     static @NonNull Rect computeDispatchCropRect(@Nullable Rect viewPortCropRect,
1112             @Nullable Rational cropAspectRatio, int rotationDegrees,
1113             @NonNull Size dispatchResolution, int dispatchRotationDegrees) {
1114         if (viewPortCropRect != null) {
1115             return ImageUtil.computeCropRectFromDispatchInfo(viewPortCropRect, rotationDegrees,
1116                     dispatchResolution, dispatchRotationDegrees);
1117         } else if (cropAspectRatio != null) {
1118             // Fall back to crop aspect ratio if view port is not available.
1119             Rational aspectRatio = cropAspectRatio;
1120             if ((dispatchRotationDegrees % 180) != 0) {
1121                 aspectRatio = new Rational(
1122                         /* invert the ratio numerator=*/ cropAspectRatio.getDenominator(),
1123                         /* invert the ratio denominator=*/ cropAspectRatio.getNumerator());
1124             }
1125             if (ImageUtil.isAspectRatioValid(dispatchResolution, aspectRatio)) {
1126                 return requireNonNull(
1127                         ImageUtil.computeCropRectFromAspectRatio(dispatchResolution, aspectRatio));
1128             }
1129         }
1130 
1131         return new Rect(0, 0, dispatchResolution.getWidth(), dispatchResolution.getHeight());
1132     }
1133 
1134     /**
1135      * {@inheritDoc}
1136      */
1137     @RestrictTo(Scope.LIBRARY_GROUP)
1138     @UiThread
1139     @Override
onStateDetached()1140     public void onStateDetached() {
1141         abortImageCaptureRequests();
1142     }
1143 
1144     @UiThread
abortImageCaptureRequests()1145     private void abortImageCaptureRequests() {
1146         // Camera2CapturePipeline ScreenFlash#clear event may come a bit later due to
1147         // thread-hopping or listener invocation delay. When all requests are aborted anyway, we can
1148         // complete all pending tasks earlier and ignore any that comes from user/camera-camera2.
1149         mScreenFlashWrapper.completePendingTasks();
1150 
1151         if (mTakePictureManager != null) {
1152             mTakePictureManager.abortRequests();
1153         }
1154     }
1155 
lockFlashMode()1156     void lockFlashMode() {
1157         synchronized (mLockedFlashMode) {
1158             if (mLockedFlashMode.get() != null) {
1159                 // FlashMode is locked.
1160                 return;
1161             }
1162             mLockedFlashMode.set(getFlashMode());
1163         }
1164     }
1165 
unlockFlashMode()1166     void unlockFlashMode() {
1167         synchronized (mLockedFlashMode) {
1168             Integer lockedFlashMode = mLockedFlashMode.getAndSet(null);
1169             if (lockedFlashMode == null) {
1170                 // FlashMode is not locked yet.
1171                 return;
1172             }
1173             if (lockedFlashMode != getFlashMode()) {
1174                 // Flash Mode is changed during lock session.
1175                 trySetFlashModeToCameraControl();
1176             }
1177         }
1178     }
1179 
trySetFlashModeToCameraControl()1180     private void trySetFlashModeToCameraControl() {
1181         synchronized (mLockedFlashMode) {
1182             if (mLockedFlashMode.get() != null) {
1183                 // Flash Mode is locked.
1184                 return;
1185             }
1186             getCameraControl().setFlashMode(getFlashMode());
1187         }
1188     }
1189 
1190     /**
1191      * Gets the JPEG quality based on {@link #mCaptureMode}.
1192      *
1193      * <p> Range is 1-100; larger is higher quality.
1194      *
1195      * @return Compression quality of the captured JPEG image.
1196      */
1197     @OptIn(markerClass = ExperimentalZeroShutterLag.class)
1198     @IntRange(from = 1, to = 100)
getJpegQualityInternal()1199     private int getJpegQualityInternal() {
1200         ImageCaptureConfig imageCaptureConfig = (ImageCaptureConfig) getCurrentConfig();
1201 
1202         if (imageCaptureConfig.containsOption(OPTION_JPEG_COMPRESSION_QUALITY)) {
1203             return imageCaptureConfig.getJpegQuality();
1204         }
1205 
1206         switch (mCaptureMode) {
1207             case CAPTURE_MODE_MAXIMIZE_QUALITY:
1208                 return JPEG_QUALITY_MAXIMIZE_QUALITY_MODE;
1209             case CAPTURE_MODE_MINIMIZE_LATENCY:
1210             case CAPTURE_MODE_ZERO_SHUTTER_LAG:
1211                 return JPEG_QUALITY_MINIMIZE_LATENCY_MODE;
1212             default:
1213                 throw new IllegalStateException("CaptureMode " + mCaptureMode + " is invalid");
1214         }
1215     }
1216 
1217     @Override
toString()1218     public @NonNull String toString() {
1219         return TAG + ":" + getName();
1220     }
1221 
1222     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
1223     @ImageCaptureError
getError(Throwable throwable)1224     static int getError(Throwable throwable) {
1225         if (throwable instanceof CameraClosedException) {
1226             return ERROR_CAMERA_CLOSED;
1227         } else if (throwable instanceof ImageCaptureException) {
1228             return ((ImageCaptureException) throwable).getImageCaptureError();
1229         } else {
1230             return ERROR_UNKNOWN;
1231         }
1232     }
1233 
1234     /**
1235      * Disables software JPEG if it is requested and the provided config and/or device
1236      * characteristics are incompatible.
1237      *
1238      * @return {@code true} if software JPEG will be used after applying constraints.
1239      */
enforceSoftwareJpegConstraints(@onNull MutableConfig mutableConfig)1240     boolean enforceSoftwareJpegConstraints(@NonNull MutableConfig mutableConfig) {
1241         if (Boolean.TRUE.equals(
1242                 mutableConfig.retrieveOption(OPTION_USE_SOFTWARE_JPEG_ENCODER, false))) {
1243             boolean supported = true;
1244             if (isSessionProcessorEnabledInCurrentCamera()) {
1245                 // SessionProcessor requires JPEG input format so it is incompatible with SW Jpeg.
1246                 Logger.w(TAG, "Software JPEG cannot be used with Extensions.");
1247                 supported = false;
1248             }
1249             Integer bufferFormat = mutableConfig.retrieveOption(OPTION_BUFFER_FORMAT, null);
1250             if (bufferFormat != null && bufferFormat != JPEG) {
1251                 Logger.w(TAG, "Software JPEG cannot be used with non-JPEG output buffer format.");
1252                 supported = false;
1253             }
1254 
1255             if (!supported) {
1256                 Logger.w(TAG, "Unable to support software JPEG. Disabling.");
1257                 mutableConfig.insertOption(OPTION_USE_SOFTWARE_JPEG_ENCODER, false);
1258             }
1259 
1260             return supported;
1261         }
1262         return false;
1263     }
1264 
1265     /**
1266      * {@inheritDoc}
1267      */
1268     @RestrictTo(Scope.LIBRARY_GROUP)
1269     @Override
onUnbind()1270     public void onUnbind() {
1271         abortImageCaptureRequests();
1272         clearPipeline();
1273         setScreenFlashToCameraControl(null);
1274     }
1275 
1276     /**
1277      * {@inheritDoc}
1278      */
1279     @Override
1280     @RestrictTo(Scope.LIBRARY_GROUP)
onBind()1281     public void onBind() {
1282         CameraInternal camera = getCamera();
1283         checkNotNull(camera, "Attached camera cannot be null");
1284 
1285         if (getFlashMode() == FLASH_MODE_SCREEN
1286                 && getCameraLens() != CameraSelector.LENS_FACING_FRONT) {
1287             throw new IllegalArgumentException(
1288                     "Not a front camera despite setting FLASH_MODE_SCREEN in ImageCapture");
1289         }
1290     }
1291 
getSessionProcessor()1292     private @Nullable SessionProcessor getSessionProcessor() {
1293         CameraConfig cameraConfig = getCamera().getExtendedConfig();
1294         return cameraConfig.getSessionProcessor(null);
1295     }
1296 
1297     /**
1298      * {@inheritDoc}
1299      */
1300     @Override
1301     @RestrictTo(Scope.LIBRARY_GROUP)
onSuggestedStreamSpecUpdated( @onNull StreamSpec primaryStreamSpec, @Nullable StreamSpec secondaryStreamSpec)1302     protected @NonNull StreamSpec onSuggestedStreamSpecUpdated(
1303             @NonNull StreamSpec primaryStreamSpec,
1304             @Nullable StreamSpec secondaryStreamSpec) {
1305         mSessionConfigBuilder = createPipeline(getCameraId(),
1306                 (ImageCaptureConfig) getCurrentConfig(), primaryStreamSpec);
1307 
1308         updateSessionConfig(List.of(mSessionConfigBuilder.build()));
1309 
1310         // In order to speed up the take picture process, notifyActive at an early stage to
1311         // attach the session capture callback to repeating and get capture result all the time.
1312         notifyActive();
1313         return primaryStreamSpec;
1314     }
1315 
1316     /**
1317      * {@inheritDoc}
1318      */
1319     @Override
1320     @RestrictTo(Scope.LIBRARY_GROUP)
onSuggestedStreamSpecImplementationOptionsUpdated( @onNull Config config)1321     protected @NonNull StreamSpec onSuggestedStreamSpecImplementationOptionsUpdated(
1322             @NonNull Config config) {
1323         mSessionConfigBuilder.addImplementationOptions(config);
1324         updateSessionConfig(List.of(mSessionConfigBuilder.build()));
1325         return getAttachedStreamSpec().toBuilder().setImplementationOptions(config).build();
1326     }
1327 
1328     /**
1329      * An {@link ImageCaptureControl} implementation to control this {@link ImageCapture} instance.
1330      */
1331     private final ImageCaptureControl mImageCaptureControl = new ImageCaptureControl() {
1332 
1333         @MainThread
1334         @Override
1335         public void lockFlashMode() {
1336             ImageCapture.this.lockFlashMode();
1337         }
1338 
1339         @MainThread
1340         @Override
1341         public void unlockFlashMode() {
1342             ImageCapture.this.unlockFlashMode();
1343         }
1344 
1345         @MainThread
1346         @Override
1347         public @NonNull ListenableFuture<Void> submitStillCaptureRequests(
1348                 @NonNull List<CaptureConfig> captureConfigs) {
1349             return ImageCapture.this.submitStillCaptureRequest(captureConfigs);
1350         }
1351     };
1352 
1353     /**
1354      * Creates the pipeline for both capture request configuration and image post-processing.
1355      */
1356     @OptIn(markerClass = {ExperimentalZeroShutterLag.class})
1357     @MainThread
createPipeline(@onNull String cameraId, @NonNull ImageCaptureConfig config, @NonNull StreamSpec streamSpec)1358     private SessionConfig.Builder createPipeline(@NonNull String cameraId,
1359             @NonNull ImageCaptureConfig config, @NonNull StreamSpec streamSpec) {
1360         checkMainThread();
1361         Log.d(TAG, String.format("createPipeline(cameraId: %s, streamSpec: %s)",
1362                 cameraId, streamSpec));
1363         Size resolution = streamSpec.getResolution();
1364         boolean isVirtualCamera = !requireNonNull(getCamera()).getHasTransform();
1365         if (mImagePipeline != null) {
1366             checkState(isVirtualCamera);
1367             // On LEGACY devices, when the app is backgrounded, it will trigger StreamSharing's
1368             // SessionConfig error callback and recreate children pipeline.
1369             mImagePipeline.close();
1370         }
1371 
1372         Set<Integer> supportedOutputFormats = getImageCaptureCapabilities(
1373                 getCamera().getCameraInfo()).getSupportedOutputFormats();
1374         Preconditions.checkArgument(supportedOutputFormats.contains(getOutputFormat()),
1375                 "The specified output format (" + getOutputFormat()
1376                         + ") is not supported by current configuration. Supported output formats: "
1377                         + supportedOutputFormats);
1378 
1379         PostviewSettings postviewSettings = isPostviewEnabled() ? calculatePostviewSettings(
1380                 resolution) : null;
1381 
1382         CameraCharacteristics cameraCharacteristics = null;
1383         if (getCamera() != null) {
1384             try {
1385                 Object obj = getCamera().getCameraInfoInternal().getCameraCharacteristics();
1386                 if (obj instanceof CameraCharacteristics) {
1387                     cameraCharacteristics = (CameraCharacteristics) obj;
1388                 }
1389             } catch (Exception e) {
1390                 Log.e(TAG, "getCameraCharacteristics failed", e);
1391             }
1392         }
1393 
1394         mImagePipeline = new ImagePipeline(config, resolution, cameraCharacteristics, getEffect(),
1395                 isVirtualCamera, postviewSettings);
1396 
1397         if (mTakePictureManager == null) {
1398             // mTakePictureManager is reused when the Surface is reset.
1399             mTakePictureManager = getCurrentConfig().getTakePictureManagerProvider().newInstance(
1400                     mImageCaptureControl);
1401         }
1402         mTakePictureManager.setImagePipeline(mImagePipeline);
1403 
1404         SessionConfig.Builder sessionConfigBuilder =
1405                 mImagePipeline.createSessionConfigBuilder(streamSpec.getResolution());
1406         sessionConfigBuilder.setSessionType(streamSpec.getSessionType());
1407         if (Build.VERSION.SDK_INT >= 23
1408                 && getCaptureMode() == CAPTURE_MODE_ZERO_SHUTTER_LAG
1409                 && !streamSpec.getZslDisabled()) {
1410             getCameraControl().addZslConfig(sessionConfigBuilder);
1411         }
1412         if (streamSpec.getImplementationOptions() != null) {
1413             sessionConfigBuilder.addImplementationOptions(streamSpec.getImplementationOptions());
1414         }
1415         // Close the old error listener
1416         if (mCloseableErrorListener != null) {
1417             mCloseableErrorListener.close();
1418         }
1419         mCloseableErrorListener = new SessionConfig.CloseableErrorListener(
1420                 (sessionConfig, error) -> {
1421                     // Do nothing when the use case has been unbound.
1422                     if (getCamera() == null) {
1423                         return;
1424                     }
1425 
1426                     mTakePictureManager.pause();
1427                     clearPipeline(/*keepTakePictureManager=*/ true);
1428                     mSessionConfigBuilder = createPipeline(getCameraId(),
1429                             (ImageCaptureConfig) getCurrentConfig(),
1430                             Preconditions.checkNotNull(getAttachedStreamSpec()));
1431                     updateSessionConfig(List.of(mSessionConfigBuilder.build()));
1432                     notifyReset();
1433                     mTakePictureManager.resume();
1434                 });
1435         sessionConfigBuilder.setErrorListener(mCloseableErrorListener);
1436         return sessionConfigBuilder;
1437     }
1438 
1439     /**
1440      * Calculates the best format and size to generate the postview output bitmap according to the
1441      * info retrieved from the use case config.
1442      *
1443      * @param targetResolution the target resolution of the still image
1444      * @return the settings for the postview, or <code>null</code> if no supported format or
1445      * output size can be found.
1446      */
calculatePostviewSettings(@onNull Size targetResolution)1447     private @Nullable PostviewSettings calculatePostviewSettings(@NonNull Size targetResolution) {
1448         SessionProcessor sessionProcessor = getSessionProcessor();
1449 
1450         // No session processor can be found which is necessary for supporting postview
1451         if (sessionProcessor == null) {
1452             return null;
1453         }
1454 
1455         Map<Integer, List<Size>> formatSizesMap = sessionProcessor.getSupportedPostviewSize(
1456                 targetResolution);
1457 
1458         int format = ImageFormat.UNKNOWN;
1459         // Prefer YUV because it takes less time to decode to bitmap.
1460         if (isPostviewImageFormatSupported(formatSizesMap, ImageFormat.YUV_420_888)) {
1461             format = ImageFormat.YUV_420_888;
1462         } else if (isPostviewImageFormatSupported(formatSizesMap, ImageFormat.JPEG)) {
1463             format = ImageFormat.JPEG;
1464         } else if (isPostviewImageFormatSupported(formatSizesMap, ImageFormat.JPEG_R)) {
1465             format = ImageFormat.JPEG_R;
1466         }
1467 
1468         // No supported postview image format can be found
1469         if (format == ImageFormat.UNKNOWN) {
1470             return null;
1471         }
1472 
1473         List<Size> sizes = formatSizesMap.get(format);
1474         ResolutionSelector postviewSizeSelector = getCurrentConfig().retrieveOption(
1475                 OPTION_POSTVIEW_RESOLUTION_SELECTOR, null);
1476 
1477         if (postviewSizeSelector != null) {
1478             Collections.sort(sizes, new CompareSizesByArea(true));
1479             CameraInternal camera = getCamera();
1480             Rect sensorRect = camera.getCameraControlInternal().getSensorRect();
1481             CameraInfoInternal cameraInfo = camera.getCameraInfoInternal();
1482             Rational fullFov = new Rational(sensorRect.width(), sensorRect.height());
1483             List<Size> result =
1484                     SupportedOutputSizesSorter.sortSupportedOutputSizesByResolutionSelector(
1485                             postviewSizeSelector, sizes, null, getTargetRotation(), fullFov,
1486                             cameraInfo.getSensorRotationDegrees(), cameraInfo.getLensFacing());
1487 
1488             if (result.isEmpty()) {
1489                 throw new IllegalArgumentException(
1490                         "The postview ResolutionSelector cannot select a valid size for the "
1491                                 + "postview.");
1492             }
1493             return PostviewSettings.create(result.get(0), format);
1494         } else {
1495             return PostviewSettings.create(Collections.max(sizes, new CompareSizesByArea()),
1496                     format);
1497         }
1498     }
1499 
isPostviewImageFormatSupported(@onNull Map<Integer, List<Size>> formatSizesMap, int format)1500     private boolean isPostviewImageFormatSupported(@NonNull Map<Integer, List<Size>> formatSizesMap,
1501             int format) {
1502         return formatSizesMap.containsKey(format) && !formatSizesMap.get(format).isEmpty();
1503     }
1504 
1505     /**
1506      * Takes a picture with the new architecture.
1507      *
1508      * @throws IllegalArgumentException If {@link ImageCapture#FLASH_MODE_SCREEN} is used without a
1509      *                                  non-null {@code ScreenFlash} instance set.
1510      */
1511     @MainThread
takePictureInternal(@onNull Executor executor, @Nullable OnImageCapturedCallback inMemoryCallback, @Nullable OnImageSavedCallback onDiskCallback, @Nullable OutputFileOptions outputFileOptions, @Nullable OutputFileOptions secondaryOutputFileOptions)1512     private void takePictureInternal(@NonNull Executor executor,
1513             @Nullable OnImageCapturedCallback inMemoryCallback,
1514             @Nullable OnImageSavedCallback onDiskCallback,
1515             @Nullable OutputFileOptions outputFileOptions,
1516             @Nullable OutputFileOptions secondaryOutputFileOptions) {
1517         checkMainThread();
1518         if (getFlashMode() == ImageCapture.FLASH_MODE_SCREEN
1519                 && mScreenFlashWrapper.getBaseScreenFlash() == null) {
1520             throw new IllegalArgumentException("ScreenFlash not set for FLASH_MODE_SCREEN");
1521         }
1522         Log.d(TAG, "takePictureInternal");
1523         CameraInternal camera = getCamera();
1524         if (camera == null) {
1525             sendInvalidCameraError(executor, inMemoryCallback, onDiskCallback);
1526             return;
1527         }
1528         boolean isSimultaneousCapture = getCurrentConfig()
1529                 .getSecondaryInputFormat() != ImageFormat.UNKNOWN;
1530         if (isSimultaneousCapture && secondaryOutputFileOptions == null) {
1531             throw new IllegalArgumentException(
1532                     "Simultaneous capture RAW and JPEG needs two output file options");
1533         }
1534         if (!isSimultaneousCapture && secondaryOutputFileOptions != null) {
1535             throw new IllegalArgumentException(
1536                     "Non simultaneous capture cannot have two output file options");
1537         }
1538         requireNonNull(mTakePictureManager).offerRequest(TakePictureRequest.of(
1539                 executor,
1540                 inMemoryCallback,
1541                 onDiskCallback,
1542                 outputFileOptions,
1543                 secondaryOutputFileOptions,
1544                 getTakePictureCropRect(),
1545                 getSensorToBufferTransformMatrix(),
1546                 getRelativeRotation(camera),
1547                 getJpegQualityInternal(),
1548                 getCaptureMode(),
1549                 isSimultaneousCapture,
1550                 mSessionConfigBuilder.getSingleCameraCaptureCallbacks()));
1551     }
1552 
sendInvalidCameraError(@onNull Executor executor, @Nullable OnImageCapturedCallback inMemoryCallback, ImageCapture.@Nullable OnImageSavedCallback onDiskCallback)1553     private void sendInvalidCameraError(@NonNull Executor executor,
1554             @Nullable OnImageCapturedCallback inMemoryCallback,
1555             ImageCapture.@Nullable OnImageSavedCallback onDiskCallback) {
1556         ImageCaptureException exception = new ImageCaptureException(ERROR_INVALID_CAMERA,
1557                 "Not bound to a valid Camera [" + ImageCapture.this + "]", null);
1558         if (inMemoryCallback != null) {
1559             inMemoryCallback.onError(exception);
1560         } else if (onDiskCallback != null) {
1561             onDiskCallback.onError(exception);
1562         } else {
1563             throw new IllegalArgumentException("Must have either in-memory or on-disk callback.");
1564         }
1565     }
1566 
1567     /**
1568      * Calculates a snapshot of crop rect when app calls {@link #takePicture}.
1569      */
getTakePictureCropRect()1570     private @NonNull Rect getTakePictureCropRect() {
1571         Rect rect = getViewPortCropRect();
1572         Size resolution = requireNonNull(getAttachedSurfaceResolution());
1573         if (rect != null) {
1574             return rect;
1575         } else if (isAspectRatioValid(mCropAspectRatio)) {
1576             int rotationDegrees = getRelativeRotation(requireNonNull(getCamera()));
1577             Rational rotatedAspectRatio = new Rational(
1578                     /* numerator= */ mCropAspectRatio.getDenominator(),
1579                     /* denominator= */ mCropAspectRatio.getNumerator());
1580             Rational sensorCropRatio = is90or270(rotationDegrees)
1581                     ? rotatedAspectRatio : mCropAspectRatio;
1582             return requireNonNull(computeCropRectFromAspectRatio(resolution, sensorCropRatio));
1583         }
1584         return new Rect(0, 0, resolution.getWidth(), resolution.getHeight());
1585     }
1586 
1587     /**
1588      * Clears the pipeline without keeping the {@link TakePictureManager}.
1589      */
1590     @MainThread
clearPipeline()1591     private void clearPipeline() {
1592         clearPipeline(/*keepTakePictureManager=*/false);
1593     }
1594 
1595     /**
1596      * Clears the pipeline.
1597      */
1598     @MainThread
clearPipeline(boolean keepTakePictureManager)1599     private void clearPipeline(boolean keepTakePictureManager) {
1600         Log.d(TAG, "clearPipeline");
1601         checkMainThread();
1602 
1603         // Close the old error listener
1604         if (mCloseableErrorListener != null) {
1605             mCloseableErrorListener.close();
1606             mCloseableErrorListener = null;
1607         }
1608 
1609         if (mImagePipeline != null) {
1610             mImagePipeline.close();
1611             mImagePipeline = null;
1612         }
1613         // TODO: no need to abort requests when UseCase unbinds. Clean this up when the old
1614         //  pipeline is removed.
1615         if (!keepTakePictureManager && mTakePictureManager != null) {
1616             mTakePictureManager.abortRequests();
1617             mTakePictureManager = null;
1618         }
1619         // Always clear ZSL resources to release the RingBuffer.
1620         getCameraControl().clearZslConfig();
1621     }
1622 
1623     /**
1624      * Submits still capture requests with the current configurations.
1625      */
1626     @MainThread
submitStillCaptureRequest( @onNull List<CaptureConfig> captureConfigs)1627     ListenableFuture<Void> submitStillCaptureRequest(
1628             @NonNull List<CaptureConfig> captureConfigs) {
1629         checkMainThread();
1630         return Futures.transform(getCameraControl().submitStillCaptureRequests(
1631                         captureConfigs, mCaptureMode, mFlashType),
1632                 input -> null, CameraXExecutors.directExecutor());
1633     }
1634 
1635     @VisibleForTesting
isProcessingPipelineEnabled()1636     boolean isProcessingPipelineEnabled() {
1637         return mImagePipeline != null && mTakePictureManager != null;
1638     }
1639 
1640     @VisibleForTesting
getImagePipeline()1641     @Nullable ImagePipeline getImagePipeline() {
1642         return mImagePipeline;
1643     }
1644 
1645     @VisibleForTesting
getTakePictureManager()1646     @NonNull TakePictureManager getTakePictureManager() {
1647         return requireNonNull(mTakePictureManager);
1648     }
1649 
1650     /**
1651      * @inheritDoc
1652      */
1653     @RestrictTo(Scope.LIBRARY_GROUP)
1654     @Override
getSupportedEffectTargets()1655     public @NonNull Set<Integer> getSupportedEffectTargets() {
1656         Set<Integer> targets = new HashSet<>();
1657         targets.add(IMAGE_CAPTURE);
1658         return targets;
1659     }
1660 
1661     /**
1662      * Returns an estimate of the capture and processing sequence duration based on the current
1663      * camera configuration and scene conditions. The value will vary as the scene and/or camera
1664      * configuration change.
1665      *
1666      * <p>The processing estimate can vary based on device processing load.
1667      *
1668      * <p>If this method returns
1669      * {@link ImageCaptureLatencyEstimate#UNDEFINED_IMAGE_CAPTURE_LATENCY}, it means that the
1670      * camera HAL doesn't provide latency information. In this case, this method will
1671      * consistently return {@link ImageCaptureLatencyEstimate#UNDEFINED_IMAGE_CAPTURE_LATENCY}
1672      * for the current camera configuration, as long as the UseCase and camera configuration
1673      * remain unchanged (e.g., extensions mode and camera settings are kept the same).
1674      */
getRealtimeCaptureLatencyEstimate()1675     public @NonNull ImageCaptureLatencyEstimate getRealtimeCaptureLatencyEstimate() {
1676         final CameraInternal camera = getCamera();
1677         if (camera == null) {
1678             return ImageCaptureLatencyEstimate.UNDEFINED_IMAGE_CAPTURE_LATENCY;
1679         }
1680 
1681         final CameraConfig config = camera.getExtendedConfig();
1682         final SessionProcessor sessionProcessor = config.getSessionProcessor(null);
1683         final Pair<Long, Long> latencyEstimate =
1684                 sessionProcessor != null ? sessionProcessor.getRealtimeCaptureLatency() : null;
1685         if (latencyEstimate == null) {
1686             return ImageCaptureLatencyEstimate.UNDEFINED_IMAGE_CAPTURE_LATENCY;
1687         }
1688         return new ImageCaptureLatencyEstimate(latencyEstimate.first, latencyEstimate.second);
1689     }
1690 
1691     /**
1692      * Returns if postview is enabled or not.
1693      *
1694      * @see Builder#setPostviewEnabled(boolean)
1695      */
isPostviewEnabled()1696     public boolean isPostviewEnabled() {
1697         return getCurrentConfig().retrieveOption(OPTION_POSTVIEW_ENABLED, false);
1698     }
1699 
1700     /**
1701      * Returns the {@link ResolutionSelector} used to select the postview size.
1702      *
1703      * @see Builder#setPostviewResolutionSelector(ResolutionSelector)
1704      */
getPostviewResolutionSelector()1705     public @Nullable ResolutionSelector getPostviewResolutionSelector() {
1706         return getCurrentConfig().retrieveOption(OPTION_POSTVIEW_RESOLUTION_SELECTOR,
1707                 null);
1708     }
1709 
1710     /**
1711      * Describes the error that occurred during an image capture operation (such as {@link
1712      * ImageCapture#takePicture(Executor, OnImageCapturedCallback)}).
1713      *
1714      * <p>This is a parameter sent to the error callback functions set in listeners such as {@link
1715      * ImageCapture.OnImageSavedCallback#onError(ImageCaptureException)}.
1716      */
1717     @IntDef({ERROR_UNKNOWN, ERROR_FILE_IO, ERROR_CAPTURE_FAILED, ERROR_CAMERA_CLOSED,
1718             ERROR_INVALID_CAMERA})
1719     @Retention(RetentionPolicy.SOURCE)
1720     @RestrictTo(Scope.LIBRARY_GROUP)
1721     public @interface ImageCaptureError {
1722     }
1723 
1724     /**
1725      * Capture mode options for ImageCapture. A picture will always be taken regardless of
1726      * mode, and the mode will be used on devices that support it.
1727      */
1728     @OptIn(markerClass = androidx.camera.core.ExperimentalZeroShutterLag.class)
1729     @IntDef({CAPTURE_MODE_MAXIMIZE_QUALITY, CAPTURE_MODE_MINIMIZE_LATENCY,
1730             CAPTURE_MODE_ZERO_SHUTTER_LAG})
1731     @Retention(RetentionPolicy.SOURCE)
1732     @RestrictTo(Scope.LIBRARY_GROUP)
1733     public @interface CaptureMode {
1734     }
1735 
1736     /**
1737      * The flash mode options when taking a picture using ImageCapture.
1738      *
1739      * <p>Applications can check if there is a flash unit via {@link CameraInfo#hasFlashUnit()} and
1740      * update UI component if necessary. If there is no flash unit, then the FlashMode set to
1741      * {@link #setFlashMode(int)} will take no effect for the subsequent photo capture requests
1742      * and they will act like {@link #FLASH_MODE_OFF}.
1743      *
1744      * <p>When the torch is enabled via {@link CameraControl#enableTorch(boolean)}, the torch
1745      * will remain enabled during photo capture regardless of flash mode setting. When
1746      * the torch is disabled, flash will function as specified by
1747      * {@link #setFlashMode(int)}.
1748      */
1749     @IntDef({FLASH_MODE_UNKNOWN, FLASH_MODE_AUTO, FLASH_MODE_ON, FLASH_MODE_SCREEN, FLASH_MODE_OFF})
1750     @Retention(RetentionPolicy.SOURCE)
1751     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
1752     public @interface FlashMode {
1753     }
1754 
1755     /**
1756      * The flash type options when flash is required for taking a picture.
1757      */
1758     @IntDef({FLASH_TYPE_ONE_SHOT_FLASH, FLASH_TYPE_USE_TORCH_AS_FLASH})
1759     @Retention(RetentionPolicy.SOURCE)
1760     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
1761     public @interface FlashType {
1762     }
1763 
1764     /**
1765      * The output format of the captured image.
1766      */
1767     @Target({ElementType.TYPE_USE})
1768     @IntDef({OUTPUT_FORMAT_JPEG, OUTPUT_FORMAT_JPEG_ULTRA_HDR,
1769             OUTPUT_FORMAT_RAW, OUTPUT_FORMAT_RAW_JPEG})
1770     @Retention(RetentionPolicy.SOURCE)
1771     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
1772     public @interface OutputFormat {
1773     }
1774 
1775     /** Listener containing callbacks for image file I/O events. */
1776     public interface OnImageSavedCallback {
1777         /**
1778          * Called when the capture is started.
1779          *
1780          * <p>This callback is guaranteed to be called once and before
1781          * {@link #onImageSaved(OutputFileResults)} for the same invocation of
1782          * {@link #takePicture(OutputFileOptions, Executor, OnImageSavedCallback)}.
1783          *
1784          * <p>It's recommended to play shutter sound or trigger UI indicators of
1785          * capture when receiving this callback.
1786          */
onCaptureStarted()1787         default void onCaptureStarted() {
1788         }
1789 
1790         /** Called when an image has been successfully saved. */
onImageSaved(@onNull OutputFileResults outputFileResults)1791         void onImageSaved(@NonNull OutputFileResults outputFileResults);
1792 
1793         /**
1794          * Called when an error occurs while attempting to save an image.
1795          *
1796          * @param exception An {@link ImageCaptureException} that contains the type of error, the
1797          *                  error message and the throwable that caused it.
1798          */
onError(@onNull ImageCaptureException exception)1799         void onError(@NonNull ImageCaptureException exception);
1800 
1801         /**
1802          * Callback to report the progress of the capture's processing.
1803          *
1804          * <p>To know in advanced if this callback will be invoked or not, check the
1805          * capabilities by {@link #getImageCaptureCapabilities(CameraInfo)} and
1806          * {@link ImageCaptureCapabilities#isCaptureProcessProgressSupported()}. If supported,
1807          * this callback will be called multiple times with monotonically increasing
1808          * values. At the minimum the callback will be called once with value 100 to
1809          * indicate the processing is finished. This callback will always be called before
1810          * {@link #onImageSaved(OutputFileResults)}.
1811          *
1812          * @param progress the progress ranging from 0 to 100.
1813          */
onCaptureProcessProgressed(int progress)1814         default void onCaptureProcessProgressed(int progress) {
1815         }
1816 
1817         /**
1818          * Callback to notify that the postview bitmap is available. The postview is intended to be
1819          * shown on UI before the long-processing capture is completed in order to provide a
1820          * better UX.
1821          *
1822          * <p>The postview is only available when the
1823          * {@link ImageCaptureCapabilities#isPostviewSupported()} returns true for the specified
1824          * {@link CameraInfo} and applications must explicitly enable the postview using the
1825          * {@link Builder#setPostviewEnabled(boolean)}. This callback will be called before
1826          * {@link #onImageSaved(OutputFileResults)}. But if something goes wrong when processing
1827          * the postview, this callback method could be skipped.
1828          *
1829          * <p>The bitmap is rotated according to the target rotation set to the {@link ImageCapture}
1830          * to make it upright. If target rotation is not set, the display rotation is used.
1831          *
1832          * <p>See also {@link ImageCapture.Builder#setTargetRotation(int)} and
1833          * {@link #setTargetRotation(int)}.
1834          *
1835          * @param bitmap the postview bitmap.
1836          */
onPostviewBitmapAvailable(@onNull Bitmap bitmap)1837         default void onPostviewBitmapAvailable(@NonNull Bitmap bitmap) {
1838         }
1839     }
1840 
1841     /**
1842      * Callback for image capture events.
1843      */
1844     public abstract static class OnImageCapturedCallback {
1845         /**
1846          * Callback for when the camera has started exposing a frame.
1847          *
1848          * <p>This callback is guaranteed to be called once and before
1849          * {@link #onCaptureSuccess(ImageProxy)} for the same invocation of
1850          * {@link #takePicture(Executor, OnImageCapturedCallback)}.
1851          *
1852          * <p>It's recommended to play shutter sound or trigger UI indicators of
1853          * capture when receiving this callback.
1854          */
onCaptureStarted()1855         public void onCaptureStarted() {
1856         }
1857 
1858         /**
1859          * Callback for when the image has been captured.
1860          *
1861          * <p>The application is responsible for calling {@link ImageProxy#close()} to close the
1862          * image.
1863          *
1864          * <p>The image is of format {@link ImageFormat#JPEG} or {@link ImageFormat#JPEG_R},
1865          * queryable via {@link ImageProxy#getFormat()}.
1866          *
1867          * <p>The image is provided as captured by the underlying {@link ImageReader} without
1868          * rotation applied. The value in {@code image.getImageInfo().getRotationDegrees()}
1869          * describes the magnitude of clockwise rotation, which if applied to the image will make
1870          * it match the currently configured target rotation.
1871          *
1872          * <p>For example, if the current target rotation is set to the display rotation,
1873          * rotationDegrees is the rotation to apply to the image to match the display orientation.
1874          * A rotation of 90 degrees would mean rotating the image 90 degrees clockwise produces an
1875          * image that will match the display orientation.
1876          *
1877          * <p>See also {@link ImageCapture.Builder#setTargetRotation(int)} and
1878          * {@link #setTargetRotation(int)}.
1879          *
1880          * <p>Timestamps are in nanoseconds and monotonic and can be compared to timestamps from
1881          * images produced from UseCases bound to the same camera instance.  More detail is
1882          * available depending on the implementation.  For example with CameraX using a
1883          * {@link androidx.camera.camera2} implementation additional detail can be found in
1884          * {@link android.hardware.camera2.CameraDevice} documentation.
1885          *
1886          * @param image The captured image
1887          */
onCaptureSuccess(@onNull ImageProxy image)1888         public void onCaptureSuccess(@NonNull ImageProxy image) {
1889         }
1890 
1891         /**
1892          * Callback for when an error occurred during image capture.
1893          *
1894          * @param exception An {@link ImageCaptureException} that contains the type of error, the
1895          *                  error message and the throwable that caused it.
1896          */
onError(final @NonNull ImageCaptureException exception)1897         public void onError(final @NonNull ImageCaptureException exception) {
1898         }
1899 
1900         /**
1901          * Callback to report the progress of the capture's processing.
1902          *
1903          * <p>To know in advanced if this callback will be invoked or not, check the
1904          * capabilities by {@link #getImageCaptureCapabilities(CameraInfo)} and
1905          * {@link ImageCaptureCapabilities#isCaptureProcessProgressSupported()}. If supported,
1906          * this callback will be called multiple times with monotonically increasing
1907          * values. At the minimum the callback will be called once with value 100 to
1908          * indicate the processing is finished. This callback will always be called before
1909          * {@link #onCaptureSuccess(ImageProxy)}.
1910          *
1911          * @param progress the progress ranging from 0 to 100.
1912          */
onCaptureProcessProgressed(int progress)1913         public void onCaptureProcessProgressed(int progress) {
1914         }
1915 
1916         /**
1917          * Callback to notify that the postview bitmap is available. The postview is intended to be
1918          * shown on UI before the long-processing capture is completed in order to provide a
1919          * better UX.
1920          *
1921          * <p>The postview is only available when the
1922          * {@link ImageCaptureCapabilities#isPostviewSupported()} returns true for the specified
1923          * {@link CameraInfo} and applications must explicitly enable the postview using the
1924          * {@link Builder#setPostviewEnabled(boolean)}. This callback will be called before
1925          * {@link #onCaptureSuccess(ImageProxy)}. But if something goes wrong when processing the
1926          * postview, this callback method could be skipped.
1927          *
1928          * <p>The bitmap is rotated according to the target rotation set to the {@link ImageCapture}
1929          * to make it upright. If target rotation is not set, the display rotation is used.
1930          *
1931          * <p>See also {@link ImageCapture.Builder#setTargetRotation(int)} and
1932          * {@link #setTargetRotation(int)}.
1933          *
1934          * @param bitmap the postview bitmap.
1935 
1936          */
onPostviewBitmapAvailable(@onNull Bitmap bitmap)1937         public void onPostviewBitmapAvailable(@NonNull Bitmap bitmap) {
1938         }
1939     }
1940 
1941     /**
1942      * Callback listener for discovering when the application has completed its changes for a
1943      * screen flash image capture.
1944      *
1945      * <p> For example, an application may change its UI to a full white screen with maximum
1946      * brightness for a proper screen flash capture.
1947      *
1948      * @see ScreenFlash#apply(long, ScreenFlashListener)
1949      */
1950     public interface ScreenFlashListener {
1951         /**
1952          * Invoked by the application when it has completed its changes due to a screen flash
1953          * image capture.
1954          *
1955          * @see ScreenFlash#apply
1956          */
onCompleted()1957         void onCompleted();
1958     }
1959 
1960     /**
1961      * Interface to do the application changes required for screen flash operations.
1962      *
1963      * <p> Each {@link #apply} invocation will be followed up with a corresponding {@link #clear}
1964      * invocation. For each image capture, {@code #apply} and {@code #clear} will be invoked only
1965      * once.
1966      */
1967     public interface ScreenFlash {
1968         /**
1969          * Applies the necessary application changes for a screen flash photo capture.
1970          *
1971          * <p>When the application UI needs to be changed for a successful photo capture with
1972          * screen flash feature, CameraX will invoke this method and wait for the application to
1973          * complete its changes. When this API is invoked, the application UI should utilize the
1974          * screen to provide extra light as an alternative to physical flash. For example, the
1975          * screen brightness can be maximized and screen color can be covered with some bright
1976          * color like white.
1977          *
1978          * <p>The parameter {@code expirationTimeMillis} is based on
1979          * {@link System#currentTimeMillis()}. It is at least 3 seconds later from the start of a
1980          * screen flash image capture operation. Until the timestamp of {@code expirationTimeMillis}
1981          * parameter, CameraX will wait for the application to notify the completion of the
1982          * application-side changes using the {@link ScreenFlashListener} parameter of this
1983          * method. Applications must call {@link ScreenFlashListener#onCompleted()} after their
1984          * UI changes are done so that CameraX is not unnecessarily waiting. If the application
1985          * does not call {@code ScreenFlashListener#onCompleted} before {@code
1986          * expirationTimeMillis}, CameraX will stop waiting and move forward with the subsequent
1987          * operations regardless. In such case, the application no longer needs to call {@code
1988          * ScreenFlashListener#onCompleted()}. If {@link #clear} has also been invoked while the
1989          * application is still doing the changes, it is the application's responsibility to
1990          * clear any UI change done after {@link #clear} has been invoked.
1991          *
1992          * <p>The following code snippet shows an example implementation of this API.
1993          * <pre>{@code
1994          * @Override
1995          * public void apply(long expirationTimeMillis,
1996          *         @NonNull ScreenFlashListener screenFlashListener) {
1997          *     // Enable top overlay to make screen color white
1998          *     whiteColorOverlay.setVisible(true);
1999          *     // Maximize screen brightness
2000          *     maximizeScreenBrightness();
2001          *     screenFlashListener.onCompleted();
2002          * }}</pre>
2003          *
2004          * @param expirationTimeMillis The timestamp after which CameraX will no longer listen
2005          *                             to {@code screenFlashListener}.
2006          * @param screenFlashListener  Used to notify when UI changes have been applied.
2007          */
2008         // ExecutorRegistration lint suppressed since this is called by app and CameraX supports
2009         // receiving the call on any thread. Adding executor will make it harder for apps.
2010         @SuppressWarnings("ExecutorRegistration")
2011         @UiThread
apply(long expirationTimeMillis, @NonNull ScreenFlashListener screenFlashListener)2012         void apply(long expirationTimeMillis, @NonNull ScreenFlashListener screenFlashListener);
2013 
2014         /**
2015          * Clears any application change done for screen flash operation, if required.
2016          *
2017          * <p>CameraX will invoke this method when a screen flash photo capture has been completed
2018          * and the application screen can be safely changed to a state not conforming to screen
2019          * flash photo capture.
2020          */
2021         @UiThread
clear()2022         void clear();
2023     }
2024 
2025     /**
2026      * Provides a base static default configuration for the ImageCapture
2027      *
2028      * <p>These values may be overridden by the implementation. They only provide a minimum set of
2029      * defaults that are implementation independent.
2030      */
2031     @RestrictTo(Scope.LIBRARY_GROUP)
2032     public static final class Defaults
2033             implements ConfigProvider<ImageCaptureConfig> {
2034         private static final int DEFAULT_SURFACE_OCCUPANCY_PRIORITY = 4;
2035         private static final int DEFAULT_ASPECT_RATIO = AspectRatio.RATIO_4_3;
2036         private static final int DEFAULT_OUTPUT_FORMAT = OUTPUT_FORMAT_JPEG;
2037 
2038         private static final ResolutionSelector DEFAULT_RESOLUTION_SELECTOR =
2039                 new ResolutionSelector.Builder().setAspectRatioStrategy(
2040                         AspectRatioStrategy.RATIO_4_3_FALLBACK_AUTO_STRATEGY).setResolutionStrategy(
2041                         ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY).build();
2042 
2043         private static final ImageCaptureConfig DEFAULT_CONFIG;
2044         // ImageCapture does not yet support HDR so we must default to SDR. This ensures it won't
2045         // choose an HDR format when other use cases have selected HDR.
2046         private static final DynamicRange DEFAULT_DYNAMIC_RANGE = DynamicRange.SDR;
2047 
2048         static {
2049             Builder builder = new Builder()
2050                     .setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY)
2051                     .setTargetAspectRatio(DEFAULT_ASPECT_RATIO)
2052                     .setResolutionSelector(DEFAULT_RESOLUTION_SELECTOR)
2053                     .setOutputFormat(DEFAULT_OUTPUT_FORMAT)
2054                     .setDynamicRange(DEFAULT_DYNAMIC_RANGE);
2055 
2056             DEFAULT_CONFIG = builder.getUseCaseConfig();
2057         }
2058 
2059         @Override
getConfig()2060         public @NonNull ImageCaptureConfig getConfig() {
2061             return DEFAULT_CONFIG;
2062         }
2063     }
2064 
2065     /**
2066      * Options for saving newly captured image.
2067      *
2068      * <p> this class is used to configure save location and metadata. Save location can be
2069      * either a {@link File}, {@link MediaStore} or a {@link OutputStream}. The metadata will be
2070      * stored with the saved image. For JPEG this will be included in the EXIF.
2071      */
2072     public static final class OutputFileOptions {
2073 
2074         private final @Nullable File mFile;
2075         private final @Nullable ContentResolver mContentResolver;
2076         private final @Nullable Uri mSaveCollection;
2077         private final @Nullable ContentValues mContentValues;
2078         private final @Nullable OutputStream mOutputStream;
2079         private final @NonNull Metadata mMetadata;
2080 
OutputFileOptions(@ullable File file, @Nullable ContentResolver contentResolver, @Nullable Uri saveCollection, @Nullable ContentValues contentValues, @Nullable OutputStream outputStream, @Nullable Metadata metadata)2081         OutputFileOptions(@Nullable File file,
2082                 @Nullable ContentResolver contentResolver,
2083                 @Nullable Uri saveCollection,
2084                 @Nullable ContentValues contentValues,
2085                 @Nullable OutputStream outputStream,
2086                 @Nullable Metadata metadata) {
2087             mFile = file;
2088             mContentResolver = contentResolver;
2089             mSaveCollection = saveCollection;
2090             mContentValues = contentValues;
2091             mOutputStream = outputStream;
2092             mMetadata = metadata == null ? new Metadata() : metadata;
2093         }
2094 
2095         /**
2096          *
2097          */
2098         @RestrictTo(Scope.LIBRARY_GROUP)
getFile()2099         public @Nullable File getFile() {
2100             return mFile;
2101         }
2102 
2103         /**
2104          *
2105          */
2106         @RestrictTo(Scope.LIBRARY_GROUP)
getContentResolver()2107         public @Nullable ContentResolver getContentResolver() {
2108             return mContentResolver;
2109         }
2110 
2111         /**
2112          *
2113          */
2114         @RestrictTo(Scope.LIBRARY_GROUP)
getSaveCollection()2115         public @Nullable Uri getSaveCollection() {
2116             return mSaveCollection;
2117         }
2118 
2119         /**
2120          *
2121          */
2122         @RestrictTo(Scope.LIBRARY_GROUP)
getContentValues()2123         public @Nullable ContentValues getContentValues() {
2124             return mContentValues;
2125         }
2126 
2127         /**
2128          *
2129          */
2130         @RestrictTo(Scope.LIBRARY_GROUP)
getOutputStream()2131         public @Nullable OutputStream getOutputStream() {
2132             return mOutputStream;
2133         }
2134 
2135         /**
2136          * Exposed internally so that CameraController can overwrite the flip horizontal flag for
2137          * front camera. External core API users shouldn't need this because they are the ones who
2138          * created the {@link Metadata}.
2139          */
2140         @RestrictTo(Scope.LIBRARY_GROUP)
getMetadata()2141         public @NonNull Metadata getMetadata() {
2142             return mMetadata;
2143         }
2144 
2145         @Override
toString()2146         public @NonNull String toString() {
2147             return "OutputFileOptions{"
2148                     + "mFile=" + mFile + ", "
2149                     + "mContentResolver=" + mContentResolver + ", "
2150                     + "mSaveCollection=" + mSaveCollection + ", "
2151                     + "mContentValues=" + mContentValues + ", "
2152                     + "mOutputStream=" + mOutputStream + ", "
2153                     + "mMetadata=" + mMetadata
2154                     + "}";
2155         }
2156 
2157         /**
2158          * Builder class for {@link OutputFileOptions}.
2159          */
2160         public static final class Builder {
2161             private @Nullable File mFile;
2162             private @Nullable ContentResolver mContentResolver;
2163             private @Nullable Uri mSaveCollection;
2164             private @Nullable ContentValues mContentValues;
2165             private @Nullable OutputStream mOutputStream;
2166             private @Nullable Metadata mMetadata;
2167 
2168             /**
2169              * Creates options to write captured image to a {@link File}.
2170              *
2171              * @param file save location of the image.
2172              */
Builder(@onNull File file)2173             public Builder(@NonNull File file) {
2174                 mFile = file;
2175             }
2176 
2177             /**
2178              * Creates options to write captured image to {@link MediaStore}.
2179              *
2180              * Example:
2181              *
2182              * <pre>{@code
2183              *
2184              * ContentValues contentValues = new ContentValues();
2185              * contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "NEW_IMAGE");
2186              * contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
2187              *
2188              * ImageCapture.OutputFileOptions options = new ImageCapture.OutputFileOptions.Builder(
2189              *         getContentResolver(),
2190              *         MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
2191              *         contentValues).build();
2192              *
2193              * }</pre>
2194              *
2195              * @param contentResolver to access {@link MediaStore}
2196              * @param saveCollection  The URL of the table to insert into.
2197              * @param contentValues   to be included in the created image file.
2198              */
Builder(@onNull ContentResolver contentResolver, @NonNull Uri saveCollection, @NonNull ContentValues contentValues)2199             public Builder(@NonNull ContentResolver contentResolver,
2200                     @NonNull Uri saveCollection,
2201                     @NonNull ContentValues contentValues) {
2202                 mContentResolver = contentResolver;
2203                 mSaveCollection = saveCollection;
2204                 mContentValues = contentValues;
2205             }
2206 
2207             /**
2208              * Creates options that write captured image to a {@link OutputStream}.
2209              *
2210              * @param outputStream save location of the image.
2211              */
Builder(@onNull OutputStream outputStream)2212             public Builder(@NonNull OutputStream outputStream) {
2213                 mOutputStream = outputStream;
2214             }
2215 
2216             /**
2217              * Sets the metadata to be stored with the saved image.
2218              *
2219              * <p> For JPEG this will be included in the EXIF.
2220              *
2221              * @param metadata Metadata to be stored with the saved image. For JPEG this will
2222              *                 be included in the EXIF.
2223              */
setMetadata(@onNull Metadata metadata)2224             public @NonNull Builder setMetadata(@NonNull Metadata metadata) {
2225                 mMetadata = metadata;
2226                 return this;
2227             }
2228 
2229             /**
2230              * Builds {@link OutputFileOptions}.
2231              */
build()2232             public @NonNull OutputFileOptions build() {
2233                 return new OutputFileOptions(mFile, mContentResolver, mSaveCollection,
2234                         mContentValues, mOutputStream, mMetadata);
2235             }
2236         }
2237     }
2238 
2239     /**
2240      * Info about the saved image file.
2241      */
2242     public static class OutputFileResults {
2243         private final @Nullable Uri mSavedUri;
2244 
2245         private final int mImageFormat;
2246 
2247         @RestrictTo(Scope.LIBRARY_GROUP)
OutputFileResults(@ullable Uri savedUri)2248         public OutputFileResults(@Nullable Uri savedUri) {
2249             this(savedUri, JPEG);
2250         }
2251 
2252         @RestrictTo(Scope.LIBRARY_GROUP)
OutputFileResults(@ullable Uri savedUri, int imageFormat)2253         public OutputFileResults(@Nullable Uri savedUri, int imageFormat) {
2254             mSavedUri = savedUri;
2255             mImageFormat = imageFormat;
2256         }
2257 
2258         /**
2259          * Returns the {@link Uri} of the saved file.
2260          *
2261          * <p> Returns null if the {@link OutputFileOptions} is constructed with
2262          * {@link androidx.camera.core.ImageCapture.OutputFileOptions.Builder
2263          * #Builder(OutputStream)}.
2264          */
getSavedUri()2265         public @Nullable Uri getSavedUri() {
2266             return mSavedUri;
2267         }
2268 
2269         /**
2270          * Returns the {@link ImageFormat} of the saved file.
2271          */
getImageFormat()2272         public int getImageFormat() {
2273             return mImageFormat;
2274         }
2275     }
2276 
2277     /** Holder class for metadata that will be saved with captured images. */
2278     public static final class Metadata {
2279         /**
2280          * Indicates a left-right mirroring (reflection).
2281          *
2282          * <p>The reflection is meant to be applied to the upright image (after rotation to the
2283          * target orientation). When saving the image to file, it is combined with the rotation
2284          * degrees, to generate the corresponding EXIF orientation value.
2285          */
2286         private boolean mIsReversedHorizontal;
2287 
2288         /**
2289          * Whether the mIsReversedHorizontal has been set by the app explicitly.
2290          */
2291         private boolean mIsReversedHorizontalSet = false;
2292 
2293         /**
2294          * Indicates an upside down mirroring, equivalent to a horizontal mirroring (reflection)
2295          * followed by a 180 degree rotation.
2296          *
2297          * <p>The reflection is meant to be applied to the upright image (after rotation to the
2298          * target orientation). When saving the image to file, it is combined with the rotation
2299          * degrees, to generate the corresponding EXIF orientation value.
2300          */
2301         private boolean mIsReversedVertical;
2302         /** Data representing a geographic location. */
2303         private @Nullable Location mLocation;
2304 
2305         /**
2306          * Gets left-right mirroring of the capture.
2307          *
2308          * @return true if the capture is left-right mirrored.
2309          */
isReversedHorizontal()2310         public boolean isReversedHorizontal() {
2311             return mIsReversedHorizontal;
2312         }
2313 
2314         /**
2315          * Returns true if {@link #setReversedHorizontal} has been called.
2316          *
2317          * <p> CameraController's default behavior is mirroring the picture when front camera is
2318          * used. This method is used to check if reverseHorizontal is set explicitly by the app.
2319          */
2320         @RestrictTo(Scope.LIBRARY_GROUP)
isReversedHorizontalSet()2321         public boolean isReversedHorizontalSet() {
2322             return mIsReversedHorizontalSet;
2323         }
2324 
2325         /**
2326          * Sets left-right mirroring of the capture.
2327          *
2328          * @param isReversedHorizontal true if the capture is left-right mirrored.
2329          */
setReversedHorizontal(boolean isReversedHorizontal)2330         public void setReversedHorizontal(boolean isReversedHorizontal) {
2331             mIsReversedHorizontal = isReversedHorizontal;
2332             mIsReversedHorizontalSet = true;
2333         }
2334 
2335         /**
2336          * Gets upside-down mirroring of the capture.
2337          *
2338          * @return true if the capture is upside-down.
2339          */
isReversedVertical()2340         public boolean isReversedVertical() {
2341             return mIsReversedVertical;
2342         }
2343 
2344         /**
2345          * Sets upside-down mirroring of the capture.
2346          *
2347          * @param isReversedVertical true if the capture is upside-down.
2348          */
setReversedVertical(boolean isReversedVertical)2349         public void setReversedVertical(boolean isReversedVertical) {
2350             mIsReversedVertical = isReversedVertical;
2351         }
2352 
2353         /**
2354          * Gets the geographic location of the capture.
2355          *
2356          * @return the geographic location.
2357          */
getLocation()2358         public @Nullable Location getLocation() {
2359             return mLocation;
2360         }
2361 
2362         /**
2363          * Sets the geographic location of the capture.
2364          *
2365          * @param location the geographic location.
2366          */
setLocation(@ullable Location location)2367         public void setLocation(@Nullable Location location) {
2368             mLocation = location;
2369         }
2370 
2371         @Override
toString()2372         public @NonNull String toString() {
2373             return "Metadata{"
2374                     + "mIsReversedHorizontal=" + mIsReversedHorizontal + ", "
2375                     + "mIsReversedVertical=" + mIsReversedVertical + ", "
2376                     + "mLocation=" + mLocation
2377                     + "}";
2378         }
2379     }
2380 
2381     /** Builder for an {@link ImageCapture}. */
2382     @SuppressWarnings({"ObjectToString", "unused", "HiddenSuperclass"})
2383     public static final class Builder implements
2384             UseCaseConfig.Builder<ImageCapture, ImageCaptureConfig, Builder>,
2385             ImageOutputConfig.Builder<Builder>,
2386             IoConfig.Builder<Builder>,
2387             ImageInputConfig.Builder<Builder> {
2388 
2389         private final MutableOptionsBundle mMutableConfig;
2390 
2391         /** Creates a new Builder object. */
Builder()2392         public Builder() {
2393             this(MutableOptionsBundle.create());
2394         }
2395 
Builder(MutableOptionsBundle mutableConfig)2396         private Builder(MutableOptionsBundle mutableConfig) {
2397             mMutableConfig = mutableConfig;
2398 
2399             Class<?> oldConfigClass =
2400                     mutableConfig.retrieveOption(TargetConfig.OPTION_TARGET_CLASS, null);
2401             if (oldConfigClass != null && !oldConfigClass.equals(ImageCapture.class)) {
2402                 throw new IllegalArgumentException(
2403                         "Invalid target class configuration for "
2404                                 + Builder.this
2405                                 + ": "
2406                                 + oldConfigClass);
2407             }
2408 
2409             setCaptureType(UseCaseConfigFactory.CaptureType.IMAGE_CAPTURE);
2410             setTargetClass(ImageCapture.class);
2411         }
2412 
2413         /**
2414          * Generates a Builder from another Config object
2415          *
2416          * @param configuration An immutable configuration to pre-populate this builder.
2417          * @return The new Builder.
2418          */
2419         @RestrictTo(Scope.LIBRARY_GROUP)
fromConfig(@onNull Config configuration)2420         public static @NonNull Builder fromConfig(@NonNull Config configuration) {
2421             return new Builder(MutableOptionsBundle.from(configuration));
2422         }
2423 
2424         /**
2425          * Generates a Builder from another Config object
2426          *
2427          * @param configuration An immutable configuration to pre-populate this builder.
2428          * @return The new Builder.
2429          */
2430         @RestrictTo(Scope.LIBRARY_GROUP)
fromConfig(@onNull ImageCaptureConfig configuration)2431         static @NonNull Builder fromConfig(@NonNull ImageCaptureConfig configuration) {
2432             return new Builder(MutableOptionsBundle.from(configuration));
2433         }
2434 
2435         /**
2436          * {@inheritDoc}
2437          */
2438         @RestrictTo(Scope.LIBRARY_GROUP)
2439         @Override
getMutableConfig()2440         public @NonNull MutableConfig getMutableConfig() {
2441             return mMutableConfig;
2442         }
2443 
2444         /**
2445          * {@inheritDoc}
2446          */
2447         @RestrictTo(Scope.LIBRARY_GROUP)
2448         @Override
getUseCaseConfig()2449         public @NonNull ImageCaptureConfig getUseCaseConfig() {
2450             return new ImageCaptureConfig(OptionsBundle.from(mMutableConfig));
2451         }
2452 
2453         /**
2454          * Builds an immutable {@link ImageCapture} from the current state.
2455          *
2456          * @return A {@link ImageCapture} populated with the current state.
2457          * @throws IllegalArgumentException if attempting to set both target aspect ratio and
2458          *                                  target resolution, or attempting to set
2459          *                                  {@link ImageCapture#FLASH_MODE_SCREEN} without
2460          *                                  setting a non-null {@link ScreenFlash} instance.
2461          */
2462         @Override
build()2463         public @NonNull ImageCapture build() {
2464             // Update the input format base on the other options set (mainly whether processing
2465             // is done)
2466             Integer bufferFormat = getMutableConfig().retrieveOption(OPTION_BUFFER_FORMAT, null);
2467             if (bufferFormat != null) {
2468                 getMutableConfig().insertOption(OPTION_INPUT_FORMAT, bufferFormat);
2469             } else {
2470                 if (isOutputFormatRaw(getMutableConfig())) {
2471                     getMutableConfig().insertOption(OPTION_INPUT_FORMAT, RAW_SENSOR);
2472                 } else if (isOutputFormatRawJpeg(getMutableConfig())) {
2473                     getMutableConfig().insertOption(OPTION_INPUT_FORMAT, RAW_SENSOR);
2474                     getMutableConfig().insertOption(OPTION_SECONDARY_INPUT_FORMAT, JPEG);
2475                 } else if (isOutputFormatUltraHdr(getMutableConfig())) {
2476                     getMutableConfig().insertOption(OPTION_INPUT_FORMAT, JPEG_R);
2477                     getMutableConfig().insertOption(OPTION_INPUT_DYNAMIC_RANGE,
2478                             DynamicRange.UNSPECIFIED);
2479                 } else {
2480                     getMutableConfig().insertOption(OPTION_INPUT_FORMAT, JPEG);
2481                 }
2482             }
2483 
2484             ImageCaptureConfig imageCaptureConfig = getUseCaseConfig();
2485             ImageOutputConfig.validateConfig(imageCaptureConfig);
2486             ImageCapture imageCapture = new ImageCapture(imageCaptureConfig);
2487 
2488             // Makes the crop aspect ratio match the target resolution setting as what mentioned
2489             // in javadoc of setTargetResolution(). When the target resolution is set, {@link
2490             // ImageCapture#setCropAspectRatio(Rational)} will be automatically called to set
2491             // corresponding value.
2492             Size targetResolution = getMutableConfig().retrieveOption(OPTION_TARGET_RESOLUTION,
2493                     null);
2494             if (targetResolution != null) {
2495                 imageCapture.setCropAspectRatio(new Rational(targetResolution.getWidth(),
2496                         targetResolution.getHeight()));
2497             }
2498 
2499             checkNotNull(getMutableConfig().retrieveOption(OPTION_IO_EXECUTOR,
2500                     CameraXExecutors.ioExecutor()), "The IO executor can't be null");
2501 
2502             if (getMutableConfig().containsOption(OPTION_FLASH_MODE)) {
2503                 Integer flashMode = getMutableConfig().retrieveOption(OPTION_FLASH_MODE);
2504 
2505                 if (flashMode == null || (flashMode != FLASH_MODE_AUTO && flashMode != FLASH_MODE_ON
2506                         && flashMode != FLASH_MODE_SCREEN && flashMode != FLASH_MODE_OFF)) {
2507                     throw new IllegalArgumentException(
2508                             "The flash mode is not allowed to set: " + flashMode);
2509                 }
2510 
2511                 if (flashMode == FLASH_MODE_SCREEN) {
2512                     if (getMutableConfig().retrieveOption(OPTION_SCREEN_FLASH, null)
2513                             == null) {
2514                         throw new IllegalArgumentException(
2515                                 "The flash mode is not allowed to set to FLASH_MODE_SCREEN "
2516                                         + "without setting ScreenFlash");
2517                     }
2518                 }
2519             }
2520 
2521             return imageCapture;
2522         }
2523 
2524         /**
2525          * Sets the image capture mode.
2526          *
2527          * <p>Valid capture modes are {@link CaptureMode#CAPTURE_MODE_MINIMIZE_LATENCY}, which
2528          * prioritizes
2529          * latency over image quality, or {@link CaptureMode#CAPTURE_MODE_MAXIMIZE_QUALITY},
2530          * which prioritizes
2531          * image quality over latency.
2532          *
2533          * <p>If not set, the capture mode will default to
2534          * {@link CaptureMode#CAPTURE_MODE_MINIMIZE_LATENCY}.
2535          *
2536          * @param captureMode The requested image capture mode.
2537          * @return The current Builder.
2538          */
setCaptureMode(@aptureMode int captureMode)2539         public @NonNull Builder setCaptureMode(@CaptureMode int captureMode) {
2540             getMutableConfig().insertOption(OPTION_IMAGE_CAPTURE_MODE, captureMode);
2541             return this;
2542         }
2543 
2544         /**
2545          * Sets the flashMode.
2546          *
2547          * <p>If not set, the flash mode will default to {@link #FLASH_MODE_OFF}.
2548          *
2549          * <p>If set to {@link #FLASH_MODE_SCREEN}, a non-null {@link ScreenFlash} instance must
2550          * also be set with {@link #setScreenFlash(ScreenFlash)}. Otherwise, an
2551          * {@link IllegalArgumentException} will be thrown when {@link #build()} is invoked.
2552          *
2553          * <p>See {@link ImageCapture#setFlashMode(int)} for more information.
2554          *
2555          * @param flashMode The requested flash mode. Value is {@link #FLASH_MODE_AUTO},
2556          *                  {@link #FLASH_MODE_ON}, {@link #FLASH_MODE_SCREEN}, or
2557          *                  {@link #FLASH_MODE_OFF}.
2558          * @return The current Builder.
2559          */
setFlashMode(@lashMode int flashMode)2560         public @NonNull Builder setFlashMode(@FlashMode int flashMode) {
2561             getMutableConfig().insertOption(OPTION_FLASH_MODE, flashMode);
2562             return this;
2563         }
2564 
2565         /**
2566          * Sets the {@link ScreenFlash} instance necessary for screen flash operations.
2567          *
2568          * <p>If not set, the instance will be set to null and users will need to set it later
2569          * before calling {@link #setFlashMode(int)} with {@link #FLASH_MODE_SCREEN}.
2570          *
2571          * <p>See {@link ImageCapture#setScreenFlash(ScreenFlash)} for more
2572          * information.
2573          *
2574          * @param screenFlash The {@link ScreenFlash} to notify caller for the
2575          *                             UI side changes required for photo capture with
2576          *                             {@link #FLASH_MODE_SCREEN}.
2577          * @return The current Builder.
2578          */
setScreenFlash(@onNull ScreenFlash screenFlash)2579         public @NonNull Builder setScreenFlash(@NonNull ScreenFlash screenFlash) {
2580             getMutableConfig().insertOption(OPTION_SCREEN_FLASH, screenFlash);
2581             return this;
2582         }
2583 
2584         /**
2585          * Sets the {@link ImageFormat} of the {@link ImageProxy} returned by the
2586          * {@link ImageCapture.OnImageCapturedCallback}.
2587          *
2588          * <p>Warning. This could lead to an invalid configuration as image format support is per
2589          * device. Also, setting the buffer format in conjuncture with image capture extensions will
2590          * result in an invalid configuration. In this case {@link
2591          * ImageCapture#ImageCapture(ImageCaptureConfig)} will throw an
2592          * {@link IllegalArgumentException}.
2593          *
2594          * @param bufferImageFormat The image format for captured images.
2595          * @return The current Builder.
2596          */
2597         @RestrictTo(Scope.LIBRARY_GROUP)
setBufferFormat(int bufferImageFormat)2598         public @NonNull Builder setBufferFormat(int bufferImageFormat) {
2599             getMutableConfig().insertOption(OPTION_BUFFER_FORMAT, bufferImageFormat);
2600             return this;
2601         }
2602 
2603         @RestrictTo(Scope.LIBRARY_GROUP)
2604         @Override
setSupportedResolutions( @onNull List<Pair<Integer, Size[]>> resolutions)2605         public @NonNull Builder setSupportedResolutions(
2606                 @NonNull List<Pair<Integer, Size[]>> resolutions) {
2607             getMutableConfig().insertOption(OPTION_SUPPORTED_RESOLUTIONS, resolutions);
2608             return this;
2609         }
2610 
2611         @RestrictTo(Scope.LIBRARY_GROUP)
2612         @Override
setCustomOrderedResolutions(@onNull List<Size> resolutions)2613         public @NonNull Builder setCustomOrderedResolutions(@NonNull List<Size> resolutions) {
2614             getMutableConfig().insertOption(OPTION_CUSTOM_ORDERED_RESOLUTIONS, resolutions);
2615             return this;
2616         }
2617 
2618         // Implementations of TargetConfig.Builder default methods
2619 
2620         @RestrictTo(Scope.LIBRARY_GROUP)
2621         @Override
setTargetClass(@onNull Class<ImageCapture> targetClass)2622         public @NonNull Builder setTargetClass(@NonNull Class<ImageCapture> targetClass) {
2623             getMutableConfig().insertOption(OPTION_TARGET_CLASS, targetClass);
2624 
2625             // If no name is set yet, then generate a unique name
2626             if (null == getMutableConfig().retrieveOption(OPTION_TARGET_NAME, null)) {
2627                 String targetName = targetClass.getCanonicalName() + "-" + UUID.randomUUID();
2628                 setTargetName(targetName);
2629             }
2630 
2631             return this;
2632         }
2633 
2634         /**
2635          * Sets the name of the target object being configured, used only for debug logging.
2636          *
2637          * <p>The name should be a value that can uniquely identify an instance of the object being
2638          * configured.
2639          *
2640          * <p>If not set, the target name will default to a unique name automatically generated
2641          * with the class canonical name and random UUID.
2642          *
2643          * @param targetName A unique string identifier for the instance of the class being
2644          *                   configured.
2645          * @return the current Builder.
2646          */
2647         @Override
setTargetName(@onNull String targetName)2648         public @NonNull Builder setTargetName(@NonNull String targetName) {
2649             getMutableConfig().insertOption(OPTION_TARGET_NAME, targetName);
2650             return this;
2651         }
2652 
2653         // Implementations of ImageOutputConfig.Builder default methods
2654 
2655         /**
2656          * Sets the aspect ratio of the intended target for images from this configuration.
2657          *
2658          * <p>The aspect ratio is the ratio of width to height in the sensor orientation.
2659          *
2660          * <p>It is not allowed to set both target aspect ratio and target resolution on the same
2661          * use case. Attempting so will throw an IllegalArgumentException when building the Config.
2662          *
2663          * <p>The target aspect ratio is used as a hint when determining the resulting output aspect
2664          * ratio which may differ from the request, possibly due to device constraints.
2665          * Application code should check the resulting output's resolution and the resulting
2666          * aspect ratio may not be exactly as requested.
2667          *
2668          * <p>If not set, or {@link AspectRatio#RATIO_DEFAULT} is supplied, resolutions with
2669          * aspect ratio 4:3 will be considered in higher priority.
2670          *
2671          * @param aspectRatio The desired ImageCapture {@link AspectRatio}
2672          * @return The current Builder.
2673          * @deprecated use {@link ResolutionSelector} with {@link AspectRatioStrategy} to specify
2674          * the preferred aspect ratio settings instead.
2675          */
2676         @Override
2677         @Deprecated
setTargetAspectRatio(@spectRatio.Ratio int aspectRatio)2678         public @NonNull Builder setTargetAspectRatio(@AspectRatio.Ratio int aspectRatio) {
2679             if (aspectRatio == AspectRatio.RATIO_DEFAULT) {
2680                 aspectRatio = Defaults.DEFAULT_ASPECT_RATIO;
2681             }
2682             getMutableConfig().insertOption(OPTION_TARGET_ASPECT_RATIO, aspectRatio);
2683             return this;
2684         }
2685 
2686         /**
2687          * Sets the rotation of the intended target for images from this configuration.
2688          *
2689          * <p>This will affect the EXIF rotation metadata in images saved by takePicture calls and
2690          * the {@link ImageInfo#getRotationDegrees()} value of the {@link ImageProxy} returned by
2691          * {@link OnImageCapturedCallback}. These will be set to be the rotation, which if
2692          * applied to the output image data, will make the image match the target rotation
2693          * specified here.
2694          *
2695          * <p>This is one of four valid values: {@link Surface#ROTATION_0}, {@link
2696          * Surface#ROTATION_90}, {@link Surface#ROTATION_180}, {@link Surface#ROTATION_270}.
2697          * Rotation values are relative to the "natural" rotation, {@link Surface#ROTATION_0}.
2698          *
2699          * <p>In general, it is best to additionally set the target rotation dynamically on the use
2700          * case. See {@link androidx.camera.core.ImageCapture#setTargetRotation(int)} for
2701          * additional documentation.
2702          *
2703          * <p>If not set, the target rotation will default to the value of
2704          * {@link android.view.Display#getRotation()} of the default display at the time the use
2705          * case is created. The use case is fully created once it has been attached to a camera.
2706          *
2707          * @param rotation The rotation of the intended target.
2708          * @return The current Builder.
2709          * @see androidx.camera.core.ImageCapture#setTargetRotation(int)
2710          * @see android.view.OrientationEventListener
2711          */
2712         @Override
setTargetRotation(@otationValue int rotation)2713         public @NonNull Builder setTargetRotation(@RotationValue int rotation) {
2714             getMutableConfig().insertOption(OPTION_TARGET_ROTATION, rotation);
2715             return this;
2716         }
2717 
2718         /**
2719          * setMirrorMode is not supported on ImageCapture.
2720          */
2721         @RestrictTo(Scope.LIBRARY_GROUP)
2722         @Override
setMirrorMode(@irrorMode.Mirror int mirrorMode)2723         public @NonNull Builder setMirrorMode(@MirrorMode.Mirror int mirrorMode) {
2724             throw new UnsupportedOperationException("setMirrorMode is not supported.");
2725         }
2726 
2727         /**
2728          * Sets the intended output target resolution.
2729          *
2730          * <p>The target resolution attempts to establish a minimum bound for the image resolution.
2731          * The actual image resolution will be the closest available resolution in size that is not
2732          * smaller than the target resolution, as determined by the Camera implementation. However,
2733          * if no resolution exists that is equal to or larger than the target resolution, the
2734          * nearest available resolution smaller than the target resolution will be chosen.
2735          * Resolutions with the same aspect ratio of the provided {@link Size} will be considered in
2736          * higher priority before resolutions of different aspect ratios.
2737          *
2738          * <p>It is not allowed to set both target aspect ratio and target resolution on the same
2739          * use case. Attempting so will throw an IllegalArgumentException when building the Config.
2740          *
2741          * <p>The resolution {@link Size} should be expressed in the coordinate frame after
2742          * rotating the supported sizes by the target rotation. For example, a device with
2743          * portrait natural orientation in natural target rotation requesting a portrait image
2744          * may specify 480x640, and the same device, rotated 90 degrees and targeting landscape
2745          * orientation may specify 640x480.
2746          *
2747          * <p>When the target resolution is set,
2748          * {@link ImageCapture#setCropAspectRatio(Rational)} will be automatically called to set
2749          * corresponding value. Such that the output image will be cropped into the desired
2750          * aspect ratio.
2751          *
2752          * <p>The maximum available resolution that could be selected for an {@link ImageCapture}
2753          * will depend on the camera device's capability.
2754          *
2755          * <p>If not set, the largest available resolution will be selected to use. Usually,
2756          * users will intend to get the largest still image that the camera device can support.
2757          *
2758          * <p>When using the <code>camera-camera2</code> CameraX implementation, which resolution
2759          * will be finally selected will depend on the camera device's hardware level and the
2760          * bound use cases combination. For more details see the guaranteed supported
2761          * configurations tables in {@link android.hardware.camera2.CameraDevice}'s
2762          * href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice
2763          * #regular-capture">Regular capture</a> section.
2764          *
2765          * @param resolution The target resolution to choose from supported output sizes list.
2766          * @return The current Builder.
2767          * @deprecated use {@link ResolutionSelector} with {@link ResolutionStrategy} to specify
2768          * the preferred resolution settings instead.
2769          */
2770         @Override
2771         @Deprecated
setTargetResolution(@onNull Size resolution)2772         public @NonNull Builder setTargetResolution(@NonNull Size resolution) {
2773             getMutableConfig().insertOption(OPTION_TARGET_RESOLUTION, resolution);
2774             return this;
2775         }
2776 
2777         /**
2778          * Sets the default resolution of the intended target from this configuration.
2779          *
2780          * @param resolution The default resolution to choose from supported output sizes list.
2781          * @return The current Builder.
2782          */
2783         @RestrictTo(Scope.LIBRARY_GROUP)
2784         @Override
setDefaultResolution(@onNull Size resolution)2785         public @NonNull Builder setDefaultResolution(@NonNull Size resolution) {
2786             getMutableConfig().insertOption(ImageOutputConfig.OPTION_DEFAULT_RESOLUTION,
2787                     resolution);
2788             return this;
2789         }
2790 
2791         @RestrictTo(Scope.LIBRARY_GROUP)
2792         @Override
setMaxResolution(@onNull Size resolution)2793         public @NonNull Builder setMaxResolution(@NonNull Size resolution) {
2794             getMutableConfig().insertOption(OPTION_MAX_RESOLUTION, resolution);
2795             return this;
2796         }
2797 
2798         /**
2799          * Sets the resolution selector to select the preferred supported resolution.
2800          *
2801          * <p>The default resolution strategy for ImageCapture is
2802          * {@link ResolutionStrategy#HIGHEST_AVAILABLE_STRATEGY}, which will select the largest
2803          * available resolution to use. Applications can override this default strategy with a
2804          * different resolution strategy.
2805          *
2806          * <p>The existing {@link #setTargetResolution(Size)} and
2807          * {@link #setTargetAspectRatio(int)} APIs are deprecated and are not compatible with
2808          * {@link #setResolutionSelector(ResolutionSelector)}. Calling either of these APIs
2809          * together with {@link #setResolutionSelector(ResolutionSelector)} will result in an
2810          * {@link IllegalArgumentException} being thrown when you attempt to build the
2811          * {@link ImageCapture} instance.
2812          *
2813          * @return The current Builder.
2814          */
2815         @Override
setResolutionSelector( @onNull ResolutionSelector resolutionSelector)2816         public @NonNull Builder setResolutionSelector(
2817                 @NonNull ResolutionSelector resolutionSelector) {
2818             getMutableConfig().insertOption(OPTION_RESOLUTION_SELECTOR, resolutionSelector);
2819             return this;
2820         }
2821 
2822         /**
2823          * Enables postview image generation. A postview image is a low-quality image
2824          * that's produced earlier during image capture than the final high-quality image,
2825          * and can be used as a thumbnail or placeholder until the final image is ready.
2826          *
2827          * <p>When the postview is available,
2828          * {@link OnImageCapturedCallback#onPostviewBitmapAvailable(Bitmap)} or
2829          * {@link OnImageSavedCallback#onPostviewBitmapAvailable(Bitmap)} will be called.
2830          *
2831          * <p>By default the largest available postview size that is smaller or equal to the
2832          * ImagaeCapture size will be used to configure the postview. The {@link ResolutionSelector}
2833          * can also be used to select a specific size via
2834          * {@link #setPostviewResolutionSelector(ResolutionSelector)}.
2835          *
2836          * <p>You can query the postview capability by invoking
2837          * {@link #getImageCaptureCapabilities(CameraInfo)}. If
2838          * {@link ImageCaptureCapabilities#isPostviewSupported()} returns false and you still
2839          * enable the postview, the postview image won't be generated.
2840          *
2841          * @param postviewEnabled whether postview is enabled or not
2842          * @return the current Builder.
2843          */
setPostviewEnabled(boolean postviewEnabled)2844         public @NonNull Builder setPostviewEnabled(boolean postviewEnabled) {
2845             getMutableConfig().insertOption(OPTION_POSTVIEW_ENABLED,
2846                     postviewEnabled);
2847             return this;
2848         }
2849 
2850         /**
2851          * Set the {@link ResolutionSelector} to select the postview size from the available
2852          * postview sizes. These available postview sizes are smaller or equal to the
2853          * ImageCapture size. You can implement the
2854          * {@link androidx.camera.core.resolutionselector.ResolutionFilter} and set it to the
2855          * {@link ResolutionSelector} to get the list of available sizes and determine which size
2856          * to use.
2857          *
2858          * <p>If no sizes can be selected using the given {@link ResolutionSelector}, it will throw
2859          * an {@link IllegalArgumentException} when {@code bindToLifecycle()} is invoked.
2860          *
2861          * @return the current Builder.
2862          */
setPostviewResolutionSelector( @onNull ResolutionSelector resolutionSelector)2863         public @NonNull Builder setPostviewResolutionSelector(
2864                 @NonNull ResolutionSelector resolutionSelector) {
2865             getMutableConfig().insertOption(OPTION_POSTVIEW_RESOLUTION_SELECTOR,
2866                     resolutionSelector);
2867             return this;
2868         }
2869 
2870         @RestrictTo(Scope.LIBRARY_GROUP)
setImageReaderProxyProvider( @onNull ImageReaderProxyProvider imageReaderProxyProvider)2871         public @NonNull Builder setImageReaderProxyProvider(
2872                 @NonNull ImageReaderProxyProvider imageReaderProxyProvider) {
2873             getMutableConfig().insertOption(OPTION_IMAGE_READER_PROXY_PROVIDER,
2874                     imageReaderProxyProvider);
2875             return this;
2876         }
2877 
2878         @RestrictTo(Scope.LIBRARY_GROUP)
setSoftwareJpegEncoderRequested(boolean requestSoftwareJpeg)2879         public @NonNull Builder setSoftwareJpegEncoderRequested(boolean requestSoftwareJpeg) {
2880             getMutableConfig().insertOption(OPTION_USE_SOFTWARE_JPEG_ENCODER,
2881                     requestSoftwareJpeg);
2882             return this;
2883         }
2884 
2885         /**
2886          * Sets the flashType.
2887          *
2888          * <p>If not set, the flash type will default to {@link #FLASH_TYPE_ONE_SHOT_FLASH}.
2889          *
2890          * @param flashType The requested flash mode. Value is {@link #FLASH_TYPE_ONE_SHOT_FLASH}
2891          *                  or {@link #FLASH_TYPE_USE_TORCH_AS_FLASH}.
2892          * @return The current Builder.
2893          */
2894         @RestrictTo(Scope.LIBRARY_GROUP)
setFlashType(@lashType int flashType)2895         public @NonNull Builder setFlashType(@FlashType int flashType) {
2896             getMutableConfig().insertOption(OPTION_FLASH_TYPE, flashType);
2897             return this;
2898         }
2899 
2900         /**
2901          * Sets the output JPEG image compression quality.
2902          *
2903          * <p>This is used for the {@link ImageProxy} which is returned by
2904          * {@link #takePicture(Executor, OnImageCapturedCallback)} or the output JPEG image which
2905          * is saved by {@link #takePicture(OutputFileOptions, Executor, OnImageSavedCallback)}.
2906          * The saved JPEG image might be cropped according to the {@link ViewPort} setting or
2907          * the crop aspect ratio set by {@link #setCropAspectRatio(Rational)}. The JPEG quality
2908          * setting will also be used to compress the cropped output image.
2909          *
2910          * <p>If not set, a default value will be used according to the capture mode setting.
2911          * JPEG compression quality 95 is used for {@link #CAPTURE_MODE_MINIMIZE_LATENCY} and 100
2912          * is used for {@link #CAPTURE_MODE_MAXIMIZE_QUALITY}.
2913          *
2914          * @param jpegQuality The requested output JPEG image compression quality. The value must
2915          *                    be in range [1..100] which larger is higher quality.
2916          * @return The current Builder.
2917          * @throws IllegalArgumentException if the input value is not in range [1..100].
2918          */
setJpegQuality(@ntRangefrom = 1, to = 100) int jpegQuality)2919         public @NonNull Builder setJpegQuality(@IntRange(from = 1, to = 100) int jpegQuality) {
2920             Preconditions.checkArgumentInRange(jpegQuality, 1, 100, "jpegQuality");
2921             getMutableConfig().insertOption(OPTION_JPEG_COMPRESSION_QUALITY,
2922                     jpegQuality);
2923             return this;
2924         }
2925 
2926         // Implementations of IoConfig.Builder default methods
2927 
2928         /**
2929          * Sets the default executor that will be used for IO tasks.
2930          *
2931          * <p> This executor will be used for any IO tasks specifically for ImageCapture, such as
2932          * {@link ImageCapture#takePicture(OutputFileOptions, Executor,
2933          * ImageCapture.OnImageSavedCallback)}. If no executor is set, then a default Executor
2934          * specifically for IO will be used instead.
2935          *
2936          * @param executor The executor which will be used for IO tasks.
2937          * @return the current Builder.
2938          */
2939         @Override
setIoExecutor(@onNull Executor executor)2940         public @NonNull Builder setIoExecutor(@NonNull Executor executor) {
2941             getMutableConfig().insertOption(OPTION_IO_EXECUTOR, executor);
2942             return this;
2943         }
2944 
2945         // Implementations of UseCaseConfig.Builder default methods
2946 
2947         @RestrictTo(Scope.LIBRARY_GROUP)
2948         @Override
setDefaultSessionConfig(@onNull SessionConfig sessionConfig)2949         public @NonNull Builder setDefaultSessionConfig(@NonNull SessionConfig sessionConfig) {
2950             getMutableConfig().insertOption(OPTION_DEFAULT_SESSION_CONFIG, sessionConfig);
2951             return this;
2952         }
2953 
2954         /**
2955          * Sets the output format of the captured image.
2956          *
2957          * <p>The supported output formats for capturing image depend on the capabilities of the
2958          * camera. The supported output formats of the camera can be queried using
2959          * {@link ImageCaptureCapabilities#getSupportedOutputFormats()}.
2960          *
2961          * <p>If not set, the output format will default to {@link #OUTPUT_FORMAT_JPEG}.
2962          *
2963          * <p>An {@link IllegalArgumentException} will be thrown when binding the UseCase if the
2964          * specified output format is not supported. Please note that the supported output formats
2965          * might be changed when Extensions is enabled.
2966          *
2967          * @param outputFormat The output image format. Value is {@link #OUTPUT_FORMAT_JPEG} or
2968          *                     {@link #OUTPUT_FORMAT_JPEG_ULTRA_HDR} or {@link #OUTPUT_FORMAT_RAW}.
2969          * @return The current Builder.
2970          *
2971          * @see OutputFormat
2972          * @see ImageCaptureCapabilities#getSupportedOutputFormats()
2973          */
setOutputFormat(@utputFormat int outputFormat)2974         public @NonNull Builder setOutputFormat(@OutputFormat int outputFormat) {
2975             getMutableConfig().insertOption(OPTION_OUTPUT_FORMAT, outputFormat);
2976             return this;
2977         }
2978 
2979         @RestrictTo(Scope.LIBRARY_GROUP)
2980         @Override
setDefaultCaptureConfig(@onNull CaptureConfig captureConfig)2981         public @NonNull Builder setDefaultCaptureConfig(@NonNull CaptureConfig captureConfig) {
2982             getMutableConfig().insertOption(OPTION_DEFAULT_CAPTURE_CONFIG, captureConfig);
2983             return this;
2984         }
2985 
2986         @RestrictTo(Scope.LIBRARY_GROUP)
2987         @Override
setSessionOptionUnpacker( SessionConfig.@onNull OptionUnpacker optionUnpacker)2988         public @NonNull Builder setSessionOptionUnpacker(
2989                 SessionConfig.@NonNull OptionUnpacker optionUnpacker) {
2990             getMutableConfig().insertOption(OPTION_SESSION_CONFIG_UNPACKER, optionUnpacker);
2991             return this;
2992         }
2993 
2994         @RestrictTo(Scope.LIBRARY_GROUP)
2995         @Override
setCaptureOptionUnpacker( CaptureConfig.@onNull OptionUnpacker optionUnpacker)2996         public @NonNull Builder setCaptureOptionUnpacker(
2997                 CaptureConfig.@NonNull OptionUnpacker optionUnpacker) {
2998             getMutableConfig().insertOption(OPTION_CAPTURE_CONFIG_UNPACKER, optionUnpacker);
2999             return this;
3000         }
3001 
3002         @RestrictTo(Scope.LIBRARY_GROUP)
3003         @Override
setSurfaceOccupancyPriority(int priority)3004         public @NonNull Builder setSurfaceOccupancyPriority(int priority) {
3005             getMutableConfig().insertOption(OPTION_SURFACE_OCCUPANCY_PRIORITY, priority);
3006             return this;
3007         }
3008 
3009         /**
3010          * {@inheritDoc}
3011          */
3012         @RestrictTo(Scope.LIBRARY_GROUP)
3013         @Override
setZslDisabled(boolean disabled)3014         public @NonNull Builder setZslDisabled(boolean disabled) {
3015             getMutableConfig().insertOption(OPTION_ZSL_DISABLED, disabled);
3016             return this;
3017         }
3018 
3019         /**
3020          * {@inheritDoc}
3021          */
3022         @RestrictTo(Scope.LIBRARY_GROUP)
3023         @Override
setHighResolutionDisabled(boolean disabled)3024         public @NonNull Builder setHighResolutionDisabled(boolean disabled) {
3025             getMutableConfig().insertOption(OPTION_HIGH_RESOLUTION_DISABLED, disabled);
3026             return this;
3027         }
3028 
3029         /**
3030          * {@inheritDoc}
3031          */
3032         @RestrictTo(Scope.LIBRARY_GROUP)
3033         @Override
setCaptureType( UseCaseConfigFactory.@onNull CaptureType captureType)3034         public @NonNull Builder setCaptureType(
3035                 UseCaseConfigFactory.@NonNull CaptureType captureType) {
3036             getMutableConfig().insertOption(OPTION_CAPTURE_TYPE, captureType);
3037             return this;
3038         }
3039 
3040         // Implementations of ImageInputConfig.Builder default methods
3041 
3042         /**
3043          * Sets the {@link DynamicRange}.
3044          *
3045          * <p>This is currently only exposed to internally set the dynamic range to SDR.
3046          *
3047          * @return The current Builder.
3048          * @see DynamicRange
3049          */
3050         @RestrictTo(Scope.LIBRARY)
3051         @Override
setDynamicRange(@onNull DynamicRange dynamicRange)3052         public @NonNull Builder setDynamicRange(@NonNull DynamicRange dynamicRange) {
3053             getMutableConfig().insertOption(OPTION_INPUT_DYNAMIC_RANGE, dynamicRange);
3054             return this;
3055         }
3056     }
3057 }
3058