public interface LayerOptionsCallbackHandler {

    void select(int index);

    void delete(int index);

    void rename(int index);
} public class ShapeDialog extends BasicDialog implements View.OnClickListener{

    private Button mButton;
    private RadioGroup mRadioGroup;
    private EditText mEditText;
    private ShapeSetCallbackHandler mOnClickHandler;

    /**
     * Instantiates a dialog instance.
     *
     * @param activity   caller activity
     */
    public ShapeDialog(Activity activity, ShapeSetCallbackHandler onClickHandler) {
        super(activity, R.layout.dialog_shape);

        mRadioGroup = (RadioGroup)getLayout().findViewById(R.id.shape_group);
        mButton = (Button)getLayout().findViewById(R.id.selectbutton);
        mEditText = (EditText) getLayout().findViewById(R.id.numberpicker);
        mButton.setOnClickListener(this);

        mOnClickHandler = onClickHandler;
        getDialog().show();
    }


    @Override
    public void onClick(View v) {
        int id = mRadioGroup.getCheckedRadioButtonId();
        if(mEditText.getText().toString() == null || mEditText.getText().toString().isEmpty()){
            Toaster.makeShortToast(getActivity(), "Please input number of points.");
        }
        else {
            int type;
            if(mRadioGroup.getCheckedRadioButtonId() == R.id.star_radio)
                type = 0;
            else
            type = 1;
            mOnClickHandler.onShapeSet(Integer.parseInt(mEditText.getText().toString()), type);
            getDialog().dismiss();
        }
    }

} public interface ShapeSetCallbackHandler {

    void onShapeSet(int vertexSize, int type);
} public final class BitmapLayer extends BaseBitmapLayer {

    // debug tag
    private static final String TAG = "BitmapLayer";

    /**
     * Bitmap object.
     */
    private Bitmap mBitmap;


    private float mCacheRealRot;

    /**
     * Path to the bitmap file.
     */
    private String mBitmapPath = null;


    /**
     * Cached position, usually set on pointer down.
     */
    private PointF mCachedPosition;

    private PointF mCachedUnrotatedScale = new PointF(1, 1);

    /**
     * Max of bitmap width, height.
     */
    private float mMaxDimensionSize;

    /**
     * Cached position for scaling bitmaps.
     */
    private PointF mScalePosition;


    /**
     * RTS transform matrix, fro transforming the bitmap.
     */
    private Matrix mMatrix = new Matrix();


    protected RectF mBounds;


    /**
     * True once bitmap appears on the screen. This flag is essentially to distinguish between
     * Bitmaps that have at least been shown and those that were instantiated and unused.
     * (e.g. another layer was selected / instantiated before this layer could be used - in that
     * case, this layer can be discarded.
     */
    private boolean mInitialized = false;


    /**
     * True, while this is currently selected, and being manipulated, false otherwise.
     */
    private boolean mInteracting = true;


    /**
     *
     */
    private boolean mSet = false;


    /**
     * Constructor for <code>BitmapLayer</code> taking Bitmap object, x,y initial coordinates, and
     * references to RTS icons.
     *
     * @param bitmap Bitmap object
     * @param x      x coordinate of initial position.
     * @param y      y coordinate of initial position.
     */
    public BitmapLayer(Bitmap bitmap, float x, float y) {
        super(x, y);

        initialize(bitmap, null);
    }

    /**
     * Copy Constructor
     *
     * @param bitmap
     * @param bitmapPath
     * @param x
     * @param y
     * @param rotation
     * @param scaleX
     * @param scaleY
     */
    private BitmapLayer(Bitmap bitmap, String bitmapPath, float x, float y, float rotation, float scaleX, float scaleY) {
        super(x, y);
        setScaleValueX(scaleX);
        setScaleValueY(scaleY);
        setRotValue(rotation);
        setCachedRotValue(rotation);

        initialize(bitmap, bitmapPath);
    }


    /**
     * @param bitmap
     * @param path
     */
    private void initialize(Bitmap bitmap, String path) {
        mBitmap = bitmap;
        mBitmapPath = path;
        //init fields
        mCachedPosition = new PointF(0, 0);
        mScalePosition = new PointF(0, 0);
        mBounds = new RectF();

        mMode = Mode.TRANSLATE;

        // find the max dimension. Needed for clean scaling and rotating.
        mMaxDimensionSize = Math.max(mBitmap.getWidth(), mBitmap.getHeight());
    }


    /**
     * Gets the  bitmap.
     *
     * @return bitmap
     */
    public Bitmap getBitmap() {
        return mBitmap;
    }


    /**
     * Sets the bitmap object.
     *
     * @param bitmap
     */
    public void setBitmap(Bitmap bitmap) {
        mBitmap = bitmap;
    }


    /**
     * Reinitialize the layer.
     */
    private void reInitialize() {
        mInteracting = true;
    }


    /**
     * Set the cached Position
     *
     * @param value <code>PointF</code> position.
     */
    private void setCachedPosition(PointF value) {
        mCachedPosition = value;
    }


    /**
     * Makes a bounding rect for the Bitmap. Should match the RTS transforms of
     * the bitmap and will be drawn on top of the bitmap. Useful for getting the
     * boundary coords of the transformed bitmap.
     */
    private void makeBoundingRectF() {
        float rot = this.getRotValue();
        float sx = getScaleValueX();
        float sy = getScaleValueY();
//        float w = mBitmap.getWidth();
//        float h = mBitmap.getHeight();
        float w = mMaxDimensionSize, h = mMaxDimensionSize;

        mBounds = new RectF(-w / 2, -h / 2, w / 2, h / 2);

        PointF tl = new PointF(mBounds.left, mBounds.top);
        PointF tr = new PointF(mBounds.right, mBounds.top);
        PointF bl = new PointF(mBounds.left, mBounds.bottom);
        PointF br = new PointF(mBounds.right, mBounds.bottom);
        setMatrix();
        scalePoint(tl);
        scalePoint(tr);
        scalePoint(bl);
        scalePoint(br);

        float right = Math.max(Math.max(tl.x, tr.x), Math.max(bl.x, br.x));
        float left = Math.min(Math.min(tl.x, tr.x), Math.min(bl.x, br.x));
        float top = Math.min(Math.min(tl.y, tr.y), Math.min(bl.y, br.y));
        float bottom = Math.max(Math.max(tl.y, tr.y), Math.max(bl.y, br.y));
        mBounds.set(left, top, right, bottom);
        PointF c = this.getCenter();

        mBounds.set(c.x - mBounds.width() / 2, c.y - mBounds.height() / 2, c.x + mBounds.width()
                / 2, c.y + mBounds.height() / 2);
    }


    /**
     * Scales around point.
     *
     * @param point Point to scale and rotate
     */
    private void scalePoint(PointF point) {

        float x = point.x;
        float y = point.y;
        float[] f = new float[]{
                x, y
        };

        mMatrix.mapPoints(f);
        point.x = f[0];
        point.y = f[1];
    }


    /**
     * Sets the matrix to the correct parameters for rotating,
     * scaling and translating (in the correct order)
     */
    private void setMatrix() {
        mMatrix.reset();
        if (getRotValue() != 0) {

            mMatrix.postRotate(getRotValue(), 0 + mBitmap.getWidth() / 2,
                    0 + mBitmap.getHeight() / 2);
        }
        mMatrix.postScale(getScaleValueX(), getScaleValueY());
        mMatrix.postTranslate(getX(), getY()); // translate to correct
    }


    /**
     * Performs RTS transformations on the bitmap and then draws to the canvas.
     * Matrix order is Scale, Translate, Rotate.
     */
    private void transformBitmap() {

        setMatrix();
        setIconCoords();
        makeBoundingRectF();
        return;
    }


    /**
     * Performs the same operation as transformBitmap, but stores the matrix as
     * a MatrixAction for undoing.
     */
    private IUndo setBitmapOnDown() {
        MatrixUndo ma = new MatrixUndo(this);
        ma.x = getX();
        ma.y = getY();
        ma.scaleX = this.getScaleValueX();
        ma.scaleY = this.getScaleValueY();
        ma.rotation = this.getRotValue();
        return new IUndo(ma);

    }


    //
    // Motion event actions.
    //


    /**
     * On Up Motion event. User raises finger from screen action.
     *
     * @param x the x coordinate of the action event
     * @param y the y coordinate of the action event
     * @return <code>IUndo</code> action, to be place on undo stack.
     */
    private IUndo onDown(float x, float y) {
        if (!mInitialized) {

            final float u = getX();
            final float v = getY();

            setCoords(x, y);
            setCachedPosition(new PointF(getX(), getY()));


            makeBoundingRectF();
            float norm = (float) Math.sqrt(((mBounds.bottom - mBounds.centerY())
                    * (mBounds.bottom - mBounds.centerY()) + (mBounds.left - mBounds.centerX())
                    * (mBounds.left - mBounds.centerX())));
            // account for the rotation icon being not at the top center
            float iconAngle = (float) Math.acos(
                    (mBounds.bottom - mBounds.centerY()) / norm);

            mCacheRealRot = (float) (iconAngle * 180f / Math.PI);

            mInitialized = true;
            return new IUndo(new IUndoHandler() {

                float redoX;
                float redoY;

                @Override
                public void handleUndo() {
                    redoX = getX();
                    redoY = getY();
                    setCoords(u, v);
                    setCachedPosition(new PointF(getX(), getY()));
                }

                @Override
                public void handleRedo() {
                    setCoords(redoX, redoY);
                    setCachedPosition(new PointF(getX(), getY()));
                }
            });

        }
        // set the correct mode
        if (mSet) {
            if (IconManager.instance().getRotationIcon().isInIcon(x, y)) {
                setMode(Mode.ROTATE);

                return setBitmapOnDown();
            } else if (IconManager.instance().getScaleIcon().isInIcon(x, y)) {
                if (getScaleValueX() < 0) {
                    mScalePosition.x = mBounds.right;
                } else {
                    mScalePosition.x = mBounds.left;
                }
                if (getScaleValueY() < 0) {
                    mScalePosition.y = mBounds.bottom;
                } else {
                    mScalePosition.y = mBounds.top;

                }
                setMode(Mode.SCALE);
                return setBitmapOnDown();
            } else if (IconManager.instance().getTranslateIcon().isInIcon(x, y)) {
                setMode(Mode.TRANSLATE);
                return setBitmapOnDown();
            } else {
                setMode(Mode.NONE);
            }
        }
        return null;
    }


    /**
     * On Move Motion event. User moves finger on screen action.
     *
     * @param x the x coordinate of the action event
     * @param y the y coordinate of the action event
     */
    private void onMove(float x, float y) {
        if (!mSet)
            return;
        else {
            makeBoundingRectF();
            if (getMode() == Mode.ROTATE) {

                /*
                 * The rotation value needs to be chosen carefully. If the
                 * x-scale value is negative the current rotation value needs to
                 * be subtracted from the cached rotation value. Likewise for
                 * the y-scale value. Also, if the y-scale value is negative we
                 * must subtract 180 from the current rotation value. Otherwise,
                 * the rotation is flipped the wrong way.
                 */
                float rot = getCachedRotValue() + Math.signum(getCenter().x - getX())
                        * Math.signum(getCenter().y - getY()) * getRotationAngle(x, y);

                if (getY() - getCenter().y > 0) {
                    rot = rot - 180;

                }

                float norm = (float) Math.sqrt(((mBounds.bottom - mBounds.centerY())
                        * (mBounds.bottom - mBounds.centerY()) + (mBounds.left - mBounds.centerX())
                        * (mBounds.left - mBounds.centerX())));
                // account for the rotation icon being not at the top center
                float iconAngle = (float) Math.acos(
                        (mBounds.bottom - mBounds.centerY()) / norm);
                iconAngle = (float) (iconAngle * 180 / Math.PI);
                rot -= mCacheRealRot - 180;//iconAngle- 180;

                setRotValue(rot % 360);

                float r = getRotValue();

                // lets rotate the scale factors too.
                float rads = r * (float) Math.PI / 180.0f;
                float COS = (float) Math.cos(rads);
                float SIN = (float) Math.sin(rads);
                float TAN = (float) Math.tan(rads);

                float ox = mCachedUnrotatedScale.x;
                float oy = mCachedUnrotatedScale.y;

                float A = (float) Math.sqrt((ox * ox * COS * COS - oy * oy * SIN * SIN) / (COS * COS - SIN * SIN));
                float B = (float) (Math.sqrt((oy * oy - A * A * SIN * SIN) / (COS * COS)));
                if (TAN != TAN || Math.abs(COS - 0) < 0.001f) {
                    A = oy;
                    B = ox;
                }

                float diff = Math.abs(0 - (rot % 45.0f));

                if (diff < 5 || diff > 40) {
                    return;
                }


            } else if (getMode() == Mode.SCALE) {
                float xs = (x - mScalePosition.x) / mBounds.width();
                float ys = (y - mScalePosition.y) / mBounds.height();

                setScaleValueX(Math.abs(getScaleValueX()) * xs);
                setScaleValueY(Math.abs(getScaleValueY()) * ys);

                if (Math.abs(getRotValue() - 0) < 0.001f) {
                    mCachedUnrotatedScale.x = getScaleValueX();
                    mCachedUnrotatedScale.y = getScaleValueY();
                }

                if (getScaleValueX() < 0) {
                    setX(getX() + (-mBounds.right + mScalePosition.x) / 2);
                } else {
                    setX(getX() + (-mBounds.left + mScalePosition.x) / 2);
                }
                if (getScaleValueY() < 0) {
                    setY(getY() + (-mBounds.bottom + mScalePosition.y) / 2);
                } else {
                    setY(getY() + (-mBounds.top + mScalePosition.y) / 2);
                }
                makeBoundingRectF();
                float norm = (float) Math.sqrt(((mBounds.bottom - mBounds.centerY())
                        * (mBounds.bottom - mBounds.centerY()) + (mBounds.left - mBounds.centerX())
                        * (mBounds.left - mBounds.centerX())));
                // account for the rotation icon being not at the top center
                float iconAngle = (float) Math.acos(
                        (mBounds.bottom - mBounds.centerY()) / norm);

                mCacheRealRot = (float) (iconAngle * 180f / Math.PI);
                return;
            } else if (getMode() == Mode.TRANSLATE) {

                float xc = getX();
                float yc = getY();
                // calc dist from rect topleft/topright
                float distx = getX() - mBounds.left;
                float disty = getY() - mBounds.top;
                if (getScaleValueX() < 0) {
                    distx = getX() - mBounds.right;
                }
                if (getScaleValueY() < 0) {
                    disty = getY() - mBounds.bottom;
                }
                setX(x + distx);
                setY(y + disty);
                float newCachedX = mCachedPosition.x + getX() - xc;
                float newCachedY = mCachedPosition.y + getY() - yc;
                mCachedPosition.set(new PointF(newCachedX, newCachedY));

            } else if (getMode() == Mode.NONE) {
                // empty
            }
        }
    }

    /**
     * On Up Motion event. User raises finger from screen action.
     *
     * @param x the x coordinate of the action event
     * @param y the y coordinate of the action event
     */
    private void onUp(float x, float y) {
        if (getMode() == Mode.ROTATE) {
            setCachedRotValue(getRotValue());
        }
        if (getMode() == Mode.NONE && mSet) {
        }
        mSet = true;
    }


    //
    // Abstract methods from <code>BaseBitmapLayer</code>
    //

    @Override
    protected void setIconCoords() {
        // if not initialized then bitmap has no coords
        if (mBitmap == null || !mInitialized) {
            return;
        }

        if (mBounds != null) {
            float left = mBounds.left, top = mBounds.top, right = mBounds.right, bottom = mBounds.bottom;
            if (getScaleValueX() < 0) {
                left = mBounds.right;
                right = mBounds.left;
            }
            if (getScaleValueY() < 0) {
                top = mBounds.bottom;
                bottom = mBounds.top;
            }
            IconManager.instance().getRotationIcon().setXCoord(left);
            IconManager.instance().getRotationIcon().setYCoord(bottom);

            IconManager.instance().getScaleIcon().setXCoord(right);
            IconManager.instance().getScaleIcon().setYCoord(bottom);

            IconManager.instance().getTranslateIcon().setXCoord(left);
            IconManager.instance().getTranslateIcon().setYCoord(top);

        }
    }

    @Override
    protected PointF getCenter() {
        float x = getX() + mBitmap.getWidth() * getScaleValueX() / 2;
        float y = getY() + mBitmap.getHeight() * getScaleValueY() / 2;
        return new PointF(x, y);
    }


    //
    // Methods from <code>ILayer</code>
    //

    @Override
    public void setStart() {
        mInteracting = true;
    }

    @Override
    public void setStop() {
        mInteracting = false;
    }

    @Override
    public void draw(Canvas canvas, boolean showTransformBox) {
        canvas.drawBitmap(mBitmap, mMatrix, null);
        if (mSet && mInteracting && showTransformBox) {

            if (mBounds != null) {
                Paint p = new Paint(Color.RED);
                p.setStyle(Paint.Style.STROKE);
                canvas.drawRect(mBounds, p);
            }

            drawIcons(canvas);
        }
    }

    @Override
    public IUndo onEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            return onDown(event.getX(), event.getY());
        } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
            onMove(event.getX(), event.getY());
            // return null;
        } else if (event.getAction() == MotionEvent.ACTION_UP) {
            onUp(event.getX(), event.getY());
            //return null;
        }
        transformBitmap();
        return null;
    }

    @Override
    public LayerType getLayerType() {
        return LayerType.IMAGE;
    }

    @Override
    public String getTag() {
        return mName;
    }

    @Override
    public void setTag(String tag) {
        mName = tag;
    }

    @Override
    public ILayer copy() {
        Bitmap bmp = Bitmap.createBitmap(getBitmap());
        BitmapLayer bml = new BitmapLayer(bmp, mBitmapPath, this.getX(), this.getY(), getRotValue(), getScaleValueX(), getScaleValueY());

        return bml;

    }

    @Override
    public String getXml() {
        // String ext = FileUtils.getImageExtension(mBitmapPath);

        String ext = "png";
        String xml = "";
        String encode = DataUtils.encodeTobase64(mBitmap, Bitmap.CompressFormat.PNG);

        int w = mBitmap.getWidth();
        int h = mBitmap.getHeight();
        float x = getX();
        float y = getY();
        xml = "<g transform=\"translate(" + x + ", " + y + ") scale("
                + getScaleValueX() + ", " + getScaleValueY() + ") rotate(" + getRotValue() + " "
                + w / 2 + " " + h / 2 + ") \"><image x=\"" + 0
                + "\" y=\"" + 0 + "\" width=\"" + w + "\" height=\"" + h
                + "\" xlink:href=\"data:image/" + ext + ";base64, " + encode + " \"/></g>";

        return xml;
    }

    /**
     * Returns the rotation, scale and translation information for the bitmap in a
     * SVG <i>transform</i> string.
     *
     * @return SVG transform data
     */
    /* default */ String wrapXmlWithTransformation() {
        int w = mBitmap.getWidth();
        int h = mBitmap.getHeight();
        float x = getX();
        float y = getY();
        String xml = "transform=\"translate(" + x + ", " + y + ") scale("
                + getScaleValueX() + ", " + getScaleValueY() + ") rotate(" + getRotValue() + " "
                + w / 2 + " " + h / 2 + ")\"";

        return xml;
    }

    @Override
    public void accept(ILayerVisitor visitor) {
        visitor.visitBitmap(this);
    }


    //
    // <code>IUndo</code> implementations.
    //


    /**
     *
     */
    private class MatrixUndo implements IUndoHandler {

        /**
         *
         */
        public float x, y;


        /**
         *
         */
        public float scaleX, scaleY;


        /**
         *
         */
        public float rotation;


        /**
         *
         */
        private BitmapLayer mBitmapLayer;


        public MatrixUndo(BitmapLayer bitmapLayer) {
            mBitmapLayer = bitmapLayer;
        }


        @Override
        public void handleUndo() {
            setRotValue(rotation);
            setScaleValueX(scaleX);
            setScaleValueY(scaleY);
            setX(x);
            setY(y);
            transformBitmap();
            if (mBounds != null) {
                makeBoundingRectF();
            }
            setIconCoords();
        }

        @Override
        public void handleRedo() {

        }
    }
} public class TextLayer implements ILayer {


    /**
     * Unique name of this <code>ILayer</code>.
     */
    protected String mName;
    private BitmapLayer mBitmapLayer;
    private String mText;
    private int mColor;
    private int mSize;
    private Typeface mTypeface;
    private Paint mPaint;
    public TextLayer(String text, int color, int size, Typeface typeface) {
        mText = text;
        mColor = color;
        mSize = size;
        mTypeface = typeface;
        initializePaint();
        initBitmap();
    }

    public TextLayer(TextLayer original){
        mText = original.getText();
        mPaint = new Paint(original.getPaint());
        mColor = mPaint.getColor();
        mSize = (int)mPaint.getTextSize();
        mTypeface = mPaint.getTypeface();
        mBitmapLayer = (BitmapLayer)original.getBitmapLayer().copy();
    }

    private void initializePaint() {
        mPaint = new Paint();
        mPaint.setColor(mColor);
        mPaint.setTextSize(mSize);
        mPaint.setTypeface(mTypeface);
        mPaint.setTextAlign(Paint.Align.LEFT);
        mPaint.setTextSize(70);
    }

    /* default */ String getText(){
        return mText;
    }

    /* default */ Paint getPaint(){
        return mPaint;
    }

    /* default */ BitmapLayer getBitmapLayer(){
        return mBitmapLayer;
    }

    /**
     * @param color
     * @return
     */
    public IUndo setColor(int color) {
        ColorUndo ca = new ColorUndo();
        ca.preColor = this.mColor;
        ca.postColor = color;
        this.mColor = color;

        if (mPaint != null) {
            mPaint.setColor(mColor);
            initBitmap();
        }
        return new IUndo(ca);
    }


    /**
     * Sets the color of the text.
     *
     * @param color text color.
     */
    /*default*/ void setColorNoUndo(int color) {
        this.setColor(color);
        initBitmap();
    }


    private void initBitmap(){

        float w =  mPaint.measureText(mText);
        float h = mPaint.getTextSize();
        w = mPaint.measureText(mText);
        h = mPaint.getTextSize();
        Bitmap output = Bitmap.createBitmap((int)w, (int)h ,
                Bitmap.Config.ARGB_8888);

        Canvas canvas = new Canvas(output);
        canvas.drawText(mText, 0, h - 20, mPaint);
        if(mBitmapLayer == null)
            mBitmapLayer = new BitmapLayer(output,0,0);
        else mBitmapLayer.setBitmap(output);
    }

    public void setCapType(Paint.Cap cap){
        mPaint.setStrokeCap(cap);
    }

    public void setJoinType(Paint.Join join){
        mPaint.setStrokeJoin(join);
    }

    @Override
    public void setStart() {
        mBitmapLayer.setStart();
    }

    @Override
    public void setStop() {
        mBitmapLayer.setStop();
    }



    @Override
    public void draw(Canvas canvas, boolean showTransformBox) {
        mBitmapLayer.draw(canvas, showTransformBox);
    }

    @Override
    public IUndo onEvent(MotionEvent event) {
        return mBitmapLayer.onEvent(event);
    }

    @Override
    public LayerType getLayerType() {
        return LayerType.TEXT;
    }

    @Override
    public String getTag() {
        return mName;
    }

    @Override
    public void setTag(String tag) {
        mName = tag;
    }

    @Override
    public ILayer copy() {
        return new TextLayer(this);
    }

    @Override
    public String getXml() {
        if (mText == null || mText.isEmpty())
            return "";

        String xml = "<text "+mBitmapLayer.wrapXmlWithTransformation()+" x='" + 70 * mBitmapLayer.getScaleValueX()+ "' y='" + 70 *mBitmapLayer.getScaleValueY() + "' style=\"font-size  :"
                + 70 + ";" + "fill : #" + DataUtils.getRGBHex(this.mColor) + ";"
                + "\" >" + this.mText + "</text>";

        return xml;
    }

    @Override
    public void accept(ILayerVisitor visitor) {
        visitor.visitText(this);
    }
} public class Toaster {

    /**
     * Makes a short duration toast message
     *
     * @param activity Activity
     * @param message  message id.
     */
    public static void makeShortToast(Activity activity, int message) {
        Toast.makeText(activity, message, Toast.LENGTH_SHORT).show();
    }


    /**
     * Makes a short duration toast message
     *
     * @param activity Activity
     * @param message  message string.
     */
    public static void makeShortToast(Activity activity, String message) {
        Toast.makeText(activity, message, Toast.LENGTH_SHORT).show();

    }


    /**
     * Makes a long duration toast message
     *
     * @param activity Activity
     * @param message  message id.
     */
    public static void makeLongToast(Activity activity, int message) {
        Toast.makeText(activity, message, Toast.LENGTH_LONG).show();
    }


    /**
     * Makes a long duration toast message
     *
     * @param activity Activity
     * @param message  message string.
     */
    public static void makeLongToast(Activity activity, String message) {
        Toast.makeText(activity, message, Toast.LENGTH_LONG).show();

    }
} public class ModeSelectVisitor implements ILayerVisitor {


    /**
     *
     */
    public enum Mode {
        DRAW, VERTEX, BEZIER, ROTATE, TRANSLATE, SCALE
    }


    /**
     *
     */
    private Mode mMode;

    /**
     *
     */
    public ModeSelectVisitor() {

    }


    /**
     * Sets the Mode for <code>ILayer</code> actions.
     *
     * @param mode
     */
    public void setMode(Mode mode) {
        mMode = mode;
    }

    @Override
    public void visit(ILayer layer) {
        layer.accept(this);
    }

    @Override
    public void visitPen(BasePen basePen) {
        if (basePen == null)
            return;

        switch (mMode) {
            case DRAW:
                basePen.setState(BasePen.State.DRAWING);
                break;
            case BEZIER:
                basePen.setState(BasePen.State.BEZIER);
                break;
            case VERTEX:
                basePen.setState(BasePen.State.VERTEX);
                break;
            case ROTATE:
                basePen.setState(BasePen.State.ROTATING);
                break;
            case SCALE:
                basePen.setState(BasePen.State.SCALING);
                break;
            case TRANSLATE:
                basePen.setState(BasePen.State.MOVING);
                break;
            default:
                break;
        }
    }

    @Override
    public void visitBitmap(BaseBitmapLayer layer) {

    }


    @Override
    public void visitText(TextLayer layer) {

    }
} public class Pen extends BasePen {


    /**
     * Becomes true if the Pen has been set on canvas. ie. there
     * is something to draw.
     */
    protected boolean mSet = false;

    /**
     * Transformer object. Performs the RST transforms
     * on the pen's vertices.
     */
    protected PenTransformer mTransformer;


    /**
     * Bezier controller, controls bezier control
     * points.
     */
    protected BezierController mBezierController;


    /**
     * Controls the vertices.
     */
    protected VertexController mVertexController;


    /**
     * Width of screen
     */
    protected float mScreenWidth;


    /**
     * Height of screen.
     */
    protected float mScreenHeight;


    protected boolean mInteracting = false;

    /**
     * Handler class for undoing rotation transformations.
     */
    protected RotateUndoHandler mRotateUndoHandler = null;

    /**
     * Handler class for undoing scale transformations.
     */
    protected ScaleUndoHandler mScaleUndoHandler = null;

    /**
     * Handler class for undoing translate transformations.
     */
    protected TranslateUndoHandler mTranslateUndoHandler = null;

    /**
     *
     */
    public Pen() {
        mVertexList = new ArrayList<>();
        mBezierVertexList = new ArrayList<>();
        mPath = new Path();
        mStrokePaint = new Paint(Color.GREEN);
        mStrokePaint.setColor(Color.GREEN);
        mStrokePaint.setStyle(Paint.Style.STROKE);
        mFillPaint = new Paint();
        mFillPaint.setColor(0x00FFFFFF);
        mTransformer = new PenTransformer(this);
        mRotMatrix = new Matrix();
        mLastPosition = new PointF();
        mBezierController = new BezierController();
        mVertexController = new VertexController();
    }


    /**
     * @param strokePaint
     * @param fillPaint
     * @param bezierPoints
     * @param vertexPoints
     */
    protected Pen(Paint strokePaint, Paint fillPaint, ArrayList<PointF> bezierPoints,
                  ArrayList<PointF> vertexPoints) {
        mVertexList = new ArrayList<>();
        mBezierVertexList = new ArrayList<>();
        mPath = new Path();
        mStrokePaint = new Paint(strokePaint);
        mFillPaint = new Paint(fillPaint);
        mTransformer = new PenTransformer(this);
        mRotMatrix = new Matrix();
        mLastPosition = new PointF();
        mBezierController = new BezierController();
        for (PointF p : bezierPoints) {
            mBezierVertexList.add(new PointF(p.x, p.y));
        }
        for (PointF p : vertexPoints) {
            mVertexList.add(new PointF(p.x, p.y));
        }
        mVertexController = new VertexController();
        mSet = true;
    }

    @Override
    public void setStrokeColor(int color) {
        super.setStrokeColor(color);


    }


    //
    // BasePen methods
    //

    @Override
    protected IUndo handleDownAction(float x, float y) {
        if (IconManager.instance().getRotationIcon().isInIcon(x, y)) {
            mState = State.ROTATING;

            RotateUndoHandler rh = new RotateUndoHandler();
            rh.cachedRotation = mTransformer.getCurrentRotation();
            return new IUndo(rh);

        } else if (IconManager.instance().getScaleIcon().isInIcon(x, y)) {
            mState = State.SCALING;

            float u = IconManager.instance().getScaleIcon().getXCoord() +
                    IconManager.instance().getScaleIcon().getBitmap().getWidth() * 0.5f;
            float v = IconManager.instance().getScaleIcon().getYCoord() +
                    IconManager.instance().getScaleIcon().getBitmap().getHeight() * 0.5f;
            mScaleUndoHandler = new ScaleUndoHandler();
            mScaleUndoHandler.scaleX = u;
            mScaleUndoHandler.scaleY = v;
            return new IUndo(mScaleUndoHandler);

        } else if (IconManager.instance().getTranslateIcon().isInIcon(x, y)) {
            mState = State.MOVING;

            float u = IconManager.instance().getTranslateIcon().getXCoord() +
                    IconManager.instance().getTranslateIcon().getBitmap().getWidth() * 0.5f;
            float v = IconManager.instance().getTranslateIcon().getYCoord() +
                    IconManager.instance().getTranslateIcon().getBitmap().getHeight() * 0.5f;
            mTranslateUndoHandler = new TranslateUndoHandler();
            mTranslateUndoHandler.xPos = u;
            mTranslateUndoHandler.yPos = v;
            return new IUndo(mTranslateUndoHandler);

        } else {
            PointF lastPoint = null;
            if (mVertexList.isEmpty() == false) {
                lastPoint = mVertexList.get(mVertexList.size() - 1);
            }
            final PointF newPoint = new PointF(x, y);
            mVertexList.add(newPoint);
            //bezier
            if (lastPoint != null) {
                final PointF b1 = new PointF(0.6667f * x + 0.3333f * lastPoint.x, 0.6667f * y + 0.3333f * lastPoint.y);

                final PointF b2 = new PointF(0.6667f * lastPoint.x + 0.3333f * x, 0.6667f * lastPoint.y + 0.3333f * y);
                mBezierVertexList.add(b1);
                mBezierVertexList.add(b2);

                /*
                 * return an anonymous IUndoHandler here. This handler needs to be able to remove
                 * the recently added point and bezier points, if necessary.
                 */
                return new IUndo(new IUndoHandler() {

                    @Override
                    public void handleUndo() {
                        mBezierVertexList.remove(b1);

                        mBezierVertexList.remove(b2);

                        mVertexList.remove(newPoint);
                    }

                    @Override
                    public void handleRedo() {

                    }
                });
            }

            // no bezier points to remove.

            return new IUndo(new IUndoHandler() {

                @Override
                public void handleUndo() {

                    mVertexList.remove(newPoint);
                }

                @Override
                public void handleRedo() {

                }
            });

        }
    }

    @Override
    protected IUndo handleMoveAction(float x, float y) {
        return null;
    }

    @Override
    protected IUndo handleUpAction(float x, float y) {
        return null;
    }


    //
    // ILayer methods
    //

    @Override
    public void setStart() {
        mInteracting = true;
    }

    @Override
    public void setStop() {
        mInteracting = false;
    }

    @Override
    public void draw(Canvas canvas, boolean showTransformBox) {
        if (mSet) {
            mScreenWidth = canvas.getWidth();
            mScreenHeight = canvas.getHeight();
            resetPath();
            if (mVertexList.size() > 1) {
                canvas.drawPath(mPath, mFillPaint);
                canvas.drawPath(mPath, mStrokePaint);

                //only show transform box if this
                // layer is current layer and showtransformbox flag is true.
                if (mInteracting && showTransformBox) {
                    if (mState == State.BEZIER)
                        mBezierController.draw(mVertexList, mBezierVertexList, canvas);
                    else if (mState == State.VERTEX)
                        mVertexController.draw(mVertexList, canvas);
                    //bounding rect and icons
                    mTransformer.draw(canvas);
                }
            }
        }
    }


    @Override
    public IUndo onEvent(MotionEvent event) {
        // first touch, we register, so that the Pen can be drawn on the canvas.
        if (!mSet)
            mSet = true;

        float x, y;
        x = event.getX();
        y = event.getY();
        switch (mState) {
            case UNUSED:
            case IDLE:
                mInteracting = true;
                mState = State.DRAWING;
                break;
            case DRAWING:
                mLastPosition.set(x, y);
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    return handleDownAction(x, y);
                }
                break;
            case ROTATING:
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    mState = State.IDLE;


                } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
                    float angle = mTransformer.getAngleBetweenPoints(mLastPosition, new PointF(x, y));
                    mTransformer.rotate(angle);
                    mLastPosition.set(x, y);
                } else if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    //nothing.

                }
                break;
            case SCALING:
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    mState = State.IDLE;
                } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
                    mTransformer.scale(x, y);
                } else if (event.getAction() == MotionEvent.ACTION_DOWN) {

                }
                break;
            case MOVING:
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    mState = State.IDLE;
                } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
                    mTransformer.translate(x, y);

                } else if (event.getAction() == MotionEvent.ACTION_DOWN) {

                }
                break;
            case BEZIER:
                return mBezierController.onMotionEvent(event, mBezierVertexList);

            case VERTEX:
                return mVertexController.onMotionEvent(event, mVertexList);

            default:
                break;
        }
        return null;
    }

    @Override
    public LayerType getLayerType() {
        return LayerType.PEN;
    }

    @Override
    public String getTag() {
        return mName;
    }

    @Override
    public void setTag(String tag) {
        mName = tag;
    }

    @Override
    public ILayer copy() {
        Pen pen = new Pen(this.mStrokePaint, this.mFillPaint, mBezierVertexList, mVertexList);
        return pen;
    }

    @Override
    public String getXml() {
        String xml = "<path d='";
        for (int counter = 0; counter < getVertexList().size(); counter++) {
            PointF point = getVertexList().get(counter);
            if (counter == 0)
                xml += " M";
            if (counter != getVertexList().size() - 1) {
                xml += point.x + " " + point.y + " C" + getBezierVertexList().get(2 * counter).x
                        + " " +
                        getBezierVertexList().get(2 * counter).y + " ";
                xml += getBezierVertexList().get(2 * counter + 1).x
                        + " " +
                        getBezierVertexList().get(2 * counter + 1).y + " ";
            } else {
                xml += " " + point.x + ", " + point.y;
            }
        }
        xml += "' ";

        xml += "style='fill: #" + DataUtils.getRGBHex(mFillPaint.getColor()) + ";  fill-opacity: "
                + DataUtils.getAlphaDecimal(DataUtils.getAlpha(mFillPaint.getColor())) + " ; ";
        xml += "stroke: #"
                + DataUtils.getRGBHex(mStrokePaint.getColor()) + ";stroke-width:" + mStrokeThickness + "px; stroke-opacity: "
                + DataUtils.getAlphaDecimal(DataUtils.getAlpha(mStrokePaint.getColor())) + "'";

        xml += "/> ";

        return xml;
    }

    @Override
    public void accept(ILayerVisitor visitor) {
        visitor.visitPen(this);
    }


    /* ------------------------------- Undo Handlers -------------------------------
     * The undo handlers are used to cache some previous state (e.g. translation position etc)
     * that can be easily undone at a later time. The handlers are wrapped in an IUndo
     * object and placed on a stack when being returned from the onMotionEvent(...) method.
     */

    /**
     * Handles undoing a rotation. To undo the rotation it needs to know the angle between
     * the (x,y) pointer position (i.e. user finger on screen) when pointer is moved down,
     * and when it is moved up. To do this, the handler caches the (x,y) position for
     * ON_DOWN actions, and then gets the (x,y) position for ON_UP actions.
     */
    protected class RotateUndoHandler implements IUndoHandler {

        float cachedRotation; /* radians */

        @Override
        public void handleUndo() {
            mTransformer.rotate(cachedRotation - mTransformer.getCurrentRotation());
        }

        @Override
        public void handleRedo() {

        }
    }

    /**
     * Handles undoing scaling. To undo scaling it simply needs to cache the original
     * scale when the  (x,y) pointer position (user finger on screen) was moved down.
     */
    protected class ScaleUndoHandler implements IUndoHandler {

        float scaleX;
        float scaleY;

        @Override
        public void handleUndo() {
            mTransformer.scale(scaleX, scaleY);
        }

        @Override
        public void handleRedo() {

        }
    }

    /**
     * Handles undoing translating. To undo scaling it simply needs to cache the
     * original position of the parent <code>Pen</code> when the
     * (x,y) pointer position (user finger on screen) was moved down.
     */
    protected class TranslateUndoHandler implements IUndoHandler {

        float xPos;
        float yPos;

        @Override
        public void handleUndo() {
            mTransformer.translate(xPos, yPos);
        }

        @Override
        public void handleRedo() {

        }
    }
} public class Pencil extends Pen {

    public Pencil() {
        mVertexList = new ArrayList<>();
        mBezierVertexList = new ArrayList<>();
        mPath = new Path();
        mStrokePaint = new Paint(Color.GREEN);
        mStrokePaint.setColor(Color.GREEN);
        mStrokePaint.setStyle(Paint.Style.STROKE);
        mFillPaint = new Paint();
        mFillPaint.setColor(0x00FFFFFF);
        mTransformer = new PenTransformer(this);
        mRotMatrix = new Matrix();
        mLastPosition = new PointF();
        mBezierController = new BezierController();
        mVertexController = new VertexController();
    }

    private Pencil(Paint strokePaint, Paint fillPaint, ArrayList<PointF> bezierPoints,
                ArrayList<PointF> vertexPoints) {
        mVertexList = new ArrayList<>();
        mBezierVertexList = new ArrayList<>();
        mPath = new Path();
        mStrokePaint = new Paint(strokePaint);
        mFillPaint = new Paint(fillPaint);
        mTransformer = new PenTransformer(this);
        mRotMatrix = new Matrix();
        mLastPosition = new PointF();
        mBezierController = new BezierController();
        for(PointF p : bezierPoints){
            mBezierVertexList.add(new PointF(p.x, p.y));
        }
        for(PointF p : vertexPoints){
            mVertexList.add(new PointF(p.x, p.y));
        }
        mVertexController = new VertexController();
        mSet = true;
    }

    @Override
    public ILayer copy(){
        Pencil pencil = new Pencil(mStrokePaint, mFillPaint, mBezierVertexList, mVertexList);
        return pencil;
    }

    @Override
    protected IUndo handleMoveAction(float x, float y) {
        PointF lastPoint = null;
        if (mVertexList.isEmpty() == false) {
            lastPoint = mVertexList.get(mVertexList.size() - 1);
        }
        final PointF newPoint = new PointF(x, y);
        mVertexList.add(newPoint);
        //bezier
        if (lastPoint != null) {
            final PointF b1 = new PointF(0.6667f * x + 0.3333f * lastPoint.x, 0.6667f * y + 0.3333f * lastPoint.y);

            final PointF b2 = new PointF(0.6667f * lastPoint.x + 0.3333f * x, 0.6667f * lastPoint.y + 0.3333f * y);
            mBezierVertexList.add(b1);
            mBezierVertexList.add(b2);


            return new IUndo(new IUndoHandler() {

                @Override
                public void handleUndo() {
                    mBezierVertexList.remove(b1);

                    mBezierVertexList.remove(b2);

                    mVertexList.remove(newPoint);
                }

                @Override
                public void handleRedo() {

                }
            });
        }
        else{
            // no bezier points to remove.

            return new IUndo(new IUndoHandler() {

                @Override
                public void handleUndo() {

                    mVertexList.remove(newPoint);
                }

                @Override
                public void handleRedo() {

                }
            });
        }
    }


    @Override
    public IUndo onEvent(MotionEvent event) {

        float x, y;
        x = event.getX();
        y = event.getY();

        if(!mSet) {
            mSet = true;
            mTransformer.translate(x, y);
        }

        switch (mState) {
            case UNUSED:
            case IDLE:
                mState = State.DRAWING;
                break;
            case DRAWING:
                mLastPosition.set(x, y);
                mTransformer.SetBounds();
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    return handleDownAction(x, y);
                } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
                    return handleMoveAction(x, y);
                }

                break;
            case ROTATING:
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    mState = State.IDLE;
                } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
                    float angle = mTransformer.getAngleBetweenPoints(mLastPosition, new PointF(x, y));
                    mTransformer.rotate(angle);
                    mLastPosition.set(x, y);
                } else if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    //nothing.
                    mLastPosition.set(x, y);

                }
                break;
            case SCALING:
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    mState = State.IDLE;
                } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
                    mTransformer.scale(x, y);
                } else if (event.getAction() == MotionEvent.ACTION_DOWN) {

                }
                break;
            case MOVING:
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    mState = State.IDLE;
                } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
                    mTransformer.translate(x, y);

                } else if (event.getAction() == MotionEvent.ACTION_DOWN) {

                }
                break;
            case BEZIER:
                return mBezierController.onMotionEvent(event, mBezierVertexList);
            case VERTEX:
                return mVertexController.onMotionEvent(event, mVertexList);
            default:
                break;
        }
        return null;
    }

    @Override
    public LayerType getLayerType() {
        return LayerType.PENCIL;
    }
} public class Polygon extends Pen {


    /**
     * Handler class for undoing rotation transformations.
     */
    protected RotateUndoHandler mRotateUndoHandler = null;

    /**
     * Handler class for undoing scale transformations.
     */
    protected ScaleUndoHandler mScaleUndoHandler = null;

    /**
     * Handler class for undoing translate transformations.
     */
    protected TranslateUndoHandler mTranslateUndoHandler = null;

    /**
     * Constructor. Takes an array of vertices, which is implicitly elementwise immutable.
     * The vertixe list is copied into the internal list and its contents cannot be
     * modified (aside from using the event functions.)
     *
     * @param vertices
     */
    public Polygon(ArrayList<PointF> vertices) {
        super();
        mVertexList = new ArrayList<>(vertices);
        setControlPoints();
    }

    /**
     * @param strokePaint
     * @param fillPaint
     * @param bezierPoints
     * @param vertexPoints
     */
    protected Polygon(Paint strokePaint, Paint fillPaint, ArrayList<PointF> bezierPoints,
                      ArrayList<PointF> vertexPoints) {
        mVertexList = new ArrayList<>();
        mBezierVertexList = new ArrayList<>();
        mPath = new Path();
        mStrokePaint = new Paint(strokePaint);
        mFillPaint = new Paint(fillPaint);
        mTransformer = new PenTransformer(this);
        mRotMatrix = new Matrix();
        mLastPosition = new PointF();
        mBezierController = new BezierController();
        for (PointF p : bezierPoints) {
            mBezierVertexList.add(new PointF(p.x, p.y));
        }
        for (PointF p : vertexPoints) {
            mVertexList.add(new PointF(p.x, p.y));
        }
        mVertexController = new VertexController();
        mSet = true;
    }

    private void setControlPoints() {
        mBezierVertexList = new ArrayList<>();
        for (int i = 0; i < mVertexList.size()-1; i++) {

            PointF p = mVertexList.get(i);
            PointF q = mVertexList.get((i + 1) % mVertexList.size());
            PointF b1 = new PointF(0.6667f * p.x + 0.3333f * q.x, 0.6667f * p.y + 0.3333f * q.y);

            PointF b2 = new PointF(0.6667f * q.x + 0.3333f * p.x, 0.6667f * q.y + 0.3333f * p.y);
            mBezierVertexList.add(b1);
            mBezierVertexList.add(b2);
        }

    }


    @Override
    protected void resetPath() {
        mPath.reset();

        for (int i = 0; i < mVertexList.size(); i++) {
            PointF p = mVertexList.get(i);
            if (i == 0) {
                mPath.moveTo(p.x, p.y);
            } else {
                PointF b1 = mBezierVertexList.get((i - 1) * 2);
                PointF b2 = mBezierVertexList.get((i - 1) * 2 + 1);
                mPath.cubicTo(b1.x, b1.y, b2.x, b2.y, p.x, p.y);
            }
        }
        mPath.close();
    }


    @Override
    protected IUndo handleDownAction(float x, float y) {
        if (IconManager.instance().getRotationIcon().isInIcon(x, y)) {
            mState = State.ROTATING;

            mRotateUndoHandler = new RotateUndoHandler();
            mRotateUndoHandler.cachedRotation = mTransformer.getCurrentRotation();
            RotateUndoHandler rh = new RotateUndoHandler();
            rh.cachedRotation = mTransformer.getCurrentRotation();
            return new IUndo(rh);

        } else if (IconManager.instance().getScaleIcon().isInIcon(x, y)) {
            mState = State.SCALING;

            float u = IconManager.instance().getScaleIcon().getXCoord() + IconManager.instance().getScaleIcon().getBitmap().getWidth() * 0.5f;
            float v = IconManager.instance().getScaleIcon().getYCoord() + IconManager.instance().getScaleIcon().getBitmap().getHeight() * 0.5f;
            mScaleUndoHandler = new ScaleUndoHandler();
            mScaleUndoHandler.scaleX = u;
            mScaleUndoHandler.scaleY = v;
            return new IUndo(mScaleUndoHandler);
        } else if (IconManager.instance().getTranslateIcon().isInIcon(x, y)) {
            mState = State.MOVING;

            mTranslateUndoHandler = new TranslateUndoHandler();
            float u = IconManager.instance().getTranslateIcon().getXCoord() + IconManager.instance().getTranslateIcon().getBitmap().getWidth() * 0.5f;
            float v = IconManager.instance().getTranslateIcon().getYCoord() + IconManager.instance().getTranslateIcon().getBitmap().getHeight() * 0.5f;
            mTranslateUndoHandler.xPos = u;
            mTranslateUndoHandler.yPos = v;
            return new IUndo(mTranslateUndoHandler);
        }
        return null;
    }

    @Override
    public IUndo onEvent(MotionEvent event) {

        float x, y;
        x = event.getX();
        y = event.getY();

        if (!mSet) {
            mSet = true;
            mTransformer.translate(x, y);
        }

        switch (mState) {
            case UNUSED:
            case IDLE:
                mState = State.DRAWING;
                break;
            case DRAWING:
                /* drawing - i.e. adding points to the vertex list - is prohibited.*/
                mLastPosition.set(x, y);
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    return handleDownAction(x, y);
                }
                break;
            case ROTATING:
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    mState = State.IDLE;
                } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
                    float angle = mTransformer.getAngleBetweenPoints(mLastPosition, new PointF(x, y));
                    mTransformer.rotate(angle);
                    mLastPosition.set(x, y);
                } else if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    //nothing.
                    mLastPosition.set(x, y);
                }
                break;
            case SCALING:
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    mState = State.IDLE;
                } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
                    mTransformer.scale(x, y);
                } else if (event.getAction() == MotionEvent.ACTION_DOWN) {

                }
                break;
            case MOVING:
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    mState = State.IDLE;
                } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
                    mTransformer.translate(x, y);

                } else if (event.getAction() == MotionEvent.ACTION_DOWN) {

                }
                break;
            case BEZIER:
                mBezierController.onMotionEvent(event, mBezierVertexList);
                break;
            case VERTEX:
                mVertexController.onMotionEvent(event, mVertexList);
                break;
            default:
                break;
        }
        return null;
    }


    @Override
    public ILayer copy() {
        Polygon pen = new Polygon(this.mStrokePaint, this.mFillPaint, mBezierVertexList, mVertexList);
        return pen;
    }

    @Override
    public String getXml() {
        String xml = "<path d='";
        for (int counter = 0; counter < getVertexList().size(); counter++) {
            PointF point = getVertexList().get(counter);
            if (counter == 0)
                xml += " M";
            if (counter != getVertexList().size() - 1) {
                xml += point.x + " " + point.y + " C" + getBezierVertexList().get(2 * counter).x
                        + " " +
                        getBezierVertexList().get(2 * counter).y + " ";
                xml += getBezierVertexList().get(2 * counter + 1).x
                        + " " +
                        getBezierVertexList().get(2 * counter + 1).y + " ";
            } else {
                PointF startP = getVertexList().get(0);
                xml += " " + point.x + " " + point.y + " Q"
                        + getBezierVertexList().get(counter).x + " " +
                        getBezierVertexList().get(counter).y + " " + startP.x + " " + startP.y + " Z ";
            }
        }
        xml += "' ";

        xml += "style='fill: #" + DataUtils.getRGBHex(mFillPaint.getColor()) + ";  fill-opacity: "
                + DataUtils.getAlphaDecimal(DataUtils.getAlpha(mFillPaint.getColor())) + " ; ";
        xml += "stroke: #"
                + DataUtils.getRGBHex(mStrokePaint.getColor()) + ";stroke-width:" + mStrokeThickness + "px; stroke-opacity: "
                + DataUtils.getAlphaDecimal(DataUtils.getAlpha(mStrokePaint.getColor())) + "'";

        xml += "/> ";

        return xml;
    }

    @Override
    public LayerType getLayerType() {
        return LayerType.SHAPE;
    }
} public class PolygonFactory {


    /**
     * Creates a List of <code>PointF</code>s forming a regular polygon.
     *
     * @param vertex number of vertices in the polygon
     * @param x      center x
     * @param y      center y
     * @param radius radius from center to apex point.
     * @return <code>ArrayList</code> of points
     */
    public static ArrayList<PointF> createPolygon(int vertex, float x, float y, float radius) {
        if (vertex < 3) {
            throw new IllegalArgumentException("You must have 3 or more vertices.");
        }
        if (radius <= 0) {
            throw new IllegalArgumentException("You need a positive radius.");
        }
        ArrayList<PointF> pointsArray = new ArrayList<PointF>();
        float degrees = 2 * (float) Math.PI / vertex;

        pointsArray.add(new PointF(x, y + radius));// first vertex is bottom
        // point.
        // Polygon will not have flat base.

        for (int i = 1; i < vertex; i++) {
            float nextX;
            float nextY;

            if (i <= ((float) vertex) / 4) {// first quadrant, anticlockwise
                // from bottom.
                nextX = (float) (x + radius * Math.sin(degrees * i));
                nextY = (float) (y + radius * Math.cos(degrees * i));
            } else if (i <= ((float) vertex) / 2) {// second quadrant
                nextX = (float) (x + radius * Math.sin(degrees * i));
                nextY = (float) (y + radius * Math.cos(degrees * i));
            } else if (i <= ((float) vertex / 2 * 3)) {// third quadrant
                nextX = (float) (x + radius * Math.sin(degrees * i));
                nextY = (float) (y + radius * Math.cos(degrees * i));
            } else {// fourth quadrant
                nextX = (float) (x + radius * Math.sin(degrees * i));
                nextY = (float) (y - radius * Math.cos(degrees * i));
            }
            pointsArray.add(new PointF(nextX, nextY));

        }
        return pointsArray;
    }


    /**
     * Creates a List of <code>PointF</code>s forming a regular star.
     *
     * @param vertex number of vertices in the polygon
     * @param x      center x
     * @param y      center y
     * @param radius radius from center to apex point.
     * @return <code>ArrayList</code> of points
     */
    public static ArrayList<PointF> createShapeStar(int vertex, float x, float y, float radius) {
        if (vertex < 3) {
            throw new IllegalArgumentException("You must have 3 or more vertices.");
        }
        if (radius <= 0) {
            throw new IllegalArgumentException("You need a positive radius.");
        }

        ArrayList<PointF> pointsArray = new ArrayList<PointF>();
        float degrees = 2 * (float) Math.PI / vertex;

        pointsArray.add(new PointF(x, y + radius));// first vertex is bottom
        // point.
        // Polygon will not have flat base.

        // get List of vertices in smaller polygon.
        ArrayList<PointF> InnerP = getInnerVertices(vertex, x, y, radius);

        for (int i = 1; i < vertex; i++) {
            float nextX;
            float nextY;
            if (i <= ((float) vertex) / 4) {// first quadrant, anticlockwise
                // from bottom.
                nextX = (float) (x + radius * Math.sin(degrees * i));
                nextY = (float) (y + radius * Math.cos(degrees * i));
            } else if (i <= ((float) vertex) / 2) {// second quadrant
                nextX = (float) (x + radius * Math.sin(degrees * i));
                nextY = (float) (y + radius * Math.cos(degrees * i));
            } else if (i <= ((float) vertex / 2 * 3)) {// third quadrant
                nextX = (float) (x + radius * Math.sin(degrees * i));
                nextY = (float) (y + radius * Math.cos(degrees * i));
            } else {// fourth quadrant
                nextX = (float) (x + radius * Math.sin(degrees * i));
                nextY = (float) (y - radius * Math.cos(degrees * i));
            }

            pointsArray.add(InnerP.get(2 * i - 1));
            pointsArray.add(new PointF(nextX, nextY));

        }
        pointsArray.add(InnerP.get(InnerP.size() - 1));

        return pointsArray;
    }

    /**
     * Private method, used by createShapeStar()
     *
     * @param startPoints
     * @param x
     * @param y
     * @param line
     * @return
     */
    private static ArrayList<PointF> getInnerVertices(int startPoints, float x, float y, float line) {
        int vertex = startPoints * 2;
        float degrees = 2 * (float) Math.PI / vertex;
        line = line / 2;
        ArrayList<PointF> InnerPoints = new ArrayList<PointF>();
        InnerPoints.add(new PointF(x, y + line));

        for (int i = 1; i < vertex; i++) {
            float nextX;
            float nextY;
            if (i <= ((float) vertex) / 4) {// first quadrant, anticlockwise
                // from bottom.
                nextX = (float) (x + line * Math.sin(degrees * i));
                nextY = (float) (y + line * Math.cos(degrees * i));
            } else if (i <= ((float) vertex) / 2) {// second quadrant
                nextX = (float) (x + line * Math.sin(degrees * i));
                nextY = (float) (y + line * Math.cos(degrees * i));
            } else if (i <= ((float) vertex / 2 * 3)) {// third quadrant
                nextX = (float) (x + line * Math.sin(degrees * i));
                nextY = (float) (y + line * Math.cos(degrees * i));
            } else {// fourth quadrant
                nextX = (float) (x + line * Math.sin(degrees * i));
                nextY = (float) (y - line * Math.cos(degrees * i));
            }
            InnerPoints.add(new PointF(nextX, nextY));
        }
        return InnerPoints;
    }
} public final class ColorPickerDialog extends BasicDialog implements View.OnClickListener {

    /*
     * Seekbars for the ARGB channels.
     */
    private SeekBar mAlphaSeek, mRedSeek, mGreenSeek, mBlueSeek;

    /**
     * Select button for finalizing the current selection.
     */
    private Button mSelectButton;

    /**
     * The given color. i.e. the default color
     */
    private int mColor;

    /**
     * Currently selected ARGB values.
     */
    private int mAlpha, mRed, mGreen, mBlue;

    private ColorSetCallbackHandler mCallbackHandler;

    private PaletteView mPaletteView;

    /**
     * @param activity - mainactivity
     * @param color    - current color value
     */
    public ColorPickerDialog(Activity activity, int color, ColorSetCallbackHandler callbackHandler) {
        super(activity, R.layout.dialog_colorpicker);
        mCallbackHandler = callbackHandler;
        mColor = color;

        /*
         * Extract each ARGB channel form the color
         */
        mAlpha = (color >> 24) & 0xFF;
        mRed = (color & 0xFF0000) >> 16;
        mGreen = (color & 0x0000FF00) >> 8;
        mBlue = (color & 0x000000FF);

        // get the seekbar references
        mAlphaSeek = (SeekBar) getLayout().findViewById(R.id.alphaseekbar);
        mRedSeek = (SeekBar) getLayout().findViewById(R.id.redseekbar);
        mGreenSeek = (SeekBar) getLayout().findViewById(R.id.greenseekbar);
        mBlueSeek = (SeekBar) getLayout().findViewById(R.id.blueseekbar);

        //get the paletteview referecne and set to default color.
        mPaletteView = (PaletteView) getLayout().findViewById(R.id.palette);
        mPaletteView.setColor(mColor);

        mSelectButton = (Button) getLayout().findViewById(R.id.selectbutton);
        mSelectButton.setOnClickListener(this);

        // set the seekbar default values.
        mAlphaSeek.setProgress(mAlpha);
        mRedSeek.setProgress(mRed);
        mGreenSeek.setProgress(mGreen);
        mBlueSeek.setProgress(mBlue);

        mAlphaSeek.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {

            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                mAlpha = progress;
                mAlpha = mAlpha << 24;
                mColor = (mColor & 0x00ffffff) | mAlpha;

                mPaletteView.setColor(mColor);
                mPaletteView.invalidate();
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                // TODO Auto-generated method stub

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                // TODO Auto-generated method stub

            }

        });

        mRedSeek.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {

            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

                mRed = progress;
                mRed = mRed << 16;
                mColor = (mColor & 0xff00ffff) | mRed;

                mPaletteView.setColor(mColor);
                mPaletteView.invalidate();
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                // TODO Auto-generated method stub

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                // TODO Auto-generated method stub

            }

        });

        mGreenSeek.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {

            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

                mGreen = progress;
                mGreen = mGreen << 8;
                mColor = (mColor & 0xffff00ff) | mGreen;

                mPaletteView.setColor(mColor);
                mPaletteView.invalidate();
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                // TODO Auto-generated method stub

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                // TODO Auto-generated method stub

            }

        });

        mBlueSeek.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {

            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

                mBlue = progress;
                mColor = (mColor & 0xffffff00) | mBlue;

                mPaletteView.setColor(mColor);
                mPaletteView.invalidate();
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                // TODO Auto-generated method stub

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                // TODO Auto-generated method stub

            }

        });

        getDialog().show();
    }

    @Override
    public void onClick(View v) {
        if (v == mSelectButton) {

            mCallbackHandler.setColor(mColor);
            getDialog().dismiss();
        }


    }

} public interface ColorSetCallbackHandler {

    void setColor(int color);
} public class LayerListDialog extends BasicDialog{

    private ListView mList;

    public LayerListDialog(final Activity activity, int rootLayout, ArrayList<String> layerNames, final LayerOptionsCallbackHandler callbackHandler) {

        super(activity, rootLayout);

        ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(),android.R.layout.simple_list_item_1);

        if(layerNames!=null){
            adapter.addAll(layerNames);
            mList = (ListView)getLayout().findViewById(R.id.layerlist_item);
            mList.setAdapter(adapter);
            mList.setOnItemClickListener(new AdapterView.OnItemClickListener(){

                @Override
                public void onItemClick(AdapterView<?> arg0, View view, int position, long arg3) {
                    new SingleLayerOptionsDialog(activity, position, callbackHandler);
                    getDialog().dismiss();


                }

            });
        }

        getDialog().show();
    }
} public interface OnClickHandler {

    void onClickWithInt(int value);

    void onClickWithFloat(float value);
} public class SingleSliderDialog extends BasicDialog implements View.OnClickListener {

    private String mSlidertText, mButtonText;
    private SeekBar mSeekBar;
    private Button mButton;
    private OnClickHandler mOnClickHandler;

    public SingleSliderDialog(Activity activity, int defaultValue, String sliderText, String buttonText, OnClickHandler onClickHandler) {
        super(activity, R.layout.dialog_singleslider);

        mSlidertText = sliderText;
        mButtonText = buttonText;

        mButton = (Button) getLayout().findViewById(R.id.selectbutton);
        mButton.setText(mButtonText);
        mButton.setOnClickListener(this);

        mSeekBar = (SeekBar) getLayout().findViewById(R.id.seekbar);
        mSeekBar.setProgress(defaultValue);

        ((TextView) getLayout().findViewById(R.id.seekbartext)).setText(mSlidertText);

        mOnClickHandler = onClickHandler;

        getDialog().show();
    }

    @Override
    public void onClick(View v) {
        if (v == mButton) {
            mOnClickHandler.onClickWithInt(mSeekBar.getProgress());
            getDialog().dismiss();
        }
    }
} public class TextPickerDialog extends BasicDialog implements View.OnClickListener {

    private String mText = "";
    private EditText mEditText;
    private Button mButton;

    private TextPickerCallbackHandler mCallback;

    public TextPickerDialog(Activity activity, String defaultText, TextPickerCallbackHandler callback) {
        super(activity, R.layout.dialog_textpicker);
        mText = defaultText;
        mCallback = callback;
        mEditText = (EditText) getLayout().findViewById(R.id.textpicker);
        mButton = (Button) getLayout().findViewById(R.id.selectbutton);
        mButton.setOnClickListener(this);

        getDialog().show();
    }

    @Override
    public void onClick(View v) {

        mText = mEditText.getText().toString();
        if (mText == null || mText.isEmpty()) {
            return;
        } else {
            mCallback.onTextChosen(mText);

            getDialog().dismiss();
        }
    }

    public interface TextPickerCallbackHandler {

        void onTextChosen(String text);
    }
} public class UserFileViewDialog extends BasicDialog implements AdapterView.OnItemClickListener {

        private GridView mGridView;
        private BaseAdapter mAdapter;
        private String mChosenPath;
        private ArrayList<String> mFilenames;

        public enum FileType {
            IMAGE, SVG
        };

        private FileType mFileType = FileType.IMAGE;

        /**
         *
         * @param activity
         * @param filetype
         */
        public UserFileViewDialog(Activity activity, FileType filetype) {
            super(activity, R.layout.dialog_userfiles);
            mFileType = filetype;
            if (mFileType == FileType.IMAGE) {
                mFilenames = FileUtils.getUserImages();
                mAdapter = new ImageAdapter(getActivity(), mFilenames);
            }
            else {
                mFilenames = FileUtils.getUserSVGFiles();
                mAdapter = new FileListAdapter(getActivity(), mFilenames);
            }
            mGridView = (GridView) getLayout().findViewById(R.id.filesgridview);

            mGridView.setAdapter(mAdapter);
            mGridView.setOnItemClickListener(this);

            this.getDialog().show();
        }

        /**
         *
         */
        @Override
        public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) {
            if (mFilenames == null || position >= mFilenames.size())
                return;
            mChosenPath = mFilenames.get(position);

            if (mChosenPath == null || mChosenPath == "")
                return;
            if (mFileType == FileType.IMAGE) {

                new UserFileSelectDialog(getActivity(), mChosenPath);
            }
            else if (mFileType == FileType.SVG) {
                new UserSvgSelectDialog(getActivity(), mChosenPath);
            }
            getDialog().dismiss();

        }

    } public final class IconManager {


    /**
     * icon manager instance
     */
    private static IconManager sIconManager;


    /**
     * The icons for rotate, scale and translate.
     */
    private LayerIconHolder mIconRotate, mIconScale, mIconTranslate;


    /**
     * private constructor
     */
    private IconManager() {
    }


    /**
     * Private constructor for the IconManager.
     *
     * @param rotBmp   the rotate icon bitmap
     * @param scaleBmp the scale icon bitmap
     * @param transBmp the translate icon bitmap
     */
    private void init(Bitmap rotBmp, Bitmap scaleBmp, Bitmap transBmp) {
        mIconRotate = new LayerIconHolder(rotBmp);
        mIconScale = new LayerIconHolder(scaleBmp);
        mIconTranslate = new LayerIconHolder(transBmp);
    }


    /**
     * Returns the instance of the IconManager. At the start of the app's life cycle
     * the IconManager's bitmaps icons should be setup first, and this should only be called
     * subsequently.
     *
     * @return instance of the <code>IconManager</code>
     */
    public static IconManager instance() {
        if (sIconManager == null)
            sIconManager = new IconManager();
        return sIconManager;
    }


    /**
     * Initializes the singleton instance with the bitmaps necessary for rotating, scaling and
     * translating layers.
     * @param rotBmp   the rotate icon bitmap
     * @param scaleBmp the scale icon bitmap
     * @param transBmp the translate icon bitmap
     */
    public void initialize(Bitmap rotBmp, Bitmap scaleBmp, Bitmap transBmp) {
        init(rotBmp, scaleBmp, transBmp);

    }


    /**
     * Gets the <i>Rotate</i> icon LayerIconHolder.
     *
     * @return rotate icon
     */
    public LayerIconHolder getRotationIcon() {
        return mIconRotate;
    }


    /**
     * Gets the <i>Scale</i> icon LayerIconHolder.
     *
     * @return scale icon
     */
    public LayerIconHolder getScaleIcon() {
        return mIconScale;
    }


    /**
     * Gets the <i>Translate</i> icon LayerIconHolder.
     *
     * @return translate icon
     */
    public LayerIconHolder getTranslateIcon() {
        return mIconTranslate;
    }


    /**
     * Sets the icon coordinates, defined by the <code>SpecialRectF</code> bounds.
     * The translate icon will be set at the top left position, the rotate icon will be set at
     * the bottom left position and the scale icon will be set at the bototm right position.
     *
     * @param mBounds The boundary rect around the current <code>ILayer</code> object.
     */
    public void setIconCoords(SpecialRectF mBounds) {

        mIconRotate.setXCoord(mBounds.left);
        mIconRotate.setYCoord(mBounds.bottom);

        mIconScale.setXCoord(mBounds.right);
        mIconScale.setYCoord(mBounds.bottom);

        mIconTranslate.setXCoord(mBounds.left);
        mIconTranslate.setYCoord(mBounds.top);
    }

    public void setShowingIcons(boolean showing){
        mIconRotate.setShowing(showing);
        mIconScale.setShowing(showing);
        mIconTranslate.setShowing(showing);
    }

} public class FileUtils {

    /**
     * Logging Tag
     */
    public static final String TAG = "FileUtils";

    /**
     * Name of root directory for application files.
     */
    public static final String ROOT_DIR = "ArtisteX_Files";

    /**
     * Environment path separator.
     */
    public static final String SPTR = File.separator;

    /**
     * Image directory name.
     */
    public static final String IMAGES = "Images";

    /**
     * Temp directory name.
     */
    public static final String TEMP = "temp";

    /**
     * PNG suffix
     */
    public static final String PNG = ".png";

    /**
     * JPG suffix.
     */
    public static final String JPG = ".jpg";

    /**
     * Prefix for SVG file string.
     */
    public static final String SVG = "SVG";

    /**
     * Header for generated SVG files.
     */
    public static final String SVG_HEADER = "<?xml version=\"1.0\" standalone=\"no\"?><!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1 //EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">";

    /**
     * Footer for generated SVG files.
     */
    public static final String SVG_FOOTER = "</svg>";


    /**
     * Returns a unique filename based on the current time (to second accuracy).
     *
     * @param prefix    file name prefix string. If null, no prefix will be prepended.
     * @param extension the file extension.
     * @return Filename including extension.
     */
    public static String getUniqueFilename(String prefix, String extension) {
        String filePrefix = prefix == null ? "" : prefix;

        Calendar calendar = Calendar.getInstance();
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH);// 0.. 11
        int day = calendar.get(Calendar.DATE);
        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        int minute = calendar.get(Calendar.MINUTE);
        int second = calendar.get(Calendar.SECOND);

        String filename = "" + year + "_" + getTwoDigitString(month + 1) +
                "_"
                + getTwoDigitString(day) + "_" +
                hour + "_" + minute + "_" + second + "." + extension;

        return filename;
    }


    /**
     * Gets a unique name for a temporary file. File will have <i>tmp</i> extension and
     * be prepended by <i>temp_</i>.
     * @param extension
     * @return a uinque file name for a temporary file.
     */


    public static String getUniqueTempFilename(String extension) {
        return getUniqueFilename("temp_", extension);
    }


    /**
     * Returns a unique svg filename, a filename with .svg exentsion.
     *
     * @return Unique SVG filename
     */
    public static String getSvgFilename() {
        return getUniqueFilename(null, "svg");
    }


    /**
     * Returns a two digit string
     *
     * @param value (1 or two digits, i.e. in range [0,99])
     * @return Two character string represenitng the number.
     */
    private static String getTwoDigitString(int value) {
        String val = "";
        if (value < 10)
            val = "0" + value;
        else
            val = "" + value;

        return val;
    }


    /**
     * Gets the path to the <i>images</i> directory
     *
     * @return path string
     */
    public static String getImagesDirectory() {
        return Environment.getExternalStorageDirectory() + SPTR + ROOT_DIR + SPTR
                + IMAGES + SPTR;
    }


    /**
     * Gets the path to the <i>temp</i> directory
     *
     * @return path string
     */
    public static String getTempDirectory() {
        return Environment.getExternalStorageDirectory() + SPTR + ROOT_DIR + SPTR
                + IMAGES + SPTR + TEMP + SPTR;
    }


    /**
     * Creates the images directory.
     *
     * @return true if success, false otherwise.
     */
    public static boolean makeImagesDirectory() {
        File folder = new File(Environment.getExternalStorageDirectory() + SPTR + ROOT_DIR + SPTR
                + IMAGES + SPTR);

        boolean success = false;
        if (!folder.exists()) {
            success = folder.mkdirs();
        }
        if (!success) {
            Log.d(TAG, "Folder not created.");
        } else {
            Log.d(TAG, "Folder created!");
        }

        return success;
    }

    /**
     * Creates the temp directory. If directory already exists, all files in the directory will be
     * deleted
     *
     * @return true if success, false otherwise.
     */
    public static boolean makeTempDirectory() {
        File folder = new File(Environment.getExternalStorageDirectory() + SPTR + ROOT_DIR + SPTR
                + IMAGES + SPTR + TEMP + SPTR);
        boolean success = false;
        if (!folder.exists()) {
            success = folder.mkdirs();
        } else {
            File[] children = folder.listFiles();
            if (children != null && children.length > 0) {
                for (File f : children) {
                    f.delete();
                }
            }
            success = true;
        }

        if (!success) {
            Log.d(TAG, "Folder not created.");
        } else {
            Log.d(TAG, "Folder created!");
        }
        return success;
    }


    /**
     * Gets the user's saved image files, from the <i>images</i> directory.
     *
     * @return List of filenames (the image files)
     */
    public static ArrayList<String> getUserImages() {
        ArrayList<String> filenames = new ArrayList<String>();
        String dir = getImagesDirectory();
        File file = new File(dir);
        for (File f : file.listFiles()) {
            if (!f.isDirectory() && isImageFile(f)) {
                filenames.add(f.getPath());
            }
        }

        return filenames;
    }


    /**
     * Gets the user's saved SVG files, form the <i>images</i> directory.
     *
     * @return List of filenames.
     */
    public static ArrayList<String> getUserSVGFiles() {
        ArrayList<String> filenames = new ArrayList<String>();
        String dir = getImagesDirectory();
        File file = new File(dir);
        for (File f : file.listFiles()) {

            if (!f.isDirectory() && f.getName().endsWith(".svg")) {
                filenames.add(f.getPath());
            }
        }

        return filenames;
    }


    /**
     * Checks if the given file is an image file. Image file means a file with
     * a <code>png</code>, <code>jpg</code>, or <code>jpeg</code> extension.
     *
     * @param file the file to be checked.
     * @return true if image file, false otherwise.
     */
    private static boolean isImageFile(File file) {
        if (file.getName().toLowerCase().endsWith(".png"))
            return true;
        if (file.getName().toLowerCase().endsWith(".jpg"))
            return true;
        if (file.getName().toLowerCase().endsWith(".jpeg"))
            return true;

        return false;
    }


    /**
     * Returns the extension of the file.
     *
     * @param path file path.
     * @return image file extension.
     */
    public static String getImageExtension(String path) {

        if (path.endsWith(".png"))
            return ".png";
        if (path.endsWith(".jpg"))
            return ".jpg";
        if (path.endsWith(".jpeg"))
            return "jpeg";

        return "";
    }



    /**
     * Returns a bitmap from the given file name and absolute path.
     *
     * @param path absolute path to file.
     * @return bitmap or null if not a bitmap file.
     */
    public static Bitmap getBitmapFromUserFile(String path) {

        File file = new File(path);
        if (file.exists()) {
            if (isImageFile(file)) {
                Bitmap bmp = BitmapFactory.decodeFile(path);
                return bmp;
            } else
                return null;
        } else
            return null;
    }


    /**
     * Returns a thumbnail-esque bitmap from the bitmap file at the given location.
     *
     * @param path bitmap file name and absolute path.
     * @return A little bitmap.
     */
    public static Bitmap getThumbnailBitmapFromUserFile(String path) {

        File file = new File(path);
        if (file.exists()) {
            if (isImageFile(file)) {
                Bitmap bmp = BitmapFactory.decodeFile(path);
                bmp = Bitmap.createScaledBitmap(bmp, bmp.getWidth() / 10, bmp.getHeight() / 10,
                        false);
                return bmp;
            } else
                return null;
        } else
            return null;
    }


    /**
     * Saves the temp file.
     *
     * @param filename the file name
     * @param contents the file's contents
     * @throws IOException if problem attempting save.
     */
    public static void saveTempFile(String filename, String contents) throws IOException {

        File file = new File(filename);

        try {
            file.createNewFile();
        } catch (IOException e1) {

            throw e1;
        }

        FileWriter writer = null;
        try {
            writer = new FileWriter(file);
        } catch (IOException e2) {
            writer.close();
            throw e2;
        }
        try {

            writer.append(contents);
        } catch (IOException e) {
            writer.close();
            throw e;

        }
        try {
            writer.flush();
        } catch (IOException e1) {
            writer.close();
            throw e1;
        }
        try {
            writer.close();
        } catch (IOException e) {
            writer.close();
            throw e;
        }
    }


    /**
     * Returns the contents of a text file.
     *
     * @param filename absolute path to file.
     * @return string contents of file.
     */
    public static String readTextFile(String filename) {
        File file = new File(filename);
        if (!file.exists())
            return null;
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            return null;
        }
        BufferedReader br = new BufferedReader(
                new InputStreamReader(fis));
        String data = "";
        String buffer = "";

        try {
            while ((data = br.readLine()) != null) {
                buffer += data;
            }
        } catch (IOException e) {

            return null;
        }
        try {
            br.close();
        } catch (IOException e) {

        }
        return buffer;
    }
}
import android.Manifest;
import android.app.ActionBar;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.PointF;
import android.graphics.Typeface;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.provider.SyncStateContract;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Enumeration;
import jgh.artistex.dialogs.LayerOptionsCallbackHandler;
import jgh.artistex.dialogs.ShapeDialog;
import jgh.artistex.dialogs.ShapeSetCallbackHandler;
import jgh.artistex.engine.Layers.Bitmaps.BitmapLayer;
import jgh.artistex.engine.Layers.Bitmaps.TextLayer;
import jgh.artistex.engine.utils.Toaster;
import jgh.artistex.engine.visitors.ModeSelectVisitor;
import jgh.artistex.engine.Layers.Pens.Pen;
import jgh.artistex.engine.Layers.Pens.Pencil;
import jgh.artistex.engine.Layers.Pens.Polygon;
import jgh.artistex.engine.Layers.Pens.PolygonFactory;
import jgh.artistex.dialogs.ColorPickerDialog;
import jgh.artistex.dialogs.ColorSetCallbackHandler;
import jgh.artistex.dialogs.LayerListDialog;
import jgh.artistex.dialogs.OnClickHandler;
import jgh.artistex.dialogs.SingleSliderDialog;
import jgh.artistex.dialogs.TextPickerDialog;
import jgh.artistex.dialogs.UserFileViewDialog;
import jgh.artistex.engine.IconManager;
import jgh.artistex.engine.utils.FileUtils;
    }


    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        menu.clear();
        getMenuInflater().inflate(R.menu.menu_main, menu);
        MenuItem item = menu.findItem(R.id.showbounding);
        if (item != null) {
            if (mView.getShowTransformBox())
                item.setTitle("Hide bounding box");
            else
                item.setTitle("Show bounding box");
        }
        return true;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        MenuItem item = menu.findItem(R.id.showbounding);
        if (item != null) {
            if (mView.getShowTransformBox())
                item.setTitle("Hide bounding box");
            else
                item.setTitle("Show bounding box");
        }
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        int id = item.getItemId();

        if (id == R.id.drawing) {
            ModeSelectVisitor v = new ModeSelectVisitor();
            v.setMode(ModeSelectVisitor.Mode.DRAW);
            mView.visitLayer(v);
        } else if (id == R.id.control_points) {
            ModeSelectVisitor v = new ModeSelectVisitor();
            v.setMode(ModeSelectVisitor.Mode.BEZIER);
            mView.visitLayer(v);
        } else if (id == R.id.vertex) {
            ModeSelectVisitor v = new ModeSelectVisitor();
            v.setMode(ModeSelectVisitor.Mode.VERTEX);
            mView.visitLayer(v);
        } else if (id == R.id.strokecolor) {
            new ColorPickerDialog(this, mView.getStrokeColor(), new ColorSetCallbackHandler() {

                @Override
                public void setColor(int color) {
                    mView.setStrokeColor(color);
                    mView.invalidate();
                }
            });
        } else if (id == R.id.strokethickness) {
            new SingleSliderDialog(this, 1, "Thickness", "Select", new OnClickHandler() {
                @Override
                public void onClickWithInt(int value) {
                    mView.setStrokeThickness(value);
                    mView.invalidate();
                }

                @Override
                public void onClickWithFloat(float value) {
                    //not used.
                }
            });
        } else if (id == R.id.fillcolor) {
            new ColorPickerDialog(this, mView.getFillColor(), new ColorSetCallbackHandler() {

                @Override
                public void setColor(int color) {
                    mView.setFillColor(color);
                    mView.invalidate();
                }
            });
        } else if (id == R.id.backgroundcolor) {
            new ColorPickerDialog(this, mView.getBackgroundColor(), new ColorSetCallbackHandler() {

                @Override
                public void setColor(int color) {
                    mView.setBackgroundColor(color);
                    mView.invalidate();
                }
            });
        } else if (id == R.id.copy) {
            mView.copy();
        } else if (id == R.id.undo) {
            mView.undo();
        } else if (id == R.id.image) {
            Intent i = new Intent(Intent.ACTION_PICK,
                    android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);

            startActivityForResult(i, GALLERY);
        } else if (id == R.id.pen) {
            Pen p = new Pen();
            mView.setLayer(p);
        } else if (id == R.id.pencil) {
            Pencil p = new Pencil();
            mView.setLayer(p);
        } else if (id == R.id.polygon) {
            new ShapeDialog(this, new ShapeSetCallbackHandler() {
                @Override
                public void onShapeSet(int vertexSize, int type) {
                    if (type == 0) {
                        ArrayList<PointF> p = PolygonFactory.createShapeStar(vertexSize, 100, 100, 100);
                        mView.setLayer(new Polygon(p));
                    } else if (type == 1) {
                        ArrayList<PointF> p = PolygonFactory.createPolygon(vertexSize, 100, 100, 100);
                        mView.setLayer(new Polygon(p));
                    }
                }


            });

        } else if (id == R.id.text) {
            new TextPickerDialog(this, "Default", new TextPickerDialog.TextPickerCallbackHandler() {