/**
  * Sets a Bitmap and initializes the image rotation according to the EXIT data.<br>
  * <br>
  * The EXIF can be retrieved by doing the following: <code>
  * ExifInterface exif = new ExifInterface(path);</code>
  *
  * @param bitmap the original bitmap to set; if null, this
  * @param exif the EXIF information about this bitmap; may be null
  */
 public void setImageBitmap(Bitmap bitmap, ExifInterface exif) {
   if (bitmap != null && exif != null) {
     ImageViewUtil.RotateBitmapResult result = ImageViewUtil.rotateBitmapByExif(bitmap, exif);
     bitmap = result.bitmap;
     mDegreesRotated = result.degrees;
   }
   setImageBitmap(bitmap);
 }
  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {

    if (mBitmap != null) {
      final Rect bitmapRect = ImageViewUtil.getBitmapRect(mBitmap, this, mScaleType);
      mCropOverlayView.setBitmapRect(bitmapRect);
    } else {
      mCropOverlayView.setBitmapRect(EMPTY_RECT);
    }
  }
  /**
   * Sets a bitmap loaded from the given Android URI as the content of the CropImageView.<br>
   * Can be used with URI from gallery or camera source.<br>
   * Will rotate the image by exif data.<br>
   *
   * @param uri the URI to load the image from
   */
  public void setImageUri(Uri uri) {
    if (uri != null) {

      DisplayMetrics metrics = getResources().getDisplayMetrics();
      double densityAdj = metrics.density > 1 ? 1 / metrics.density : 1;

      int width = (int) (metrics.widthPixels * densityAdj);
      int height = (int) (metrics.heightPixels * densityAdj);
      ImageViewUtil.DecodeBitmapResult decodeResult =
          ImageViewUtil.decodeSampledBitmap(getContext(), uri, width, height);

      ImageViewUtil.RotateBitmapResult rotateResult =
          ImageViewUtil.rotateBitmapByExif(getContext(), decodeResult.bitmap, uri);

      setImageBitmap(rotateResult.bitmap);

      mLoadedImageUri = uri;
      mLoadedSampleSize = decodeResult.sampleSize;
      mDegreesRotated = rotateResult.degrees;
    }
  }
  /**
   * Gets the cropped image based on the current crop window.<br>
   * If image loaded from URI will use sample size to fir the requested width and height.
   *
   * @return a new Bitmap representing the cropped image
   */
  public Bitmap getCroppedImage(int reqWidth, int reqHeight) {
    if (mBitmap != null) {
      if (mLoadedImageUri != null && mLoadedSampleSize > 1) {
        Rect rect = getActualCropRectNoRotation();
        reqWidth = reqWidth > 0 ? reqWidth : rect.width();
        reqHeight = reqHeight > 0 ? reqHeight : rect.height();
        ImageViewUtil.DecodeBitmapResult result =
            ImageViewUtil.decodeSampledBitmapRegion(
                getContext(), mLoadedImageUri, rect, reqWidth, reqHeight);

        Bitmap bitmap = result.bitmap;
        if (mDegreesRotated > 0) {
          bitmap = ImageViewUtil.rotateBitmap(bitmap, mDegreesRotated);
        }

        return bitmap;
      } else {
        Rect rect = getActualCropRect();
        return Bitmap.createBitmap(mBitmap, rect.left, rect.top, rect.width(), rect.height());
      }
    } else {
      return null;
    }
  }
  /**
   * Gets the crop window's position relative to the source Bitmap (not the image displayed in the
   * CropImageView).
   *
   * @return a RectF instance containing cropped area boundaries of the source Bitmap
   */
  public Rect getActualCropRect() {
    if (mBitmap != null) {
      final Rect displayedImageRect = ImageViewUtil.getBitmapRect(mBitmap, mImageView, mScaleType);

      // Get the scale factor between the actual Bitmap dimensions and the displayed dimensions for
      // width.
      final float actualImageWidth = mBitmap.getWidth();
      final float displayedImageWidth = displayedImageRect.width();
      final float scaleFactorWidth = actualImageWidth / displayedImageWidth;

      // Get the scale factor between the actual Bitmap dimensions and the displayed dimensions for
      // height.
      final float actualImageHeight = mBitmap.getHeight();
      final float displayedImageHeight = displayedImageRect.height();
      final float scaleFactorHeight = actualImageHeight / displayedImageHeight;

      // Get crop window position relative to the displayed image.
      final float displayedCropLeft = Edge.LEFT.getCoordinate() - displayedImageRect.left;
      final float displayedCropTop = Edge.TOP.getCoordinate() - displayedImageRect.top;
      final float displayedCropWidth = Edge.getWidth();
      final float displayedCropHeight = Edge.getHeight();

      // Scale the crop window position to the actual size of the Bitmap.
      float actualCropLeft = displayedCropLeft * scaleFactorWidth;
      float actualCropTop = displayedCropTop * scaleFactorHeight;
      float actualCropRight = actualCropLeft + displayedCropWidth * scaleFactorWidth;
      float actualCropBottom = actualCropTop + displayedCropHeight * scaleFactorHeight;

      // Correct for floating point errors. Crop rect boundaries should not exceed the source Bitmap
      // bounds.
      actualCropLeft = Math.max(0f, actualCropLeft);
      actualCropTop = Math.max(0f, actualCropTop);
      actualCropRight = Math.min(mBitmap.getWidth(), actualCropRight);
      actualCropBottom = Math.min(mBitmap.getHeight(), actualCropBottom);

      return new Rect(
          (int) actualCropLeft, (int) actualCropTop, (int) actualCropRight, (int) actualCropBottom);
    } else {
      return null;
    }
  }
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    if (mBitmap != null) {

      super.onMeasure(widthMeasureSpec, heightMeasureSpec);

      // Bypasses a baffling bug when used within a ScrollView, where
      // heightSize is set to 0.
      if (heightSize == 0) {
        heightSize = mBitmap.getHeight();
      }

      int desiredWidth;
      int desiredHeight;

      double viewToBitmapWidthRatio = Double.POSITIVE_INFINITY;
      double viewToBitmapHeightRatio = Double.POSITIVE_INFINITY;

      // Checks if either width or height needs to be fixed
      if (widthSize < mBitmap.getWidth()) {
        viewToBitmapWidthRatio = (double) widthSize / (double) mBitmap.getWidth();
      }
      if (heightSize < mBitmap.getHeight()) {
        viewToBitmapHeightRatio = (double) heightSize / (double) mBitmap.getHeight();
      }

      // If either needs to be fixed, choose smallest ratio and calculate
      // from there
      if (viewToBitmapWidthRatio != Double.POSITIVE_INFINITY
          || viewToBitmapHeightRatio != Double.POSITIVE_INFINITY) {
        if (viewToBitmapWidthRatio <= viewToBitmapHeightRatio) {
          desiredWidth = widthSize;
          desiredHeight = (int) (mBitmap.getHeight() * viewToBitmapWidthRatio);
        } else {
          desiredHeight = heightSize;
          desiredWidth = (int) (mBitmap.getWidth() * viewToBitmapHeightRatio);
        }
      }

      // Otherwise, the picture is within frame layout bounds. Desired
      // width is
      // simply picture size
      else {
        desiredWidth = mBitmap.getWidth();
        desiredHeight = mBitmap.getHeight();
      }

      int width = getOnMeasureSpec(widthMode, widthSize, desiredWidth);
      int height = getOnMeasureSpec(heightMode, heightSize, desiredHeight);

      mLayoutWidth = width;
      mLayoutHeight = height;

      final Rect bitmapRect =
          ImageViewUtil.getBitmapRect(
              mBitmap.getWidth(), mBitmap.getHeight(), mLayoutWidth, mLayoutHeight, mScaleType);
      mCropOverlayView.setBitmapRect(bitmapRect);

      // MUST CALL THIS
      setMeasuredDimension(mLayoutWidth, mLayoutHeight);

    } else {

      mCropOverlayView.setBitmapRect(EMPTY_RECT);
      setMeasuredDimension(widthSize, heightSize);
    }
  }