content: package com.agenthun.readingroutine.views;
  
  import android.content.Context;
  import android.graphics.Bitmap;
  import android.graphics.Canvas;
  import android.graphics.ColorMatrix;
  import android.graphics.ColorMatrixColorFilter;
  import android.graphics.Matrix;
  import android.graphics.Paint;
  import android.graphics.Path;
  import android.graphics.PointF;
  import android.graphics.Region;
  import android.graphics.drawable.GradientDrawable;
  import android.util.AttributeSet;
  import android.view.MotionEvent;
  import android.widget.Scroller;
  import android.widget.TextView;
  
  import com.agenthun.readingroutine.utils.UiUtils;
  
  /**
   * @project ReadingRoutine
   * @authors agenthun
   * @date 15/11/6 上午12:43.
   */
  public class PageView extends TextView {
      private static final String TAG = "PageView";
  
      private int mViewWidth;
      private int mViewHeight;
      private int mCornerX = 0;
      private int mCornerY = 0;
  
      private Paint mPaint;
      private Path mPath0;
      private Path mPath1;
      private Bitmap mCurPageBitmap = null;
      private Bitmap mNextPageBitmap = null;
  
      private PointF mTouch = new PointF();
      private PointF mBezierStart1 = new PointF();
      private PointF mBezierControl1 = new PointF();
      private PointF mBezierVertex1 = new PointF();
      private PointF mBezierEnd1 = new PointF();
  
      private PointF mBezierStart2 = new PointF();
      private PointF mBezierControl2 = new PointF();
      private PointF mBezierVertex2 = new PointF();
      private PointF mBezierEnd2 = new PointF();
  
      private float mMiddleX;
      private float mMiddleY;
      private float mDegrees;
      private float mTouchToCornerDis;
      private ColorMatrixColorFilter colorFilter;
      private Matrix mMatrix;
      private float[] mMatrixArray = {0, 0, 0, 0, 0, 0, 0, 0, 1.0f};
  
      private boolean mIsRTandLB;
      private float mMaxLength;
  
      private int[] mBackShadowColors;
      private int[] mFrontShadowColors;
      private GradientDrawable mBackShadowDrawableLR;
      private GradientDrawable mBackShadowDrawableRL;
      private GradientDrawable mFolderShadowDrawableLR;
      private GradientDrawable mFolderShadowDrawableRL;
  
      private GradientDrawable mFrontShadowDrawableHBT;
      private GradientDrawable mFrontShadowDrawableHTB;
      private GradientDrawable mFrontShadowDrawableVLR;
      private GradientDrawable mFrontShadowDrawableVRL;
  
      Scroller mScroller;
  
      public PageView(Context context) {
          super(context);
          initView(context);
      }
  
      public PageView(Context context, AttributeSet attrs) {
          super(context, attrs);
          initView(context);
      }
  
      public PageView(Context context, AttributeSet attrs, int defStyleAttr) {
          super(context, attrs, defStyleAttr);
          initView(context);
      }
  
      private void initView(Context context) {
          mViewWidth = UiUtils.getScreenWidthPixels(context);
          mViewHeight = UiUtils.getScreenHeightPixels(context);
  
          mPath0 = new Path();
          mPath1 = new Path();
          createDrawable();
          initPaint();
  
          ColorMatrix colorMatrix = new ColorMatrix();
          float[] array = {0.55f, 0, 0, 0, 80.0f, 0, 0.55f, 0, 0, 80.0f, 0, 0,
                  0.55f, 0, 80.0f, 0, 0, 0, 0.2f, 0};
          colorMatrix.set(array);
          colorFilter = new ColorMatrixColorFilter(colorMatrix);
  
          mMatrix = new Matrix();
          mScroller = new Scroller(getContext());
  
          mTouch.x = 0.01f;
          mTouch.y = 0.01f;
  
          mMaxLength = (float) Math.hypot(mViewWidth, mViewHeight);
      }
  
      private void initPaint() {
          mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
          mPaint.setStyle(Paint.Style.FILL);
      }
  
      private void createDrawable() {
          int[] color = {0xffffff, 0xb0ffffff};
          mFolderShadowDrawableRL = new GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, color);
          mFolderShadowDrawableRL.setGradientType(GradientDrawable.LINEAR_GRADIENT);
          mFolderShadowDrawableLR = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, color);
          mFolderShadowDrawableLR.setGradientType(GradientDrawable.LINEAR_GRADIENT);
  
          mBackShadowColors = new int[]{0xff111111, 0x111111};
          mBackShadowDrawableRL = new GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, mBackShadowColors);
          mBackShadowDrawableRL.setGradientType(GradientDrawable.LINEAR_GRADIENT);
          mBackShadowDrawableLR = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, mBackShadowColors);
          mBackShadowDrawableLR.setGradientType(GradientDrawable.LINEAR_GRADIENT);
  
          mFrontShadowColors = new int[]{0x80e0e0e0, 0x111111};
          mFrontShadowDrawableVLR = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, mFrontShadowColors);
          mFrontShadowDrawableVLR.setGradientType(GradientDrawable.LINEAR_GRADIENT);
          mFrontShadowDrawableVRL = new GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, mFrontShadowColors);
          mFrontShadowDrawableVRL.setGradientType(GradientDrawable.LINEAR_GRADIENT);
          mFrontShadowDrawableHTB = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, mFrontShadowColors);
          mFrontShadowDrawableHTB.setGradientType(GradientDrawable.LINEAR_GRADIENT);
          mFrontShadowDrawableHBT = new GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP, mFrontShadowColors);
          mFrontShadowDrawableHBT.setGradientType(GradientDrawable.LINEAR_GRADIENT);
      }
  
      @Override
      protected void onSizeChanged(int w, int h, int oldw, int oldh) {
          super.onSizeChanged(w, h, oldw, oldh);
          mViewWidth = w;
          mViewHeight = h;
      }
  
  /*    @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
          super.onMeasure(widthMeasureSpec, heightMeasureSpec);
          mViewWidth = getMeasuredWidth();
          mViewHeight = getMeasuredHeight();
      }*/
  
      public void calcCornerXY(float x, float y) {
          if (x <= mViewWidth / 2) mCornerX = 0;
          else mCornerX = mViewWidth;
  
          if (y <= mViewHeight / 2) mCornerY = 0;
          else mCornerY = mViewHeight;
  
          mIsRTandLB = (mCornerX == 0 && mCornerY == mViewHeight) || (mCornerX == mViewWidth && mCornerY == 0);
      }
  
      public boolean doTouchEvent(MotionEvent event) {
          if (event.getAction() == MotionEvent.ACTION_MOVE) {
              mTouch.x = event.getX();
              mTouch.y = event.getY();
              postInvalidate();
          } else if (event.getAction() == MotionEvent.ACTION_DOWN) {
              mTouch.x = event.getX();
              mTouch.y = event.getY();
              calcCornerXY(mTouch.x, mTouch.y);
              postInvalidate();
          } else if (event.getAction() == MotionEvent.ACTION_UP) {
              if (canDragOver()) {
                  startAnimation(1200);
              } else {
                  mTouch.x = mCornerX - 0.09f;
                  mTouch.y = mCornerY - 0.09f;
              }
              postInvalidate();
          }
          return true;
      }
  
      public void setTouch(float x, float y) {
          this.mTouch.x = x;
          this.mTouch.y = y;
          this.postInvalidate();
      }
  
      public boolean canDragOver() {
          return mTouchToCornerDis > mViewWidth / 10;
      }
  
      public boolean dragToRight() {
          return mCornerX <= 0;
      }
  
      public void abortAnimation() {
          if (!mScroller.isFinished()) {
              mScroller.abortAnimation();
          }
      }
  
      public void startAnimation(int delayMillis) {
          int dx, dy;
          if (mCornerX > 0) {
              dx = -(int) (mViewWidth + mTouch.x);
          } else {
              dx = (int) (mViewWidth - mTouch.x + mViewWidth);
          }
  
          if (mCornerY > 0) {
              dy = (int) (mViewHeight - mTouch.y);
          } else {
              dy = (int) (1 - mTouch.y);
          }
          mScroller.startScroll((int) mTouch.x, (int) mTouch.y, dx, dy, delayMillis);
      }
  
      public void computeScroll() {
          super.computeScroll();
          if (mScroller.computeScrollOffset()) {
              float x = mScroller.getCurrX();
              float y = mScroller.getCurrY();
              mTouch.x = x;
              mTouch.y = y;
              postInvalidate();
          }
      }
  
      @Override
      protected void onDraw(Canvas canvas) {
          canvas.drawColor(0xffaaaaaa);
          calcPoints();
          drawCurrentPageArea(canvas, mCurPageBitmap, mPath0);
          drawNextPageAreaAndShadow(canvas, mNextPageBitmap);
          drawCurrentPageShadow(canvas);
          drawCurrentBackArea(canvas, mCurPageBitmap);
      }
  
      private void calcPoints() {
          mMiddleX = (mTouch.x + mCornerX) / 2;
          mMiddleY = (mTouch.y + mCornerY) / 2;
          mBezierControl1.x = mMiddleX - (mCornerY - mMiddleY) * (mCornerY - mMiddleY) / (mCornerX - mMiddleX);
          mBezierControl1.y = mCornerY;
          mBezierControl2.x = mCornerX;
          mBezierControl2.y = mMiddleY - (mCornerX - mMiddleX) * (mCornerX - mMiddleX) / (mCornerY - mMiddleY);
  
          mBezierStart1.x = mBezierControl1.x - (mCornerX - mBezierControl1.x) / 2;
          mBezierStart1.y = mCornerY;
  
          if (mTouch.x > 0 && mTouch.x < mViewWidth) {
              if (mBezierStart1.x < 0 || mBezierStart1.x > mViewWidth) {
                  if (mBezierStart1.x < 0)
                      mBezierStart1.x = mViewWidth - mBezierStart1.x;
  
                  float f1 = Math.abs(mCornerX - mTouch.x);
                  float f2 = mViewWidth * f1 / mBezierStart1.x;
                  mTouch.x = Math.abs(mCornerX - f2);
  
                  float f3 = Math.abs(mCornerX - mTouch.x) * Math.abs(mCornerY - mTouch.y) / f1;
                  mTouch.y = Math.abs(mCornerY - f3);
  
                  mMiddleX = (mTouch.x + mCornerX) / 2;
                  mMiddleY = (mTouch.y + mCornerY) / 2;
  
                  mBezierControl1.x = mMiddleX - (mCornerY - mMiddleY) * (mCornerY - mMiddleY) / (mCornerX - mMiddleX);
                  mBezierControl1.y = mCornerY;
  
                  mBezierControl2.x = mCornerX;
                  mBezierControl2.y = mMiddleY - (mCornerX - mMiddleX) * (mCornerX - mMiddleX) / (mCornerY - mMiddleY);
  
                  mBezierStart1.x = mBezierControl1.x - (mCornerX - mBezierControl1.x) / 2;
              }
          }
          mBezierStart2.x = mCornerX;
          mBezierStart2.y = mBezierControl2.y - (mCornerY - mBezierControl2.y) / 2;
  
          mTouchToCornerDis = (float) Math.hypot((mTouch.x - mCornerX), (mTouch.y - mCornerY));
  
          mBezierEnd1 = getCross(mTouch, mBezierControl1, mBezierStart1, mBezierStart2);
          mBezierEnd2 = getCross(mTouch, mBezierControl2, mBezierStart1, mBezierStart2);
  
          mBezierVertex1.x = (mBezierStart1.x + 2 * mBezierControl1.x + mBezierEnd1.x) / 4;
          mBezierVertex1.y = (2 * mBezierControl1.y + mBezierStart1.y + mBezierEnd1.y) / 4;
          mBezierVertex2.x = (mBezierStart2.x + 2 * mBezierControl2.x + mBezierEnd2.x) / 4;
          mBezierVertex2.y = (2 * mBezierControl2.y + mBezierStart2.y + mBezierEnd2.y) / 4;
      }
  
      private PointF getCross(PointF p1, PointF p2, PointF p3, PointF p4) {
          PointF crossP = new PointF();
  
          float a1 = (p2.y - p1.y) / (p2.x - p1.x);
          float b1 = ((p1.x * p2.y) - (p2.x * p1.y)) / (p1.x - p2.x);
  
          float a2 = (p4.y - p3.y) / (p4.x - p3.x);
          float b2 = ((p3.x * p4.y) - (p4.x * p3.y)) / (p3.x - p4.x);
  
          crossP.x = (b2 - b1) / (a1 - a2);
          crossP.y = a1 * crossP.x + b1;
          return crossP;
      }
  
      private void drawCurrentPageArea(Canvas canvas, Bitmap bitmap, Path path) {
          mPath0.reset();
          mPath0.moveTo(mBezierStart1.x, mBezierStart1.y);
          mPath0.quadTo(mBezierControl1.x, mBezierControl1.y, mBezierEnd1.x, mBezierEnd1.y);
          mPath0.lineTo(mTouch.x, mTouch.y);
          mPath0.lineTo(mBezierEnd2.x, mBezierEnd2.y);
          mPath0.quadTo(mBezierControl2.x, mBezierControl2.y, mBezierStart2.x, mBezierStart2.y);
          mPath0.lineTo(mCornerX, mCornerY);
          mPath0.close();
  
          canvas.save();
          canvas.clipPath(path, Region.Op.XOR);
          canvas.drawBitmap(bitmap, 0, 0, null);
          canvas.restore();
      }
  
      private void drawNextPageAreaAndShadow(Canvas canvas, Bitmap bitmap) {
          mPath1.reset();
          mPath1.moveTo(mBezierStart1.x, mBezierStart1.y);
          mPath1.lineTo(mBezierVertex1.x, mBezierVertex1.y);
          mPath1.lineTo(mBezierVertex2.x, mBezierVertex2.y);
          mPath1.lineTo(mBezierStart2.x, mBezierStart2.y);
          mPath1.lineTo(mCornerX, mCornerY);
          mPath1.close();
  
          mDegrees = (float) Math.toDegrees(Math.atan2(mBezierControl1.x - mCornerX, mBezierControl2.y - mCornerY));
          int leftx;
          int rightx;
          GradientDrawable mBackShadowDrawable;
          if (mIsRTandLB) {
              leftx = (int) (mBezierStart1.x);
              rightx = (int) (mBezierStart1.x + mTouchToCornerDis / 4);
              mBackShadowDrawable = mBackShadowDrawableLR;
          } else {
              leftx = (int) (mBezierStart1.x - mTouchToCornerDis / 4);
              rightx = (int) mBezierStart1.x;
              mBackShadowDrawable = mBackShadowDrawableRL;
          }
          canvas.save();
          canvas.clipPath(mPath0);
          canvas.clipPath(mPath1, Region.Op.INTERSECT);
          canvas.drawBitmap(bitmap, 0, 0, null);
          canvas.rotate(mDegrees, mBezierStart1.x, mBezierStart1.y);
          mBackShadowDrawable.setBounds(leftx, (int) mBezierStart1.y, rightx, (int) (mMaxLength + mBezierStart1.y));
          mBackShadowDrawable.draw(canvas);
          canvas.restore();
      }
  
      public void drawCurrentPageShadow(Canvas canvas) {
          double degree;
          if (mIsRTandLB) {
              degree = Math.PI
                      / 4
                      - Math.atan2(mBezierControl1.y - mTouch.y, mTouch.x
                      - mBezierControl1.x);
          } else {
              degree = Math.PI
                      / 4
                      - Math.atan2(mTouch.y - mBezierControl1.y, mTouch.x
                      - mBezierControl1.x);
          }
  
          double d1 = (float) 25 * 1.414 * Math.cos(degree);
          double d2 = (float) 25 * 1.414 * Math.sin(degree);
          float x = (float) (mTouch.x + d1);
          float y;
          if (mIsRTandLB) {
              y = (float) (mTouch.y + d2);
          } else {
              y = (float) (mTouch.y - d2);
          }
          mPath1.reset();
          mPath1.moveTo(x, y);
          mPath1.lineTo(mTouch.x, mTouch.y);
          mPath1.lineTo(mBezierControl1.x, mBezierControl1.y);
          mPath1.lineTo(mBezierStart1.x, mBezierStart1.y);
          mPath1.close();
          float rotateDegrees;
          canvas.save();
  
          canvas.clipPath(mPath0, Region.Op.XOR);
          canvas.clipPath(mPath1, Region.Op.INTERSECT);
          int leftx;
          int rightx;
          GradientDrawable mCurrentPageShadow;
          if (mIsRTandLB) {
              leftx = (int) (mBezierControl1.x);
              rightx = (int) mBezierControl1.x + 25;
              mCurrentPageShadow = mFrontShadowDrawableVLR;
          } else {
              leftx = (int) (mBezierControl1.x - 25);
              rightx = (int) mBezierControl1.x + 1;
              mCurrentPageShadow = mFrontShadowDrawableVRL;
          }
  
          rotateDegrees = (float) Math.toDegrees(Math.atan2(mTouch.x
                  - mBezierControl1.x, mBezierControl1.y - mTouch.y));
          canvas.rotate(rotateDegrees, mBezierControl1.x, mBezierControl1.y);
          mCurrentPageShadow.setBounds(leftx,
                  (int) (mBezierControl1.y - mMaxLength), rightx,
                  (int) (mBezierControl1.y));
          mCurrentPageShadow.draw(canvas);
          canvas.restore();
  
          mPath1.reset();
          mPath1.moveTo(x, y);
          mPath1.lineTo(mTouch.x, mTouch.y);
          mPath1.lineTo(mBezierControl2.x, mBezierControl2.y);
          mPath1.lineTo(mBezierStart2.x, mBezierStart2.y);
          mPath1.close();
          canvas.save();
          canvas.clipPath(mPath0, Region.Op.XOR);
          canvas.clipPath(mPath1, Region.Op.INTERSECT);
          if (mIsRTandLB) {
              leftx = (int) (mBezierControl2.y);
              rightx = (int) (mBezierControl2.y + 25);
              mCurrentPageShadow = mFrontShadowDrawableHTB;
          } else {
              leftx = (int) (mBezierControl2.y - 25);
              rightx = (int) (mBezierControl2.y + 1);
              mCurrentPageShadow = mFrontShadowDrawableHBT;
          }
          rotateDegrees = (float) Math.toDegrees(Math.atan2(mBezierControl2.y - mTouch.y, mBezierControl2.x - mTouch.x));
          canvas.rotate(rotateDegrees, mBezierControl2.x, mBezierControl2.y);
          float temp;
          if (mBezierControl2.y < 0)
              temp = mBezierControl2.y - mViewHeight;
          else
              temp = mBezierControl2.y;
  
          int hmg = (int) Math.hypot(mBezierControl2.x, temp);
          if (hmg > mMaxLength) {
              mCurrentPageShadow
                      .setBounds((int) (mBezierControl2.x - 25) - hmg, leftx,
                              (int) (mBezierControl2.x + mMaxLength) - hmg,
                              rightx);
          } else {
              mCurrentPageShadow.setBounds(
                      (int) (mBezierControl2.x - mMaxLength), leftx,
                      (int) (mBezierControl2.x), rightx);
          }
          mCurrentPageShadow.draw(canvas);
          canvas.restore();
      }
  
      private void drawCurrentBackArea(Canvas canvas, Bitmap bitmap) {
          int i = (int) (mBezierStart1.x + mBezierControl1.x) / 2;
          float f1 = Math.abs(i - mBezierControl1.x);
          int i1 = (int) (mBezierStart2.y + mBezierControl2.y) / 2;
          float f2 = Math.abs(i1 - mBezierControl2.y);
          float f3 = Math.min(f1, f2);
          mPath1.reset();
          mPath1.moveTo(mBezierVertex2.x, mBezierVertex2.y);
          mPath1.lineTo(mBezierVertex1.x, mBezierVertex1.y);
          mPath1.lineTo(mBezierEnd1.x, mBezierEnd1.y);
          mPath1.lineTo(mTouch.x, mTouch.y);
          mPath1.lineTo(mBezierEnd2.x, mBezierEnd2.y);
          mPath1.close();
          GradientDrawable mFolderShadowDrawable;
          int left;
          int right;
          if (mIsRTandLB) {
              left = (int) (mBezierStart1.x - 1);
              right = (int) (mBezierStart1.x + f3 + 1);
              mFolderShadowDrawable = mFolderShadowDrawableLR;
          } else {
              left = (int) (mBezierStart1.x - f3 - 1);
              right = (int) (mBezierStart1.x + 1);
              mFolderShadowDrawable = mFolderShadowDrawableRL;
          }
          canvas.save();
          canvas.clipPath(mPath0);
          canvas.clipPath(mPath1, Region.Op.INTERSECT);
  
          mPaint.setColorFilter(colorFilter);
  
          float dis = (float) Math.hypot(mCornerX - mBezierControl1.x, mBezierControl2.y - mCornerY);
          float f8 = (mCornerX - mBezierControl1.x) / dis;
          float f9 = (mBezierControl2.y - mCornerY) / dis;
          mMatrixArray[0] = 1 - 2 * f9 * f9;
          mMatrixArray[1] = 2 * f8 * f9;
          mMatrixArray[3] = mMatrixArray[1];
          mMatrixArray[4] = 1 - 2 * f8 * f8;
          mMatrix.reset();
          mMatrix.setValues(mMatrixArray);
          mMatrix.preTranslate(-mBezierControl1.x, -mBezierControl1.y);
          mMatrix.postTranslate(mBezierControl1.x, mBezierControl1.y);
          canvas.drawBitmap(bitmap, mMatrix, mPaint);
  
          mPaint.setColorFilter(null);
          canvas.rotate(mDegrees, mBezierStart1.x, mBezierStart1.y);
          mFolderShadowDrawable.setBounds(left, (int) mBezierStart1.y, right, (int) (mBezierStart1.y + mMaxLength));
          mFolderShadowDrawable.draw(canvas);
          canvas.restore();
      }
  
      public int getViewWidth() {
          return mViewWidth;
      }
  
      public int getViewHeight() {
          return mViewHeight;
      }
  
      public void setViewSize(int mViewWidth, int mViewHeight) {
          this.mViewWidth = mViewWidth;
          this.mViewHeight = mViewHeight;
          refresh();
      }
  
      public void setBitmaps(Bitmap bitmap1, Bitmap bitmap2) {
          this.mCurPageBitmap = bitmap1;
          this.mNextPageBitmap = bitmap2;
      }
  
      public void setCurPageBitmap(Bitmap mCurPageBitmap) {
          this.mCurPageBitmap = mCurPageBitmap;
      }
  
      public void setNextPageBitmap(Bitmap mNextPageBitmap) {
          this.mNextPageBitmap = mNextPageBitmap;
      }
  
      public int getCornerX() {
          return mCornerX;
      }
  
      public int getCornerY() {
          return mCornerY;
      }
  
      public void refresh() {
          postInvalidate();
      }
  }
  