op == null) ? new FilterCropRepresentation() : crop;

package com.android.gallery3d.filtershow.imageshow;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import com.android.gallery3d.R;
import com.android.gallery3d.filtershow.crop.CropDrawingUtils;
import com.android.gallery3d.filtershow.crop.CropMath;
import com.android.gallery3d.filtershow.crop.CropObject;
import com.android.gallery3d.filtershow.editors.EditorCrop;
import com.android.gallery3d.filtershow.filters.FilterCropRepresentation;
import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils.GeometryHolder;

public class ImageCrop extends ImageShow {

    private static final String TAG = ImageCrop.class.getSimpleName();

    private RectF mImageBounds = new RectF();

    private RectF mScreenCropBounds = new RectF();

    private Paint mPaint = new Paint();

    private CropObject mCropObj = null;

    private GeometryHolder mGeometry = new GeometryHolder();

    private GeometryHolder mUpdateHolder = new GeometryHolder();

    private Drawable mCropIndicator;

    private int mIndicatorSize;

    private boolean mMovingBlock = false;

    private Matrix mDisplayMatrix = null;

    private Matrix mDisplayCropMatrix = null;

    private Matrix mDisplayMatrixInverse = null;

    private float mPrevX = 0;

    private float mPrevY = 0;

    private int mMinSideSize = 90;

    private int mTouchTolerance = 40;

    private enum Mode {

        NONE, MOVE
    }

    private Mode mState = Mode.NONE;

    private boolean mValidDraw = false;

    FilterCropRepresentation mLocalRep = new FilterCropRepresentation();

    EditorCrop mEditorCrop;

    public ImageCrop(Context context) {
        super(context);
        setup(context);
    }

    public ImageCrop(Context context, AttributeSet attrs) {
        super(context, attrs);
        setup(context);
    }

    public ImageCrop(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setup(context);
    }

    private void setup(Context context) {
        Resources rsc = context.getResources();
        mCropIndicator = rsc.getDrawable(R.drawable.camera_crop);
        mIndicatorSize = (int) rsc.getDimension(R.dimen.crop_indicator_size);
        mMinSideSize = (int) rsc.getDimension(R.dimen.crop_min_side);
        mTouchTolerance = (int) rsc.getDimension(R.dimen.crop_touch_tolerance);
    }

