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;
 31import androidx.core.content.ContextCompat;
 32import com.google.zxing.ResultPoint;
 33import eu.siacs.conversations.R;
 34import java.util.HashMap;
 35import java.util.Iterator;
 36import java.util.Map;
 37
 38/**
 39 * @author Andreas Schildbach
 40 */
 41public class ScannerView extends View {
 42    private static final long LASER_ANIMATION_DELAY_MS = 100L;
 43    private static final int DOT_OPACITY = 0xa0;
 44    private static final int DOT_TTL_MS = 500;
 45
 46    private final Paint maskPaint;
 47    private final Paint laserPaint;
 48    private final Paint dotPaint;
 49    private boolean isResult;
 50    private final int maskColor, maskResultColor;
 51    private final int laserColor;
 52    private final int dotColor, dotResultColor;
 53    private final Map<float[], Long> dots = new HashMap<float[], Long>(16);
 54    private Rect frame;
 55    private final Matrix matrix = new Matrix();
 56
 57    public ScannerView(final Context context, final AttributeSet attrs) {
 58        super(context, attrs);
 59        final Resources resources = context.getResources();
 60        maskColor = ContextCompat.getColor(context, R.color.black54);
 61        maskResultColor = ContextCompat.getColor(context, R.color.black87);
 62        laserColor = ContextCompat.getColor(context, R.color.red_500);
 63        dotColor = ContextCompat.getColor(context, R.color.orange_500);
 64        dotResultColor = ContextCompat.getColor(context, R.color.green_500);
 65
 66        maskPaint = new Paint();
 67        maskPaint.setStyle(Style.FILL);
 68
 69        laserPaint = new Paint();
 70        laserPaint.setStrokeWidth(resources.getDimensionPixelSize(R.dimen.scan_laser_width));
 71        laserPaint.setStyle(Style.STROKE);
 72
 73        dotPaint = new Paint();
 74        dotPaint.setAlpha(DOT_OPACITY);
 75        dotPaint.setStyle(Style.STROKE);
 76        dotPaint.setStrokeWidth(resources.getDimension(R.dimen.scan_dot_size));
 77        dotPaint.setAntiAlias(true);
 78    }
 79
 80    public void setFraming(
 81            final Rect frame,
 82            final RectF framePreview,
 83            final int displayRotation,
 84            final int cameraRotation,
 85            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) return;
110
111        final long now = System.currentTimeMillis();
112
113        final int width = canvas.getWidth();
114        final int height = canvas.getHeight();
115
116        final float[] point = new float[2];
117
118        // draw mask darkened
119        maskPaint.setColor(isResult ? maskResultColor : maskColor);
120        canvas.drawRect(0, 0, width, frame.top, maskPaint);
121        canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, maskPaint);
122        canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, maskPaint);
123        canvas.drawRect(0, frame.bottom + 1, width, height, maskPaint);
124
125        if (isResult) {
126            laserPaint.setColor(dotResultColor);
127            laserPaint.setAlpha(160);
128
129            dotPaint.setColor(dotResultColor);
130        } else {
131            laserPaint.setColor(laserColor);
132            final boolean laserPhase = (now / 600) % 2 == 0;
133            laserPaint.setAlpha(laserPhase ? 160 : 255);
134
135            dotPaint.setColor(dotColor);
136
137            // schedule redraw
138            postInvalidateDelayed(LASER_ANIMATION_DELAY_MS);
139        }
140
141        canvas.drawRect(frame, laserPaint);
142
143        // draw points
144        for (final Iterator<Map.Entry<float[], Long>> i = dots.entrySet().iterator();
145                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}