content: /*
   * Copyright (C) 2016 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.google.android.cameraview;
  
  import android.content.Context;
  import android.graphics.ImageFormat;
  import android.graphics.Rect;
  import android.graphics.RectF;
  import android.hardware.camera2.CameraCaptureSession;
  import android.hardware.camera2.CameraCharacteristics;
  import android.hardware.camera2.CameraDevice;
  import android.hardware.camera2.CameraManager;
  import android.hardware.camera2.CaptureRequest;
  import android.hardware.camera2.CaptureResult;
  import android.hardware.camera2.TotalCaptureResult;
  import android.hardware.camera2.params.MeteringRectangle;
  import android.hardware.camera2.params.StreamConfigurationMap;
  import android.media.Image;
  import android.media.ImageReader;
  import android.os.Build;
  import android.util.SparseIntArray;
  import android.view.Surface;
  
  import androidx.annotation.NonNull;
  
  import com.ailiwean.core.helper.CameraHelper;
  import com.ailiwean.core.helper.ScanHelper;
  
  import java.nio.ByteBuffer;
  import java.util.Arrays;
  import java.util.Set;
  import java.util.SortedSet;
  
  @SuppressWarnings("MissingPermission")
  @androidx.annotation.RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
  class Camera2 extends CameraViewImpl {
  
      private static final String TAG = "Camera2";
  
      private static final SparseIntArray INTERNAL_FACINGS = new SparseIntArray();
  
      static {
          INTERNAL_FACINGS.put(Constants.FACING_BACK, CameraCharacteristics.LENS_FACING_BACK);
          INTERNAL_FACINGS.put(Constants.FACING_FRONT, CameraCharacteristics.LENS_FACING_FRONT);
      }
  
      /**
       * Max preview width that is guaranteed by Camera2 API
       */
      private static final int MAX_PREVIEW_WIDTH = 1920;
  
      /**
       * Max preview height that is guaranteed by Camera2 API
       */
      private static final int MAX_PREVIEW_HEIGHT = 1080;
  
      private final CameraManager mCameraManager;
  
      private final CameraDevice.StateCallback mCameraDeviceCallback
              = new CameraDevice.StateCallback() {
  
          @Override
          public void onOpened(@NonNull CameraDevice camera) {
              mCamera = camera;
              mCallback.onCameraOpened();
              startCaptureSession();
          }
  
          @Override
          public void onClosed(@NonNull CameraDevice camera) {
              mCallback.onCameraClosed();
          }
  
          @Override
          public void onDisconnected(@NonNull CameraDevice camera) {
              mCamera = null;
          }
  
          @Override
          public void onError(@NonNull CameraDevice camera, int error) {
              mCamera = null;
              restart();
          }
  
      };
  
      private final CameraCaptureSession.StateCallback mSessionCallback
              = new CameraCaptureSession.StateCallback() {
  
          @Override
          public void onConfigured(@NonNull CameraCaptureSession session) {
              if (!isCameraOpened()) {
                  return;
              }
              mCaptureSession = session;
              updateAutoFocus();
              updateFlash();
              try {
                  CaptureRequest request = mPreviewRequestBuilder.build();
                  mCaptureSession.setRepeatingRequest(request,
                          mCaptureCallback, null);
              } catch (Exception e) {
                  restart();
              }
          }
  
          @Override
          public void onConfigureFailed(@NonNull CameraCaptureSession session) {
              restart();
          }
  
          @Override
          public void onClosed(@NonNull CameraCaptureSession session) {
              if (mCaptureSession != null && mCaptureSession.equals(session)) {
                  mCaptureSession = null;
              }
          }
  
      };
  
      PictureCaptureCallback mCaptureCallback = new PictureCaptureCallback() {
  
          @Override
          public void onPrecaptureRequired() {
              mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
                      CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
              setState(STATE_PRECAPTURE);
              try {
                  mCaptureSession.capture(mPreviewRequestBuilder.build(), this, null);
                  mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
                          CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
              } catch (Exception e) {
                  restart();
              }
          }
  
          @Override
          public void onReady() {
              captureStillPicture();
          }
  
      };
  
      private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
              = reader -> {
          try (Image image = reader.acquireNextImage()) {
              Image.Plane[] planes = image.getPlanes();
              if (planes.length > 0) {
                  ByteBuffer buffer = planes[0].getBuffer();
                  byte[] data = new byte[buffer.remaining()];
                  buffer.get(data);
                  mCallback.onPictureTaken(data);
              }
          }
      };
  
  
      private String mCameraId;
  
      private CameraCharacteristics mCameraCharacteristics;
  
      volatile CameraDevice mCamera;
  
      CameraCaptureSession mCaptureSession;
  
      CaptureRequest.Builder mPreviewRequestBuilder;
  
      private ImageReader mImageReader;
  
      private ImageReader mYuvReader;
  
      private final SizeMap mPreviewSizes = new SizeMap();
  
      private final SizeMap mPictureSizes = new SizeMap();
  
      private int mFacing;
  
      private AspectRatio mAspectRatio = Constants.DEFAULT_ASPECT_RATIO;
  
      private boolean mAutoFocus;
  
      private int mFlash;
  
      private int mDisplayOrientation;
  
      Camera2(Callback callback, Context context) {
          super(callback);
          mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
      }
  
      @Override
      public void updatePreView(PreviewImpl preview) {
          super.updatePreView(preview);
          mPreview.setCallback(this::startCaptureSession);
      }
  
      @Override
      boolean start() {
          synchronized (Camera2.class) {
              if (isCameraOpened())
                  return true;
  
              if (!chooseCameraIdByFacing())
                  return false;
  
              if (!collectCameraInfo())
                  return false;
  
              if (!prepareImageReader())
                  return false;
  
              return startOpeningCamera();
          }
      }
  
      @Override
      void stop() {
          synchronized (Camera2.class) {
              if (mCamera != null) {
                  CameraDevice temp = mCamera;
                  mCamera = null;
                  temp.close();
              }
              if (mCaptureSession != null) {
                  mCaptureSession.close();
                  mCaptureSession = null;
              }
              if (mImageReader != null) {
                  mImageReader.close();
                  mImageReader = null;
              }
              if (mYuvReader != null) {
                  mYuvReader.close();
                  mYuvReader = null;
              }
          }
      }
  
      @Override
      boolean isCameraOpened() {
          return mCamera != null;
      }
  
      @Override
      void setFacing(int facing) {
          if (mFacing == facing) {
              return;
          }
          mFacing = facing;
          if (isCameraOpened()) {
              restart();
          }
      }
  
      @Override
      int getFacing() {
          return mFacing;
      }
  
      @Override
      Set<AspectRatio> getSupportedAspectRatios() {
          return mPreviewSizes.ratios();
      }
  
      @Override
      boolean setAspectRatio(AspectRatio ratio) {
  //        if (ratio == null || ratio.equals(mAspectRatio) ||
  //                !mPreviewSizes.ratios().contains(ratio)) {
  //            // TODO: Better error handling
  //            return false;
  //        }
  //        mAspectRatio = ratio;
  //        prepareImageReader();
  //        if (mCaptureSession != null) {
  //            mCaptureSession.close();
  //            mCaptureSession = null;
  //            startCaptureSession();
  //        }
          mAspectRatio = ratio;
          return true;
      }
  
      @Override
      AspectRatio getAspectRatio() {
          return mAspectRatio;
      }
  
      @Override
      void setAutoFocus(boolean autoFocus) {
          if (mAutoFocus == autoFocus) {
              return;
          }
          mAutoFocus = autoFocus;
          if (mPreviewRequestBuilder != null) {
              updateAutoFocus();
              if (mCaptureSession != null) {
                  try {
                      mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(),
                              mCaptureCallback, null);
                  } catch (Exception e) {
                      mAutoFocus = !mAutoFocus; // Revert
                      restart();
                  }
              }
          }
      }
  
      @Override
      boolean getAutoFocus() {
          return mAutoFocus;
      }
  
      @Override
      void setFlash(int flash) {
          if (mFlash == flash) {
              return;
          }
          int saved = mFlash;
          mFlash = flash;
          if (mPreviewRequestBuilder != null) {
              updateFlash();
              if (mCaptureSession != null) {
                  try {
                      mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(),
                              mCaptureCallback, null);
                  } catch (Exception e) {
                      mFlash = saved; // Revert
                      restart();
                  }
              }
          }
      }
  
      @Override
      int getFlash() {
          return mFlash;
      }
  
      @Override
      void takePicture() {
          if (mAutoFocus) {
              lockFocus();
          } else {
              captureStillPicture();
          }
      }
  
      @Override
      void setDisplayOrientation(int displayOrientation) {
          mDisplayOrientation = displayOrientation;
          if (mPreview != null)
              mPreview.setDisplayOrientation(mDisplayOrientation);
      }
  
      @Override
      void toZoomMax() {
          setZoom(1);
      }
  
      @Override
      void toZoomMin() {
          setZoom(0);
      }
  
      @Override
      void setZoom(float percent) {
          synchronized (Camera2.class) {
              if (!isCameraOpened())
                  return;
              try {
                  Rect showRect = CameraHelper.getZoomRect(mCameraCharacteristics, percent);
                  mPreviewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, showRect);
                  rectMeteringWithFocus(showRect);
              } catch (Exception e) {
                  restart();
              }
          }
      }
  
      @Override
      void lightOperator(boolean isOpen) {
          synchronized (Camera2.class) {
              if (!isCameraOpened())
                  return;
              if (isOpen)
                  setFlash(Constants.FLASH_TORCH);
              else setFlash(Constants.FLASH_OFF);
          }
      }
  
      /**
       * <p>Chooses a camera ID by the specified camera facing ({@link #mFacing}).</p>
       * <p>This rewrites {@link #mCameraId}, {@link #mCameraCharacteristics}, and optionally
       * {@link #mFacing}.</p>
       */
      private boolean chooseCameraIdByFacing() {
          try {
              int internalFacing = INTERNAL_FACINGS.get(mFacing);
              final String[] ids = mCameraManager.getCameraIdList();
              if (ids.length == 0) { // No camera
                  return false;
              }
              for (String id : ids) {
                  CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(id);
                  Integer level = characteristics.get(
                          CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
                  if (level == null ||
                          level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
                      continue;
                  }
                  Integer internal = characteristics.get(CameraCharacteristics.LENS_FACING);
                  if (internal == null) {
                      return false;
                  }
                  if (internal == internalFacing) {
                      mCameraId = id;
                      mCameraCharacteristics = characteristics;
                      return true;
                  }
              }
              // Not found
              mCameraId = ids[0];
              mCameraCharacteristics = mCameraManager.getCameraCharacteristics(mCameraId);
              Integer level = mCameraCharacteristics.get(
                      CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
              if (level == null ||
                      level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
                  return false;
              }
              Integer internal = mCameraCharacteristics.get(CameraCharacteristics.LENS_FACING);
              if (internal == null) {
                  return false;
              }
              for (int i = 0, count = INTERNAL_FACINGS.size(); i < count; i++) {
                  if (INTERNAL_FACINGS.valueAt(i) == internal) {
                      mFacing = INTERNAL_FACINGS.keyAt(i);
                      return true;
                  }
              }
              // The operation can reach here when the only camera device is an external one.
              // We treat it as facing back.
              mFacing = Constants.FACING_BACK;
              return true;
          } catch (Exception e) {
              return false;
          }
      }
  
      /**
       * <p>Collects some information from {@link #mCameraCharacteristics}.</p>
       * <p>This rewrites {@link #mPreviewSizes}, {@link #mPictureSizes}, and optionally,
       * {@link #mAspectRatio}.</p>
       */
      private boolean collectCameraInfo() {
          StreamConfigurationMap map = mCameraCharacteristics.get(
                  CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
          if (map == null) {
              return false;
          }
  
          mPreviewSizes.clear();
          for (android.util.Size size : map.getOutputSizes(mPreview.getOutputClass())) {
              int width = size.getWidth();
              int height = size.getHeight();
  //            if (width <= MAX_PREVIEW_WIDTH && height <= MAX_PREVIEW_HEIGHT) {
  //                mPreviewSizes.add(new Size(width, height));
  //            }
              mPreviewSizes.add(new Size(width, height));
          }
  
          mPictureSizes.clear();
          for (android.util.Size size : map.getOutputSizes(ImageFormat.JPEG)) {
              mPictureSizes.add(new Size(size.getWidth(), size.getHeight()));
          }
          for (AspectRatio ratio : mPreviewSizes.ratios()) {
              if (!mPictureSizes.ratios().contains(ratio)) {
                  mPreviewSizes.remove(ratio);
              }
          }
  
          if (!mPreviewSizes.ratios().contains(mAspectRatio)) {
              //扫码没有设置的则取4:3
              mAspectRatio = Constants.DEFAULT_ASPECT_RATIO;
              //没有4：3则取最接近的
              if (!mPreviewSizes.ratios().contains(mAspectRatio)) {
                  mAspectRatio = findNeartoDefaultAspectRatio(mPreviewSizes);
                  mPreview.updateAspectRatio(mAspectRatio);
                  return true;
              }
              mPreview.updateAspectRatio(mAspectRatio);
              return true;
          }
          return true;
      }
  
      private AspectRatio findNeartoDefaultAspectRatio(SizeMap sizeMap) {
          float minRation = Float.MAX_VALUE;
          AspectRatio minAspectRatio = sizeMap.ratios().iterator().next();
          for (AspectRatio ratio : sizeMap.ratios()) {
              for (Size size : mPreviewSizes.sizes(ratio)) {
                  float currentRatio = Math.abs(1 - ((float) size.getWidth() / MAX_PREVIEW_WIDTH) *
                          ((float) size.getHeight() / MAX_PREVIEW_HEIGHT));
                  if (currentRatio < minRation) {
                      minRation = currentRatio;
                      minAspectRatio = ratio;
                  }
              }
          }
          return minAspectRatio;
      }
  
      private boolean prepareImageReader() {
          try {
  
              if (mImageReader != null) {
                  mImageReader.close();
              }
              Size largest = mPictureSizes.sizes(mAspectRatio).last();
              mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(),
                      ImageFormat.JPEG, /* maxImages */ 2);
              mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, null);
  
          } catch (Exception e) {
              return false;
          }
          return true;
      }
  
      /***
       * yuv数据回调
       */
      private ImageReader.OnImageAvailableListener mOnYuvAvailableListener = reader -> mCallback.onPreviewByte(CameraHelper.readYuv(reader));
  
  
      /**
       * <p>Starts opening a camera device.</p>
       * <p>The result will be processed in {@link #mCameraDeviceCallback}.</p>
       */
      private boolean startOpeningCamera() {
          try {
              mCameraManager.openCamera(mCameraId, mCameraDeviceCallback, null);
          } catch (Exception e) {
              return false;
          }
          return true;
      }
  
      /**
       * <p>Starts a capture session for camera preview.</p>
       * <p>This rewrites {@link #mPreviewRequestBuilder}.</p>
       * <p>The result will be continuously processed in {@link #mSessionCallback}.</p>
       */
      void startCaptureSession() {
          synchronized (Camera2.class) {
              if (!isCameraOpened() || !mPreview.isReady() || mImageReader == null) {
                  return;
              }
  
              Size previewSize = chooseOptimalSize();
  
              mYuvReader = ImageReader.newInstance(previewSize.getWidth(), previewSize.getHeight(),
                      ImageFormat.YUV_420_888, /* maxImages */ 2);
              mYuvReader.setOnImageAvailableListener(mOnYuvAvailableListener, null);
  
              mPreview.setBufferSize(previewSize.getWidth(), previewSize.getHeight());
              Surface surface = mPreview.getSurface();
              try {
                  mPreviewRequestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                  mPreviewRequestBuilder.addTarget(surface);
                  mPreviewRequestBuilder.addTarget(mYuvReader.getSurface());
                  mCamera.createCaptureSession(Arrays.asList(surface
                          , mImageReader.getSurface()
                          , mYuvReader.getSurface()
                          ),
                          mSessionCallback, null);
              } catch (Exception ignored) {
                  restart();
              }
          }
      }
  
      /**
       * Chooses the optimal preview size based on {@link #mPreviewSizes} and the surface size.
       *
       * @return The picked size for camera preview.
       */
      private Size chooseOptimalSize() {
          int surfaceLonger, surfaceShorter;
          int surfaceWidth = mPreview.getWidth();
          int surfaceHeight = mPreview.getHeight();
          if (surfaceWidth == 0 || surfaceHeight == 0) {
              try {
                  Thread.sleep(2000);
              } catch (Exception ignored) {
              } finally {
                  surfaceWidth = mPreview.getWidth();
                  surfaceHeight = mPreview.getHeight();
              }
          }
          if (surfaceWidth == 0 || surfaceHeight == 0) {
              surfaceWidth = MAX_PREVIEW_HEIGHT;
              surfaceHeight = MAX_PREVIEW_WIDTH;
          }
          if (surfaceWidth < surfaceHeight) {
              surfaceLonger = surfaceHeight;
              surfaceShorter = surfaceWidth;
          } else {
              surfaceLonger = surfaceWidth;
              surfaceShorter = surfaceHeight;
          }
          SortedSet<Size> candidates = mPreviewSizes.sizes(mAspectRatio);
  
          float minRation = Float.MAX_VALUE;
          Size suitSize = null;
          for (Size size : candidates) {
              float currentRation = Math.abs(1 - (float) surfaceLonger / size.getWidth() *
                      (float) surfaceShorter / size.getHeight());
              if (currentRation < minRation) {
                  minRation = currentRation;
                  suitSize = size;
              }
          }
          // If no size is big enough, pick the largest one.
          return suitSize;
      }
  
      /**
       * Updates the internal state of auto-focus to {@link #mAutoFocus}.
       */
      void updateAutoFocus() {
          if (mAutoFocus) {
              int[] modes = mCameraCharacteristics.get(
                      CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
              // Auto focus is not supported
              if (modes == null || modes.length == 0 ||
                      (modes.length == 1 && modes[0] == CameraCharacteristics.CONTROL_AF_MODE_OFF)) {
                  mAutoFocus = false;
                  mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                          CaptureRequest.CONTROL_AF_MODE_OFF);
              } else {
                  mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                          CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
              }
          } else {
              mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                      CaptureRequest.CONTROL_AF_MODE_OFF);
          }
      }
  
      /**
       * Updates the internal state of flash to {@link #mFlash}.
       */
      void updateFlash() {
          switch (mFlash) {
              case Constants.FLASH_OFF:
                  mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
                          CaptureRequest.CONTROL_AE_MODE_ON);
                  mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE,
                          CaptureRequest.FLASH_MODE_OFF);
                  break;
              case Constants.FLASH_ON:
                  mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
                          CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
                  mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE,
                          CaptureRequest.FLASH_MODE_OFF);
                  break;
              case Constants.FLASH_TORCH:
                  mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
                          CaptureRequest.CONTROL_AE_MODE_ON);
                  mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE,
                          CaptureRequest.FLASH_MODE_TORCH);
                  break;
              case Constants.FLASH_AUTO:
                  mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
                          CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
                  mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE,
                          CaptureRequest.FLASH_MODE_OFF);
                  break;
              case Constants.FLASH_RED_EYE:
                  mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
                          CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE);
                  mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE,
                          CaptureRequest.FLASH_MODE_OFF);
                  break;
          }
      }
  
      /**
       * Locks the focus as the first step for a still image capture.
       */
      private void lockFocus() {
          mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
                  CaptureRequest.CONTROL_AF_TRIGGER_START);
          try {
              mCaptureCallback.setState(PictureCaptureCallback.STATE_LOCKING);
              mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, null);
          } catch (Exception e) {
              restart();
          }
      }
  
      /**
       * Captures a still picture.
       */
      void captureStillPicture() {
          try {
              CaptureRequest.Builder captureRequestBuilder = mCamera.createCaptureRequest(
                      CameraDevice.TEMPLATE_STILL_CAPTURE);
              captureRequestBuilder.addTarget(mImageReader.getSurface());
              captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                      mPreviewRequestBuilder.get(CaptureRequest.CONTROL_AF_MODE));
              switch (mFlash) {
                  case Constants.FLASH_OFF:
                      captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
                              CaptureRequest.CONTROL_AE_MODE_ON);
                      captureRequestBuilder.set(CaptureRequest.FLASH_MODE,
                              CaptureRequest.FLASH_MODE_OFF);
                      break;
                  case Constants.FLASH_ON:
                      captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
                              CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
                      break;
                  case Constants.FLASH_TORCH:
                      captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
                              CaptureRequest.CONTROL_AE_MODE_ON);
                      captureRequestBuilder.set(CaptureRequest.FLASH_MODE,
                              CaptureRequest.FLASH_MODE_TORCH);
                      break;
                  case Constants.FLASH_AUTO:
                      captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
                              CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
                      break;
                  case Constants.FLASH_RED_EYE:
                      captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
                              CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
                      break;
              }
              // Calculate JPEG orientation.
              @SuppressWarnings("ConstantConditions")
              int sensorOrientation = mCameraCharacteristics.get(
                      CameraCharacteristics.SENSOR_ORIENTATION);
              captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION,
                      (sensorOrientation +
                              mDisplayOrientation * (mFacing == Constants.FACING_FRONT ? 1 : -1) +
                              360) % 360);
              // Stop preview and capture a still picture.
              mCaptureSession.stopRepeating();
              mCaptureSession.capture(captureRequestBuilder.build(),
                      new CameraCaptureSession.CaptureCallback() {
                          @Override
                          public void onCaptureCompleted(@NonNull CameraCaptureSession session,
                                                         @NonNull CaptureRequest request,
                                                         @NonNull TotalCaptureResult result) {
                              unlockFocus();
                          }
                      }, null);
          } catch (Exception e) {
              restart();
          }
      }
  
      /**
       * Unlocks the auto-focus and restart camera preview. This is supposed to be called after
       * capturing a still picture.
       */
      void unlockFocus() {
          //防止拍完照外部调用stop()，而内部回调重新阅览崩溃问题
          if (!isCameraOpened()) {
              return;
          }
  
          mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
                  CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
          try {
              mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, null);
              updateAutoFocus();
              updateFlash();
              mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
                      CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
              mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback,
                      null);
              mCaptureCallback.setState(PictureCaptureCallback.STATE_PREVIEW);
          } catch (Exception e) {
              restart();
          }
      }
  
      private void restart() {
          stop();
          try {
              Thread.sleep(500);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
          start();
      }
  
      /**
       * A {@link CameraCaptureSession.CaptureCallback} for capturing a still picture.
       */
      private static abstract class PictureCaptureCallback
              extends CameraCaptureSession.CaptureCallback {
  
          static final int STATE_PREVIEW = 0;
          static final int STATE_LOCKING = 1;
          static final int STATE_LOCKED = 2;
          static final int STATE_PRECAPTURE = 3;
          static final int STATE_WAITING = 4;
          static final int STATE_CAPTURING = 5;
  
          private int mState;
  
          PictureCaptureCallback() {
          }
  
          void setState(int state) {
              mState = state;
          }
  
          @Override
          public void onCaptureProgressed(@NonNull CameraCaptureSession session,
                                          @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
              process(partialResult);
          }
  
          @Override
          public void onCaptureCompleted(@NonNull CameraCaptureSession session,
                                         @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
              process(result);
          }
  
          private void process(@NonNull CaptureResult result) {
              switch (mState) {
                  case STATE_LOCKING: {
                      Integer af = result.get(CaptureResult.CONTROL_AF_STATE);
                      if (af == null) {
                          break;
                      }
                      if (af == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED ||
                              af == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
                          Integer ae = result.get(CaptureResult.CONTROL_AE_STATE);
                          if (ae == null || ae == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
                              setState(STATE_CAPTURING);
                              onReady();
                          } else {
                              setState(STATE_LOCKED);
                              onPrecaptureRequired();
                          }
                      }
                      break;
                  }
                  case STATE_PRECAPTURE: {
                      Integer ae = result.get(CaptureResult.CONTROL_AE_STATE);
                      if (ae == null || ae == CaptureResult.CONTROL_AE_STATE_PRECAPTURE ||
                              ae == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED ||
                              ae == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
                          setState(STATE_WAITING);
                      }
                      break;
                  }
                  case STATE_WAITING: {
                      Integer ae = result.get(CaptureResult.CONTROL_AE_STATE);
                      if (ae == null || ae != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
                          setState(STATE_CAPTURING);
                          onReady();
                      }
                      break;
                  }
              }
          }
  
          /**
           * Called when it is ready to take a still picture.
           */
          public abstract void onReady();
  
          /**
           * Called when it is necessary to run the precapture sequence.
           */
          public abstract void onPrecaptureRequired();
  
      }
  
      RectF rectF;
  
      @Override
      protected void rectMeteringWithFocus(RectF rectF) {
  
          this.rectF = rectF;
  
          if (mCamera == null || mCameraCharacteristics == null)
              return;
  
          Integer maxNum = mCameraCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AF);
          if (maxNum == null || maxNum <= 0)
              return;
  
          Rect dateRect = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
          rectMeteringWithFocus(dateRect);
      }
  
      protected void rectMeteringWithFocus(Rect dateRect) {
          synchronized (Camera2.class) {
  
              if (mCamera == null || mCameraCharacteristics == null)
                  return;
  
              Integer maxNum = mCameraCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AF);
              if (maxNum == null || maxNum <= 0)
                  return;
  
              if (rectF == null)
                  return;
  
              if (dateRect == null)
                  dateRect = new Rect(0, 0, 1, 1);
  
              int dataWidth = dateRect.width() - 1;
              int dataHeight = dateRect.height() - 1;
  
              RectF cropRect = ScanHelper.copyRect(rectF);
              cropRect.left += (cropRect.right - cropRect.left) / 4;
              cropRect.right -= (cropRect.right - cropRect.left) / 4;
              cropRect.top += (cropRect.bottom - cropRect.top) / 4;
              cropRect.bottom -= (cropRect.bottom - cropRect.top) / 4;
  
              int left = (int) (cropRect.top * dataWidth) + dateRect.left;
              int top = (int) ((1f - cropRect.right) * dataHeight) + dateRect.top;
              int right = (int) (cropRect.bottom * dataWidth) + dateRect.left;
              int bottom = (int) ((1f - cropRect.left) * dataHeight) + dateRect.top;
              Rect realRect = new Rect(left, top, right, bottom);
  
              MeteringRectangle meteringRectangle = new MeteringRectangle(realRect, 1000);
              MeteringRectangle[] meteringRectangles = new MeteringRectangle[]{meteringRectangle};
              try {
                  mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AWB_REGIONS, meteringRectangles);
                  mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_REGIONS, meteringRectangles);
                  mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_REGIONS, meteringRectangles);
                  mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback, null);
              } catch (Exception ignored) {
              }
  
          }
      }
  
  }
  