com.pdftron.pdf.controls.UserCropDialogFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.pdftron.pdf.controls.UserCropDialogFragment.java

Source

//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2016 by PDFTron Systems Inc. All Rights Reserved.
// Consult legal.txt regarding legal and license information.
//-------------------------------------------------------------------------

package com.pdftron.pdf.controls;

import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.util.Log;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.LinearInterpolator;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.pdftron.common.Matrix2D;
import com.pdftron.common.PDFNetException;
import com.pdftron.pdf.PDFDoc;
import com.pdftron.pdf.PDFRasterizer;
import com.pdftron.pdf.PDFViewCtrl;
import com.pdftron.pdf.Page;
import com.pdftron.pdf.PageIterator;
import com.pdftron.pdf.Point;
import com.pdftron.pdf.Rect;
import com.pdftron.pdf.tools.R;
import com.pdftron.pdf.utils.AnalyticsHandlerAdapter;
import com.pdftron.pdf.utils.CustomAsyncTask;
import com.pdftron.pdf.utils.ImageMemoryCache;
import com.pdftron.pdf.utils.Utils;
import com.pdftron.pdf.widget.ContentLoadingRelativeLayout;

import com.pdftron.pdf.utils.UserCropUtilities;

public class UserCropDialogFragment extends DialogFragment {

    public interface UserCropDialogListener {
        void onUserCropDialogDismiss(int pageNumberAtDismiss);
    }

    // Default memory cache size in kilobytes
    private static final int DEFAULT_MEM_CACHE_SIZE = 1024 * 50; // 50MB

    // The number of images to render ahead and behind the current page
    private static final int MAX_PAGES_TO_PRERENDER_PER_DIRECTION = 4;

    private static final long MILLISECONDS_BEFORE_SHOWING_PROGRESS = 500;

    private boolean mRemoveCropHelperMode;

    private UserCropDialogListener mListener;

    private String mLayoutName;

    private boolean mIsActive;
    private boolean mIsCroppingConfirmed = false; // used for analytics

    private int mCurrentPage = 0;
    private int mTotalPages = 0;
    private PDFViewCtrl mPdfViewCtrl;

    // layout parameters
    private RelativeLayout mCropPageHost;
    private View mBlankPagePlaceholder;
    private ContentLoadingRelativeLayout mBlankPageSpinnerHost;
    private View mCropImageBorder;
    private com.edmodo.cropper.CropImageView mCropImageView; // TODO - Acknowledge in legal
    private TextView mPageNumberTextView;

    // Progress
    private View mDisablingOverlay;
    private View mProgressBarHost;
    private boolean mDisablingOverlayShowing;
    private boolean mSpinnerShowing;

    // Pre-rendering
    private int mPagesToPreRenderPerDirection;

    // Page Properites /////////////////////////////////////////////////////////

    private class PageCropProperties {

        public Rect mUserCropBox;
        public Rect mCropBox;
        public int mRotation;

        public PageCropProperties() {
        }
    }

    private PageCropProperties[] mPageProperties;

    // Bitmap Handling /////////////////////////////////////////////////////////

    ImageMemoryCache mImageCache;

    public static UserCropDialogFragment newInstance() {
        UserCropDialogFragment dialog = new UserCropDialogFragment();
        dialog.mRemoveCropHelperMode = false;
        return dialog;
    }

    public static UserCropDialogFragment newInstanceAsRemoveCropHelper() {
        UserCropDialogFragment dialog = new UserCropDialogFragment();
        dialog.mRemoveCropHelperMode = true;
        return dialog;
    }

    public UserCropDialogFragment() {
        // Required empty public constructor
    }

