Newer
Older
Telegram / TMessagesProj / src / main / java / org / telegram / ui / Components / PhotoCropView.java
ubt on 31 Oct 2017 10 KB init
/*
 * This is the source code of Telegram for Android v. 3.x.x
 * It is licensed under GNU GPL v. 2 or later.
 * You should have received a copy of the license in this archive (see LICENSE).
 *
 * Copyright Nikolai Kudashov, 2013-2017.
 */

package org.telegram.ui.Components;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.RectF;
import android.os.Build;
import android.view.Gravity;
import android.widget.FrameLayout;

import org.telegram.messenger.AndroidUtilities;
import org.telegram.ui.Components.Crop.CropRotationWheel;
import org.telegram.ui.Components.Crop.CropView;

public class PhotoCropView extends FrameLayout {

    public interface PhotoCropViewDelegate {
        void needMoveImageTo(float x, float y, float s, boolean animated);
        Bitmap getBitmap();

        void onChange(boolean reset);
    }

    private boolean freeformCrop = true;
    private float rectSizeX = 600;
    private float rectSizeY = 600;
    private int draggingState = 0;
    private int orientation;
    private float oldX = 0, oldY = 0;
    private int bitmapWidth = 1, bitmapHeight = 1, bitmapX, bitmapY;
    private float rectX = -1, rectY = -1;
    private float bitmapGlobalScale = 1;
    private float bitmapGlobalX = 0;
    private float bitmapGlobalY = 0;
    private PhotoCropViewDelegate delegate;
    private Bitmap bitmapToEdit;
    private boolean showOnSetBitmap;

    private RectF animationStartValues;
    private RectF animationEndValues;
    private Runnable animationRunnable;

    private CropView cropView;
    private CropRotationWheel wheelView;

    public PhotoCropView(Context context) {
        super(context);
    }

    public void setBitmap(Bitmap bitmap, int rotation, boolean freeform) {
        bitmapToEdit = bitmap;
        rectSizeX = 600;
        rectSizeY = 600;
        draggingState = 0;
        oldX = 0;
        oldY = 0;
        bitmapWidth = 1;
        bitmapHeight = 1;
        rectX = -1;
        rectY = -1;
        freeformCrop = freeform;
        orientation = rotation;
        requestLayout();

        if (cropView == null) {
            cropView = new CropView(getContext());
            cropView.setListener(new CropView.CropViewListener() {
                @Override
                public void onChange(boolean reset) {
                    if (delegate != null) {
                        delegate.onChange(reset);
                    }
                }

                @Override
                public void onAspectLock(boolean enabled) {
                    wheelView.setAspectLock(enabled);
                }
            });
            cropView.setBottomPadding(AndroidUtilities.dp(64));
            addView(cropView);

            wheelView = new CropRotationWheel(getContext());
            wheelView.setListener(new CropRotationWheel.RotationWheelListener() {
                @Override
                public void onStart() {
                    cropView.onRotationBegan();
                }

                @Override
                public void onChange(float angle) {
                    cropView.setRotation(angle);
                    if (delegate != null) {
                        delegate.onChange(false);
                    }
                }

                @Override
                public void onEnd(float angle) {
                    cropView.onRotationEnded();
                }

                @Override
                public void aspectRatioPressed() {
                    cropView.showAspectRatioDialog();
                }

                @Override
                public void rotate90Pressed() {
                    wheelView.reset();
                    cropView.rotate90Degrees();
                }
            });
            addView(wheelView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER | Gravity.BOTTOM, 0, 0, 0, 0));
        }

        cropView.setVisibility(VISIBLE);
        cropView.setBitmap(bitmap, rotation, freeform);

        if (showOnSetBitmap) {
            showOnSetBitmap = false;
            cropView.show();
        }

