@Override
  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    int width = 320;
    int height = 240;
    if (mCameraSource != null) {
      Size size = mCameraSource.getPreviewSize();
      if (size != null) {
        width = size.getWidth();
        height = size.getHeight();
      }
    }

    // Swap width and height sizes when in portrait, since it will be rotated 90 degrees
    if (isPortraitMode()) {
      int tmp = width;
      //noinspection SuspiciousNameCombination
      width = height;
      height = tmp;
    }

    final int layoutWidth = right - left;
    final int layoutHeight = bottom - top;

    // Computes height and width for potentially doing fit width.
    int childWidth = layoutWidth;
    int childHeight = (int) (((float) layoutWidth / (float) width) * height);

    // If height is too tall using fit width, does fit height instead.
    if (childHeight > layoutHeight) {
      childHeight = layoutHeight;
      childWidth = (int) (((float) layoutHeight / (float) height) * width);
    }

    for (int i = 0; i < getChildCount(); ++i) {
      getChildAt(i).layout(0, 0, childWidth, childHeight);
    }

    try {
      startIfReady();
    } catch (SecurityException se) {
      Log.e(TAG, "Do not have permission to start the camera", se);
    } catch (IOException e) {
      Log.e(TAG, "Could not start camera source.", e);
    }
  }
 @RequiresPermission(Manifest.permission.CAMERA)
 private void startIfReady() throws IOException, SecurityException {
   if (mStartRequested && mSurfaceAvailable) {
     mCameraSource.start(mSurfaceView.getHolder());
     if (mOverlay != null) {
       Size size = mCameraSource.getPreviewSize();
       int min = Math.min(size.getWidth(), size.getHeight());
       int max = Math.max(size.getWidth(), size.getHeight());
       if (isPortraitMode()) {
         // Swap width and height sizes when in portrait, since it will be rotated by
         // 90 degrees
         mOverlay.setCameraInfo(min, max, mCameraSource.getCameraFacing());
       } else {
         mOverlay.setCameraInfo(max, min, mCameraSource.getCameraFacing());
       }
       mOverlay.clear();
     }
     mStartRequested = false;
   }
 }
  /**
   * Selects the most suitable preview and picture size, given the desired width and height.
   *
   * <p>Even though we may only need the preview size, it's necessary to find both the preview size
   * and the picture size of the camera together, because these need to have the same aspect ratio.
   * On some hardware, if you would only set the preview size, you will get a distorted image.
   *
   * @param camera the camera to select a preview size from
   * @param desiredWidth the desired width of the camera preview frames
   * @param desiredHeight the desired height of the camera preview frames
   * @return the selected preview and picture size pair
   */
  private static SizePair selectSizePair(Camera camera, int desiredWidth, int desiredHeight) {
    List<SizePair> validPreviewSizes = generateValidPreviewSizeList(camera);

    // The method for selecting the best size is to minimize the sum of the differences between
    // the desired values and the actual values for width and height.  This is certainly not the
    // only way to select the best size, but it provides a decent tradeoff between using the
    // closest aspect ratio vs. using the closest pixel area.
    SizePair selectedPair = null;
    int minDiff = Integer.MAX_VALUE;
    for (SizePair sizePair : validPreviewSizes) {
      Size size = sizePair.previewSize();
      int diff =
          Math.abs(size.getWidth() - desiredWidth) + Math.abs(size.getHeight() - desiredHeight);
      if (diff < minDiff) {
        selectedPair = sizePair;
        minDiff = diff;
      }
    }

    return selectedPair;
  }
  /**
   * Creates one buffer for the camera preview callback. The size of the buffer is based off of the
   * camera preview size and the format of the camera image.
   *
   * @return a new preview buffer of the appropriate size for the current camera settings
   */
  private byte[] createPreviewBuffer(Size previewSize) {
    int bitsPerPixel = ImageFormat.getBitsPerPixel(ImageFormat.NV21);
    long sizeInBits = previewSize.getHeight() * previewSize.getWidth() * bitsPerPixel;
    int bufferSize = (int) Math.ceil(sizeInBits / 8.0d) + 1;

    //
    // NOTICE: This code only works when using play services v. 8.1 or higher.
    //

    // Creating the byte array this way and wrapping it, as opposed to using .allocate(),
    // should guarantee that there will be an array to work with.
    byte[] byteArray = new byte[bufferSize];
    ByteBuffer buffer = ByteBuffer.wrap(byteArray);
    if (!buffer.hasArray() || (buffer.array() != byteArray)) {
      // I don't think that this will ever happen.  But if it does, then we wouldn't be
      // passing the preview content to the underlying detector later.
      throw new IllegalStateException(mContext.getString(R.string.failed_to_create_vaild_buffer));
    }

    mBytesToByteBuffer.put(byteArray, buffer);
    return byteArray;
  }
  /**
   * Opens the camera and applies the user settings.
   *
   * @throws RuntimeException if the method fails
   */
  @SuppressLint("InlinedApi")
  private Camera createCamera() {
    int requestedCameraId = getIdForRequestedCamera(mFacing);
    if (requestedCameraId == -1) {
      throw new RuntimeException(mContext.getString(R.string.no_requested_camera));
    }
    Camera camera = Camera.open(requestedCameraId);

    SizePair sizePair =
        selectSizePair(camera, mRequestedPreviewWidth, mRequestedPreviewHeight, mContext);
    if (sizePair == null) {
      throw new RuntimeException(mContext.getString(R.string.no_suitable_preview_size));
    }
    Size pictureSize = sizePair.pictureSize();
    mPreviewSize = sizePair.previewSize();

    int[] previewFpsRange = selectPreviewFpsRange(camera, mRequestedFps);
    if (previewFpsRange == null) {
      throw new RuntimeException(mContext.getString(R.string.no_suitable_frames_per_second));
    }

    Camera.Parameters parameters = camera.getParameters();

    parameters.setPictureSize(pictureSize.getWidth(), pictureSize.getHeight());
    parameters.setPreviewSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
    parameters.setPreviewFpsRange(
        previewFpsRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
        previewFpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
    parameters.setPreviewFormat(ImageFormat.NV21);

    setRotation(camera, parameters, requestedCameraId);

    if (mFocusMode != null) {
      if (parameters.getSupportedFocusModes().contains(mFocusMode)) {
        parameters.setFocusMode(mFocusMode);
      } else {
        Log.i(
            TAG,
            mContext.getString(R.string.camera_focus_mode)
                + mFocusMode
                + mContext.getString(R.string.not_supported));
      }
    }

    // setting mFocusMode to the one set in the params
    mFocusMode = parameters.getFocusMode();

    if (mFlashMode != null) {
      if (parameters.getSupportedFlashModes().contains(mFlashMode)) {
        parameters.setFlashMode(mFlashMode);
      } else {
        Log.i(
            TAG,
            mContext.getString(R.string.flash_mode)
                + mFlashMode
                + mContext.getString(R.string.not_supported));
      }
    }

    // setting mFlashMode to the one set in the params
    mFlashMode = parameters.getFlashMode();

    camera.setParameters(parameters);

    // Four frame buffers are needed for working with the camera:
    //
    //   one for the frame that is currently being executed upon in doing detection
    //   one for the next pending frame to process immediately upon completing detection
    //   two for the frames that the camera uses to populate future preview images
    camera.setPreviewCallbackWithBuffer(new CameraPreviewCallback());
    camera.addCallbackBuffer(createPreviewBuffer(mPreviewSize));
    camera.addCallbackBuffer(createPreviewBuffer(mPreviewSize));
    camera.addCallbackBuffer(createPreviewBuffer(mPreviewSize));
    camera.addCallbackBuffer(createPreviewBuffer(mPreviewSize));

    return camera;
  }