    public void setFilterCropRepresentation(FilterCropRepresentation crop) {
        mLocalRep = (cr

        GeometryMathUtils.initializeHolder(mUpdateHolder, mLocalRep);
        mValidDraw = true;
    }

    public FilterCropRepresentation getFinalRepresentation() {
        return mLocalRep;
    }

    private void internallyUpdateLocalRep(RectF crop, RectF image) {
        FilterCropRepresentation.findNormalizedCrop(crop, (int) image.width(), (int) image.height());
        mGeometry.crop.set(crop);
        mUpdateHolder.set(mGeometry);
        mLocalRep.setCrop(crop);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        if (mDisplayMatrix == null || mDisplayMatrixInverse == null) {
            return true;
        }
        float[] touchPoint = { x, y };
        mDisplayMatrixInverse.mapPoints(touchPoint);
        x = touchPoint[0];
        y = touchPoint[1];
        switch(event.getActionMasked()) {
            case (MotionEvent.ACTION_DOWN):
                if (mState == Mode.NONE) {
                    if (!mCropObj.selectEdge(x, y)) {
                        mMovingBlock = mCropObj.selectEdge(CropObject.MOVE_BLOCK);
                    }
                    mPrevX = x;
                    mPrevY = y;
                    mState = Mode.MOVE;
                }
                break;
            case (MotionEvent.ACTION_UP):
                if (mState == Mode.MOVE) {
                    mCropObj.selectEdge(CropObject.MOVE_NONE);
                    mMovingBlock = false;
                    mPrevX = x;
                    mPrevY = y;
                    mState = Mode.NONE;
                    internallyUpdateLocalRep(mCropObj.getInnerBounds(), mCropObj.getOuterBounds());
                }
                break;
            case (MotionEvent.ACTION_MOVE):
                if (mState == Mode.MOVE) {
                    float dx = x - mPrevX;
                    float dy = y - mPrevY;
                    mCropObj.moveCurrentSelection(dx, dy);
                    mPrevX = x;
                    mPrevY = y;
                }
                break;
            default:
                break;
        }
        invalidate();
        return true;
    }

    private void clearDisplay() {
        mDisplayMatrix = null;
        mDisplayMatrixInverse = null;
        invalidate();
    }

    public void applyFreeAspect() {
        mCropObj.unsetAspectRatio();
        invalidate();
    }

    public void applyOriginalAspect() {
        RectF outer = mCropObj.getOuterBounds();
        float w = outer.width();
        float h = outer.height();
        if (w > 0 && h > 0) {
            applyAspect(w, h);
            mCropObj.resetBoundsTo(outer, outer);
            internallyUpdateLocalRep(mCropObj.getInnerBounds(), mCropObj.getOuterBounds());
        } else {
            Log.w(TAG, "failed to set aspect ratio original");
        }
        invalidate();
    }

    public void applyAspect(float x, float y) {
        if (x <= 0 || y <= 0) {
            throw new IllegalArgumentException("Bad arguments to applyAspect");
        }
        // If we are rotated by 90 degrees from horizontal, swap x and y
        if (GeometryMathUtils.needsDimensionSwap(mGeometry.rotation)) {
            float tmp = x;
            x = y;
            y = tmp;
        }
        if (!mCropObj.setInnerAspectRatio(x, y)) {
            Log.w(TAG, "failed to set aspect ratio");
        }
        internallyUpdateLocalRep(mCropObj.getInnerBounds(), mCropObj.getOuterBounds());
        invalidate();
    }

    /**
     * Rotates first d bits in integer x to the left some number of times.
     */
    private int bitCycleLeft(int x, int times, int d) {
        int mask = (1 << d) - 1;
        int mout = x & mask;
        times %= d;
        int hi = mout >> (d - times);
        int low = (mout << times) & mask;
        int ret = x & ~mask;
        ret |= low;
        ret |= hi;
        return ret;
    }

    /**
     * Find the selected edge or corner in screen coordinates.
     */
    private int decode(int movingEdges, float rotation) {
        int rot = CropMath.constrainedRotation(rotation);
        switch(rot) {
            case 90:
                return bitCycleLeft(movingEdges, 1, 4);
            case 180:
                return bitCycleLeft(movingEdges, 2, 4);
            case 270:
                return bitCycleLeft(movingEdges, 3, 4);
            default:
                return movingEdges;
        }
    }

    private void forceStateConsistency() {
        MasterImage master = MasterImage.getImage();
        Bitmap image = master.getFiltersOnlyImage();
        int width = image.getWidth();
        int height = image.getHeight();
        if (mCropObj == null || !mUpdateHolder.equals(mGeometry) || mImageBounds.width() != width || mImageBounds.height() != height || !mLocalRep.getCrop().equals(mUpdateHolder.crop)) {
            mImageBounds.set(0, 0, width, height);
            mGeometry.set(mUpdateHolder);
            mLocalRep.setCrop(mUpdateHolder.crop);
            RectF scaledCrop = new RectF(mUpdateHolder.crop);
            FilterCropRepresentation.findScaledCrop(scaledCrop, width, height);
            mCropObj = new CropObject(mImageBounds, scaledCrop, (int) mUpdateHolder.straighten);
            mState = Mode.NONE;
            clearDisplay();
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        clearDisplay();
    }

    @Override
    public void onDraw(Canvas canvas) {
        Bitmap bitmap = MasterImage.getImage().getFiltersOnlyImage();
        if (bitmap == null) {
            MasterImage.getImage().invalidateFiltersOnly();
        }
        if (!mValidDraw || bitmap == null) {
            return;
        }
        forceStateConsistency();
        mImageBounds.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
        // If display matrix doesn't exist, create it and its dependencies
        if (mDisplayCropMatrix == null || mDisplayMatrix == null || mDisplayMatrixInverse == null) {
            mCropObj.unsetAspectRatio();
            mDisplayMatrix = GeometryMathUtils.getFullGeometryToScreenMatrix(mGeometry, bitmap.getWidth(), bitmap.getHeight(), canvas.getWidth(), canvas.getHeight());
            float straighten = mGeometry.straighten;
            mGeometry.straighten = 0;
            mDisplayCropMatrix = GeometryMathUtils.getFullGeometryToScreenMatrix(mGeometry, bitmap.getWidth(), bitmap.getHeight(), canvas.getWidth(), canvas.getHeight());
            mGeometry.straighten = straighten;
            mDisplayMatrixInverse = new Matrix();
            mDisplayMatrixInverse.reset();
            if (!mDisplayCropMatrix.invert(mDisplayMatrixInverse)) {
                Log.w(TAG, "could not invert display matrix");
                mDisplayMatrixInverse = null;
                return;
            }
            // Scale min side and tolerance by display matrix scale factor
            mCropObj.setMinInnerSideSize(mDisplayMatrixInverse.mapRadius(mMinSideSize));
            mCropObj.setTouchTolerance(mDisplayMatrixInverse.mapRadius(mTouchTolerance));
            // drive Crop engine to clamp to crop bounds
            int[] sides = { CropObject.MOVE_TOP, CropObject.MOVE_BOTTOM, CropObject.MOVE_LEFT, CropObject.MOVE_RIGHT };
            int delta = Math.min(canvas.getWidth(), canvas.getHeight()) / 4;
            int[] dx = { delta, -delta, 0, 0 };
            int[] dy = { 0, 0, delta, -delta };
            for (int i = 0; i < sides.length; i++) {
                mCropObj.selectEdge(sides[i]);
                mCropObj.moveCurrentSelection(dx[i], dy[i]);
                mCropObj.moveCurrentSelection(-dx[i], -dy[i]);
            }
            mCropObj.selectEdge(CropObject.MOVE_NONE);
        }
        // Draw actual bitmap
        mPaint.reset();
        mPaint.setAntiAlias(true);
        mPaint.setFilterBitmap(true);
        canvas.drawBitmap(bitmap, mDisplayMatrix, mPaint);
        mCropObj.getInnerBounds(mScreenCropBounds);
        RectF outer = mCropObj.getOuterBounds();
        FilterCropRepresentation.findNormalizedCrop(mScreenCropBounds, (int) outer.width(), (int) outer.height());
        FilterCropRepresentation.findScaledCrop(mScreenCropBounds, bitmap.getWidth(), bitmap.getHeight());
        if (mDisplayCropMatrix.mapRect(mScreenCropBounds)) {
            // Draw crop rect and markers
            CropDrawingUtils.drawCropRect(canvas, mScreenCropBounds);
            CropDrawingUtils.drawShade(canvas, mScreenCropBounds);
            CropDrawingUtils.drawRuleOfThird(canvas, mScreenCropBounds);
            CropDrawingUtils.drawIndicators(canvas, mCropIndicator, mIndicatorSize, mScreenCropBounds, mCropObj.isFixedAspect(), decode(mCropObj.getSelectState(), mGeometry.rotation.value()));
        }
    }

    public void setEditor(EditorCrop editorCrop) {
        mEditorCrop = editorCrop;
    }
}


private int decode(int movingEdges, float rotation) {
        int rot = CropMath.constrainedRotation(rotation);
        switch(rot) {
            case 90:
                return bitCycleLeft(movingEdges, 1, 4);
            case 180:
                return bitCycleLeft(movingEdges, 2, 4);
            case 270:
                return bitCycleLeft(movingEdges, 3, 4);
            default:
                return movingEdges;
        }
    }

package com.android.gallery3d.filtershow.imageshow;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import com.android.gallery3d.R;
import com.android.gallery3d.filtershow.crop.CropDrawingUtils;
import com.android.gallery3d.filtershow.crop.CropMath;
import com.android.gallery3d.filtershow.crop.CropObject;
import com.android.gallery3d.filtershow.editors.EditorCrop;
import com.android.gallery3d.filtershow.filters.FilterCropRepresentation;
import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils.GeometryHolder;

public class ImageCrop extends ImageShow {

    private static final String TAG = ImageCrop.class.getSimpleName();

    private RectF mImageBounds = new RectF();

    private RectF mScreenCropBounds = new RectF();

    private Paint mPaint = new Paint();

    private CropObject mCropObj = null;

    private GeometryHolder mGeometry = new GeometryHolder();

    private GeometryHolder mUpdateHolder = new GeometryHolder();

    private Drawable mCropIndicator;

    private int mIndicatorSize;

    private boolean mMovingBlock = false;

    private Matrix mDisplayMatrix = null;

    private Matrix mDisplayCropMatrix = null;

    private Matrix mDisplayMatrixInverse = null;

    private float mPrevX = 0;

    private float mPrevY = 0;

    private int mMinSideSize = 90;

    private int mTouchTolerance = 40;

    private enum Mode {

        NONE, MOVE
    }

    private Mode mState = Mode.NONE;

    private boolean mValidDraw = false;

    FilterCropRepresentation mLocalRep = new FilterCropRepresentation();

    EditorCrop mEditorCrop;

    public ImageCrop(Context context) {
        super(context);
        setup(context);
    }

    public ImageCrop(Context context, AttributeSet attrs) {
        super(context, attrs);
        setup(context);
    }

    public ImageCrop(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setup(context);
    }

    private void setup(Context context) {
        Resources rsc = context.getResources();
        mCropIndicator = rsc.getDrawable(R.drawable.camera_crop);
        mIndicatorSize = (int) rsc.getDimension(R.dimen.crop_indicator_size);
        mMinSideSize = (int) rsc.getDimension(R.dimen.crop_min_side);
        mTouchTolerance = (int) rsc.getDimension(R.dimen.crop_touch_tolerance);
    }

    public void setFilterCropRepresentation(FilterCropRepresentation crop) {
        mLocalRep = (crop == null) ? new FilterCropRepresentation() : crop;
        GeometryMathUtils.initializeHolder(mUpdateHolder, mLocalRep);
        mValidDraw = true;
    }

    public FilterCropRepresentation getFinalRepresentation() {
        return mLocalRep;
    }

    private void internallyUpdateLocalRep(RectF crop, RectF image) {
        FilterCropRepresentation.findNormalizedCrop(crop, (int) image.width(), (int) image.height());
        mGeometry.crop.set(crop);
        mUpdateHolder.set(mGeometry);
        mLocalRep.setCrop(crop);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        if (mDisplayMatrix == null || mDisplayMatrixInverse == null) {
            return true;
        }
        float[] touchPoint = { x, y };
        mDisplayMatrixInverse.mapPoints(touchPoint);
        x = touchPoint[0];
        y = touchPoint[1];
        switch(event.getActionMasked()) {
            case (MotionEvent.ACTION_DOWN):
                if (mState == Mode.NONE) {
                    if (!mCropObj.selectEdge(x, y)) {
                        mMovingBlock = mCropObj.selectEdge(CropObject.MOVE_BLOCK);
                    }
                    mPrevX = x;
                    mPrevY = y;
                    mState = Mode.MOVE;
                }
                break;
            case (MotionEvent.ACTION_UP):
                if (mState == Mode.MOVE) {
                    mCropObj.selectEdge(CropObject.MOVE_NONE);
                    mMovingBlock = false;
                    mPrevX = x;
                    mPrevY = y;
                    mState = Mode.NONE;
                    internallyUpdateLocalRep(mCropObj.getInnerBounds(), mCropObj.getOuterBounds());
                }
                break;
            case (MotionEvent.ACTION_MOVE):
                if (mState == Mode.MOVE) {
                    float dx = x - mPrevX;
                    float dy = y - mPrevY;
                    mCropObj.moveCurrentSelection(dx, dy);
                    mPrevX = x;
                    mPrevY = y;
                }
                break;
            default:
                break;
        }
        invalidate();
        return true;
    }

    private void clearDisplay() {
        mDisplayMatrix = null;
        mDisplayMatrixInverse = null;
        invalidate();
    }

    public void applyFreeAspect() {
        mCropObj.unsetAspectRatio();
        invalidate();
    }

    public void applyOriginalAspect() {
        RectF outer = mCropObj.getOuterBounds();
        float w = outer.width();
        float h = outer.height();
        if (w > 0 && h > 0) {
            applyAspect(w, h);
            mCropObj.resetBoundsTo(outer, outer);
            internallyUpdateLocalRep(mCropObj.getInnerBounds(), mCropObj.getOuterBounds());
        } else {
            Log.w(TAG, "failed to set aspect ratio original");
        }
        invalidate();
    }

    public void applyAspect(float x, float y) {
        if (x <= 0 || y <= 0) {
            throw new IllegalArgumentException("Bad arguments to applyAspect");
        }
        // If we are rotated by 90 degrees from horizontal, swap x and y
        if (GeometryMathUtils.needsDimensionSwap(mGeometry.rotation)) {
            float tmp = x;
            x = y;
            y = tmp;
        }
        if (!mCropObj.setInnerAspectRatio(x, y)) {
            Log.w(TAG, "failed to set aspect ratio");
        }
        internallyUpdateLocalRep(mCropObj.getInnerBounds(), mCropObj.getOuterBounds());
        invalidate();
    }

    /**
     * Rotates first d bits in integer x to the left some number of times.
     */
    private int bitCycleLeft(int x, int times, int d) {
        int mask = (1 << d) - 1;
        int mout = x & mask;
        times %= d;
        int hi = mout >> (d - times);
        int low = (mout << times) & mask;
        int ret = x & ~mask;
        ret |= low;
        ret |= hi;
        return ret;
    }

    /**
     * Find the selected edge or corner in screen coordinates.
     */

    private void forceStateConsistency() {
        MasterImage master = MasterImage.getImage();
        Bitmap image = master.getFiltersOnlyImage();
        int width = image.getWidth();
        int height = image.getHeight();
        if (mCropObj == null || !mUpdateHolder.equals(mGeometry) || mImageBounds.width() != width || mImageBounds.height() != height || !mLocalRep.getCrop().equals(mUpdateHolder.crop)) {
            mImageBounds.set(0, 0, width, height);
            mGeometry.set(mUpdateHolder);
            mLocalRep.setCrop(mUpdateHolder.crop);
            RectF scaledCrop = new RectF(mUpdateHolder.crop);
            FilterCropRepresentation.findScaledCrop(scaledCrop, width, height);
            mCropObj = new CropObject(mImageBounds, scaledCrop, (int) mUpdateHolder.straighten);
            mState = Mode.NONE;
            clearDisplay();
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        clearDisplay();
    }

    @Override
    public void onDraw(Canvas canvas) {
        Bitmap bitmap = MasterImage.getImage().getFiltersOnlyImage();
        if (bitmap == null) {
            MasterImage.getImage().invalidateFiltersOnly();
        }
        if (!mValidDraw || bitmap == null) {
            return;
        }
        forceStateConsistency();
        mImageBounds.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
        // If display matrix doesn't exist, create it and its dependencies
        if (mDisplayCropMatrix == null || mDisplayMatrix == null || mDisplayMatrixInverse == null) {
            mCropObj.unsetAspectRatio();
            mDisplayMatrix = GeometryMathUtils.getFullGeometryToScreenMatrix(mGeometry, bitmap.getWidth(), bitmap.getHeight(), canvas.getWidth(), canvas.getHeight());
            float straighten = mGeometry.straighten;
            mGeometry.straighten = 0;
            mDisplayCropMatrix = GeometryMathUtils.getFullGeometryToScreenMatrix(mGeometry, bitmap.getWidth(), bitmap.getHeight(), canvas.getWidth(), canvas.getHeight());
            mGeometry.straighten = straighten;
            mDisplayMatrixInverse = new Matrix();
            mDisplayMatrixInverse.reset();
            if (!mDisplayCropMatrix.invert(mDisplayMatrixInverse)) {
                Log.w(TAG, "could not invert display matrix");
                mDisplayMatrixInverse = null;
                return;
            }
            // Scale min side and tolerance by display matrix scale factor
            mCropObj.setMinInnerSideSize(mDisplayMatrixInverse.mapRadius(mMinSideSize));
            mCropObj.setTouchTolerance(mDisplayMatrixInverse.mapRadius(mTouchTolerance));
            // drive Crop engine to clamp to crop bounds
            int[] sides = { CropObject.MOVE_TOP, CropObject.MOVE_BOTTOM, CropObject.MOVE_LEFT, CropObject.MOVE_RIGHT };
            int delta = Math.min(canvas.getWidth(), canvas.getHeight()) / 4;
            int[] dx = { delta, -delta, 0, 0 };
            int[] dy = { 0, 0, delta, -delta };
            for (int i = 0; i < sides.length; i++) {
                mCropObj.selectEdge(sides[i]);
                mCropObj.moveCurrentSelection(dx[i], dy[i]);
                mCropObj.moveCurrentSelection(-dx[i], -dy[i]);
            }
            mCropObj.selectEdge(CropObject.MOVE_NONE);
        }
        // Draw actual bitmap
        mPaint.reset();
        mPaint.setAntiAlias(true);
        mPaint.setFilterBitmap(true);
        canvas.drawBitmap(bitmap, mDisplayMatrix, mPaint);
        mCropObj.getInnerBounds(mScreenCropBounds);
        RectF outer = mCropObj.getOuterBounds();
        FilterCropRepresentation.findNormalizedCrop(mScreenCropBounds, (int) outer.width(), (int) outer.height());
        FilterCropRepresentation.findScaledCrop(mScreenCropBounds, bitmap.getWidth(), bitmap.getHeight());
        if (mDisplayCropMatrix.mapRect(mScreenCropBounds)) {
            // Draw crop rect and markers
            CropDrawingUtils.drawCropRect(canvas, mScreenCropBounds);
            CropDrawingUtils.drawShade(canvas, mScreenCropBounds);
            CropDrawingUtils.drawRuleOfThird(canvas, mScreenCropBounds);
            CropDrawingUtils.drawIndicators(canvas, mCropIndicator, mIndicatorSize, mScreenCropBounds, mCropObj.isFixedAspect(), decode(mCropObj.getSelectState(), mGeometry.rotation.value()));
        }
    }

    public void setEditor(EditorCrop editorCrop) {
        mEditorCrop = editorCrop;
    }
}


if (x <= 0 || y <= 0) {
            throw new IllegalArgumentException("Bad arguments to applyAspect");
        }

package com.android.gallery3d.filtershow.imageshow;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import com.android.gallery3d.R;
import com.android.gallery3d.filtershow.crop.CropDrawingUtils;
import com.android.gallery3d.filtershow.crop.CropMath;
import com.android.gallery3d.filtershow.crop.CropObject;
import com.android.gallery3d.filtershow.editors.EditorCrop;
import com.android.gallery3d.filtershow.filters.FilterCropRepresentation;
import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils.GeometryHolder;

public class ImageCrop extends ImageShow {

    private static final String TAG = ImageCrop.class.getSimpleName();

    private RectF mImageBounds = new RectF();

    private RectF mScreenCropBounds = new RectF();

    private Paint mPaint = new Paint();

    private CropObject mCropObj = null;

    private GeometryHolder mGeometry = new GeometryHolder();

    private GeometryHolder mUpdateHolder = new GeometryHolder();

    private Drawable mCropIndicator;

    private int mIndicatorSize;

    private boolean mMovingBlock = false;

    private Matrix mDisplayMatrix = null;

    private Matrix mDisplayCropMatrix = null;

    private Matrix mDisplayMatrixInverse = null;

    private float mPrevX = 0;

    private float mPrevY = 0;

    private int mMinSideSize = 90;

    private int mTouchTolerance = 40;

    private enum Mode {

        NONE, MOVE
    }

    private Mode mState = Mode.NONE;

    private boolean mValidDraw = false;

    FilterCropRepresentation mLocalRep = new FilterCropRepresentation();

    EditorCrop mEditorCrop;

    public ImageCrop(Context context) {
        super(context);
        setup(context);
    }

    public ImageCrop(Context context, AttributeSet attrs) {
        super(context, attrs);
        setup(context);
    }

    public ImageCrop(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setup(context);
    }

    private void setup(Context context) {
        Resources rsc = context.getResources();
        mCropIndicator = rsc.getDrawable(R.drawable.camera_crop);
        mIndicatorSize = (int) rsc.getDimension(R.dimen.crop_indicator_size);
        mMinSideSize = (int) rsc.getDimension(R.dimen.crop_min_side);
        mTouchTolerance = (int) rsc.getDimension(R.dimen.crop_touch_tolerance);
    }

    public void setFilterCropRepresentation(FilterCropRepresentation crop) {
        mLocalRep = (crop == null) ? new FilterCropRepresentation() : crop;
        GeometryMathUtils.initializeHolder(mUpdateHolder, mLocalRep);
        mValidDraw = true;
    }

    public FilterCropRepresentation getFinalRepresentation() {
        return mLocalRep;
    }

    private void internallyUpdateLocalRep(RectF crop, RectF image) {
        FilterCropRepresentation.findNormalizedCrop(crop, (int) image.width(), (int) image.height());
        mGeometry.crop.set(crop);
        mUpdateHolder.set(mGeometry);
        mLocalRep.setCrop(crop);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        if (mDisplayMatrix == null || mDisplayMatrixInverse == null) {
            return true;
        }
        float[] touchPoint = { x, y };
        mDisplayMatrixInverse.mapPoints(touchPoint);
        x = touchPoint[0];
        y = touchPoint[1];
        switch(event.getActionMasked()) {
            case (MotionEvent.ACTION_DOWN):
                if (mState == Mode.NONE) {
                    if (!mCropObj.selectEdge(x, y)) {
                        mMovingBlock = mCropObj.selectEdge(CropObject.MOVE_BLOCK);
                    }
                    mPrevX = x;
                    mPrevY = y;
                    mState = Mode.MOVE;
                }
                break;
            case (MotionEvent.ACTION_UP):
                if (mState == Mode.MOVE) {
                    mCropObj.selectEdge(CropObject.MOVE_NONE);
                    mMovingBlock = false;
                    mPrevX = x;
                    mPrevY = y;
                    mState = Mode.NONE;
                    internallyUpdateLocalRep(mCropObj.getInnerBounds(), mCropObj.getOuterBounds());
                }
                break;
            case (MotionEvent.ACTION_MOVE):
                if (mState == Mode.MOVE) {
                    float dx = x - mPrevX;
                    float dy = y - mPrevY;
                    mCropObj.moveCurrentSelection(dx, dy);
                    mPrevX = x;
                    mPrevY = y;
                }
                break;
            default:
                break;
        }
        invalidate();
        return true;
    }

    private void clearDisplay() {
        mDisplayMatrix = null;
        mDisplayMatrixInverse = null;
        invalidate();
    }

    public void applyFreeAspect() {
        mCropObj.unsetAspectRatio();
        invalidate();
    }

    public void applyOriginalAspect() {
        RectF outer = mCropObj.getOuterBounds();
        float w = outer.width();
        float h = outer.height();
        if (w > 0 && h > 0) {
            applyAspect(w, h);
            mCropObj.resetBoundsTo(outer, outer);
            internallyUpdateLocalRep(mCropObj.getInnerBounds(), mCropObj.getOuterBounds());
        } else {
            Log.w(TAG, "failed to set aspect ratio original");
        }
        invalidate();
    }

    public void applyAspect(float x, float y) {

        // If we are rotated by 90 degrees from horizontal, swap x and y
        if (GeometryMathUtils.needsDimensionSwap(mGeometry.rotation)) {
            float tmp = x;
            x = y;
            y = tmp;
        }
        if (!mCropObj.setInnerAspectRatio(x, y)) {
            Log.w(TAG, "failed to set aspect ratio");
        }
        internallyUpdateLocalRep(mCropObj.getInnerBounds(), mCropObj.getOuterBounds());
        invalidate();
    }

    /**
     * Rotates first d bits in integer x to the left some number of times.
     */
    private int bitCycleLeft(int x, int times, int d) {
        int mask = (1 << d) - 1;
        int mout = x & mask;
        times %= d;
        int hi = mout >> (d - times);
        int low = (mout << times) & mask;
        int ret = x & ~mask;
        ret |= low;
        ret |= hi;
        return ret;
    }

    /**
     * Find the selected edge or corner in screen coordinates.
     */
    private int decode(int movingEdges, float rotation) {
        int rot = CropMath.constrainedRotation(rotation);
        switch(rot) {
            case 90:
                return bitCycleLeft(movingEdges, 1, 4);
            case 180:
                return bitCycleLeft(movingEdges, 2, 4);
            case 270:
                return bitCycleLeft(movingEdges, 3, 4);
            default:
                return movingEdges;
        }
    }

    private void forceStateConsistency() {
        MasterImage master = MasterImage.getImage();
        Bitmap image = master.getFiltersOnlyImage();
        int width = image.getWidth();
        int height = image.getHeight();
        if (mCropObj == null || !mUpdateHolder.equals(mGeometry) || mImageBounds.width() != width || mImageBounds.height() != height || !mLocalRep.getCrop().equals(mUpdateHolder.crop)) {
            mImageBounds.set(0, 0, width, height);
            mGeometry.set(mUpdateHolder);
            mLocalRep.setCrop(mUpdateHolder.crop);
            RectF scaledCrop = new RectF(mUpdateHolder.crop);
            FilterCropRepresentation.findScaledCrop(scaledCrop, width, height);
            mCropObj = new CropObject(mImageBounds, scaledCrop, (int) mUpdateHolder.straighten);
            mState = Mode.NONE;
            clearDisplay();
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        clearDisplay();
    }

    @Override
    public void onDraw(Canvas canvas) {
        Bitmap bitmap = MasterImage.getImage().getFiltersOnlyImage();
        if (bitmap == null) {
            MasterImage.getImage().invalidateFiltersOnly();
        }
        if (!mValidDraw || bitmap == null) {
            return;
        }
        forceStateConsistency();
        mImageBounds.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
        // If display matrix doesn't exist, create it and its dependencies
        if (mDisplayCropMatrix == null || mDisplayMatrix == null || mDisplayMatrixInverse == null) {
            mCropObj.unsetAspectRatio();
            mDisplayMatrix = GeometryMathUtils.getFullGeometryToScreenMatrix(mGeometry, bitmap.getWidth(), bitmap.getHeight(), canvas.getWidth(), canvas.getHeight());
            float straighten = mGeometry.straighten;
            mGeometry.straighten = 0;
            mDisplayCropMatrix = GeometryMathUtils.getFullGeometryToScreenMatrix(mGeometry, bitmap.getWidth(), bitmap.getHeight(), canvas.getWidth(), canvas.getHeight());
            mGeometry.straighten = straighten;
            mDisplayMatrixInverse = new Matrix();
            mDisplayMatrixInverse.reset();
            if (!mDisplayCropMatrix.invert(mDisplayMatrixInverse)) {
                Log.w(TAG, "could not invert display matrix");
                mDisplayMatrixInverse = null;
                return;
            }
            // Scale min side and tolerance by display matrix scale factor
            mCropObj.setMinInnerSideSize(mDisplayMatrixInverse.mapRadius(mMinSideSize));
            mCropObj.setTouchTolerance(mDisplayMatrixInverse.mapRadius(mTouchTolerance));
            // drive Crop engine to clamp to crop bounds
            int[] sides = { CropObject.MOVE_TOP, CropObject.MOVE_BOTTOM, CropObject.MOVE_LEFT, CropObject.MOVE_RIGHT };
            int delta = Math.min(canvas.getWidth(), canvas.getHeight()) / 4;
            int[] dx = { delta, -delta, 0, 0 };
            int[] dy = { 0, 0, delta, -delta };
            for (int i = 0; i < sides.length; i++) {
                mCropObj.selectEdge(sides[i]);
                mCropObj.moveCurrentSelection(dx[i], dy[i]);
                mCropObj.moveCurrentSelection(-dx[i], -dy[i]);
            }
            mCropObj.selectEdge(CropObject.MOVE_NONE);
        }
        // Draw actual bitmap
        mPaint.reset();
        mPaint.setAntiAlias(true);
        mPaint.setFilterBitmap(true);
        canvas.drawBitmap(bitmap, mDisplayMatrix, mPaint);
        mCropObj.getInnerBounds(mScreenCropBounds);
        RectF outer = mCropObj.getOuterBounds();
        FilterCropRepresentation.findNormalizedCrop(mScreenCropBounds, (int) outer.width(), (int) outer.height());
        FilterCropRepresentation.findScaledCrop(mScreenCropBounds, bitmap.getWidth(), bitmap.getHeight());
        if (mDisplayCropMatrix.mapRect(mScreenCropBounds)) {
            // Draw crop rect and markers
            CropDrawingUtils.drawCropRect(canvas, mScreenCropBounds);
            CropDrawingUtils.drawShade(canvas, mScreenCropBounds);
            CropDrawingUtils.drawRuleOfThird(canvas, mScreenCropBounds);
            CropDrawingUtils.drawIndicators(canvas, mCropIndicator, mIndicatorSize, mScreenCropBounds, mCropObj.isFixedAspect(), decode(mCropObj.getSelectState(), mGeometry.rotation.value()));
        }
    }

    public void setEditor(EditorCrop editorCrop) {
        mEditorCrop = editorCrop;
    }
}


switch(rot) {
            case 90:
                return bitCycleLeft(movingEdges, 1, 4);
            case 180:
                return bitCycleLeft(movingEdges, 2, 4);
            case 270:
                return bitCycleLeft(movingEdges, 3, 4);
            default:
                return movingEdges;
        }

package com.android.gallery3d.filtershow.imageshow;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import com.android.gallery3d.R;
import com.android.gallery3d.filtershow.crop.CropDrawingUtils;
import com.android.gallery3d.filtershow.crop.CropMath;
import com.android.gallery3d.filtershow.crop.CropObject;
import com.android.gallery3d.filtershow.editors.EditorCrop;
import com.android.gallery3d.filtershow.filters.FilterCropRepresentation;
import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils.GeometryHolder;

public class ImageCrop extends ImageShow {

    private static final String TAG = ImageCrop.class.getSimpleName();

    private RectF mImageBounds = new RectF();

    private RectF mScreenCropBounds = new RectF();

    private Paint mPaint = new Paint();

    private CropObject mCropObj = null;

    private GeometryHolder mGeometry = new GeometryHolder();

    private GeometryHolder mUpdateHolder = new GeometryHolder();

    private Drawable mCropIndicator;

    private int mIndicatorSize;

    private boolean mMovingBlock = false;

    private Matrix mDisplayMatrix = null;

    private Matrix mDisplayCropMatrix = null;

    private Matrix mDisplayMatrixInverse = null;

    private float mPrevX = 0;

    private float mPrevY = 0;

    private int mMinSideSize = 90;

    private int mTouchTolerance = 40;

    private enum Mode {

        NONE, MOVE
    }

    private Mode mState = Mode.NONE;

    private boolean mValidDraw = false;

    FilterCropRepresentation mLocalRep = new FilterCropRepresentation();

    EditorCrop mEditorCrop;

    public ImageCrop(Context context) {
        super(context);
        setup(context);
    }

    public ImageCrop(Context context, AttributeSet attrs) {
        super(context, attrs);
        setup(context);
    }

    public ImageCrop(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setup(context);
    }

    private void setup(Context context) {
        Resources rsc = context.getResources();
        mCropIndicator = rsc.getDrawable(R.drawable.camera_crop);
        mIndicatorSize = (int) rsc.getDimension(R.dimen.crop_indicator_size);
        mMinSideSize = (int) rsc.getDimension(R.dimen.crop_min_side);
        mTouchTolerance = (int) rsc.getDimension(R.dimen.crop_touch_tolerance);
    }

    public void setFilterCropRepresentation(FilterCropRepresentation crop) {
        mLocalRep = (crop == null) ? new FilterCropRepresentation() : crop;
        GeometryMathUtils.initializeHolder(mUpdateHolder, mLocalRep);
        mValidDraw = true;
    }

    public FilterCropRepresentation getFinalRepresentation() {
        return mLocalRep;
    }

    private void internallyUpdateLocalRep(RectF crop, RectF image) {
        FilterCropRepresentation.findNormalizedCrop(crop, (int) image.width(), (int) image.height());
        mGeometry.crop.set(crop);
        mUpdateHolder.set(mGeometry);
        mLocalRep.setCrop(crop);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        if (mDisplayMatrix == null || mDisplayMatrixInverse == null) {
            return true;
        }
        float[] touchPoint = { x, y };
        mDisplayMatrixInverse.mapPoints(touchPoint);
        x = touchPoint[0];
        y = touchPoint[1];
        switch(event.getActionMasked()) {
            case (MotionEvent.ACTION_DOWN):
                if (mState == Mode.NONE) {
                    if (!mCropObj.selectEdge(x, y)) {
                        mMovingBlock = mCropObj.selectEdge(CropObject.MOVE_BLOCK);
                    }
                    mPrevX = x;
                    mPrevY = y;
                    mState = Mode.MOVE;
                }
                break;
            case (MotionEvent.ACTION_UP):
                if (mState == Mode.MOVE) {
                    mCropObj.selectEdge(CropObject.MOVE_NONE);
                    mMovingBlock = false;
                    mPrevX = x;
                    mPrevY = y;
                    mState = Mode.NONE;
                    internallyUpdateLocalRep(mCropObj.getInnerBounds(), mCropObj.getOuterBounds());
                }
                break;
            case (MotionEvent.ACTION_MOVE):
                if (mState == Mode.MOVE) {
                    float dx = x - mPrevX;
                    float dy = y - mPrevY;
                    mCropObj.moveCurrentSelection(dx, dy);
                    mPrevX = x;
                    mPrevY = y;
                }
                break;
            default:
                break;
        }
        invalidate();
        return true;
    }

    private void clearDisplay() {
        mDisplayMatrix = null;
        mDisplayMatrixInverse = null;
        invalidate();
    }

    public void applyFreeAspect() {
        mCropObj.unsetAspectRatio();
        invalidate();
    }

    public void applyOriginalAspect() {
        RectF outer = mCropObj.getOuterBounds();
        float w = outer.width();
        float h = outer.height();
        if (w > 0 && h > 0) {
            applyAspect(w, h);
            mCropObj.resetBoundsTo(outer, outer);
            internallyUpdateLocalRep(mCropObj.getInnerBounds(), mCropObj.getOuterBounds());
        } else {
            Log.w(TAG, "failed to set aspect ratio original");
        }
        invalidate();
    }

    public void applyAspect(float x, float y) {
        if (x <= 0 || y <= 0) {
            throw new IllegalArgumentException("Bad arguments to applyAspect");
        }
        // If we are rotated by 90 degrees from horizontal, swap x and y
        if (GeometryMathUtils.needsDimensionSwap(mGeometry.rotation)) {
            float tmp = x;
            x = y;
            y = tmp;
        }
        if (!mCropObj.setInnerAspectRatio(x, y)) {
            Log.w(TAG, "failed to set aspect ratio");
        }
        internallyUpdateLocalRep(mCropObj.getInnerBounds(), mCropObj.getOuterBounds());
        invalidate();
    }

    /**
     * Rotates first d bits in integer x to the left some number of times.
     */
    private int bitCycleLeft(int x, int times, int d) {
        int mask = (1 << d) - 1;
        int mout = x & mask;
        times %= d;
        int hi = mout >> (d - times);
        int low = (mout << times) & mask;
        int ret = x & ~mask;
        ret |= low;
        ret |= hi;
        return ret;
    }

    /**
     * Find the selected edge or corner in screen coordinates.
     */
    private int decode(int movingEdges, float rotation) {
        int rot = CropMath.constrainedRotation(rotation);

    }

    private void forceStateConsistency() {
        MasterImage master = MasterImage.getImage();
        Bitmap image = master.getFiltersOnlyImage();
        int width = image.getWidth();
        int height = image.getHeight();
        if (mCropObj == null || !mUpdateHolder.equals(mGeometry) || mImageBounds.width() != width || mImageBounds.height() != height || !mLocalRep.getCrop().equals(mUpdateHolder.crop)) {
            mImageBounds.set(0, 0, width, height);
            mGeometry.set(mUpdateHolder);
            mLocalRep.setCrop(mUpdateHolder.crop);
            RectF scaledCrop = new RectF(mUpdateHolder.crop);
            FilterCropRepresentation.findScaledCrop(scaledCrop, width, height);
            mCropObj = new CropObject(mImageBounds, scaledCrop, (int) mUpdateHolder.straighten);
            mState = Mode.NONE;
            clearDisplay();
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        clearDisplay();
    }

    @Override
    public void onDraw(Canvas canvas) {
        Bitmap bitmap = MasterImage.getImage().getFiltersOnlyImage();
        if (bitmap == null) {
            MasterImage.getImage().invalidateFiltersOnly();
        }
        if (!mValidDraw || bitmap == null) {
            return;
        }
        forceStateConsistency();
        mImageBounds.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
        // If display matrix doesn't exist, create it and its dependencies
        if (mDisplayCropMatrix == null || mDisplayMatrix == null || mDisplayMatrixInverse == null) {
            mCropObj.unsetAspectRatio();
            mDisplayMatrix = GeometryMathUtils.getFullGeometryToScreenMatrix(mGeometry, bitmap.getWidth(), bitmap.getHeight(), canvas.getWidth(), canvas.getHeight());
            float straighten = mGeometry.straighten;
            mGeometry.straighten = 0;
            mDisplayCropMatrix = GeometryMathUtils.getFullGeometryToScreenMatrix(mGeometry, bitmap.getWidth(), bitmap.getHeight(), canvas.getWidth(), canvas.getHeight());
            mGeometry.straighten = straighten;
            mDisplayMatrixInverse = new Matrix();
            mDisplayMatrixInverse.reset();
            if (!mDisplayCropMatrix.invert(mDisplayMatrixInverse)) {
                Log.w(TAG, "could not invert display matrix");
                mDisplayMatrixInverse = null;
                return;
            }
            // Scale min side and tolerance by display matrix scale factor
            mCropObj.setMinInnerSideSize(mDisplayMatrixInverse.mapRadius(mMinSideSize));
            mCropObj.setTouchTolerance(mDisplayMatrixInverse.mapRadius(mTouchTolerance));
            // drive Crop engine to clamp to crop bounds
            int[] sides = { CropObject.MOVE_TOP, CropObject.MOVE_BOTTOM, CropObject.MOVE_LEFT, CropObject.MOVE_RIGHT };
            int delta = Math.min(canvas.getWidth(), canvas.getHeight()) / 4;
            int[] dx = { delta, -delta, 0, 0 };
            int[] dy = { 0, 0, delta, -delta };
            for (int i = 0; i < sides.length; i++) {
                mCropObj.selectEdge(sides[i]);
                mCropObj.moveCurrentSelection(dx[i], dy[i]);
                mCropObj.moveCurrentSelection(-dx[i], -dy[i]);
            }
            mCropObj.selectEdge(CropObject.MOVE_NONE);
        }
        // Draw actual bitmap
        mPaint.reset();
        mPaint.setAntiAlias(true);
        mPaint.setFilterBitmap(true);
        canvas.drawBitmap(bitmap, mDisplayMatrix, mPaint);
        mCropObj.getInnerBounds(mScreenCropBounds);
        RectF outer = mCropObj.getOuterBounds();
        FilterCropRepresentation.findNormalizedCrop(mScreenCropBounds, (int) outer.width(), (int) outer.height());
        FilterCropRepresentation.findScaledCrop(mScreenCropBounds, bitmap.getWidth(), bitmap.getHeight());
        if (mDisplayCropMatrix.mapRect(mScreenCropBounds)) {
            // Draw crop rect and markers
            CropDrawingUtils.drawCropRect(canvas, mScreenCropBounds);
            CropDrawingUtils.drawShade(canvas, mScreenCropBounds);
            CropDrawingUtils.drawRuleOfThird(canvas, mScreenCropBounds);
            CropDrawingUtils.drawIndicators(canvas, mCropIndicator, mIndicatorSize, mScreenCropBounds, mCropObj.isFixedAspect(), decode(mCropObj.getSelectState(), mGeometry.rotation.value()));
        }
    }

    public void setEditor(EditorCrop editorCrop) {
        mEditorCrop = editorCrop;
    }
}


for (int i = 0; i < sides.length; i++) {
                mCropObj.selectEdge(sides[i]);
                mCropObj.moveCurrentSelection(dx[i], dy[i]);
                mCropObj.moveCurrentSelection(-dx[i], -dy[i]);
            }

package com.android.gallery3d.filtershow.imageshow;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import com.android.gallery3d.R;
import com.android.gallery3d.filtershow.crop.CropDrawingUtils;
import com.android.gallery3d.filtershow.crop.CropMath;
import com.android.gallery3d.filtershow.crop.CropObject;
import com.android.gallery3d.filtershow.editors.EditorCrop;
import com.android.gallery3d.filtershow.filters.FilterCropRepresentation;
import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils.GeometryHolder;

public class ImageCrop extends ImageShow {

    private static final String TAG = ImageCrop.class.getSimpleName();

    private RectF mImageBounds = new RectF();

    private RectF mScreenCropBounds = new RectF();

    private Paint mPaint = new Paint();

    private CropObject mCropObj = null;

    private GeometryHolder mGeometry = new GeometryHolder();

    private GeometryHolder mUpdateHolder = new GeometryHolder();

    private Drawable mCropIndicator;

    private int mIndicatorSize;

    private boolean mMovingBlock = false;

    private Matrix mDisplayMatrix = null;

    private Matrix mDisplayCropMatrix = null;

    private Matrix mDisplayMatrixInverse = null;

    private float mPrevX = 0;

    private float mPrevY = 0;

    private int mMinSideSize = 90;

    private int mTouchTolerance = 40;

    private enum Mode {

        NONE, MOVE
    }

    private Mode mState = Mode.NONE;

    private boolean mValidDraw = false;

    FilterCropRepresentation mLocalRep = new FilterCropRepresentation();

    EditorCrop mEditorCrop;

    public ImageCrop(Context context) {
        super(context);
        setup(context);
    }

    public ImageCrop(Context context, AttributeSet attrs) {
        super(context, attrs);
        setup(context);
    }

    public ImageCrop(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setup(context);
    }

    private void setup(Context context) {
        Resources rsc = context.getResources();
        mCropIndicator = rsc.getDrawable(R.drawable.camera_crop);
        mIndicatorSize = (int) rsc.getDimension(R.dimen.crop_indicator_size);
        mMinSideSize = (int) rsc.getDimension(R.dimen.crop_min_side);
        mTouchTolerance = (int) rsc.getDimension(R.dimen.crop_touch_tolerance);
    }

    public void setFilterCropRepresentation(FilterCropRepresentation crop) {
        mLocalRep = (crop == null) ? new FilterCropRepresentation() : crop;
        GeometryMathUtils.initializeHolder(mUpdateHolder, mLocalRep);
        mValidDraw = true;
    }

    public FilterCropRepresentation getFinalRepresentation() {
        return mLocalRep;
    }

    private void internallyUpdateLocalRep(RectF crop, RectF image) {
        FilterCropRepresentation.findNormalizedCrop(crop, (int) image.width(), (int) image.height());
        mGeometry.crop.set(crop);
        mUpdateHolder.set(mGeometry);
        mLocalRep.setCrop(crop);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        if (mDisplayMatrix == null || mDisplayMatrixInverse == null) {
            return true;
        }
        float[] touchPoint = { x, y };
        mDisplayMatrixInverse.mapPoints(touchPoint);
        x = touchPoint[0];
        y = touchPoint[1];
        switch(event.getActionMasked()) {
            case (MotionEvent.ACTION_DOWN):
                if (mState == Mode.NONE) {
                    if (!mCropObj.selectEdge(x, y)) {
                        mMovingBlock = mCropObj.selectEdge(CropObject.MOVE_BLOCK);
                    }
                    mPrevX = x;
                    mPrevY = y;
                    mState = Mode.MOVE;
                }
                break;
            case (MotionEvent.ACTION_UP):
                if (mState == Mode.MOVE) {
                    mCropObj.selectEdge(CropObject.MOVE_NONE);
                    mMovingBlock = false;
                    mPrevX = x;
                    mPrevY = y;
                    mState = Mode.NONE;
                    internallyUpdateLocalRep(mCropObj.getInnerBounds(), mCropObj.getOuterBounds());
                }
                break;
            case (MotionEvent.ACTION_MOVE):
                if (mState == Mode.MOVE) {
                    float dx = x - mPrevX;
                    float dy = y - mPrevY;
                    mCropObj.moveCurrentSelection(dx, dy);
                    mPrevX = x;
                    mPrevY = y;
                }
                break;
            default:
                break;
        }
        invalidate();
        return true;
    }

    private void clearDisplay() {
        mDisplayMatrix = null;
        mDisplayMatrixInverse = null;
        invalidate();
    }

    public void applyFreeAspect() {
        mCropObj.unsetAspectRatio();
        invalidate();
    }

    public void applyOriginalAspect() {
        RectF outer = mCropObj.getOuterBounds();
        float w = outer.width();
        float h = outer.height();
        if (w > 0 && h > 0) {
            applyAspect(w, h);
            mCropObj.resetBoundsTo(outer, outer);
            internallyUpdateLocalRep(mCropObj.getInnerBounds(), mCropObj.getOuterBounds());
        } else {
            Log.w(TAG, "failed to set aspect ratio original");
        }
        invalidate();
    }

    public void applyAspect(float x, float y) {
        if (x <= 0 || y <= 0) {
            throw new IllegalArgumentException("Bad arguments to applyAspect");
        }
        // If we are rotated by 90 degrees from horizontal, swap x and y
        if (GeometryMathUtils.needsDimensionSwap(mGeometry.rotation)) {
            float tmp = x;
            x = y;
            y = tmp;
        }
        if (!mCropObj.setInnerAspectRatio(x, y)) {
            Log.w(TAG, "failed to set aspect ratio");
        }
        internallyUpdateLocalRep(mCropObj.getInnerBounds(), mCropObj.getOuterBounds());
        invalidate();
    }

    /**
     * Rotates first d bits in integer x to the left some number of times.
     */
    private int bitCycleLeft(int x, int times, int d) {
        int mask = (1 << d) - 1;
        int mout = x & mask;
        times %= d;
        int hi = mout >> (d - times);
        int low = (mout << times) & mask;
        int ret = x & ~mask;
        ret |= low;
        ret |= hi;
        return ret;
    }

    /**
     * Find the selected edge or corner in screen coordinates.
     */
    private int decode(int movingEdges, float rotation) {
        int rot = CropMath.constrainedRotation(rotation);
        switch(rot) {
            case 90:
                return bitCycleLeft(movingEdges, 1, 4);
            case 180:
                return bitCycleLeft(movingEdges, 2, 4);
            case 270:
                return bitCycleLeft(movingEdges, 3, 4);
            default:
                return movingEdges;
        }
    }

    private void forceStateConsistency() {
        MasterImage master = MasterImage.getImage();
        Bitmap image = master.getFiltersOnlyImage();
        int width = image.getWidth();
        int height = image.getHeight();
        if (mCropObj == null || !mUpdateHolder.equals(mGeometry) || mImageBounds.width() != width || mImageBounds.height() != height || !mLocalRep.getCrop().equals(mUpdateHolder.crop)) {
            mImageBounds.set(0, 0, width, height);
            mGeometry.set(mUpdateHolder);
            mLocalRep.setCrop(mUpdateHolder.crop);
            RectF scaledCrop = new RectF(mUpdateHolder.crop);
            FilterCropRepresentation.findScaledCrop(scaledCrop, width, height);
            mCropObj = new CropObject(mImageBounds, scaledCrop, (int) mUpdateHolder.straighten);
            mState = Mode.NONE;
            clearDisplay();
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        clearDisplay();
    }

    @Override
    public void onDraw(Canvas canvas) {
        Bitmap bitmap = MasterImage.getImage().getFiltersOnlyImage();
        if (bitmap == null) {
            MasterImage.getImage().invalidateFiltersOnly();
        }
        if (!mValidDraw || bitmap == null) {
            return;
        }
        forceStateConsistency();
        mImageBounds.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
        // If display matrix doesn't exist, create it and its dependencies
        if (mDisplayCropMatrix == null || mDisplayMatrix == null || mDisplayMatrixInverse == null) {
            mCropObj.unsetAspectRatio();
            mDisplayMatrix = GeometryMathUtils.getFullGeometryToScreenMatrix(mGeometry, bitmap.getWidth(), bitmap.getHeight(), canvas.getWidth(), canvas.getHeight());
            float straighten = mGeometry.straighten;
            mGeometry.straighten = 0;
            mDisplayCropMatrix = GeometryMathUtils.getFullGeometryToScreenMatrix(mGeometry, bitmap.getWidth(), bitmap.getHeight(), canvas.getWidth(), canvas.getHeight());
            mGeometry.straighten = straighten;
            mDisplayMatrixInverse = new Matrix();
            mDisplayMatrixInverse.reset();
            if (!mDisplayCropMatrix.invert(mDisplayMatrixInverse)) {
                Log.w(TAG, "could not invert display matrix");
                mDisplayMatrixInverse = null;
                return;
            }
            // Scale min side and tolerance by display matrix scale factor
            mCropObj.setMinInnerSideSize(mDisplayMatrixInverse.mapRadius(mMinSideSize));
            mCropObj.setTouchTolerance(mDisplayMatrixInverse.mapRadius(mTouchTolerance));
            // drive Crop engine to clamp to crop bounds
            int[] sides = { CropObject.MOVE_TOP, CropObject.MOVE_BOTTOM, CropObject.MOVE_LEFT, CropObject.MOVE_RIGHT };
            int delta = Math.min(canvas.getWidth(), canvas.getHeight()) / 4;
            int[] dx = { delta, -delta, 0, 0 };
            int[] dy = { 0, 0, delta, -delta };

            mCropObj.selectEdge(CropObject.MOVE_NONE);
        }
        // Draw actual bitmap
        mPaint.reset();
        mPaint.setAntiAlias(true);
        mPaint.setFilterBitmap(true);
        canvas.drawBitmap(bitmap, mDisplayMatrix, mPaint);
        mCropObj.getInnerBounds(mScreenCropBounds);
        RectF outer = mCropObj.getOuterBounds();
        FilterCropRepresentation.findNormalizedCrop(mScreenCropBounds, (int) outer.width(), (int) outer.height());
        FilterCropRepresentation.findScaledCrop(mScreenCropBounds, bitmap.getWidth(), bitmap.getHeight());
        if (mDisplayCropMatrix.mapRect(mScreenCropBounds)) {
            // Draw crop rect and markers
            CropDrawingUtils.drawCropRect(canvas, mScreenCropBounds);
            CropDrawingUtils.drawShade(canvas, mScreenCropBounds);
            CropDrawingUtils.drawRuleOfThird(canvas, mScreenCropBounds);
            CropDrawingUtils.drawIndicators(canvas, mCropIndicator, mIndicatorSize, mScreenCropBounds, mCropObj.isFixedAspect(), decode(mCropObj.getSelectState(), mGeometry.rotation.value()));
        }
    }

    public void setEditor(EditorCrop editorCrop) {
        mEditorCrop = editorCrop;
    }
}


package com.android.gallery3d.filtershow.imageshow;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import com.android.gallery3d.R;
import com.android.gallery3d.filtershow.crop.CropDrawingUtils;
import com.android.gallery3d.filtershow.crop.CropMath;
import com.android.gallery3d.filtershow.crop.CropObject;
import com.android.gallery3d.filtershow.editors.EditorCrop;
import com.android.gallery3d.filtershow.filters.FilterCropRepresentation;
import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils.GeometryHolder;

public class ImageCrop extends ImageShow {

    private static final String TAG = ImageCrop.class.getSimpleName();

    private RectF mImageBounds = new RectF();

    private RectF mScreenCropBounds = new RectF();

    private Paint mPaint = new Paint();

    private CropObject mCropObj = null;

    private GeometryHolder mGeometry = new GeometryHolder();

    private GeometryHolder mUpdateHolder = new GeometryHolder();

    private Drawable mCropIndicator;

    private int mIndicatorSize;

    private boolean mMovingBlock = false;

    private Matrix mDisplayMatrix = null;

    private Matrix mDisplayCropMatrix = null;

    private Matrix mDisplayMatrixInverse = null;

    private float mPrevX = 0;

    private float mPrevY = 0;

    private int mMinSideSize = 90;

    private int mTouchTolerance = 40;

    private enum Mode {

        NONE, MOVE
    }

    private Mode mState = Mode.NONE;

    private boolean mValidDraw = false;

    FilterCropRepresentation mLocalRep = new FilterCropRepresentation();

    EditorCrop mEditorCrop;

    public ImageCrop(Context context) {
        super(context);
        setup(context);
    }

    public ImageCrop(Context context, AttributeSet attrs) {
        super(context, attrs);
        setup(context);
    }

    public ImageCrop(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setup(context);
    }

    private void setup(Context context) {
        Resources rsc = context.getResources();
        mCropIndicator = rsc.getDrawable(R.drawable.camera_crop);
        mIndicatorSize = (int) rsc.getDimension(R.dimen.crop_indicator_size);
        mMinSideSize = (int) rsc.getDimension(R.dimen.crop_min_side);
        mTouchTolerance = (int) rsc.getDimension(R.dimen.crop_touch_tolerance);
    }

    public void setFilterCropRepresentation(FilterCropRepresentation crop) {
        mLocalRep = (crop == null) ? new FilterCropRepresentation() : crop;
        GeometryMathUtils.initializeHolder(mUpdateHolder, mLocalRep);
        mValidDraw = true;
    }

    public FilterCropRepresentation getFinalRepresentation() {
        return mLocalRep;
    }

    private void internallyUpdateLocalRep(RectF crop, RectF image) {
        FilterCropRepresentation.findNormalizedCrop(crop, (int) image.width(), (int) image.height());
        mGeometry.crop.set(crop);
        mUpdateHolder.set(mGeometry);
        mLocalRep.setCrop(crop);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        if (mDisplayMatrix == null || mDisplayMatrixInverse == null) {
            return true;
        }
        float[] touchPoint = { x, y };
        mDisplayMatrixInverse.mapPoints(touchPoint);
        x = touchPoint[0];
        y = touchPoint[1];
        switch(event.getActionMasked()) {
            case (MotionEvent.ACTION_DOWN):
                if (mState == Mode.NONE) {

  if (!mCropObj.selectEdge(x, y)) {
                        mMovingBlock = mCropObj.selectEdge(CropObject.MOVE_BLOCK);
                    }
                    mPrevX = x;
                    mPrevY = y;
                    mState = Mode.MOVE;
                }
                break;
            case (MotionEvent.ACTION_UP):
                if (mState == Mode.MOVE) {
                    mCropObj.selectEdge(CropObject.MOVE_NONE);
                    mMovingBlock = false;
                    mPrevX = x;
                    mPrevY = y;
                    mState = Mode.NONE;
                    internallyUpdateLocalRep(mCropObj.getInnerBounds(), mCropObj.getOuterBounds());
                }
                break;
            case (MotionEvent.ACTION_MOVE):
                if (mState == Mode.MOVE) {
                    float dx = x - mPrevX;
                    float dy = y - mPrevY;
                    mCropObj.moveCurrentSelection(dx, dy);
                    mPrevX = x;
                    mPrevY = y;
                }
                break;
            default:
                break;
        }
        invalidate();
        return true;
    }

    private void clearDisplay() {
        mDisplayMatrix = null;
        mDisplayMatrixInverse = null;
        invalidate();
    }

    public void applyFreeAspect() {
        mCropObj.unsetAspectRatio();
        invalidate();
    }

    public void applyOriginalAspect() {
        RectF outer = mCropObj.getOuterBounds();
        float w = outer.width();
        float h = outer.height();
        if (w > 0 && h > 0) {
            applyAspect(w, h);
            mCropObj.resetBoundsTo(outer, outer);
            internallyUpdateLocalRep(mCropObj.getInnerBounds(), mCropObj.getOuterBounds());
        } else {
            Log.w(TAG, "failed to set aspect ratio original");
        }
        invalidate();
    }

    public void applyAspect(float x, float y) {
        if (x <= 0 || y <= 0) {
            throw new IllegalArgumentException("Bad arguments to applyAspect");
        }
        // If we are rotated by 90 degrees from horizontal, swap x and y
        if (GeometryMathUtils.needsDimensionSwap(mGeometry.rotation)) {
            float tmp = x;
            x = y;
            y = tmp;
        }
        if (!mCropObj.setInnerAspectRatio(x, y)) {
            Log.w(TAG, "failed to set aspect ratio");
        }
        internallyUpdateLocalRep(mCropObj.getInnerBounds(), mCropObj.getOuterBounds());
        invalidate();
    }

    /**
     * Rotates first d bits in integer x to the left some number of times.
     */
    private int bitCycleLeft(int x, int times, int d) {
        int mask = (1 << d) - 1;
        int mout = x & mask;
        times %= d;
        int hi = mout >> (d - times);
        int low = (mout << times) & mask;
        int ret = x & ~mask;
        ret |= low;
        ret |= hi;
        return ret;
    }

    /**
     * Find the selected edge or corner in screen coordinates.
     */
    private int decode(int movingEdges, float rotation) {
        int rot = CropMath.constrainedRotation(rotation);
        switch(rot) {
            case 90:
                return bitCycleLeft(movingEdges, 1, 4);
            case 180:
                return bitCycleLeft(movingEdges, 2, 4);
            case 270:
                return bitCycleLeft(movingEdges, 3, 4);
            default:
                return movingEdges;
        }
    }

    private void forceStateConsistency() {
        MasterImage master = MasterImage.getImage();
        Bitmap image = master.getFiltersOnlyImage();
        int width = image.getWidth();
        int height = image.getHeight();
        if (mCropObj == null || !mUpdateHolder.equals(mGeometry) || mImageBounds.width() != width || mImageBounds.height() != height || !mLocalRep.getCrop().equals(mUpdateHolder.crop)) {
            mImageBounds.set(0, 0, width, height);
            mGeometry.set(mUpdateHolder);
            mLocalRep.setCrop(mUpdateHolder.crop);
            RectF scaledCrop = new RectF(mUpdateHolder.crop);
            FilterCropRepresentation.findScaledCrop(scaledCrop, width, height);
            mCropObj = new CropObject(mImageBounds, scaledCrop, (int) mUpdateHolder.straighten);
            mState = Mode.NONE;
            clearDisplay();
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        clearDisplay();
    }

    @Override
    public void onDraw(Canvas canvas) {
        Bitmap bitmap = MasterImage.getImage().getFiltersOnlyImage();
        if (bitmap == null) {
            MasterImage.getImage().invalidateFiltersOnly();
        }
        if (!mValidDraw || bitmap == null) {
            return;
        }
        forceStateConsistency();
        mImageBounds.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
        // If display matrix doesn't exist, create it and its dependencies
        if (mDisplayCropMatrix == null || mDisplayMatrix == null || mDisplayMatrixInverse == null) {
            mCropObj.unsetAspectRatio();
            mDisplayMatrix = GeometryMathUtils.getFullGeometryToScreenMatrix(mGeometry, bitmap.getWidth(), bitmap.getHeight(), canvas.getWidth(), canvas.getHeight());
            float straighten = mGeometry.straighten;
            mGeometry.straighten = 0;
            mDisplayCropMatrix = GeometryMathUtils.getFullGeometryToScreenMatrix(mGeometry, bitmap.getWidth(), bitmap.getHeight(), canvas.getWidth(), canvas.getHeight());
            mGeometry.straighten = straighten;
            mDisplayMatrixInverse = new Matrix();
            mDisplayMatrixInverse.reset();
            if (!mDisplayCropMatrix.invert(mDisplayMatrixInverse)) {
                Log.w(TAG, "could not invert display matrix");
                mDisplayMatrixInverse = null;
                return;
            }
            // Scale min side and tolerance by display matrix scale factor
            mCropObj.setMinInnerSideSize(mDisplayMatrixInverse.mapRadius(mMinSideSize));
            mCropObj.setTouchTolerance(mDisplayMatrixInverse.mapRadius(mTouchTolerance));
            // drive Crop engine to clamp to crop bounds
            int[] sides = { CropObject.MOVE_TOP, CropObject.MOVE_BOTTOM, CropObject.MOVE_LEFT, CropObject.MOVE_RIGHT };
            int delta = Math.min(canvas.getWidth(), canvas.getHeight()) / 4;
            int[] dx = { delta, -delta, 0, 0 };
            int[] dy = { 0, 0, delta, -delta };
            for (int i = 0; i < sides.length; i++) {
                mCropObj.selectEdge(sides[i]);
                mCropObj.moveCurrentSelection(dx[i], dy[i]);
                mCropObj.moveCurrentSelection(-dx[i], -dy[i]);
            }
            mCropObj.selectEdge(CropObject.MOVE_NONE);
        }
        // Draw actual bitmap
        mPaint.reset();
        mPaint.setAntiAlias(true);
        mPaint.setFilterBitmap(true);
        canvas.drawBitmap(bitmap, mDisplayMatrix, mPaint);
        mCropObj.getInnerBounds(mScreenCropBounds);
        RectF outer = mCropObj.getOuterBounds();
        FilterCropRepresentation.findNormalizedCrop(mScreenCropBounds, (int) outer.width(), (int) outer.height());
        FilterCropRepresentation.findScaledCrop(mScreenCropBounds, bitmap.getWidth(), bitmap.getHeight());
        if (mDisplayCropMatrix.mapRect(mScreenCropBounds)) {
            // Draw crop rect and markers
            CropDrawingUtils.drawCropRect(canvas, mScreenCropBounds);
            CropDrawingUtils.drawShade(canvas, mScreenCropBounds);
            CropDrawingUtils.drawRuleOfThird(canvas, mScreenCropBounds);
            CropDrawingUtils.drawIndicators(canvas, mCropIndicator, mIndicatorSize, mScreenCropBounds, mCropObj.isFixedAspect(), decode(mCropObj.getSelectState(), mGeometry.rotation.value()));
        }
    }

    public void setEditor(EditorCrop editorCrop) {
        mEditorCrop = editorCrop;
    }
}


@Override
    public boolean incrementToken() throws IOException

package org.elasticsearch.analyzer.tokenfilters;

import org.apache.lucene.analysis.TokenFilter;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Nariman on 10/28/2017.
 */
public final class PersianStemFilter extends TokenFilter {

    private List<String> suffixes;

    private final CharTermAttribute termAttribute = addAttribute(CharTermAttribute.class);

    public PersianStemFilter(TokenStream in) {
        super(in);
        suffixes = new ArrayList<>();
        suffixes.add("یی");
        suffixes.add("ی");
        suffixes.add("ها");
        suffixes.add("تر");
        suffixes.add("ترین");
        suffixes.add("ان");
        suffixes.add("ات");
        suffixes.add("\u200c");
    }

{
        if (input.incrementToken()) {
            String token = new String(termAttribute.buffer(), 0, termAttribute.length()).trim();
            if (token.length() > 0) {
                for (String suffix : suffixes) {
                    if (token.endsWith(suffix))
                        token = token.substring(0, token.length() - suffix.length());
                }
                termAttribute.setEmpty();
                termAttribute.append(token);
            }
            return true;
        }
        return false;
    }
}


        if (input.incrementToken()) {
            String token = new String(termAttribute.buffer(), 0, termAttribute.length()).trim();
            if (token.length() > 0) {
                for (String suffix : suffixes) {
                    if (token.endsWith(suffix))
                        token = token.substring(0, token.length() - suffix.length());
                }
                termAttribute.setEmpty();
                termAttribute.append(token);
            }
            return true;
        }

package org.elasticsearch.analyzer.tokenfilters;

import org.apache.lucene.analysis.TokenFilter;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Nariman on 10/28/2017.
 */
public final class PersianStemFilter extends TokenFilter {

    private List<String> suffixes;

    private final CharTermAttribute termAttribute = addAttribute(CharTermAttribute.class);

    public PersianStemFilter(TokenStream in) {
        super(in);
        suffixes = new ArrayList<>();
        suffixes.add("یی");
        suffixes.add("ی");
        suffixes.add("ها");
        suffixes.add("تر");
        suffixes.add("ترین");
        suffixes.add("ان");
        suffixes.add("ات");
        suffixes.add("\u200c");
    }

    @Override
    public boolean incrementToken() throws IOException {


package org.elasticsearch.analyzer.tokenfilters;

import org.apache.lucene.analysis.TokenFilter;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Nariman on 10/28/2017.
 */
public final class PersianStemFilter extends TokenFilter {

    private List<String> suffixes;

    private final CharTermAttribute termAttribute = addAttribute(CharTermAttribute.class);

    public PersianStemFilter(TokenStream in) {
        super(in);
        suffixes = new ArrayList<>();
        suffixes.add("یی");
        suffixes.add("ی");
        suffixes.add("ها");
        suffixes.add("تر");
        suffixes.add("ترین");
        suffixes.add("ان");
        suffixes.add("ات");
        suffixes.add("\u200c");
    }

    @Override
    public boolean incrementToken() throws IOException {
        if (input.incrementToken()) {
            String token = new String(termAttribute.buffer(), 0, termAttribute.length()).trim();
            if (token.length() > 0) {
                for (String suffix : suffixes) {
                    if (token.endsWith(suffix))
                        token = token.substring(0, token.length() - suffix.length());
                }
                termAttribute.setEmpty();
                termAttribute.append(token);
            }
            return true;
        }


package org.elasticsearch.analyzer.tokenfilters;

import org.apache.lucene.analysis.TokenFilter;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Nariman on 10/28/2017.
 */
public final class PersianStemFilter extends TokenFilter {

    private List<String> suffixes;

    private final CharTermAttribute termAttribute = addAttribute(CharTermAttribute.class);

    public PersianStemFilter(TokenStream in) {
        super(in);
        suffixes = new ArrayList<>();
        suffixes.add("یی");
        suffixes.add("ی");
        suffixes.add("ها");
        suffixes.add("تر");
        suffixes.add("ترین");
        suffixes.add("ان");
        suffixes.add("ات");
        suffixes.add("\u200c");
    }

    @Override
    public boolean incrementToken() throws IOException {
        if (input.incrementToken()) {
            String token = new String(termAttribute.buffer(), 0, termAttribute.length()).trim();
            if (token.length() > 0) {
                for (String suffix : suffixes) {
                    if (token.endsWith(suffix))
                        token = token.substring(0, token.length() - suffix.length());
                }
                termAttribute.setEmpty();
                termAttribute.append(token);
            }
            return true;
        }
        return false;


if (token.length() > 0) {
                for (String suffix : suffixes) {
                    if (token.endsWith(suffix))
                        token = token.substring(0, token.length() - suffix.length());
                }
                termAttribute.setEmpty();
                termAttribute.append(token);
            }

package org.elasticsearch.analyzer.tokenfilters;

import org.apache.lucene.analysis.TokenFilter;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Nariman on 10/28/2017.
 */
public final class PersianStemFilter extends TokenFilter {

    private List<String> suffixes;

    private final CharTermAttribute termAttribute = addAttribute(CharTermAttribute.class);

    public PersianStemFilter(TokenStream in) {
        super(in);
        suffixes = new ArrayList<>();
        suffixes.add("یی");
        suffixes.add("ی");
        suffixes.add("ها");
        suffixes.add("تر");
        suffixes.add("ترین");
        suffixes.add("ان");
        suffixes.add("ات");
        suffixes.add("\u200c");
    }

    @Override
    public boolean incrementToken() throws IOException {
        if (input.incrementToken()) {
            String token = new String(termAttribute.buffer(), 0, termAttribute.length()).trim();

            return true;
        }
        return false;
    }
}


package org.elasticsearch.analyzer.tokenfilters;

import org.apache.lucene.analysis.TokenFilter;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Nariman on 10/28/2017.
 */
public final class PersianStemFilter extends TokenFilter {

    private List<String> suffixes;

    private final CharTermAttribute termAttribute = addAttribute(CharTermAttribute.class);

    public PersianStemFilter(TokenStream in) {
        super(in);
        suffixes = new ArrayList<>();
        suffixes.add("یی");
        suffixes.add("ی");
        suffixes.add("ها");
        suffixes.add("تر");
        suffixes.add("ترین");
        suffixes.add("ان");
        suffixes.add("ات");
        suffixes.add("\u200c");
    }

    @Override
    public boolean incrementToken() throws IOException {
        if (input.incrementToken()) {
            String token = new String(termAttribute.buffer(), 0, termAttribute.length()).trim();
            if (token.length() > 0) {
                for (String suffix : suffixes) {
                    if (token.endsWith(suffix))
                        token = token.substring(0, token.length() - suffix.length());
                }
                termAttribute.setEmpty();
                termAttribute.append(token);
            }
            return true;


@Override
    public ActionCommand execute(ActionMapping mapping, @Form(ThresholdPO.class) FormBean formBean, HttpServletRequest req, HttpServletResponse res) throws Exception

package net.anotheria.moskito.webui.threshold.action;

import net.anotheria.maf.action.ActionCommand;
import net.anotheria.maf.action.ActionMapping;
import net.anotheria.maf.bean.FormBean;
import net.anotheria.maf.bean.annotations.Form;
import net.anotheria.moskito.webui.threshold.api.ThresholdPO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Creates a new threshold.
 *
 * @author lrosenberg
 */
public class CreateThresholdAction extends BaseThresholdsAction {

{
        ThresholdPO po = (ThresholdPO) formBean;
        getThresholdAPI().createThreshold(po);
        return mapping.redirect().addParameter("newThreshold", ((ThresholdPO) formBean).getName()).addParameter("pProducerId", po.getProducerId());
    }
}


{
        ThresholdPO po = (ThresholdPO) formBean;
        getThresholdAPI().createThreshold(po);
        return mapping.redirect().addParameter("newThreshold", ((ThresholdPO) formBean).getName()).addParameter("pProducerId", po.getProducerId());
    }

package net.anotheria.moskito.webui.threshold.action;

import net.anotheria.maf.action.ActionCommand;
import net.anotheria.maf.action.ActionMapping;
import net.anotheria.maf.bean.FormBean;
import net.anotheria.maf.bean.annotations.Form;
import net.anotheria.moskito.webui.threshold.api.ThresholdPO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Creates a new threshold.
 *
 * @author lrosenberg
 */
public class CreateThresholdAction extends BaseThresholdsAction {

    @Override
    public ActionCommand execute(ActionMapping mapping, @Form(ThresholdPO.class) FormBean formBean, HttpServletRequest req, HttpServletResponse res) throws Exception


package net.anotheria.moskito.webui.threshold.action;

import net.anotheria.maf.action.ActionCommand;
import net.anotheria.maf.action.ActionMapping;
import net.anotheria.maf.bean.FormBean;
import net.anotheria.maf.bean.annotations.Form;
import net.anotheria.moskito.webui.threshold.api.ThresholdPO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Creates a new threshold.
 *
 * @author lrosenberg
 */
public class CreateThresholdAction extends BaseThresholdsAction {

    @Override
    public ActionCommand execute(ActionMapping mapping, @Form(ThresholdPO.class) FormBean formBean, HttpServletRequest req, HttpServletResponse res) throws Exception {
        ThresholdPO po = (ThresholdPO) formBean;
        getThresholdAPI().creat

        return mapping.redirect().addParameter("newThreshold", ((ThresholdPO) formBean).getName()).addParameter("pProducerId", po.getProducerId());
    }
}


package net.anotheria.moskito.webui.threshold.action;

import net.anotheria.maf.action.ActionCommand;
import net.anotheria.maf.action.ActionMapping;
import net.anotheria.maf.bean.FormBean;
import net.anotheria.maf.bean.annotations.Form;
import net.anotheria.moskito.webui.threshold.api.ThresholdPO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Creates a new threshold.
 *
 * @author lrosenberg
 */
public class CreateThresholdAction extends BaseThresholdsAction {

    @Override
    public ActionCommand execute(ActionMapping mapping, @Form(ThresholdPO.class) FormBean formBean, HttpServletRequest req, Ht

tpServletResponse res) throws Exception {
        ThresholdPO po = (ThresholdPO) formBean;
        getThresholdAPI().createThreshold(po);
        return mapping.redirect().addParameter("newThreshold", ((ThresholdPO) formBean).getName()).addParameter("pProducerId", po.getProducerId());
    }
}


@GetMapping("/list")
    @ResponseBody
    public TableDataInfo list(Exam exam)

package com.dimple.controller;

import com.dimple.annotation.Log;
import com.dimple.entity.Exam;
import com.dimple.entity.ExamRecord;
import com.dimple.entity.SysUser;
import com.dimple.service.ExamRecordService;
import com.dimple.service.ExamService;
import com.dimple.service.ExamStudentService;
import com.dimple.service.SysUserService;
import com.dimple.utils.web.AjaxResult;
import com.dimple.utils.web.BaseController;
import com.dimple.utils.web.TableDataInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;

@Controller
// 地址栏中的url前缀
@RequestMapping("/onlineExam/review")
public class TeacherReviewController extends BaseController {

    // 注入对象
    @Autowired
    ExamService examService;

    @Autowired
    ExamRecordService examRecordService;

    @Autowired
    ExamStudentService examStudentService;

    @Autowired
    SysUserService sysUserService;

    // 当url
    @GetMapping()
    public String review() {
        return "onlineExam/review/review";
    }

{
        startPage();
        List<Exam> exams = examService.findExamListToReview(exam);
        return getDataTable(exams);
    }

    @GetMapping("/{examId}/{studentId}")
    public String reviewStuExam(@PathVariable Integer examId, @PathVariable Integer studentId, Model model) {
        model.addAttribute("exam", examService.findExamDetailToReview(examId, studentId));
        model.addAttribute("stuId", studentId);
        return "onlineExam/review/reviewExam";
    }

    // 异步提交并更新单个试题的得分
    @PostMapping("/reviewExam")
    @ResponseBody
    public AjaxResult reviewExam(ExamRecord examRecord) {
        return toAjax(examRecordService.teacherReviewRecord(examRecord));
    }

    // 完成阅卷，提交阅卷的结果，返回index界面，并更新e—s中，total-Score的值
    @PostMapping("/finisReview")
    @Log("完成阅卷")
    public String finishReview(Integer examId, Integer stuId) {
        examRecordService.finishReview(examId, stuId);
        return "/index";
    }

    // 展示试卷的详情，阅卷完成详情
    @GetMapping("/detail/{examId}/{stuId}")
    public String examDetail(@PathVariable Integer examId, @PathVariable Integer stuId, Model model) {
        model.addAttribute("exam", examService.findExamDetailToReview(examId, stuId));
        model.addAttribute("student", sysUserService.selectSysyUserById(stuId));
        model.addAttribute("examStudent", examStudentService.queryByExamIdAndStuId(examId, stuId));
        return "onlineExam/review/detail";
    }
}


{
        return toAjax(examRecordService.teacherReviewRecord(examRecord));
    }

package com.dimple.controller;

import com.dimple.annotation.Log;
import com.dimple.entity.Exam;
import com.dimple.entity.ExamRecord;
import com.dimple.entity.SysUser;
import com.dimple.service.ExamRecordService;
import com.dimple.service.ExamService;
import com.dimple.service.ExamStudentService;
import com.dimple.service.SysUserService;
import com.dimple.utils.web.AjaxResult;
import com.dimple.utils.web.BaseController;
import com.dimple.utils.web.TableDataInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;

@Controller
// 地址栏中的url前缀
@RequestMapping("/onlineExam/review")
public class TeacherReviewController extends BaseController {

    // 注入对象
    @Autowired
    ExamService examService;

    @Autowired
    ExamRecordService examRecordService;

    @Autowired
    ExamStudentService examStudentService;

    @Autowired
    SysUserService sysUserService;

    // 当url
    @GetMapping()
    public String review() {
        return "onlineExam/review/review";
    }

    @GetMapping("/list")
    @ResponseBody
    public TableDataInfo list(Exam exam) {
        startPage();
        List<Exam> exams = examService.findExamListToReview(exam);
        return getDataTable(exams);
    }

    @GetMapping("/{examId}/{studentId}")
    public String reviewStuExam(@PathVariable Integer examId, @PathVariable Integer studentId, Model model) {
        model.addAttribute("exam", examService.findExamDetailToReview(examId, studentId));
        model.addAttribute("stuId", studentId);
        return "onlineExam/review/reviewExam";
    }

    // 异步提交并更新单个试题的得分
    @PostMapping("/reviewExam")
    @ResponseBody
    public AjaxResult reviewExam(ExamRecord examRecord)

    // 完成阅卷，提交阅卷的结果，返回index界面，并更新e—s中，total-Score的值
    @PostMapping("/finisReview")
    @Log("完成阅卷")
    public String finishReview(Integer examId, Integer stuId) {
        examRecordService.finishReview(examId, stuId);
        return "/index";
    }

    // 展示试卷的详情，阅卷完成详情
    @GetMapping("/detail/{examId}/{stuId}")
    public String examDetail(@PathVariable Integer examId, @PathVariable Integer stuId, Model model) {
        model.addAttribute("exam", examService.findExamDetailToReview(examId, stuId));
        model.addAttribute("student", sysUserService.selectSysyUserById(stuId));
        model.addAttribute("examStudent", examStudentService.queryByExamIdAndStuId(examId, stuId));
        return "onlineExam/review/detail";
    }
}


package com.dimple.controller;

import com.dimple.annotation.Log;
import com.dimple.entity.Exam;
import com.dimple.entity.ExamRecord;
import com.dimple.entity.SysUser;
import com.dimple.service.ExamRecordService;
import com.dimple.service.ExamService;
import com.dimple.service.ExamStudentService;
import com.dimple.service.SysUserService;
import com.dimple.utils.web.AjaxResult;
import com.dimple.utils.web.BaseController;
import com.dimple.utils.web.TableDataInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;

@Controller
// 地址栏中的url前缀
@RequestMapping("/onlineExam/review")
public class TeacherReviewController extends BaseController {

    // 注入对象
    @Autowired
    ExamService examService;

    @Autowired
    ExamRecordService examRecordService;

    @Autowired
    ExamStudentService examStudentService;

    @Autowired
    SysUserService sysUserService;

    // 当url
    @GetMapping()
    public String review() {
        return "onlineExam/review/review";
    }

    @GetMapping("/list")
    @ResponseBody
    public TableDataInfo list(Exam exam) {
        startPage();
        List<Exam> exams = examService.findExamListToReview(exam);
        return getDataTable(exams);
    }

    @GetMapping("/{examId}/{studentId}")
    public String reviewStuExam(@PathVariable Integer examId, @PathVariable Integer studentId, Model model) {
        model.addAttribute("exam", examService.findExamDetailToReview(examId, studentId));
        model.addAttribute("stuId", studentId);
        return "onlineExam/review/reviewExam";
    }

    // 异步提交并更新单个试题的得分
    @PostMapping("/reviewExam")
    @ResponseBody
    public AjaxResult reviewExam(ExamRecord examRecord) {
        return toAjax(examRecordService.teacherReviewRecord(examRecord));
    }

    // 完成阅卷，提交阅卷的结果，返回index界面，并更新e—s中，total-Score的值
    @PostMapping("/finisReview")
    @Log("完成阅卷")
    public String finishReview(Integer examId, Integer stuId) {
        examRecordService.finishReview(examId, stuId);
        return "/index";
    }

    // 展示试卷的详情，阅卷完成详情
    @GetMapping("/detail/{examId}/{stuId}")
    public String examDetail(@PathVariable Integer examId, @PathVariable Integer stuId, Model model) {
        model.addAttribute("exam", examService.findExamDetailToReview(examId, stuId));
        model.addAttribute

        model.addAttribute("examStudent", examStudentService.queryByExamIdAndStuId(examId, stuId));
        return "onlineExam/review/detail";
    }
}


package com.dimple.controller;

import com.dimple.annotation.Log;
import com.dimple.entity.Exam;
import com.dimple.entity.ExamRecord;
import com.dimple.entity.SysUser;
import com.dimple.service.ExamRecordService;
import com.dimple.service.ExamService;
import com.dimple.service.ExamStudentService;
import com.dimple.service.SysUserService;
import com.dimple.utils.web.AjaxResult;
import com.dimple.utils.web.BaseController;
import com.dimple.utils.web.TableDataInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;

@Controller
// 地址栏中的url前缀
@RequestMapping("/onlineExam/review")
public class TeacherReviewController extends BaseController {

    // 注入对象
    @Autowired
    ExamService examService;

    @Autowired
    ExamRecordService examRecordService;

    @Autowired
    ExamStudentService examStudentService;

    @Autowired
    SysUserService sysUserService;

    // 当url
    @GetMapping()
    public String review() {
        return "onlineExam/review/review";
    }

    @GetMapping("/list")
    @ResponseBody
    public TableDataInfo list(Exam exam) {
        startPage();
        List<Exam> exams = examService.findExamListToReview(exam);
        return getDataTable(exams);
    }

    @GetMapping("/{examId}/{studentId}")
    public String reviewStuExam(@PathVariable Integer examId, @PathVariable Integer studentId, Model model) {
        model.addAttribute("exam", examService.findExamDetailToReview(examId, studentId));
        model.addAttribute("stuId", studentId);
        return "onlineExam/review/reviewExam";
    }

    // 异步提交并更新单个试题的得分
    @PostMapping("/reviewExam")
    @ResponseBody
    public AjaxResult reviewExam(ExamRecord examRecord) {
        return toAjax(examRecordService.teacherReviewRecord(examRecord));
    }

    // 完成阅卷，提交阅卷的结果，返回index界面，并更新e—s中，total-Score的值
    @PostMapping("/finisReview")
    @Log("完成阅卷")
    public String finishReview(Integer examId, Integer stuId) {
        examRecordService.finishReview(examId, stuId);
        return "/index";
    }

    // 展示试卷的详情，阅卷完成详情
    @GetMapping("/detail/{examId}/{stuId}")
    public String examDetail(@PathVariable Integer examId, @PathVariable Integer stuId, Model model) {
        model.addAttribute("exam", examService.findExamDetailToReview(examId, stuId));
        model.addAttribute("student", sysUserService.selectSysyUserById(stuId));
        model.addAttribute("examStudent", examStudentService.queryByExamIdAndStuId(examId, stuId));
        return "onlineExam/


/**
 * ****************************************************************************
 *  PGPTool is a desktop application for pgp encryption/decryption
 *
 * ****************************************************************************
 */
package org.pgptool.gui.ui.tools.geometrymemory;

import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.pgptool.gui.configpairs.api.ConfigPairs;
import org.springframework.util.StringUtils;
import com.google.common.base.Preconditions;

public class WindowGeometryPersisterImpl extends ComponentAdapter implements WindowGeometryPersister {

    static final int MINIMUM_SANE_SIZE_VALUE = 10;

    private static final long DELAY = 500;

    private static final TimeUnit DELAY_TIME_UNIT = TimeUnit.MILLISECONDS;

    private ConfigPairs configPairs;

    private ScheduledExecutorService scheduledExecutorService;

    private Window window;

    private String keyId;

    private ScheduledFuture<?> sizeSetter;

    /**
     * @param scheduledExecutorService it's used to dealy interraction with
     *                                 configPairs to avoid spamming it with values
     *                                 while user still dragging element
     */
    public WindowGeometryPersisterImpl(Window window, String windowId, ConfigPairs configPairs, ScheduledExecutorService scheduledExecutorService) {
        Preconditions.checkArgument(scheduledExecutorService != null);
        Preconditions.checkArgument(window != null);
        Preconditions.checkArgument(StringUtils.hasText(windowId));
        Preconditions.checkArgument(configPairs != null);
        this.window = window;
        // version to version application windows sizes might change
        this.keyId = windowId + "_" + window.getMinimumSize();
        this.configPairs = configPairs;
        this.scheduledExecutorService = scheduledExecutorService;
        window.addComponentListener(this);
    }

    @Override
    public boolean restoreSize() {
        Preconditions.checkState(window != null, "instance was detached, can't do much after that");
        Dimension size = configPairs.find(keySize(), null);
        if (size == null) {
            return false;
        }
        window.setSize(size);
        return true;
    }

    // @Override
    // Preconditions.checkState(window != null, "instance was detached, can't do
    // much after that");
    @Override
    public void componentResized(ComponentEvent e) {
        if (window == null) {
            return;
        }
        Dimension size = window.getSize();
        if (size.getWidth() < MINIMUM_SANE_SIZE_VALUE || size.getHeight() < MINIMUM_SANE_SIZE_VALUE) {
            return;
        }
        if (sizeSetter != null) {
            sizeSetter.cancel(true);
        }
        sizeSetter = scheduledExecutorService.schedule(() -> configPairs.put(keySize(), window.getSize()), DELAY, DELAY_TIME_UNIT);
    }

    private String keySize() {
        return keyId + "_size";
    }

    // @Override
    // locationSetter = scheduledExecutorService
    @Override
    public boolean isAttached() {
        return window != null;
    }

{
        Preconditions.checkState(window != null, "instance was detached, can't do much after that");
        window.removeComponentListener(this);
        window = null;
    }
}