        wheelView.setFreeform(freeform);
        wheelView.reset();
    }

    public void setOrientation(int rotation) {
        orientation = rotation;
        rectX = -1;
        rectY = -1;
        rectSizeX = 600;
        rectSizeY = 600;
        delegate.needMoveImageTo(0, 0, 1, false);
        requestLayout();
    }

    public boolean isReady() {
        return cropView.isReady();
    }

    public void reset() {
        wheelView.reset();
        cropView.reset();
    }

    public void onAppear() {
        if (cropView != null) {
            cropView.willShow();
        }
    }

    public void onAppeared() {
        if (cropView != null) {
            cropView.show();
        } else {
            showOnSetBitmap = true;
        }
    }

    public void onDisappear() {
        cropView.hide();
    }

    public float getRectX() {
        return cropView.getCropLeft() - AndroidUtilities.dp(14);
    }

    public float getRectY() {
        return cropView.getCropTop() - AndroidUtilities.dp(14) - (Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0);
    }

    public float getRectSizeX() {
        return cropView.getCropWidth();
    }

    public float getRectSizeY() {
        return cropView.getCropHeight();
    }

    public float getBitmapX() {
        return bitmapX - AndroidUtilities.dp(14);
    }

    public float getBitmapY() {
        float additionalY = (Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0);
        return bitmapY - AndroidUtilities.dp(14) - additionalY;
    }

    public float getLimitX() {
        return rectX - Math.max(0, (float) Math.ceil((getWidth() - bitmapWidth * bitmapGlobalScale) / 2));
    }

    public float getLimitY() {
        float additionalY = (Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0);
        return rectY - Math.max(0, (float) Math.ceil((getHeight() - bitmapHeight * bitmapGlobalScale + additionalY) / 2));
    }

    public float getLimitWidth() {
        return getWidth() - AndroidUtilities.dp(14) - rectX - (int) Math.max(0, Math.ceil((getWidth() - AndroidUtilities.dp(28) - bitmapWidth * bitmapGlobalScale) / 2)) - rectSizeX;
    }

    public float getLimitHeight() {
        float additionalY = (Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0);
        return getHeight() - AndroidUtilities.dp(14) - additionalY - rectY - (int) Math.max(0, Math.ceil((getHeight() - AndroidUtilities.dp(28) - bitmapHeight * bitmapGlobalScale - additionalY) / 2)) - rectSizeY;
    }

    public Bitmap getBitmap() {
        if (cropView != null) {
            return cropView.getResult();
        }
        return null;
    }

    public void setBitmapParams(float scale, float x, float y) {
        bitmapGlobalScale = scale;
        bitmapGlobalX = x;
        bitmapGlobalY = y;
    }

    public void startAnimationRunnable() {
        if (animationRunnable != null) {
            return;
        }
        animationRunnable = new Runnable() {
            @Override
            public void run() {
                if (animationRunnable == this) {
                    animationRunnable = null;
                    moveToFill(true);
                }
            }
        };
        AndroidUtilities.runOnUIThread(animationRunnable, 1500);
    }

    public void cancelAnimationRunnable() {
        if (animationRunnable != null) {
            AndroidUtilities.cancelRunOnUIThread(animationRunnable);
            animationRunnable = null;
            animationStartValues = null;
            animationEndValues = null;
        }
    }

    public void setAnimationProgress(float animationProgress) {
        if (animationStartValues != null) {
            if (animationProgress == 1) {
                rectX = animationEndValues.left;
                rectY = animationEndValues.top;
                rectSizeX = animationEndValues.right;
                rectSizeY = animationEndValues.bottom;
                animationStartValues = null;
                animationEndValues = null;
            } else {
                rectX = animationStartValues.left + (animationEndValues.left - animationStartValues.left) * animationProgress;
                rectY = animationStartValues.top + (animationEndValues.top - animationStartValues.top) * animationProgress;
                rectSizeX = animationStartValues.right + (animationEndValues.right - animationStartValues.right) * animationProgress;
                rectSizeY = animationStartValues.bottom + (animationEndValues.bottom - animationStartValues.bottom) * animationProgress;
            }
            invalidate();
        }
    }

    public void moveToFill(boolean animated) {
        float scaleToX = bitmapWidth / rectSizeX;
        float scaleToY = bitmapHeight / rectSizeY;
        float scaleTo = scaleToX > scaleToY ? scaleToY : scaleToX;
        if (scaleTo > 1 && scaleTo * bitmapGlobalScale > 3) {
            scaleTo = 3 / bitmapGlobalScale;
        } else if (scaleTo < 1 && scaleTo * bitmapGlobalScale < 1) {
            scaleTo = 1 / bitmapGlobalScale;
        }
        float newSizeX = rectSizeX * scaleTo;
        float newSizeY = rectSizeY * scaleTo;
        float additionalY = (Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0);
        float newX = (getWidth() - newSizeX) / 2;
        float newY = (getHeight() - newSizeY + additionalY) / 2;
        animationStartValues = new RectF(rectX, rectY, rectSizeX, rectSizeY);
        animationEndValues = new RectF(newX, newY, newSizeX, newSizeY);

        float newBitmapGlobalX = newX + getWidth() / 2 * (scaleTo - 1) + (bitmapGlobalX - rectX) * scaleTo;
        float newBitmapGlobalY = newY + (getHeight() + additionalY) / 2 * (scaleTo - 1) + (bitmapGlobalY - rectY) * scaleTo;

        delegate.needMoveImageTo(newBitmapGlobalX, newBitmapGlobalY, bitmapGlobalScale * scaleTo, animated);
    }

    public void setDelegate(PhotoCropViewDelegate delegate) {
        this.delegate = delegate;
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        Bitmap newBitmap = delegate.getBitmap();
        if (newBitmap != null) {
            bitmapToEdit = newBitmap;
        }

        if (cropView != null) {
            cropView.updateLayout();
        }
    }
}