content: package com.bihe0832.android.common.crop.view;
  
  import static com.bihe0832.android.common.crop.view.OverlayView.FREESTYLE_CROP_MODE_DISABLE;
  
  import android.content.Context;
  import android.util.AttributeSet;
  import android.view.GestureDetector;
  import android.view.MotionEvent;
  import android.view.ScaleGestureDetector;
  import com.bihe0832.android.common.crop.util.RotationGestureDetector;
  import com.bihe0832.android.common.crop.view.OverlayView.FreestyleMode;
  
  
  public class GestureCropImageView extends CropImageView {
  
      private static final int DOUBLE_TAP_ZOOM_DURATION = 200;
  
      private ScaleGestureDetector mScaleDetector;
      private RotationGestureDetector mRotateDetector;
      private GestureDetector mGestureDetector;
  
      private boolean needResize = false;
      @FreestyleMode
      private int mFreestyleCropMode = FREESTYLE_CROP_MODE_DISABLE;
      private float mMidPntX, mMidPntY;
  
      private boolean mIsRotateEnabled = true, mIsScaleEnabled = true, mIsGestureEnabled = true;
      private int mDoubleTapScaleSteps = 5;
  
      public GestureCropImageView(Context context) {
          super(context);
      }
  
      public GestureCropImageView(Context context, AttributeSet attrs) {
          this(context, attrs, 0);
      }
  
      public GestureCropImageView(Context context, AttributeSet attrs, int defStyle) {
          super(context, attrs, defStyle);
      }
  
      public boolean isScaleEnabled() {
          return mIsScaleEnabled;
      }
  
      public void setScaleEnabled(boolean scaleEnabled) {
          mIsScaleEnabled = scaleEnabled;
      }
  
      public boolean isRotateEnabled() {
          return mIsRotateEnabled;
      }
  
      public void setRotateEnabled(boolean rotateEnabled) {
          mIsRotateEnabled = rotateEnabled;
      }
  
      public boolean isGestureEnabled() {
          return mIsGestureEnabled;
      }
  
      public void setGestureEnabled(boolean gestureEnabled) {
          mIsGestureEnabled = gestureEnabled;
      }
  
      public int getDoubleTapScaleSteps() {
          return mDoubleTapScaleSteps;
      }
  
      public void setDoubleTapScaleSteps(int doubleTapScaleSteps) {
          mDoubleTapScaleSteps = doubleTapScaleSteps;
      }
  
      public void setFreestyleCropMode(@FreestyleMode int mFreestyleCropMode) {
          this.mFreestyleCropMode = mFreestyleCropMode;
      }
  
      /**
       * If it's ACTION_DOWN event - user touches the screen and all current animation must be canceled.
       * If it's ACTION_UP event - user removed all fingers from the screen and current image position must be corrected.
       * If there are more than 2 fingers - update focal point coordinates.
       * Pass the event to the gesture detectors if those are enabled.
       */
      @Override
      public boolean onTouchEvent(MotionEvent event) {
          if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
              cancelAllAnimations();
          }
  
          if (event.getPointerCount() > 1) {
              mMidPntX = (event.getX(0) + event.getX(1)) / 2;
              mMidPntY = (event.getY(0) + event.getY(1)) / 2;
          }
  
          if (mIsGestureEnabled) {
              mGestureDetector.onTouchEvent(event);
          }
  
          if (mIsScaleEnabled) {
              mScaleDetector.onTouchEvent(event);
          }
  
          if (mIsRotateEnabled) {
              mRotateDetector.onTouchEvent(event);
          }
  
          if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP && needResize) {
              setImageToWrapCropBounds();
              needResize = false;
          }
          return true;
      }
  
      @Override
      protected void init() {
          super.init();
          setupGestureListeners();
      }
  
      /**
       * This method calculates target scale value for double tap gesture.
       * User is able to zoom the image from min scale value
       * to the max scale value with {@link #mDoubleTapScaleSteps} double taps.
       */
      protected float getDoubleTapTargetScale() {
          return getCurrentScale() * (float) Math.pow(getMaxScale() / getMinScale(), 1.0f / mDoubleTapScaleSteps);
      }
  
      private void setupGestureListeners() {
          mGestureDetector = new GestureDetector(getContext(), new GestureListener(), null, true);
          mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener());
          mRotateDetector = new RotationGestureDetector(new RotateListener());
      }
  
      private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
  
          @Override
          public boolean onScale(ScaleGestureDetector detector) {
              needResize = true;
              postScale(detector.getScaleFactor(), mMidPntX, mMidPntY);
              return true;
          }
      }
  
      private class GestureListener extends GestureDetector.SimpleOnGestureListener {
  
          @Override
          public boolean onDoubleTap(MotionEvent e) {
              needResize = true;
              zoomImageToPosition(getDoubleTapTargetScale(), e.getX(), e.getY(), DOUBLE_TAP_ZOOM_DURATION);
              return super.onDoubleTap(e);
          }
  
          @Override
          public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
              if (mFreestyleCropMode > FREESTYLE_CROP_MODE_DISABLE) {
                  needResize = true;
              }
              postTranslate(-distanceX, -distanceY);
              return true;
          }
  
      }
  
      private class RotateListener extends RotationGestureDetector.SimpleOnRotationGestureListener {
  
          @Override
          public boolean onRotation(RotationGestureDetector rotationDetector) {
              needResize = true;
              postRotate(rotationDetector.getAngle(), mMidPntX, mMidPntY);
              return true;
          }
  
      }
  
  }
  