com.nextgis.maplibui.formcontrol.Sign.java Source code

Java tutorial

Introduction

Here is the source code for com.nextgis.maplibui.formcontrol.Sign.java

Source

/*
 * Project: Forest violations
 * Purpose: Mobile application for registering facts of the forest violations.
 * Author:  Dmitry Baryshnikov (aka Bishop), bishop.dev@gmail.com
 * *****************************************************************************
 * Copyright (c) 2015-2015. NextGIS, info@nextgis.com
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.nextgis.maplibui.formcontrol;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;

import com.nextgis.maplib.datasource.Field;
import com.nextgis.maplibui.R;
import com.nextgis.maplibui.api.IFormControl;
import com.nextgis.maplibui.util.ControlHelper;
import com.nextgis.maplibui.util.SettingsConstantsUI;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;

/**
 * A special control for create sign picture.
 */
public class Sign extends View implements IFormControl {
    public static final String SIGN_FILE = Integer.MAX_VALUE + "";

    protected Drawable mCleanImage;
    protected int mClearBuff;
    protected int mClearImageSize;
    protected Path mPath;
    protected final LinkedList<Path> mPaths = new LinkedList<>();
    protected Paint mPaint;
    protected float mX, mY;
    protected String mPreviousSignPath;
    protected File mPreviousSign;
    protected boolean mNotInitialized;
    protected Bitmap mPreviousSignBitmap;

    protected final int CLEAR_BUFF_DP = 15;
    protected final int CLEAR_IMAGE_SIZE_DP = 32;
    protected static final float TOUCH_TOLERANCE = 4;

    public Sign(Context context) {
        super(context);
        init();
    }

    public Sign(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public Sign(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public Sign(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    protected void init() {
        //1. get clean
        int[] attrs = new int[] { R.attr.ic_clear };
        TypedArray ta = getContext().obtainStyledAttributes(attrs);
        mCleanImage = ta.getDrawable(0);
        ta.recycle();

        mClearBuff = (int) (getContext().getResources().getDisplayMetrics().density * CLEAR_BUFF_DP);
        mClearImageSize = (int) (getContext().getResources().getDisplayMetrics().density * CLEAR_IMAGE_SIZE_DP);

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(3);
        mPaint.setDither(true);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeCap(Paint.Cap.ROUND);

        boolean bDark = PreferenceManager.getDefaultSharedPreferences(getContext())
                .getString(SettingsConstantsUI.KEY_PREF_THEME, "light").equals("dark");
        if (bDark)
            mPaint.setColor(Color.WHITE);
        else
            mPaint.setColor(Color.BLACK);

        mPath = new Path();
        mPaths.add(mPath);
    }

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

        mPreviousSign = new File(mPreviousSignPath, SIGN_FILE);
        if (mNotInitialized = mPreviousSign.exists()) {
            try {
                BitmapFactory.Options options = ControlHelper.getOptions(new FileInputStream(mPreviousSign), w, h);
                mPreviousSignBitmap = ControlHelper.getBitmap(new FileInputStream(mPreviousSign), options);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (mNotInitialized && mPreviousSignBitmap != null)
            canvas.drawBitmap(mPreviousSignBitmap, 0, 0, null);

        int posX = canvas.getWidth() - mClearImageSize - mClearBuff;
        mCleanImage.setBounds(posX, mClearBuff, posX + mClearImageSize, mClearImageSize + mClearBuff);
        mCleanImage.draw(canvas);

        if (!mNotInitialized)
            for (Path path : mPaths)
                canvas.drawPath(path, mPaint);
    }

    protected void drawSign(Canvas canvas, int bkColor, Paint paint) {
        canvas.drawColor(bkColor);

        for (Path path : mPaths)
            canvas.drawPath(path, paint);
    }

    protected void touchStart(float x, float y) {
        mPath.reset();
        mPath.moveTo(x, y);
        mX = x;
        mY = y;
    }

    protected void touchMove(float x, float y) {
        float dx = Math.abs(x - mX);
        float dy = Math.abs(y - mY);
        if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
            mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
            mX = x;
            mY = y;
        }
    }

    protected void touchUp() {
        mPath.lineTo(mX, mY);

        postInvalidate();

        // kill this so we don't double draw
        mPath = new Path();
        mPaths.add(mPath);
    }

    @Override
    public boolean onTouchEvent(@NonNull MotionEvent event) {

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

        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            if (!mNotInitialized)
                touchStart(x, y);
            break;

        case MotionEvent.ACTION_MOVE:
            if (!mNotInitialized) {
                touchMove(x, y);
                postInvalidate();
            }
            break;

        case MotionEvent.ACTION_UP:
            int posX = getWidth() - mClearImageSize - mClearBuff;
            //Log.d(Constants.TAG, "x: " + event.getX() + " y: " + event.getY() + " posX: " + posX + " posY: " + mClearBuff);
            if (event.getX() > posX && event.getY() < mClearImageSize + mClearBuff) {
                onClearSign();
            } else if (!mNotInitialized) {
                touchUp();
                invalidate();
            }
            break;

        default:
            break;
        }

        return true;
    }

    private void onClearSign() {
        if (mNotInitialized)
            mNotInitialized = false;

        mPaths.clear();
        mPath = new Path();
        mPaths.add(mPath);

        postInvalidate();
    }

    public void save(int width, int height, boolean transparentBackground, File sigFile) throws IOException {
        if (mNotInitialized)
            return;

        float scale = Math.min((float) width / getWidth(), (float) height / getHeight());
        Matrix matrix = new Matrix();
        matrix.setScale(scale, scale);

        Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bmp);
        canvas.setMatrix(matrix);

        int color = transparentBackground ? Color.TRANSPARENT : 0xFFFFFF - mPaint.getColor();
        drawSign(canvas, color, mPaint);
        if (sigFile.exists() || sigFile.createNewFile()) {
            FileOutputStream out = new FileOutputStream(sigFile);
            bmp.compress(Bitmap.CompressFormat.PNG, 90, out);
        }
    }

    public void setPath(String path) {
        mPreviousSignPath = path;
    }

    @Override
    public void init(JSONObject element, List<Field> fields, Bundle savedState, Cursor featureCursor,
            SharedPreferences lastValue) throws JSONException {
        init();
    }

    @Override
    public void saveLastValue(SharedPreferences preferences) {

    }

    @Override
    public boolean isShowLast() {
        return false;
    }

    @Override
    public String getFieldName() {
        return null;
    }

    @Override
    public void addToLayout(ViewGroup layout) {
        layout.addView(this);
    }

    @Override
    public Object getValue() {
        return null;
    }

    @Override
    public void saveState(Bundle outState) {

    }
}