1 /*
2  * Copyright 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.view;
18 
19 import static androidx.camera.core.impl.ImageOutputConfig.ROTATION_NOT_SPECIFIED;
20 import static androidx.camera.core.impl.utils.Threads.checkMainThread;
21 import static androidx.camera.core.impl.utils.TransformUtils.getNormalizedToBuffer;
22 import static androidx.core.content.ContextCompat.getMainExecutor;
23 
24 import android.annotation.SuppressLint;
25 import android.app.Activity;
26 import android.content.Context;
27 import android.content.res.TypedArray;
28 import android.graphics.Bitmap;
29 import android.graphics.Canvas;
30 import android.graphics.Matrix;
31 import android.graphics.Rect;
32 import android.hardware.camera2.CameraCharacteristics;
33 import android.hardware.display.DisplayManager;
34 import android.os.Build;
35 import android.os.Handler;
36 import android.os.Looper;
37 import android.util.AttributeSet;
38 import android.util.Rational;
39 import android.util.Size;
40 import android.view.Display;
41 import android.view.MotionEvent;
42 import android.view.Surface;
43 import android.view.SurfaceView;
44 import android.view.TextureView;
45 import android.view.View;
46 import android.view.ViewConfiguration;
47 import android.view.Window;
48 import android.widget.FrameLayout;
49 import android.widget.LinearLayout;
50 
51 import androidx.annotation.AnyThread;
52 import androidx.annotation.ColorInt;
53 import androidx.annotation.ColorRes;
54 import androidx.annotation.MainThread;
55 import androidx.annotation.OptIn;
56 import androidx.annotation.RestrictTo;
57 import androidx.annotation.UiThread;
58 import androidx.annotation.VisibleForTesting;
59 import androidx.camera.core.CameraControl;
60 import androidx.camera.core.CameraInfo;
61 import androidx.camera.core.CameraSelector;
62 import androidx.camera.core.FocusMeteringAction;
63 import androidx.camera.core.ImageAnalysis;
64 import androidx.camera.core.ImageCapture;
65 import androidx.camera.core.ImageInfo;
66 import androidx.camera.core.Logger;
67 import androidx.camera.core.MeteringPoint;
68 import androidx.camera.core.MeteringPointFactory;
69 import androidx.camera.core.Preview;
70 import androidx.camera.core.SurfaceRequest;
71 import androidx.camera.core.UseCase;
72 import androidx.camera.core.UseCaseGroup;
73 import androidx.camera.core.ViewPort;
74 import androidx.camera.core.impl.CameraInfoInternal;
75 import androidx.camera.core.impl.CameraInternal;
76 import androidx.camera.core.impl.ImageOutputConfig;
77 import androidx.camera.core.impl.utils.Threads;
78 import androidx.camera.view.impl.ZoomGestureDetector;
79 import androidx.camera.view.internal.ScreenFlashUiInfo;
80 import androidx.camera.view.internal.compat.quirk.DeviceQuirks;
81 import androidx.camera.view.internal.compat.quirk.SurfaceViewNotCroppedByParentQuirk;
82 import androidx.camera.view.internal.compat.quirk.SurfaceViewStretchedQuirk;
83 import androidx.camera.view.transform.CoordinateTransform;
84 import androidx.camera.view.transform.OutputTransform;
85 import androidx.core.content.ContextCompat;
86 import androidx.core.view.ViewCompat;
87 import androidx.fragment.app.Fragment;
88 import androidx.lifecycle.LifecycleOwner;
89 import androidx.lifecycle.LiveData;
90 import androidx.lifecycle.MutableLiveData;
91 
92 import org.jspecify.annotations.NonNull;
93 import org.jspecify.annotations.Nullable;
94 
95 import java.util.concurrent.Executor;
96 import java.util.concurrent.atomic.AtomicReference;
97 
98 /**
99  * Custom View that displays the camera feed for CameraX's {@link Preview} use case.
100  *
101  * <p> This class manages the preview {@link Surface}'s lifecycle. It internally uses either a
102  * {@link TextureView} or {@link SurfaceView} to display the camera feed, and applies required
103  * transformations on them to correctly display the preview, this involves correcting their
104  * aspect ratio, scale and rotation.
105  *
106  * <p> If {@link PreviewView} uses a {@link SurfaceView} to display the preview
107  * stream, be careful when overlapping a {@link View} that's initially not visible (either
108  * {@link View#INVISIBLE} or {@link View#GONE}) on top of it. When the {@link SurfaceView} is
109  * attached to the display window, it calls
110  * {@link android.view.ViewParent#requestTransparentRegion(View)} which requests a computation of
111  * the transparent regions on the display. At this point, the {@link View} isn't visible, causing
112  * the overlapped region between the {@link SurfaceView} and the {@link View} to be
113  * considered transparent. Later if the {@link View} becomes {@linkplain View#VISIBLE visible}, it
114  * will not be displayed on top of {@link SurfaceView}. A way around this is to call
115  * {@link android.view.ViewParent#requestTransparentRegion(View)} right after making the
116  * {@link View} visible, or initially hiding the {@link View} by setting its
117  * {@linkplain View#setAlpha(float) opacity} to 0, then setting it to 1.0F to show it.
118  *
119  * <p> There are some limitations of transition animations to {@link SurfaceView} and
120  * {@link TextureView}, which applies to {@link PreviewView} as well.
121  *
122  * @see <a href="https://developer.android.com/training/transitions#Limitations">Limitations</a>
123  */
124 public final class PreviewView extends FrameLayout {
125 
126     private static final String TAG = "PreviewView";
127 
128     @ColorRes
129     static final int DEFAULT_BACKGROUND_COLOR = android.R.color.black;
130     private static final ImplementationMode DEFAULT_IMPL_MODE = ImplementationMode.PERFORMANCE;
131 
132     // Synthetic access
133     @SuppressWarnings("WeakerAccess")
134     @NonNull ImplementationMode mImplementationMode = DEFAULT_IMPL_MODE;
135 
136     @VisibleForTesting
137     @Nullable PreviewViewImplementation mImplementation;
138 
139     final @NonNull ScreenFlashView mScreenFlashView;
140 
141     final @NonNull PreviewTransformation mPreviewTransform = new PreviewTransformation();
142     boolean mUseDisplayRotation = true;
143 
144     // Synthetic access
145     @SuppressWarnings("WeakerAccess")
146     final @NonNull MutableLiveData<StreamState> mPreviewStreamStateLiveData =
147             new MutableLiveData<>(StreamState.IDLE);
148 
149     // Synthetic access
150     @SuppressWarnings("WeakerAccess")
151     final @Nullable AtomicReference<PreviewStreamStateObserver> mActiveStreamStateObserver =
152             new AtomicReference<>();
153     // Synthetic access
154     @SuppressWarnings("WeakerAccess")
155     CameraController mCameraController;
156 
157     // Synthetic access
158     @SuppressWarnings("WeakerAccess")
159     @Nullable OnFrameUpdateListener mOnFrameUpdateListener;
160     // Synthetic access
161     @SuppressWarnings("WeakerAccess")
162     @Nullable Executor mOnFrameUpdateListenerExecutor;
163 
164     @NonNull PreviewViewMeteringPointFactory mPreviewViewMeteringPointFactory =
165             new PreviewViewMeteringPointFactory(mPreviewTransform);
166 
167     // Detector for zoom-to-scale.
168     private final @NonNull ZoomGestureDetector mZoomGestureDetector;
169 
170     // Synthetic access
171     @SuppressWarnings("WeakerAccess")
172     @Nullable CameraInfoInternal mCameraInfoInternal;
173 
174     private @Nullable MotionEvent mTouchUpEvent;
175 
176     private final @NonNull DisplayRotationListener mDisplayRotationListener =
177             new DisplayRotationListener();
178 
179     private final OnLayoutChangeListener mOnLayoutChangeListener =
180             (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
181                 boolean isSizeChanged =
182                         right - left != oldRight - oldLeft || bottom - top != oldBottom - oldTop;
183                 if (isSizeChanged) {
184                     redrawPreview();
185                     attachToControllerIfReady(true);
186                 }
187             };
188 
189     // Synthetic access
190     @SuppressWarnings("WeakerAccess")
191     final Preview.SurfaceProvider mSurfaceProvider = new Preview.SurfaceProvider() {
192 
193         @Override
194         @AnyThread
195         @SuppressWarnings("WrongThread") // View.getContext()
196         public void onSurfaceRequested(@NonNull SurfaceRequest surfaceRequest) {
197             if (!Threads.isMainThread()) {
198                 // Post on main thread to ensure thread safety.
199                 getMainExecutor(getContext()).execute(
200                         () -> mSurfaceProvider.onSurfaceRequested(surfaceRequest));
201                 return;
202             }
203             Logger.d(TAG, "Surface requested by Preview.");
204             CameraInternal camera = surfaceRequest.getCamera();
205             mCameraInfoInternal = camera.getCameraInfoInternal();
206             // PreviewViewMeteringPointFactory will convert the coordinates from previewView (x,y)
207             // to sensor coordinates and then to normalized coordinates. Thus sensor rect is needed.
208             mPreviewViewMeteringPointFactory.setSensorRect(
209                     camera.getCameraControlInternal().getSensorRect());
210             surfaceRequest.setTransformationInfoListener(
211                     getMainExecutor(getContext()),
212                     transformationInfo -> {
213                         Logger.d(TAG,
214                                 "Preview transformation info updated. " + transformationInfo);
215                         // TODO(b/159127402): maybe switch to COMPATIBLE mode if target
216                         //  rotation is not display rotation.
217                         Integer lensFacing = camera.getCameraInfoInternal().getLensFacing();
218                         boolean isFrontCamera;
219                         if (lensFacing == null) {
220                             // TODO(b/122975195): If the lens facing is null, it's probably an
221                             //  external camera. We treat it as like a front camera with
222                             //  unverified behaviors. Will have to define this later.
223                             Logger.w(TAG, "The lens facing is null, probably an external.");
224                             isFrontCamera = true;
225                         } else {
226                             isFrontCamera = lensFacing == CameraSelector.LENS_FACING_FRONT;
227                         }
228                         mPreviewTransform.setTransformationInfo(transformationInfo,
229                                 surfaceRequest.getResolution(), isFrontCamera);
230 
231                         // If targetRotation not specified or it's using SurfaceView, use current
232                         // display rotation.
233                         if (transformationInfo.getTargetRotation() == ROTATION_NOT_SPECIFIED
234                                 || (mImplementation != null
235                                 && mImplementation instanceof SurfaceViewImplementation)) {
236                             mUseDisplayRotation = true;
237                         } else {
238                             mUseDisplayRotation = false;
239                         }
240                         redrawPreview();
241                     });
242 
243             if (!shouldReuseImplementation(mImplementation, surfaceRequest, mImplementationMode)) {
244                 mImplementation = shouldUseTextureView(surfaceRequest, mImplementationMode)
245                         ? new TextureViewImplementation(PreviewView.this, mPreviewTransform)
246                         : new SurfaceViewImplementation(PreviewView.this, mPreviewTransform);
247             }
248 
249             PreviewStreamStateObserver streamStateObserver =
250                     new PreviewStreamStateObserver(camera.getCameraInfoInternal(),
251                             mPreviewStreamStateLiveData, mImplementation);
252             mActiveStreamStateObserver.set(streamStateObserver);
253 
254             camera.getCameraState().addObserver(
255                     getMainExecutor(getContext()), streamStateObserver);
256             mImplementation.onSurfaceRequested(surfaceRequest, () -> {
257                 // We've no longer needed this observer, if there is no new StreamStateObserver
258                 // (another SurfaceRequest), reset the streamState to IDLE.
259                 // This is needed for the case when unbinding preview while other use cases are
260                 // still bound.
261                 if (mActiveStreamStateObserver.compareAndSet(streamStateObserver, null)) {
262                     streamStateObserver.updatePreviewStreamState(StreamState.IDLE);
263                 }
264                 streamStateObserver.clear();
265                 camera.getCameraState().removeObserver(streamStateObserver);
266             });
267 
268             // PreviewViewImplementation#onSurfaceRequested may remove all child views, check if
269             // ScreenFlashView needs to be re-added
270             if (PreviewView.this.indexOfChild(mScreenFlashView) == -1) {
271                 PreviewView.this.addView(mScreenFlashView);
272             }
273 
274             if (mOnFrameUpdateListener != null && mOnFrameUpdateListenerExecutor != null) {
275                 mImplementation.setFrameUpdateListener(mOnFrameUpdateListenerExecutor,
276                         mOnFrameUpdateListener);
277             }
278         }
279     };
280 
281     @UiThread
PreviewView(@onNull Context context)282     public PreviewView(@NonNull Context context) {
283         this(context, null);
284     }
285 
286     @UiThread
PreviewView(@onNull Context context, @Nullable AttributeSet attrs)287     public PreviewView(@NonNull Context context, @Nullable AttributeSet attrs) {
288         this(context, attrs, 0);
289     }
290 
291     @UiThread
PreviewView(@onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr)292     public PreviewView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
293         this(context, attrs, defStyleAttr, 0);
294     }
295 
296     @UiThread
PreviewView(@onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)297     public PreviewView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
298             int defStyleRes) {
299         super(context, attrs, defStyleAttr, defStyleRes);
300         checkMainThread();
301         final TypedArray attributes = context.getTheme().obtainStyledAttributes(attrs,
302                 R.styleable.PreviewView, defStyleAttr, defStyleRes);
303         ViewCompat.saveAttributeDataForStyleable(this, context, R.styleable.PreviewView, attrs,
304                 attributes, defStyleAttr, defStyleRes);
305 
306         try {
307             final int scaleTypeId = attributes.getInteger(
308                     R.styleable.PreviewView_scaleType,
309                     mPreviewTransform.getScaleType().getId());
310             setScaleType(ScaleType.fromId(scaleTypeId));
311 
312             int implementationModeId =
313                     attributes.getInteger(R.styleable.PreviewView_implementationMode,
314                             DEFAULT_IMPL_MODE.getId());
315             setImplementationMode(ImplementationMode.fromId(implementationModeId));
316         } finally {
317             attributes.recycle();
318         }
319 
320         mZoomGestureDetector = new ZoomGestureDetector(context,
321                 zoomEvent -> {
322                     if (zoomEvent instanceof ZoomGestureDetector.ZoomEvent.Move
323                             && mCameraController != null) {
324                         mCameraController.onPinchToZoom(
325                                 ((ZoomGestureDetector.ZoomEvent.Move) zoomEvent)
326                                         .getIncrementalScaleFactor());
327                     }
328                     return true;
329                 });
330 
331         // Set background only if it wasn't already set. A default background prevents the content
332         // behind the PreviewView from being visible before the preview starts streaming.
333         if (getBackground() == null) {
334             setBackgroundColor(ContextCompat.getColor(getContext(), DEFAULT_BACKGROUND_COLOR));
335         }
336 
337         mScreenFlashView = new ScreenFlashView(context);
338         mScreenFlashView.setLayoutParams(new LinearLayout.LayoutParams(
339                 LinearLayout.LayoutParams.MATCH_PARENT,
340                 LinearLayout.LayoutParams.MATCH_PARENT));
341     }
342 
343     @Override
onAttachedToWindow()344     protected void onAttachedToWindow() {
345         super.onAttachedToWindow();
346         startListeningToDisplayChange();
347         addOnLayoutChangeListener(mOnLayoutChangeListener);
348         if (mImplementation != null) {
349             mImplementation.onAttachedToWindow();
350         }
351         attachToControllerIfReady(true);
352     }
353 
354     @Override
onDetachedFromWindow()355     protected void onDetachedFromWindow() {
356         super.onDetachedFromWindow();
357         removeOnLayoutChangeListener(mOnLayoutChangeListener);
358         if (mImplementation != null) {
359             mImplementation.onDetachedFromWindow();
360         }
361         if (mCameraController != null) {
362             mCameraController.clearPreviewSurface();
363         }
364         stopListeningToDisplayChange();
365     }
366 
367     @Override
onTouchEvent(@onNull MotionEvent event)368     public boolean onTouchEvent(@NonNull MotionEvent event) {
369         if (mCameraController == null) {
370             // Do not consume events if controller is not set.
371             return super.onTouchEvent(event);
372         }
373         boolean isSingleTouch = event.getPointerCount() == 1;
374         boolean isUpEvent = event.getAction() == MotionEvent.ACTION_UP;
375         boolean notALongPress = event.getEventTime() - event.getDownTime()
376                 < ViewConfiguration.getLongPressTimeout();
377         if (isSingleTouch && isUpEvent && notALongPress) {
378             // If the event is a click, invoke tap-to-focus and forward it to user's
379             // OnClickListener#onClick.
380             mTouchUpEvent = event;
381             performClick();
382             // A click has been detected and forwarded. Consume the event so onClick won't be
383             // invoked twice.
384             return true;
385         }
386         return mZoomGestureDetector.onTouchEvent(event) || super.onTouchEvent(event);
387     }
388 
389     @Override
390     public boolean performClick() {
391         if (mCameraController != null) {
392             // mTouchUpEvent == null means it's an accessibility click. Focus at the center instead.
393             float x = mTouchUpEvent != null ? mTouchUpEvent.getX() : getWidth() / 2f;
394             float y = mTouchUpEvent != null ? mTouchUpEvent.getY() : getHeight() / 2f;
395             mCameraController.onTapToFocus(mPreviewViewMeteringPointFactory, x, y);
396         }
397         mTouchUpEvent = null;
398         return super.performClick();
399     }
400 
401     /**
402      * Sets the {@link ImplementationMode} for the {@link PreviewView}.
403      *
404      * <p> {@link PreviewView} displays the preview with a {@link TextureView} when the
405      * mode is {@link ImplementationMode#COMPATIBLE}, and tries to use a {@link SurfaceView} if
406      * it is {@link ImplementationMode#PERFORMANCE} when possible, which depends on the device's
407      * attributes (e.g. API level, camera hardware support level). If not set, the default mode
408      * is {@link ImplementationMode#PERFORMANCE}.
409      *
410      * <p> This method needs to be called before the {@link Preview.SurfaceProvider} is set on
411      * {@link Preview}. Once changed, {@link Preview.SurfaceProvider} needs to be set again. e.g.
412      * {@code preview.setSurfaceProvider(previewView.getSurfaceProvider())}.
413      */
414     @UiThread
415     public void setImplementationMode(final @NonNull ImplementationMode implementationMode) {
416         checkMainThread();
417         mImplementationMode = implementationMode;
418 
419         if (mImplementationMode == ImplementationMode.PERFORMANCE
420                 && mOnFrameUpdateListener != null) {
421             throw new IllegalArgumentException(
422                     "PERFORMANCE mode doesn't support frame update listener");
423         }
424     }
425 
426     /**
427      * Returns the {@link ImplementationMode}.
428      *
429      * <p> If nothing is set via {@link #setImplementationMode}, the default
430      * value is {@link ImplementationMode#PERFORMANCE}.
431      *
432      * @return The {@link ImplementationMode} for {@link PreviewView}.
433      */
434     @UiThread
435     public @NonNull ImplementationMode getImplementationMode() {
436         checkMainThread();
437         return mImplementationMode;
438     }
439 
440     /**
441      * Gets a {@link Preview.SurfaceProvider} to be used with
442      * {@link Preview#setSurfaceProvider(Executor, Preview.SurfaceProvider)}. This allows the
443      * camera feed to start when the {@link Preview} use case is bound to a lifecycle.
444      *
445      * <p> The returned {@link Preview.SurfaceProvider} will provide a preview {@link Surface} to
446      * the camera that's either managed by a {@link TextureView} or {@link SurfaceView} depending
447      * on the {@link ImplementationMode} and the device's attributes (e.g. API level, camera
448      * hardware support level).
449      *
450      * @return A {@link Preview.SurfaceProvider} to attach to a {@link Preview} use case.
451      * @see ImplementationMode
452      */
453     @UiThread
454     public Preview.@NonNull SurfaceProvider getSurfaceProvider() {
455         checkMainThread();
456         return mSurfaceProvider;
457     }
458 
459     /**
460      * Applies a {@link ScaleType} to the preview.
461      *
462      * <p> If a {@link CameraController} is attached to {@link PreviewView}, the change will take
463      * immediate effect. It also takes immediate effect if {@link #getViewPort()} is not set in
464      * the bound {@link UseCaseGroup}. Otherwise, the {@link UseCase}s need to be bound again
465      * with the latest value of {@link #getViewPort()}.
466      *
467      * <p> This value can also be set in the layout XML file via the {@code app:scaleType}
468      * attribute.
469      *
470      * <p> The default value is {@link ScaleType#FILL_CENTER}.
471      *
472      * @param scaleType A {@link ScaleType} to apply to the preview.
473      * @attr name app:scaleType
474      */
475     @UiThread
476     public void setScaleType(final @NonNull ScaleType scaleType) {
477         checkMainThread();
478         mPreviewTransform.setScaleType(scaleType);
479         redrawPreview();
480         // Notify controller to re-calculate the crop rect.
481         attachToControllerIfReady(false);
482     }
483 
484     /**
485      * Returns the {@link ScaleType} currently applied to the preview.
486      *
487      * <p> The default value is {@link ScaleType#FILL_CENTER}.
488      *
489      * @return The {@link ScaleType} currently applied to the preview.
490      */
491     @UiThread
492     public @NonNull ScaleType getScaleType() {
493         checkMainThread();
494         return mPreviewTransform.getScaleType();
495     }
496 
497     /**
498      * Gets the {@link MeteringPointFactory} for the camera currently connected to the
499      * {@link PreviewView}, if any.
500      *
501      * <p> The returned {@link MeteringPointFactory} is capable of creating {@link MeteringPoint}s
502      * from (x, y) coordinates in the {@link PreviewView}. This conversion takes into account its
503      * {@link ScaleType}. The {@link MeteringPointFactory} is automatically adjusted if the
504      * {@link PreviewView} layout or the {@link ScaleType} changes.
505      *
506      * <p> The {@link MeteringPointFactory} returns invalid {@link MeteringPoint} if the
507      * preview is not ready, or the {@link PreviewView} dimension is zero. The invalid
508      * {@link MeteringPoint} will cause
509      * {@link CameraControl#startFocusAndMetering(FocusMeteringAction)} to fail but it won't
510      * crash the application. Wait for the {@link StreamState#STREAMING} state to make sure the
511      * preview is ready.
512      *
513      * @return a {@link MeteringPointFactory}
514      * @see #getPreviewStreamState()
515      */
516     @UiThread
517     public @NonNull MeteringPointFactory getMeteringPointFactory() {
518         checkMainThread();
519         return mPreviewViewMeteringPointFactory;
520     }
521 
522     /**
523      * Gets a {@link LiveData} for the preview {@link StreamState}.
524      *
525      * <p>There are two preview stream states, {@link StreamState#IDLE} and
526      * {@link StreamState#STREAMING}. {@link StreamState#IDLE} indicates the preview is currently
527      * not visible and streaming is stopped. {@link StreamState#STREAMING} means the preview is
528      * streaming or is about to start streaming. This state guarantees the preview is visible
529      * only when the {@link ImplementationMode} is {@link ImplementationMode#COMPATIBLE}. When in
530      * {@link ImplementationMode#PERFORMANCE} mode, it is possible the preview becomes
531      * visible slightly after the state changes to {@link StreamState#STREAMING}.
532      *
533      * <p>Apps that require a precise signal for when the preview starts should
534      * {@linkplain #setImplementationMode(ImplementationMode) set} the implementation mode to
535      * {@link ImplementationMode#COMPATIBLE}.
536      *
537      * @return A {@link LiveData} of the preview's {@link StreamState}. Apps can get the current
538      * state with {@link LiveData#getValue()}, or register an observer with
539      * {@link LiveData#observe} .
540      */
541     public @NonNull LiveData<StreamState> getPreviewStreamState() {
542         return mPreviewStreamStateLiveData;
543     }
544 
545     /**
546      * Returns a {@link Bitmap} representation of the content displayed on the
547      * {@link PreviewView}, or {@code null} if the camera preview hasn't started yet.
548      * <p>
549      * The returned {@link Bitmap} uses the {@link Bitmap.Config#ARGB_8888} pixel format and its
550      * dimensions are the same as this view's.
551      * <p>
552      * <strong>Do not</strong> invoke this method from a drawing method
553      * ({@link View#onDraw(Canvas)} for instance).
554      * <p>
555      * If an error occurs during the copy, an empty {@link Bitmap} will be returned.
556      * <p>
557      * If the preview hasn't started yet, the method may return null or an empty {@link Bitmap}. Use
558      * {@link #getPreviewStreamState()} to get the {@link StreamState} and wait for
559      * {@link StreamState#STREAMING} to make sure the preview is started.
560      *
561      * @return A {@link Bitmap.Config#ARGB_8888} {@link Bitmap} representing the content
562      * displayed on the {@link PreviewView}, or null if the camera preview hasn't started yet.
563      */
564     @UiThread
565     public @Nullable Bitmap getBitmap() {
566         checkMainThread();
567         return mImplementation == null ? null : mImplementation.getBitmap();
568     }
569 
570     /**
571      * Gets a {@link ViewPort} based on the current status of {@link PreviewView}.
572      *
573      * <p> Returns a {@link ViewPort} instance based on the {@link PreviewView}'s current width,
574      * height, layout direction, scale type and display rotation. By using the {@link ViewPort}, all
575      * the {@link UseCase}s in the {@link UseCaseGroup} will have the same output image that also
576      * matches the aspect ratio of the {@link PreviewView}.
577      *
578      * @return null if the view is not currently attached or the view's width/height is zero.
579      * @see ViewPort
580      * @see UseCaseGroup
581      */
582     @UiThread
583     public @Nullable ViewPort getViewPort() {
584         checkMainThread();
585         if (getDisplay() == null) {
586             // Returns null if the layout is not ready.
587             return null;
588         }
589         return getViewPort(getDisplay().getRotation());
590     }
591 
592     /**
593      * Gets a {@link ViewPort} with custom target rotation.
594      *
595      * <p>Returns a {@link ViewPort} instance based on the {@link PreviewView}'s current width,
596      * height, layout direction, scale type and the given target rotation.
597      *
598      * <p>Use this method if {@link Preview}'s desired rotation is not the default display
599      * rotation. For example, when remote display is in use and the desired rotation for the
600      * remote display is based on the accelerometer reading. In that case, use
601      * {@link android.view.OrientationEventListener} to obtain the target rotation and create
602      * {@link ViewPort} as following:
603      * <p>{@link android.view.OrientationEventListener#ORIENTATION_UNKNOWN}: orientation == -1
604      * <p>{@link Surface#ROTATION_0}: orientation >= 315 || orientation < 45
605      * <p>{@link Surface#ROTATION_90}: orientation >= 225 && orientation < 315
606      * <p>{@link Surface#ROTATION_180}: orientation >= 135 && orientation < 225
607      * <p>{@link Surface#ROTATION_270}: orientation >= 45 && orientation < 135
608      *
609      * <p> Once the target rotation is obtained, use it with {@link Preview#setTargetRotation} to
610      * update the rotation. Example:
611      *
612      * <pre><code>
613      * Preview preview = new Preview.Builder().setTargetRotation(targetRotation).build();
614      * ViewPort viewPort = previewView.getViewPort(targetRotation);
615      * UseCaseGroup useCaseGroup =
616      *     new UseCaseGroup.Builder().setViewPort(viewPort).addUseCase(preview).build();
617      * cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup);
618      * </code></pre>
619      *
620      * <p> Note that for non-display rotation to work, the mode must be set to
621      * {@link ImplementationMode#COMPATIBLE}.
622      *
623      * @param targetRotation A rotation value, expressed as one of
624      *                       {@link Surface#ROTATION_0}, {@link Surface#ROTATION_90},
625      *                       {@link Surface#ROTATION_180}, or
626      *                       {@link Surface#ROTATION_270}.
627      * @return null if the view's width/height is zero.
628      * @see ImplementationMode
629      */
630     @UiThread
631     @SuppressLint("WrongConstant")
632     public @Nullable ViewPort getViewPort(@ImageOutputConfig.RotationValue int targetRotation) {
633         checkMainThread();
634         if (getWidth() == 0 || getHeight() == 0) {
635             return null;
636         }
637         return new ViewPort.Builder(new Rational(getWidth(), getHeight()), targetRotation)
638                 .setScaleType(getViewPortScaleType())
639                 .setLayoutDirection(getLayoutDirection())
640                 .build();
641     }
642 
643     /**
644      * Converts {@link PreviewView.ScaleType} to {@link ViewPort.ScaleType}.
645      */
646     private int getViewPortScaleType() {
647         switch (getScaleType()) {
648             case FILL_END:
649                 return ViewPort.FILL_END;
650             case FILL_CENTER:
651                 return ViewPort.FILL_CENTER;
652             case FILL_START:
653                 return ViewPort.FILL_START;
654             case FIT_END:
655                 // Fallthrough
656             case FIT_CENTER:
657                 // Fallthrough
658             case FIT_START:
659                 return ViewPort.FIT;
660             default:
661                 throw new IllegalStateException("Unexpected scale type: " + getScaleType());
662         }
663     }
664 
665     // Synthetic access
666     @SuppressWarnings("WeakerAccess")
667     @MainThread
668     @OptIn(markerClass = TransformExperimental.class)
669     void redrawPreview() {
670         checkMainThread();
671         if (mImplementation != null) {
672             updateDisplayRotationIfNeeded();
673             mImplementation.redrawPreview();
674         }
675         mPreviewViewMeteringPointFactory.recalculate(new Size(getWidth(), getHeight()),
676                 getLayoutDirection());
677         if (mCameraController != null) {
678             mCameraController.updatePreviewViewTransform(getSensorToViewTransform());
679         }
680     }
681 
682     @VisibleForTesting
683     static boolean shouldReuseImplementation(@Nullable PreviewViewImplementation implementation,
684             @NonNull SurfaceRequest surfaceRequest, @NonNull ImplementationMode mode) {
685         return implementation instanceof SurfaceViewImplementation && !shouldUseTextureView(
686                 surfaceRequest, mode);
687     }
688 
689     // Synthetic access
690     @SuppressWarnings("WeakerAccess")
691     static boolean shouldUseTextureView(@NonNull SurfaceRequest surfaceRequest,
692             final @NonNull ImplementationMode implementationMode) {
693 
694         // TODO(b/159127402): use TextureView if target rotation is not display rotation.
695         boolean isLegacyDevice = surfaceRequest.getCamera().getCameraInfoInternal()
696                 .getImplementationType().equals(CameraInfo.IMPLEMENTATION_TYPE_CAMERA2_LEGACY);
697         boolean hasSurfaceViewQuirk = DeviceQuirks.get(SurfaceViewStretchedQuirk.class) != null
698                 || DeviceQuirks.get(SurfaceViewNotCroppedByParentQuirk.class) != null;
699         if (Build.VERSION.SDK_INT <= 24 || isLegacyDevice || hasSurfaceViewQuirk) {
700             // Force to use TextureView when the device is running android 7.0 and below, legacy
701             // level or SurfaceView has quirks.
702             return true;
703         }
704         switch (implementationMode) {
705             case COMPATIBLE:
706                 return true;
707             case PERFORMANCE:
708                 return false;
709             default:
710                 throw new IllegalArgumentException(
711                         "Invalid implementation mode: " + implementationMode);
712         }
713     }
714 
715     // Synthetic access
716     @SuppressWarnings("WeakerAccess")
717     void updateDisplayRotationIfNeeded() {
718         if (mUseDisplayRotation) {
719             Display display = getDisplay();
720             if (display != null && mCameraInfoInternal != null) {
721                 mPreviewTransform.overrideWithDisplayRotation(
722                         mCameraInfoInternal.getSensorRotationDegrees(
723                                 display.getRotation()), display.getRotation());
724             }
725         }
726     }
727 
728     /**
729      * Sets a listener to receive frame update event with sensor timestamp.
730      */
731     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
732     public void setFrameUpdateListener(@NonNull Executor executor,
733             @NonNull OnFrameUpdateListener listener) {
734         // SurfaceView doesn't support frame update event.
735         if (mImplementationMode == ImplementationMode.PERFORMANCE) {
736             throw new IllegalArgumentException(
737                     "PERFORMANCE mode doesn't support frame update listener");
738         }
739 
740         mOnFrameUpdateListener = listener;
741         mOnFrameUpdateListenerExecutor = executor;
742         if (mImplementation != null) {
743             mImplementation.setFrameUpdateListener(executor, listener);
744         }
745     }
746 
747     /**
748      * Listener to be notified when the frame is updated.
749      */
750     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
751     public interface OnFrameUpdateListener {
752         /**
753          * Invoked when frame updates.
754          *
755          * @param timestamp sensor timestamp of this frame.
756          */
757         void onFrameUpdate(long timestamp);
758     }
759 
760     /**
761      * The implementation mode of a {@link PreviewView}.
762      *
763      * <p> User preference on how the {@link PreviewView} should render the preview.
764      * {@link PreviewView} displays the preview with either a {@link SurfaceView} or a
765      * {@link TextureView}. A {@link SurfaceView} is generally better than a {@link TextureView}
766      * when it comes to certain key metrics, including power and latency. On the other hand,
767      * {@link TextureView} is better supported by a wider range of devices. The option is used by
768      * {@link PreviewView} to decide what is the best internal implementation given the device
769      * capabilities and user configurations.
770      */
771     public enum ImplementationMode {
772 
773         /**
774          * Use a {@link SurfaceView} for the preview when possible. If the device
775          * doesn't support {@link SurfaceView}, {@link PreviewView} will fall back to use a
776          * {@link TextureView} instead.
777          *
778          * <p>{@link PreviewView} falls back to {@link TextureView} when the API level is 24 or
779          * lower, the camera hardware support level is
780          * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY}, or
781          * {@link Preview#getTargetRotation()} is different from {@link PreviewView}'s display
782          * rotation.
783          *
784          * <p>Do not use this mode if {@link Preview.Builder#setTargetRotation(int)} is set
785          * to a value different than the display's rotation, because {@link SurfaceView} does not
786          * support arbitrary rotations. Do not use this mode if the {@link PreviewView}
787          * needs to be animated. {@link SurfaceView} animation is not supported on API level 24
788          * or lower. Also, for the preview's streaming state provided in
789          * {@link #getPreviewStreamState}, the {@link StreamState#STREAMING} state might happen
790          * prematurely if this mode is used.
791          *
792          * @see Preview.Builder#setTargetRotation(int)
793          * @see Preview.Builder#getTargetRotation()
794          * @see Display#getRotation()
795          * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
796          * @see StreamState#STREAMING
797          */
798         PERFORMANCE(0),
799 
800         /**
801          * Use a {@link TextureView} for the preview.
802          */
803         COMPATIBLE(1);
804 
805         private final int mId;
806 
807         ImplementationMode(int id) {
808             mId = id;
809         }
810 
811         int getId() {
812             return mId;
813         }
814 
815         static ImplementationMode fromId(int id) {
816             for (ImplementationMode implementationMode : values()) {
817                 if (implementationMode.mId == id) {
818                     return implementationMode;
819                 }
820             }
821             throw new IllegalArgumentException("Unknown implementation mode id " + id);
822         }
823     }
824 
825     /** Options for scaling the preview vis-à-vis its container {@link PreviewView}. */
826     public enum ScaleType {
827         /**
828          * Scale the preview, maintaining the source aspect ratio, so it fills the entire
829          * {@link PreviewView}, and align it to the start of the view, which is the top left
830          * corner in a left-to-right (LTR) layout, or the top right corner in a right-to-left
831          * (RTL) layout.
832          * <p>
833          * This may cause the preview to be cropped if the camera preview aspect ratio does not
834          * match that of its container {@link PreviewView}.
835          */
836         FILL_START(0),
837         /**
838          * Scale the preview, maintaining the source aspect ratio, so it fills the entire
839          * {@link PreviewView}, and center it in the view.
840          * <p>
841          * This may cause the preview to be cropped if the camera preview aspect ratio does not
842          * match that of its container {@link PreviewView}.
843          */
844         FILL_CENTER(1),
845         /**
846          * Scale the preview, maintaining the source aspect ratio, so it fills the entire
847          * {@link PreviewView}, and align it to the end of the view, which is the bottom right
848          * corner in a left-to-right (LTR) layout, or the bottom left corner in a right-to-left
849          * (RTL) layout.
850          * <p>
851          * This may cause the preview to be cropped if the camera preview aspect ratio does not
852          * match that of its container {@link PreviewView}.
853          */
854         FILL_END(2),
855         /**
856          * Scale the preview, maintaining the source aspect ratio, so it is entirely contained
857          * within the {@link PreviewView}, and align it to the start of the view, which is the
858          * top left corner in a left-to-right (LTR) layout, or the top right corner in a
859          * right-to-left (RTL) layout. The background area not covered by the preview stream
860          * will be black or the background of the {@link PreviewView}
861          * <p>
862          * Both dimensions of the preview will be equal or less than the corresponding dimensions
863          * of its container {@link PreviewView}.
864          */
865         FIT_START(3),
866         /**
867          * Scale the preview, maintaining the source aspect ratio, so it is entirely contained
868          * within the {@link PreviewView}, and center it inside the view. The background area not
869          * covered by the preview stream will be black or the background of the {@link PreviewView}.
870          * <p>
871          * Both dimensions of the preview will be equal or less than the corresponding dimensions
872          * of its container {@link PreviewView}.
873          */
874         FIT_CENTER(4),
875         /**
876          * Scale the preview, maintaining the source aspect ratio, so it is entirely contained
877          * within the {@link PreviewView}, and align it to the end of the view, which is the
878          * bottom right corner in a left-to-right (LTR) layout, or the bottom left corner in a
879          * right-to-left (RTL) layout. The background area not covered by the preview stream
880          * will be black or the background of the {@link PreviewView}.
881          * <p>
882          * Both dimensions of the preview will be equal or less than the corresponding dimensions
883          * of its container {@link PreviewView}.
884          */
885         FIT_END(5);
886 
887         private final int mId;
888 
889         ScaleType(int id) {
890             mId = id;
891         }
892 
893         int getId() {
894             return mId;
895         }
896 
897         static ScaleType fromId(int id) {
898             for (ScaleType scaleType : values()) {
899                 if (scaleType.mId == id) {
900                     return scaleType;
901                 }
902             }
903             throw new IllegalArgumentException("Unknown scale type id " + id);
904         }
905     }
906 
907     /**
908      * Definitions for the preview stream state.
909      */
910     public enum StreamState {
911         /** Preview is not visible yet. */
912         IDLE,
913         /**
914          * Preview is streaming.
915          *
916          * <p>This state only guarantees the preview is streaming when the implementation mode is
917          * {@link ImplementationMode#COMPATIBLE}. When in {@link ImplementationMode#PERFORMANCE}
918          * mode, it is possible that the preview becomes visible slightly after the state has
919          * changed. For apps requiring a precise signal for when the preview starts, please set
920          * {@link ImplementationMode#COMPATIBLE} mode via {@link #setImplementationMode}.
921          */
922         STREAMING
923     }
924 
925     /**
926      * Sets the {@link CameraController}.
927      *
928      * <p> Once set, the controller will use {@link PreviewView} to display camera preview feed.
929      * It also uses the {@link PreviewView}'s layout dimension to set the crop rect for all the use
930      * cases so that the output from other use cases match what the end user sees in
931      * {@link PreviewView}. It also enables features like tap-to-focus and pinch-to-zoom.
932      *
933      * <p> Setting it to {@code null} or to a different {@link CameraController} stops the previous
934      * {@link CameraController} from working. The previous {@link CameraController} will remain
935      * detached until it's set on the {@link PreviewView} again.
936      *
937      * @throws IllegalArgumentException If the {@link CameraController}'s camera selector
938      *                                  is unable to resolve a camera to be used for the enabled
939      *                                  use cases.
940      * @see CameraController
941      */
942     @UiThread
943     public void setController(@Nullable CameraController cameraController) {
944         checkMainThread();
945         if (mCameraController != null && mCameraController != cameraController) {
946             // If already bound to a different controller, ask the old controller to stop
947             // using this PreviewView.
948             mCameraController.clearPreviewSurface();
949             setScreenFlashUiInfo(null);
950         }
951         mCameraController = cameraController;
952         attachToControllerIfReady(/*shouldFailSilently=*/false);
953         setScreenFlashUiInfo(getScreenFlashInternal());
954     }
955 
956     /**
957      * Get the {@link CameraController}.
958      */
959     @UiThread
960     public @Nullable CameraController getController() {
961         checkMainThread();
962         return mCameraController;
963     }
964 
965     /**
966      * Gets the {@link OutputTransform} associated with the {@link PreviewView}.
967      *
968      * <p> Returns a {@link OutputTransform} object that represents the transform being applied to
969      * the associated {@link Preview} use case. Returns null if the transform info is not ready.
970      * For example, when the associated {@link Preview} has not been bound or the
971      * {@link PreviewView}'s layout is not ready.
972      *
973      * <p> {@link PreviewView} needs to be in {@link ImplementationMode#COMPATIBLE} mode for the
974      * transform to work correctly. For example, the returned {@link OutputTransform} may
975      * not respect the value of {@link #getMatrix()} when {@link ImplementationMode#PERFORMANCE}
976      * mode is used.
977      *
978      * @return the transform applied on the preview by this {@link PreviewView}.
979      * @see CoordinateTransform
980      */
981     @TransformExperimental
982     public @Nullable OutputTransform getOutputTransform() {
983         checkMainThread();
984         Matrix matrix = null;
985         try {
986             matrix = mPreviewTransform.getSurfaceToPreviewViewMatrix(
987                     new Size(getWidth(), getHeight()), getLayoutDirection());
988         } catch (IllegalStateException ex) {
989             // Fall-through. It will be handled below.
990         }
991 
992         Rect surfaceCropRect = mPreviewTransform.getSurfaceCropRect();
993         if (matrix == null || surfaceCropRect == null) {
994             Logger.d(TAG, "Transform info is not ready");
995             return null;
996         }
997         // Map it to the normalized space (-1, -1) - (1, 1).
998         matrix.preConcat(getNormalizedToBuffer(surfaceCropRect));
999 
1000         // Add the custom transform applied by the app. e.g. View#setScaleX.
1001         if (mImplementation instanceof TextureViewImplementation) {
1002             matrix.postConcat(getMatrix());
1003         } else {
1004             if (!getMatrix().isIdentity()) {
1005                 Logger.w(TAG, "PreviewView needs to be in COMPATIBLE mode for the transform"
1006                         + " to work correctly.");
1007             }
1008         }
1009 
1010         return new OutputTransform(matrix, new Size(surfaceCropRect.width(),
1011                 surfaceCropRect.height()));
1012     }
1013 
1014     /**
1015      * Gets the transformation matrix from camera sensor to {@link PreviewView}.
1016      *
1017      * <p>The value is a mapping from sensor coordinates to {@link PreviewView} coordinates,
1018      * which is, from the rect of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE} to the
1019      * rect defined by {@code (0, 0, PreviewView#getWidth(), PreviewView#getHeight())}. The app can
1020      * use the matrix to map the coordinates from one {@link UseCase} to another. For example,
1021      * detecting face with {@link ImageAnalysis}, and then highlighting the face in
1022      * {@link PreviewView}.
1023      *
1024      * <p>This method returns {@code null} if the transformation is not ready. It happens when
1025      * {@link PreviewView} layout has not been measured, or the associated {@link Preview} use case
1026      * is not yet bound to a camera. For the former case, the app can listen to the layout change
1027      * via e.g. {@link #addOnLayoutChangeListener}. For the latter case, the app wait until the
1028      * {@link Preview} or {@link CameraController} is bound and the {@link LifecycleOwner} is in
1029      * the {@link androidx.lifecycle.Lifecycle.State#STARTED} state. The app should call this
1030      * method to get the latest value before performing coordinates transformation.
1031      *
1032      * <p>The return value does not include the custom transform applied by the app via methods like
1033      * {@link View#setScaleX(float)}.
1034      *
1035      * @see SurfaceRequest.TransformationInfo#getSensorToBufferTransform()
1036      * @see ImageInfo#getSensorToBufferTransformMatrix()
1037      */
1038     @UiThread
1039     public @Nullable Matrix getSensorToViewTransform() {
1040         checkMainThread();
1041         if (getWidth() == 0 || getHeight() == 0) {
1042             return null;
1043         }
1044         return mPreviewTransform.getSensorToViewTransform(
1045                 new Size(getWidth(), getHeight()), getLayoutDirection());
1046     }
1047 
1048     @MainThread
1049     private void attachToControllerIfReady(boolean shouldFailSilently) {
1050         checkMainThread();
1051         ViewPort viewPort = getViewPort();
1052         if (mCameraController != null && viewPort != null && isAttachedToWindow()) {
1053             try {
1054                 mCameraController.attachPreviewSurface(getSurfaceProvider(), viewPort);
1055             } catch (IllegalStateException ex) {
1056                 if (shouldFailSilently) {
1057                     // Swallow the exception and fail silently if the method is invoked by View
1058                     // events.
1059                     Logger.e(TAG, ex.toString(), ex);
1060                 } else {
1061                     throw ex;
1062                 }
1063             }
1064         }
1065     }
1066 
1067     private void setScreenFlashUiInfo(ImageCapture.ScreenFlash control) {
1068         if (mCameraController == null) {
1069             Logger.d(TAG, "setScreenFlashUiInfo: mCameraController is null!");
1070             return;
1071         }
1072         mCameraController.setScreenFlashUiInfo(new ScreenFlashUiInfo(
1073                 ScreenFlashUiInfo.ProviderType.PREVIEW_VIEW, control));
1074     }
1075 
1076     private void startListeningToDisplayChange() {
1077         DisplayManager displayManager = getDisplayManager();
1078         if (displayManager == null) {
1079             return;
1080         }
1081         displayManager.registerDisplayListener(mDisplayRotationListener,
1082                 new Handler(Looper.getMainLooper()));
1083     }
1084 
1085     private void stopListeningToDisplayChange() {
1086         DisplayManager displayManager = getDisplayManager();
1087         if (displayManager == null) {
1088             return;
1089         }
1090         displayManager.unregisterDisplayListener(mDisplayRotationListener);
1091     }
1092 
1093     private @Nullable DisplayManager getDisplayManager() {
1094         Context context = getContext();
1095         if (context == null) {
1096             return null;
1097         }
1098         return (DisplayManager) context.getApplicationContext()
1099                 .getSystemService(Context.DISPLAY_SERVICE);
1100     }
1101 
1102     /**
1103      * Sets a {@link Window} instance for subsequent photo capture requests with
1104      * {@link ImageCapture#FLASH_MODE_SCREEN} set.
1105      *
1106      * <p>The calling of this API will take effect for {@link ImageCapture#FLASH_MODE_SCREEN} only
1107      * and the {@code Window} will be ignored for other flash modes. During screen flash photo
1108      * capture, the window is used for the purpose of changing brightness.
1109      *
1110      * <p>If the implementation provided by the user is no longer valid (e.g. due to any
1111      * {@link android.app.Activity} or {@link android.view.View} reference used in the
1112      * implementation becoming invalid), user needs to re-set a new valid window or
1113      * clear the previous one with {@code setScreenFlashWindow(null)}, whichever appropriate.
1114      *
1115      * <p>For most app scenarios, a {@link Window} instance can be obtained from
1116      * {@link Activity#getWindow()}. In case of a fragment, {@link Fragment#getActivity()} can
1117      * first be used to get the activity instance.
1118      *
1119      * @param screenFlashWindow A {@link Window} instance that is used to change the brightness
1120      *                          during screen flash photo capture.
1121      */
1122     @UiThread
1123     public void setScreenFlashWindow(@Nullable Window screenFlashWindow) {
1124         checkMainThread();
1125         mScreenFlashView.setScreenFlashWindow(screenFlashWindow);
1126         setScreenFlashUiInfo(getScreenFlashInternal());
1127     }
1128 
1129 
1130     // Workaround to expose getScreenFlash as experimental, so that other APIs already using it also
1131     // don't need to be annotated with experimental (e.g. PreviewView.setController)
1132     @UiThread
1133     private ImageCapture.@Nullable ScreenFlash getScreenFlashInternal() {
1134         return mScreenFlashView.getScreenFlash();
1135     }
1136 
1137     /**
1138      * Returns an {@link ImageCapture.ScreenFlash} implementation based
1139      * on the {@link Window} instance set via {@link #setScreenFlashWindow(Window)}.
1140      *
1141      * <p> This API uses an internally managed {@link ScreenFlashView} to provide the
1142      * {@link ImageCapture.ScreenFlash} implementation which can be passed to the
1143      * {@link ImageCapture#setScreenFlash(ImageCapture.ScreenFlash)} API. The following example
1144      * shows the API usage.
1145      * <pre>{@code
1146      * mPreviewView.setScreenFlashWindow(activity.getWindow());
1147      * mImageCapture.setScreenFlash(mPreviewView.getScreenFlash());
1148      * mImageCapture.setFlashMode(ImageCapture.FLASH_MODE_SCREEN);
1149      * mImageCapture.takePhoto(mCameraExecutor, mOnImageSavedCallback);
1150      * }</pre>
1151      *
1152      * @return An {@link ImageCapture.ScreenFlash} implementation provided by
1153      * {@link ScreenFlashView#getScreenFlash()} which can be null if a non-null {@code Window}
1154      * instance hasn't been set.
1155      *
1156      * @see ScreenFlashView#getScreenFlash()
1157      * @see ImageCapture#FLASH_MODE_SCREEN
1158      */
1159     @UiThread
1160     public ImageCapture.@Nullable ScreenFlash getScreenFlash() {
1161         return getScreenFlashInternal();
1162     }
1163 
1164     /**
1165      * Sets the color of the top overlay view during screen flash.
1166      *
1167      * @param color The color value of the top overlay.
1168      *
1169      * @see #getScreenFlash()
1170      * @see ImageCapture#FLASH_MODE_SCREEN
1171      */
1172     public void setScreenFlashOverlayColor(@ColorInt int color) {
1173         mScreenFlashView.setBackgroundColor(color);
1174     }
1175 
1176     /**
1177      * Listener for display rotation changes.
1178      *
1179      * <p> When the device is rotated 180° from side to side, the activity is not
1180      * destroyed and recreated. In some foldable or large screen devices, when rotating devices
1181      * in multi-window mode, it's also possible that activity is not recreated. This class is
1182      * necessary to make sure preview's display rotation gets updated when that happens.
1183      */
1184     // Synthetic access
1185     @SuppressWarnings("WeakerAccess")
1186     class DisplayRotationListener implements DisplayManager.DisplayListener {
1187         @Override
1188         public void onDisplayAdded(int displayId) {
1189         }
1190 
1191         @Override
1192         public void onDisplayRemoved(int displayId) {
1193         }
1194 
1195         @Override
1196         public void onDisplayChanged(int displayId) {
1197             Display display = getDisplay();
1198             if (display != null && display.getDisplayId() == displayId) {
1199                 redrawPreview();
1200             }
1201         }
1202     }
1203 }
1204