ScannerView.java

  1/*
  2 * Copyright 2012-2015 the original author or authors.
  3 *
  4 * This program is free software: you can redistribute it and/or modify
  5 * it under the terms of the GNU General Public License as published by
  6 * the Free Software Foundation, either version 3 of the License, or
  7 * (at your option) any later version.
  8 *
  9 * This program is distributed in the hope that it will be useful,
 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12 * GNU General Public License for more details.
 13 *
 14 * You should have received a copy of the GNU General Public License
 15 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 16 */
 17
 18package eu.siacs.conversations.ui.widget;
 19
 20import android.content.Context;
 21import android.content.res.Resources;
 22import android.graphics.Canvas;
 23import android.graphics.Matrix;
 24import android.graphics.Matrix.ScaleToFit;
 25import android.graphics.Paint;
 26import android.graphics.Paint.Style;
 27import android.graphics.Rect;
 28import android.graphics.RectF;
 29import android.util.AttributeSet;
 30import android.view.View;
 31
 32import androidx.core.content.ContextCompat;
 33
 34import com.google.zxing.ResultPoint;
 35
 36import java.util.HashMap;
 37import java.util.Iterator;
 38import java.util.Map;
 39
 40import eu.siacs.conversations.R;
 41
 42/**
 43 * @author Andreas Schildbach
 44 */
 45public class ScannerView extends View {
 46    private static final long LASER_ANIMATION_DELAY_MS = 100l;
 47    private static final int DOT_OPACITY = 0xa0;
 48    private static final int DOT_TTL_MS = 500;
 49
 50    private final Paint maskPaint;
 51    private final Paint laserPaint;
 52    private final Paint dotPaint;
 53    private boolean isResult;
 54    private final int maskColor, maskResultColor;
 55    private final int laserColor;
 56    private final int dotColor, dotResultColor;
 57    private final Map<float[], Long> dots = new HashMap<float[], Long>(16);
 58    private Rect frame;
 59    private final Matrix matrix = new Matrix();
 60
 61    public ScannerView(final Context context, final AttributeSet attrs) {
 62        super(context, attrs);
 63        final Resources resources = context.getResources();
 64        maskColor = ContextCompat.getColor(context, R.color.black54);
 65        maskResultColor = ContextCompat.getColor(context, R.color.black87);
 66        laserColor = ContextCompat.getColor(context, R.color.red_500);
 67        dotColor = ContextCompat.getColor(context, R.color.orange_500);
 68        dotResultColor = ContextCompat.getColor(context, R.color.green_500);
 69
 70        maskPaint = new Paint();
 71        maskPaint.setStyle(Style.FILL);
 72
 73        laserPaint = new Paint();
 74        laserPaint.setStrokeWidth(resources.getDimensionPixelSize(R.dimen.scan_laser_width));
 75        laserPaint.setStyle(Style.STROKE);
 76
 77        dotPaint = new Paint();
 78        dotPaint.setAlpha(DOT_OPACITY);
 79        dotPaint.setStyle(Style.STROKE);
 80        dotPaint.setStrokeWidth(resources.getDimension(R.dimen.scan_dot_size));
 81        dotPaint.setAntiAlias(true);
 82    }
 83
 84    public void setFraming(final Rect frame, final RectF framePreview, final int displayRotation,
 85            final int cameraRotation, final boolean cameraFlip) {
 86        this.frame = frame;
 87        matrix.setRectToRect(framePreview, new RectF(frame), ScaleToFit.FILL);
 88        matrix.postRotate(-displayRotation, frame.exactCenterX(), frame.exactCenterY());
 89        matrix.postScale(cameraFlip ? -1 : 1, 1, frame.exactCenterX(), frame.exactCenterY());
 90        matrix.postRotate(cameraRotation, frame.exactCenterX(), frame.exactCenterY());
 91
 92        invalidate();
 93    }
 94
 95    public void setIsResult(final boolean isResult) {
 96        this.isResult = isResult;
 97
 98        invalidate();
 99    }
100
101    public void addDot(final ResultPoint dot) {
102        dots.put(new float[] { dot.getX(), dot.getY() }, System.currentTimeMillis());
103
104        invalidate();
105    }
106
107    @Override
108    public void onDraw(final Canvas canvas) {
109        if (frame == null)
110            return;
111
112        final long now = System.currentTimeMillis();
113
114        final int width = canvas.getWidth();
115        final int height = canvas.getHeight();
116
117        final float[] point = new float[2];
118
119        // draw mask darkened
120        maskPaint.setColor(isResult ? maskResultColor : maskColor);
121        canvas.drawRect(0, 0, width, frame.top, maskPaint);
122        canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, maskPaint);
123        canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, maskPaint);
124        canvas.drawRect(0, frame.bottom + 1, width, height, maskPaint);
125
126        if (isResult) {
127            laserPaint.setColor(dotResultColor);
128            laserPaint.setAlpha(160);
129
130            dotPaint.setColor(dotResultColor);
131        } else {
132            laserPaint.setColor(laserColor);
133            final boolean laserPhase = (now / 600) % 2 == 0;
134            laserPaint.setAlpha(laserPhase ? 160 : 255);
135
136            dotPaint.setColor(dotColor);
137
138            // schedule redraw
139            postInvalidateDelayed(LASER_ANIMATION_DELAY_MS);
140        }
141
142        canvas.drawRect(frame, laserPaint);
143
144        // draw points
145        for (final Iterator<Map.Entry<float[], Long>> i = dots.entrySet().iterator(); i.hasNext();) {
146            final Map.Entry<float[], Long> entry = i.next();
147            final long age = now - entry.getValue();
148            if (age < DOT_TTL_MS) {
149                dotPaint.setAlpha((int) ((DOT_TTL_MS - age) * 256 / DOT_TTL_MS));
150
151                matrix.mapPoints(point, entry.getKey());
152                canvas.drawPoint(point[0], point[1], dotPaint);
153            } else {
154                i.remove();
155            }
156        }
157    }
158}