    public UserCropDialogFragment initParams(PDFViewCtrl ctrl) {
        mIsActive = true;
        mPdfViewCtrl = ctrl;
        mCurrentPage = ctrl.getCurrentPage();
        mTotalPages = ctrl.getPageCount();
        int totalItems = mTotalPages + 1;
        mPageProperties = new PageCropProperties[totalItems];

        mDisablingOverlayShowing = false;
        mSpinnerShowing = false;
        mPagesToPreRenderPerDirection = 0;

        if (!mRemoveCropHelperMode) {
            mImageCache = new ImageMemoryCache(DEFAULT_MEM_CACHE_SIZE);
        }

        return this;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (savedInstanceState != null && mPdfViewCtrl == null) {
            this.dismiss();
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.fragment_user_crop_dialog, null);
        if (mRemoveCropHelperMode) {
            getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(0));
        } else {
            int width = 10;
            int height = 10;
            WindowManager wm = (WindowManager) view.getContext().getSystemService(Context.WINDOW_SERVICE);
            Display display = wm.getDefaultDisplay();
            if (android.os.Build.VERSION.SDK_INT >= 13) {
                android.graphics.Point size = new android.graphics.Point();
                display.getSize(size);
                width = size.x - 10;
                height = size.y - 10;
            } else {
                width = display.getWidth() - 10;
                height = display.getHeight() - 10;
            }

            int maxImageSize = width * height * 4;
            if (maxImageSize > 0) {
                int maxImages = (DEFAULT_MEM_CACHE_SIZE * 1000) / maxImageSize;
                if (maxImages > 0) {
                    mPagesToPreRenderPerDirection = Math.min(MAX_PAGES_TO_PRERENDER_PER_DIRECTION,
                            (maxImages - 1) / 2);
                }
            }
        }
        initUI(view);
        return view;
    }

    private void initUI(final View view) {
        final FrameLayout layout = (FrameLayout) view.findViewById(R.id.layout_root);
        mPageNumberTextView = (TextView) view.findViewById(R.id.page_num_text_view);
        mLayoutName = layout.getTag().toString();

        mDisablingOverlay = view.findViewById(R.id.disabling_overlay);
        mProgressBarHost = view.findViewById(R.id.progress_bar_host);
        mDisablingOverlay.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                return true; // ensure the user can't click buttons
            }
        });

        mCropPageHost = (RelativeLayout) view.findViewById(R.id.page_crop_host);

        mCropImageBorder = view.findViewById(R.id.image_crop_border);

        mCropImageView = (com.edmodo.cropper.CropImageView) view.findViewById(R.id.image_crop_view);
        mCropImageView.setGuidelines(0); // off

        mCropImageBorder.setVisibility(View.GONE);

        mBlankPagePlaceholder = view.findViewById(R.id.blank_page_placeholder);
        mBlankPageSpinnerHost = (ContentLoadingRelativeLayout) view.findViewById(R.id.blank_page_progress_bar_host);

        if (mRemoveCropHelperMode) {
            View manualCropRoot = view.findViewById(R.id.manual_crop_root_layout);
            manualCropRoot.setVisibility(View.GONE);

            setCropBoxAndClose();
        } else {
            mCropPageHost.postDelayed(new Runnable() {
                @Override
                public void run() {
                    if (mIsActive) {

                        if (mLayoutName.equals("layout-sw480dp-port")
                                || mLayoutName.equals("layout-sw480dp-land")) {
                            if (view.getWidth() < 540) {
                                View buttonHost = view.findViewById(R.id.page_buttons_host);
                                int[] location = new int[2];
                                buttonHost.getLocationInWindow(location);
                                int buttonsLeft = location[0];
                                View pageNumText = view.findViewById(R.id.page_num_text_view);
                                pageNumText.getLocationInWindow(location);
                                int pageNumRight = location[0] + pageNumText.getWidth();
                                if (pageNumRight > buttonsLeft - 10) {
                                    RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) buttonHost
                                            .getLayoutParams();
                                    layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
                                    layoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL, 0);
                                }
                            }
                        }

                        mCropImageBorder.setVisibility(View.VISIBLE);
                        setPage(mCurrentPage);
                    }
                }
            }, 200); // Extra delay to ensure that view has time to settle. post alone doesn't work.
        }

        getButtons(view);

        if (mDisablingOverlayShowing) {
            mDisablingOverlay.setVisibility(View.VISIBLE);
        }
        if (mSpinnerShowing) {
            mProgressBarHost.setVisibility(View.VISIBLE);
        }
    }

    private void getButtons(final View view) {
        final View nextButton = view.findViewById(R.id.next_button);
        nextButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                if (mDisablingOverlayShowing) {
                    return;
                }
                switchPage(true);
            }
        });
        final View prevButton = view.findViewById(R.id.prev_button);
        prevButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                if (mDisablingOverlayShowing) {
                    return;
                }
                switchPage(false);
            }
        });
        final Button doneButton = (Button) view.findViewById(R.id.done_button);
        doneButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                if (mDisablingOverlayShowing) {
                    return;
                }
                mIsCroppingConfirmed = true;
                setCropBoxAndClose();
            }
        });
        final Button cancelButton = (Button) view.findViewById(R.id.cancel_button);
        cancelButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                UserCropDialogFragment.this.dismiss();
                return;
            }
        });
        final Button cropAllButton = (Button) view.findViewById(R.id.crop_all_button);
        cropAllButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                if (mDisablingOverlayShowing) {
                    return;
                }
                applyCropToAllPagesAndFlash();
            }
        });
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        try {
            mListener = (UserCropDialogListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement " + e.getClass().toString());
        }
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);

        if (mCurrentJob != null && !mCurrentJob.isDone()) {
            mCurrentJob.cancelRasterizing();
        }

        mCurrentJob = null;

        //        if (mCropImageView.hasBitmap()) {
        //            updatePageCropFromImageView(mPageProperties[mCurrentPage], mCropImageView.getCropRectPercentageMargins());
        //            Bitmap bitmap = mCropImageView.getImageBitmapAndClear();
        //        }

        mImageCache.evictAll();

        if (mSpinnerAlphaAnimation != null && !mSpinnerAlphaAnimation.hasEnded()) {
            mSpinnerAlphaAnimation.cancel();
        }
        if (mDisablingOverlayShowing) {
            mDisablingOverlay.setVisibility(View.VISIBLE);
        }
        if (mSpinnerShowing) {
            mProgressBarHost.setVisibility(View.VISIBLE);
        }

        ViewGroup viewGroup = (ViewGroup) getView();
        viewGroup.removeAllViewsInLayout();
        View view = onCreateView(getActivity().getLayoutInflater(), viewGroup, null);
        viewGroup.addView(view);

    }

    @Override
    public void onDismiss(DialogInterface dialog) {
        super.onDismiss(dialog);
        if (mCurrentJob != null && !mCurrentJob.isDone()) {
            mCurrentJob.cancelRasterizing();
        }

        mListener.onUserCropDialogDismiss(mCurrentPage);
    }

    @Override
    public void onDestroy() {
        mIsActive = false;
        if (mImageCache != null) {
            mImageCache.evictAll();
        }
        super.onDestroy();
    }

    private void switchPage(boolean next) {
        if (next && mCurrentPage < mPdfViewCtrl.getPageCount()) {
            setPage(mCurrentPage + 1);
        } else if (!next && mCurrentPage > 1) {
            setPage(mCurrentPage - 1);
        }
    }

    // Bitmap Acquisition //////////////////////////////////////////////////////

    private DrawImageTask mCurrentJob;

    private class DrawImageTask extends CustomAsyncTask<Void, Void, Bitmap> {

        private int mPageNumber;
        private PDFRasterizer mRasterizer;
        private Point mViewDimensions;
        private PDFDoc mPdfDoc;
        private boolean mGotBitmap;

        private boolean mIsCancelled;

        public int getPageNumber() {
            return mPageNumber;
        }

        public boolean cancelRasterizing() {
            Log.i("USER_CROP", "Canceling image rendering for page " + mPageNumber);
            if (mRasterizer != null) {
                try {
                    mRasterizer.setCancel(true);
                } catch (com.pdftron.common.PDFNetException e) {

                }
            }
            return this.cancel(false);
        }

        /**
         * Returns true if the task is finished or cancelled
         */
        public boolean isDone() {
            return mGotBitmap || isCancelled();
        }

        public DrawImageTask(Context context, int pageNumber, Point viewDimensions, PDFDoc doc) {
            super(context);
            mPageNumber = pageNumber;
            mViewDimensions = viewDimensions;
            mPdfDoc = doc;
            mGotBitmap = false;

            mIsCancelled = false;

            if (viewDimensions.x <= 0 || viewDimensions.y <= 0) {
                Log.e("USER_CROP", "Dimensions are 0 or less");
            }

            try {
                mRasterizer = new PDFRasterizer();
                mRasterizer.setDrawAnnotations(true);
            } catch (com.pdftron.common.PDFNetException e) {

            }
        }

        @Override
        protected Bitmap doInBackground(Void... jobParam) {
            if (mPageNumber > 0) {
                try {
                    if (isCancelled() || mRasterizer == null) {
                        return null;
                    }
                    mPdfDoc.lockRead();
                    Page page = mPdfDoc.getPage(mPageNumber);

                    double scaleFactor = 1;

                    Rect bbox = page.getCropBox();
                    bbox.normalize();

                    double pageWidth = page.getPageWidth();
                    double pageHeight = page.getPageHeight();

                    double pageScaleFactorX = mViewDimensions.x / pageWidth;
                    double pageScaleFactorY = mViewDimensions.y / pageHeight;

                    scaleFactor *= Math.max(pageScaleFactorX, pageScaleFactorY);

                    if (scaleFactor <= 0) {
                        return null;
                    }

                    // Will make the picture right side up, at the correct scale.
                    Matrix2D mtx = new Matrix2D(scaleFactor, 0, 0, scaleFactor, 0, 0);
                    mtx = mtx.multiply(page.getDefaultMatrix(true, Page.e_crop, Page.e_0));

                    int width = (int) (pageWidth * scaleFactor);
                    int height = (int) (pageHeight * scaleFactor);

                    int comps = 4; // BGRA
                    int stride = ((width * comps + 3) / 4) * 4;

                    if (isCancelled()) {
                        return null;
                    }

                    Log.i("USER_CROP",
                            "Creating image for page " + mPageNumber + " of dimensions " + width + " x " + height);
                    int[] img = new int[stride * height];
                    mRasterizer.rasterize(page, img, width, height, false, mtx, bbox);

                    if (isCancelled()) {
                        return null;
                    }

                    Bitmap bitmap = Bitmap.createBitmap(img, width, height, Bitmap.Config.ARGB_8888);
                    return bitmap;
                } catch (com.pdftron.common.PDFNetException e) {
                } finally {
                    try {
                        mPdfDoc.unlockRead();
                    } catch (com.pdftron.common.PDFNetException e) {
                    }
                }
            }
            return null;
        }

        @Override
        protected void onCancelled(Bitmap result) {
            super.onCancelled(result);
        }

        @Override
        protected void onPostExecute(Bitmap result) {
            // TODO I seem to have failed this check once even though I cancelled this task
            // is this a matter of perfect timing (cancel called after this has been posted, but before it's been executed?
            // Added the mIsCancelled and !mIsActive checks since, let's keep monitoring.
            if (isCancelled() || result == null || mIsCancelled || !mIsActive) {
                return;
            }
            mGotBitmap = true;
            if (mCurrentPage == mPageNumber) {
                mImageCache.put("" + mPageNumber, new BitmapDrawable(getContext().getResources(), result));
                readyNextPage(mPageNumber, result);
            } else {
                mImageCache.put("" + mPageNumber, new BitmapDrawable(getContext().getResources(), result));
                preRenderIfNecessary();
            }
        }
    }

    // Implementation Details //////////////////////////////////////////////////

    private void setPage(int pageNumber) {
        if (pageNumber != mCurrentPage) {
            //            if (mPageProperties[mCurrentPage] != null) {
            //                if (mCropImageView.hasBitmap()) {
            //                    updatePageCropFromImageView(mPageProperties[mCurrentPage], mCropImageView.getCropRectPercentageMargins());
            //                    Bitmap oldBitmap = mCropImageView.getImageBitmapAndClear();
            //                }
            //            }
            mCurrentPage = pageNumber;
        }

        mBlankPageSpinnerHost.hide(true, false);
        mBlankPageSpinnerHost.show();
        mPageNumberTextView.setText(String.format(
                getActivity().getResources().getString(R.string.user_crop_manual_crop_page_label), pageNumber));

        PDFDoc doc = mPdfViewCtrl.getDoc();
        try {
            doc.lockRead();
            Page page = doc.getPage(pageNumber);
            PageCropProperties currentProperties = getCropPropertiesForPage(pageNumber);
            Log.i("USER_CROP", "Just set page to + " + pageNumber + ". Calculating page size...");
            Log.i("USER_CROP", String.format("UserCropView size is (%1d, %2d)", mCropImageView.getMeasuredWidth(),
                    mCropImageView.getMeasuredHeight()));
            Point newPageSize = calculateBlankPageSize(page.getPageWidth(), page.getPageHeight());
            resizePageProperties((int) newPageSize.x, (int) newPageSize.y);
            BitmapDrawable drawable = mImageCache.get("" + pageNumber);
            if (drawable != null) {
                readyNextPage(pageNumber, drawable.getBitmap());
            } else {
                if (mCurrentJob == null || mCurrentJob.getPageNumber() != pageNumber) {
                    if (mCurrentJob != null && !mCurrentJob.isDone()) {
                        Log.i("USER_CROP", "Cancelling job for page " + mCurrentJob.mPageNumber);
                        mCurrentJob.cancelRasterizing();
                    }

                    mCurrentJob = new DrawImageTask(getActivity(), pageNumber, newPageSize, doc);
                    Log.i("USER_CROP", "Starting render job for page " + mCurrentJob.mPageNumber);
                    mCurrentJob.execute();
                } else {
                    Log.i("USER_CROP", "Page " + mCurrentJob.mPageNumber + " is already being rendered");
                }
            }
        } catch (com.pdftron.common.PDFNetException e) {

        } finally {
            try {
                doc.unlockRead();
            } catch (com.pdftron.common.PDFNetException e) {
            }
        }
    }

    private PageCropProperties getCropPropertiesForPage(int pageNumber) {
        PDFDoc doc = mPdfViewCtrl.getDoc();
        try {
            doc.lockRead();
            Page page = doc.getPage(pageNumber);
            PageCropProperties currentProperties = mPageProperties[pageNumber];
            if (currentProperties == null) {
                currentProperties = new PageCropProperties();
                mPageProperties[pageNumber] = currentProperties;
            }
            if (currentProperties.mCropBox == null) {
                currentProperties.mCropBox = page.getCropBox();
                currentProperties.mUserCropBox = page.getBox(Page.e_user_crop);
                currentProperties.mRotation = page.getRotation();
            }
            return currentProperties;
        } catch (com.pdftron.common.PDFNetException e) {
            Log.d("USER_CROP", e.getStackTrace().toString());
        } finally {
            try {
                doc.unlockRead();
            } catch (com.pdftron.common.PDFNetException e) {
            }
        }
        return null;
    }

    private void readyNextPage(int pageNumber, Bitmap newBitmap) {
        if (pageNumber == mCurrentPage && newBitmap != null) {
            //Bitmap oldBitmap = mCropImageView.getImageBitmapAndClear();

            mCropImageView.setImageBitmap(newBitmap);
            // setting the iamge bitmap above seems to auto-resize the mCropImageView to
            // the old size, so we need to set it again.
            resizePageProperties(newBitmap.getWidth(), newBitmap.getHeight());

            SetCropRectOnView();
        }
        preRenderIfNecessary();
    }

    private void preRenderIfNecessary() {
        if (mCurrentJob != null && !mCurrentJob.isDone()) {
            return;
        }
        int pageToRender = getNextPageToPreRender(mCurrentPage);
        while (pageToRender > 0 && pageToRender <= mTotalPages
                && Math.abs(pageToRender - mCurrentPage) <= mPagesToPreRenderPerDirection) {
            BitmapDrawable drawable = mImageCache.get("" + pageToRender);
            if (drawable == null) {
                Log.i("USER_CROP", "Pre-rendering page " + pageToRender);
                PDFDoc doc = mPdfViewCtrl.getDoc();
                try {
                    doc.lockRead();
                    Page page = doc.getPage(pageToRender);
                    Log.i("USER_CROP", "Calculating page size for page " + pageToRender);
                    Point newPageSize = calculateBlankPageSize(page.getPageWidth(), page.getPageHeight());
                    mCurrentJob = new DrawImageTask(getActivity(), pageToRender, newPageSize,
                            mPdfViewCtrl.getDoc());
                    mCurrentJob.execute();
                } catch (com.pdftron.common.PDFNetException e) {
                    Log.d("USER_CROP", e.getStackTrace().toString());
                } finally {
                    try {
                        doc.unlockRead();
                    } catch (com.pdftron.common.PDFNetException e) {

                    }
                }
                return;
            }
            pageToRender = getNextPageToPreRender(pageToRender);
        }
    }

    private int getNextPageToPreRender(int lastRenderedPage) {
        if (lastRenderedPage < 1 || lastRenderedPage > mTotalPages) {
            return -1;
        }
        if (lastRenderedPage > mCurrentPage) {
            int pageDiff = Math.abs(mCurrentPage - lastRenderedPage);
            int nextPage = lastRenderedPage - pageDiff - pageDiff;
            if (nextPage < 1) {
                return lastRenderedPage + 1;
            }
            return nextPage;
        } else {
            int pageDiff = Math.abs(mCurrentPage - lastRenderedPage);
            int nextPage = lastRenderedPage + pageDiff + pageDiff + 1;
            if (nextPage > mTotalPages) {
                return lastRenderedPage - 1;
            }
            return nextPage;
        }
    }

    private void SetCropRectOnView() {
        if (mPageProperties[mCurrentPage].mUserCropBox != null) {
            try {
                Rect cropBox = mPageProperties[mCurrentPage].mCropBox;
                if (cropBox.getWidth() > 0 && cropBox.getHeight() > 0) {
                    Rect userCropBox = mPageProperties[mCurrentPage].mUserCropBox;
                    RectF cropPercentageRect = new RectF();
                    cropPercentageRect.left = (float) ((userCropBox.getX1() - cropBox.getX1())
                            / cropBox.getWidth());
                    cropPercentageRect.right = (float) ((cropBox.getX2() - userCropBox.getX2())
                            / cropBox.getWidth());
                    cropPercentageRect.bottom = (float) ((userCropBox.getY1() - cropBox.getY1())
                            / cropBox.getHeight());
                    cropPercentageRect.top = (float) ((cropBox.getY2() - userCropBox.getY2())
                            / cropBox.getHeight());

                    rotateRectangle(cropPercentageRect, mPageProperties[mCurrentPage].mRotation);

                    //mCropImageView.setCropRectPercentageMargins(cropPercentageRect);
                }
            } catch (com.pdftron.common.PDFNetException e) {

            }
        }
    }

    private void updatePageCropFromImageView(PageCropProperties properties, RectF percentageRect) {
        if (properties != null) {
            try {
                int rot = Page.subtractRotations(Page.e_0, properties.mRotation);
                rotateRectangle(percentageRect, rot);

                Rect cropBox = properties.mCropBox;
                if (cropBox != null && cropBox.getWidth() > 0 && cropBox.getHeight() > 0) {
                    if (properties.mUserCropBox == null) {
                        properties.mUserCropBox = new Rect();
                    }
                    properties.mUserCropBox.setX1(cropBox.getX1() + (percentageRect.left * cropBox.getWidth()));
                    properties.mUserCropBox.setX2(cropBox.getX2() - (percentageRect.right * cropBox.getWidth()));
                    properties.mUserCropBox.setY1(cropBox.getY1() + (percentageRect.bottom * cropBox.getHeight()));
                    properties.mUserCropBox.setY2(cropBox.getY2() - (percentageRect.top * cropBox.getHeight()));
                }
            } catch (com.pdftron.common.PDFNetException e) {

            }
        }
    }

    private Point calculateBlankPageSize(double pageWidth, double pageHeight) {
        //int marginSize = mCropImageView.getMarginSize();

        //        double hostWidth = mCropPageHost.getMeasuredWidth() - (2 * marginSize);
        //        double hostHeight = mCropPageHost.getMeasuredHeight() - (2 * marginSize);

        // calculate size of page
        //        double widthScale = hostWidth / pageWidth;
        //        double heightScale = hostHeight / pageHeight;
        //
        //        if (widthScale < heightScale) {
        //            return new Point((int)hostWidth, (int)(pageHeight * widthScale));
        //        }
        //        else {
        //            return new Point((int)(pageWidth * heightScale), (int)hostHeight);
        //        }
        return new Point(0.0, 0.0);
    }

    private void resizePageProperties(int pageWidth, int pageHeight) {
        //int margin = mCropImageView.getMarginSize();

        FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mCropImageView.getLayoutParams();
        //        layoutParams.width = pageWidth + margin + margin;
        //        layoutParams.height = pageHeight + margin + margin;
        mCropImageView.setLayoutParams(layoutParams);

        layoutParams = (FrameLayout.LayoutParams) mBlankPagePlaceholder.getLayoutParams();
        layoutParams.width = pageWidth;
        layoutParams.height = pageHeight;
        mBlankPagePlaceholder.setLayoutParams(layoutParams);
    }

    // Cropping ////////////////////////////////////////////////////////////////

    private SetCropBoxTask mCropBoxTask;
    private ApplyCropToAllPagesTask mApplyCropTask;

    private void setCropBoxAndClose() {
        if (mDisablingOverlayShowing) {
            return;
        }
        if (mCurrentJob != null && !mCurrentJob.isDone()) {
            mCurrentJob.cancelRasterizing();
        }
        mIsActive = true;
        //        if (mPageProperties[mCurrentPage] != null)
        //        {
        //            if (mCropImageView.hasBitmap()) {
        //                updatePageCropFromImageView(mPageProperties[mCurrentPage], mCropImageView.getCropRectPercentageMargins());
        //            }
        //        }
        PDFDoc doc = mPdfViewCtrl.getDoc();
        mPdfViewCtrl.cancelRendering();
        mCropBoxTask = new SetCropBoxTask(getActivity(), doc);
        mCropBoxTask.execute();
    }

    void applyCropToAllPagesAndFlash() {
        if (mDisablingOverlayShowing) {
            return;
        }
        //        if (mCropImageView.hasBitmap() && mPageProperties[mCurrentPage] != null) {
        //            RectF percentageRect = mCropImageView.getCropRectPercentageMargins();
        //
        //            // Ensure the current page has the correct value now.
        //            updatePageCropFromImageView(mPageProperties[mCurrentPage], percentageRect);
        //
        //            try {
        //                int rot = Page.subtractRotations(Page.e_0, mPageProperties[mCurrentPage].mRotation);
        //                rotateRectangle(percentageRect, rot);
        //
        //                Rect cropBox = mPageProperties[mCurrentPage].mCropBox;
        //                if (cropBox != null && cropBox.getWidth() > 0 && cropBox.getHeight() > 0) {
        //                    Rect cropMargins = new Rect(percentageRect.left * cropBox.getWidth(),
        //                            percentageRect.bottom * cropBox.getHeight(),
        //                            percentageRect.right * cropBox.getWidth(),
        //                            percentageRect.top * cropBox.getHeight());
        //
        //                    mApplyCropTask = new ApplyCropToAllPagesTask(getActivity(), cropMargins, mPdfViewCtrl.getDoc());
        //                    mApplyCropTask.execute();
        //                    Utils.showToast(getActivity(), getCropAllInfoString());
        //
        //                }
        //
        //            }
        //            catch (PDFNetException e){
        //                Log.d("USER_CROP", e.getStackTrace().toString());
        //            }
        //        }
        return;
    }

    /**
     * Used to modify the user crop box to all the pages in a background thread.
     * */
    private class SetCropBoxTask extends CustomAsyncTask<Void, Integer, Boolean> {

        private PDFDoc mDoc;
        private long mTaskStartTime;

        public SetCropBoxTask(Context context, PDFDoc doc) {
            super(context);

            mDoc = doc;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();

            mDisablingOverlay.setVisibility(View.VISIBLE);
            mDisablingOverlayShowing = true;

            mTaskStartTime = System.nanoTime() / 1000000;
        }

        @Override
        protected Boolean doInBackground(Void... params) {
            long startTime = System.nanoTime();
            boolean success = true;
            try {
                mDoc.lock();

                if (mRemoveCropHelperMode) {
                    removeCropHelper();
                } else {
                    cropPagesHelper();
                }
            } catch (com.pdftron.common.PDFNetException e) {
                Log.d("USER_CROP", e.getStackTrace().toString());
                success = false;
            } finally {
                try {
                    mDoc.unlock();
                } catch (com.pdftron.common.PDFNetException e) {
                    Log.d("USER_CROP", e.getStackTrace().toString());
                }
            }

            long endTime = System.nanoTime();
            long duration = (endTime - startTime) / 1000000;
            Log.i("USER_CROP", "Permanently cropped all pages: It took " + duration + "milliseconds");
            return Boolean.valueOf(success);
        }

        private void cropPagesHelper() throws PDFNetException {
            int currPageNum = 1;
            PageIterator itr = mDoc.getPageIterator();
            while (itr.hasNext()) {
                Object obj = itr.next();
                Page page = (Page) obj;
                PageCropProperties pageProperties = mPageProperties[currPageNum];
                if (pageProperties != null && pageProperties.mUserCropBox != null) {
                    try {
                        if (pageProperties.mUserCropBox.getX1() - pageProperties.mCropBox.getX1() <= 0.1
                                && pageProperties.mCropBox.getX2() - pageProperties.mUserCropBox.getX2() <= 0.1
                                && pageProperties.mUserCropBox.getY1() - pageProperties.mCropBox.getY1() <= 0.1
                                && pageProperties.mCropBox.getY2() - pageProperties.mUserCropBox.getY2() <= 0.1) {
                            //Log.i("USER_CROP", "Permanently removing crop from page " + currPageNum);
                            com.pdftron.pdf.utils.UserCropUtilities.removeUserCropFromPage(page);
                        } else {
                            //Log.i("USER_CROP", "Permanently cropping page " + currPageNum);
                            page.setBox(Page.e_user_crop, pageProperties.mUserCropBox);
                        }
                    } catch (com.pdftron.common.PDFNetException e) {
                        Log.d("USER_CROP", e.getStackTrace().toString());
                    }
                }

                if (currPageNum % 100 == 99) {
                    publishProgress(Integer.valueOf(currPageNum));
                }

                currPageNum++;
            }
        }

        private void removeCropHelper() throws PDFNetException {
            int currPageNum = 1;
            PageIterator itr = mDoc.getPageIterator();
            while (itr.hasNext()) {
                Object obj = itr.next();
                Page page = (Page) obj;

                try {
                    UserCropUtilities.removeUserCropFromPage(page);
                } catch (com.pdftron.common.PDFNetException e) {
                    Log.d("USER_CROP", e.getStackTrace().toString());
                }

                if (currPageNum % 100 == 99) {
                    publishProgress(Integer.valueOf(currPageNum));
                }

                currPageNum++;
            }
        }

        @Override
        protected void onCancelled(Boolean succeeded) {
            super.onCancelled(succeeded.booleanValue());

            removeSpinner();
        }

        @Override
        protected void onPostExecute(Boolean succeeded) {
            super.onPostExecute(succeeded);

            removeSpinner();
            try {
                mPdfViewCtrl.updatePageLayout();
            } catch (Exception e) {
                AnalyticsHandlerAdapter.getInstance().sendException(e);
            }

            if (succeeded.booleanValue()) {
                UserCropDialogFragment.this.dismiss();
            }

            removeSpinner();
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);

            long now = System.nanoTime() / 1000000;

            if (now - mTaskStartTime > MILLISECONDS_BEFORE_SHOWING_PROGRESS) {
                showSpinner();
            }
        }
    }

    /**
     * Used to apply the crop box to each page's properties
     */
    private class ApplyCropToAllPagesTask extends CustomAsyncTask<Void, Integer, Boolean> {

        private Rect mMarginRect;
        private PDFDoc mDoc;
        private long mTaskStartTime;

        public ApplyCropToAllPagesTask(Context context, Rect marginRect, PDFDoc doc) {
            super(context);

            mMarginRect = marginRect;
            mDoc = doc;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            mDisablingOverlay.setVisibility(View.VISIBLE);
            mDisablingOverlayShowing = true;

            mTaskStartTime = System.nanoTime() / 1000000;
        }

        @Override
        protected Boolean doInBackground(Void... params) {
            boolean success = applyCropToAllPages(mMarginRect, mDoc);
            return Boolean.valueOf(success);
        }

        boolean applyCropToAllPages(Rect marginRect, PDFDoc doc) {
            long startTime = System.nanoTime();
            boolean success = false;
            // Getting a page (e.g. PDFDoc.getPage(n)) is O(n). Therefore, we create an iterator since
            // we might have to look at every page
            try {
                doc.lockRead();

                PageIterator itr = doc.getPageIterator();
                int pageNumber = 1;
                while (itr.hasNext() && !isCancelled()) {
                    Object pageObj = itr.next();
                    Page page = (Page) pageObj;

                    if (mPageProperties[pageNumber] == null) {
                        PageCropProperties cropProperties = new PageCropProperties();
                        cropProperties.mCropBox = page.getCropBox();
                        cropProperties.mRotation = page.getRotation();
                        cropProperties.mUserCropBox = new Rect();
                        mPageProperties[pageNumber] = cropProperties;
                    }
                    Rect userCrop = mPageProperties[pageNumber].mUserCropBox;
                    Rect crop = mPageProperties[pageNumber].mCropBox;

                    if (marginRect.getX1() + marginRect.getX2() + 10 > crop.getWidth()) {
                        userCrop.setX1(crop.getX1());
                        userCrop.setX2(crop.getX2());
                    } else {
                        userCrop.setX1(crop.getX1() + marginRect.getX1());
                        userCrop.setX2(crop.getX2() - marginRect.getX2());
                    }

                    if (marginRect.getY1() + marginRect.getY2() + 10 > crop.getHeight()) {
                        userCrop.setY1(crop.getY1());
                        userCrop.setY2(crop.getY2());
                    } else {
                        userCrop.setY1(crop.getY1() + marginRect.getY1());
                        userCrop.setY2(crop.getY2() - marginRect.getY2());
                    }

                    pageNumber++;

                    if (pageNumber % 100 == 99) {
                        publishProgress(Integer.valueOf(pageNumber));
                    }
                }
                success = true;
            } catch (PDFNetException e) {
                Log.d("USER_CROP", e.getStackTrace().toString());
            } finally {
                try {
                    doc.unlockRead();
                } catch (com.pdftron.common.PDFNetException e) {
                    Log.d("USER_CROP", e.getStackTrace().toString());
                }
            }
            long endTime = System.nanoTime();
            long duration = (endTime - startTime) / 1000000;
            Log.i("USER_CROP", "Applied crop to all pages. It took " + duration + "milliseconds");
            return success;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            long now = System.nanoTime() / 1000000;

            if (now - mTaskStartTime > MILLISECONDS_BEFORE_SHOWING_PROGRESS) {
                showSpinner();
            }
        }

        @Override
        protected void onCancelled(Boolean aBoolean) {
            super.onCancelled(aBoolean);

            removeSpinner();
        }

        @Override
        protected void onPostExecute(Boolean aBoolean) {
            super.onPostExecute(aBoolean);

            removeSpinner();
        }
    }

    private AlphaAnimation mSpinnerAlphaAnimation;

    private void showSpinner() {
        this.setCancelable(false);
        if (mSpinnerShowing) {
            return;
        }
        mSpinnerShowing = true;

        mProgressBarHost.setVisibility(View.VISIBLE);
        mSpinnerAlphaAnimation = new AlphaAnimation(0.0f, 1.0f);
        mSpinnerAlphaAnimation.setDuration(500);
        mSpinnerAlphaAnimation.setInterpolator(new LinearInterpolator());
        mProgressBarHost.startAnimation(mSpinnerAlphaAnimation);
    }

    private void removeSpinner() {
        this.setCancelable(true);
        mProgressBarHost.setVisibility(View.GONE);
        mDisablingOverlay.setVisibility(View.GONE);
        mDisablingOverlayShowing = false;
        if (mSpinnerAlphaAnimation != null && !mSpinnerAlphaAnimation.hasEnded()) {
            mSpinnerAlphaAnimation.cancel();
        }
        if (mSpinnerShowing) {
            mSpinnerShowing = false;
        }
    }

    // Utility Functions ///////////////////////////////////////////////////////

    void rotateRectangle(RectF rect, int rotation) {
        float temp;
        switch (rotation) {
        case Page.e_0:
            return;
        case Page.e_90:
            temp = rect.left;
            rect.left = rect.bottom;
            rect.bottom = rect.right;
            rect.right = rect.top;
            rect.top = temp;
            return;
        case Page.e_180:
            temp = rect.left;
            rect.left = rect.right;
            rect.right = temp;
            temp = rect.bottom;
            rect.bottom = rect.top;
            rect.top = temp;
            return;
        case Page.e_270:
            temp = rect.left;
            rect.left = rect.top;
            rect.top = rect.right;
            rect.right = rect.bottom;
            rect.bottom = temp;
            return;
        }
    }

    private String getCropAllInfoString() {
        return String.format(getActivity().getResources().getString(R.string.user_crop_manual_crop_crop_all_toast),
                getString(R.string.user_crop_manual_crop_done));
    }
}