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