mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-11 18:29:05 +01:00
Compare commits
1 Commits
offline-te
...
278-bqp-ui
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e32139a89 |
@@ -10,6 +10,11 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
<org.briarproject.android.util.ViewfinderView
|
||||
android:id="@+id/viewfinder_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
||||
@@ -43,4 +43,10 @@
|
||||
|
||||
<color name="spinner_border">#61000000</color> <!-- 38% Black -->
|
||||
<color name="spinner_arrow">@color/briar_blue_dark</color>
|
||||
|
||||
<!-- ViewfinderView -->
|
||||
<color name="possible_result_points">#c0ffbd21</color> <!-- Material Yellow 700 with alpha -->
|
||||
<color name="result_view">#b0000000</color>
|
||||
<color name="viewfinder_laser">#d50000</color> <!-- Red accent 700 -->
|
||||
<color name="viewfinder_mask">#60000000</color>
|
||||
</resources>
|
||||
@@ -19,6 +19,8 @@ import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.zxing.Result;
|
||||
import com.google.zxing.ResultPoint;
|
||||
import com.google.zxing.ResultPointCallback;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.AndroidComponent;
|
||||
@@ -27,6 +29,7 @@ import org.briarproject.android.fragment.BaseEventFragment;
|
||||
import org.briarproject.android.util.CameraView;
|
||||
import org.briarproject.android.util.QrCodeDecoder;
|
||||
import org.briarproject.android.util.QrCodeUtils;
|
||||
import org.briarproject.android.util.ViewfinderView;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.KeyAgreementAbortedEvent;
|
||||
import org.briarproject.api.event.KeyAgreementFailedEvent;
|
||||
@@ -55,7 +58,7 @@ import static java.util.logging.Level.WARNING;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class ShowQrCodeFragment extends BaseEventFragment
|
||||
implements QrCodeDecoder.ResultCallback {
|
||||
implements QrCodeDecoder.ResultCallback, ResultPointCallback {
|
||||
|
||||
public static final String TAG = "ShowQrCodeFragment";
|
||||
|
||||
@@ -75,6 +78,7 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
||||
protected Executor ioExecutor;
|
||||
|
||||
private CameraView cameraView;
|
||||
private ViewfinderView viewfinderView;
|
||||
private View statusView;
|
||||
private TextView status;
|
||||
private ImageView qrCode;
|
||||
@@ -109,9 +113,13 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
cameraView = (CameraView) view.findViewById(R.id.camera_view);
|
||||
viewfinderView =
|
||||
(ViewfinderView) view.findViewById(R.id.viewfinder_view);
|
||||
statusView = view.findViewById(R.id.status_container);
|
||||
status = (TextView) view.findViewById(R.id.connect_status);
|
||||
qrCode = (ImageView) view.findViewById(R.id.qr_code);
|
||||
|
||||
viewfinderView.setFrameProvider(cameraView);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -120,7 +128,7 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
||||
|
||||
getActivity().setRequestedOrientation(SCREEN_ORIENTATION_NOSENSOR);
|
||||
|
||||
decoder = new QrCodeDecoder(this);
|
||||
decoder = new QrCodeDecoder(this, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -219,6 +227,7 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
||||
getActivity().finish();
|
||||
} else {
|
||||
cameraView.start(camera, decoder, 0);
|
||||
viewfinderView.drawViewfinder();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -355,6 +364,11 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void foundPossibleResultPoint(ResultPoint point) {
|
||||
viewfinderView.addPossibleResultPoint(point);
|
||||
}
|
||||
|
||||
private class BluetoothStateReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package org.briarproject.android.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.Camera;
|
||||
import android.hardware.Camera.AutoFocusCallback;
|
||||
import android.hardware.Camera.CameraInfo;
|
||||
@@ -13,6 +15,7 @@ import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
@@ -31,17 +34,25 @@ import static java.util.logging.Level.WARNING;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
||||
AutoFocusCallback {
|
||||
AutoFocusCallback, ViewfinderView.FrameProvider {
|
||||
|
||||
private static final int AUTO_FOCUS_RETRY_DELAY = 5000; // Milliseconds
|
||||
private static final int MIN_FRAME_SIZE = 240;
|
||||
private static final int MAX_FRAME_SIZE = 675; // = 5/8 * 1080
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(CameraView.class.getName());
|
||||
|
||||
private Camera camera = null;
|
||||
private Rect framingRect;
|
||||
private Rect framingRectInPreview;
|
||||
private Rect framingRectInSensor;
|
||||
private PreviewConsumer previewConsumer = null;
|
||||
private int displayOrientation = 0, surfaceWidth = 0, surfaceHeight = 0;
|
||||
private boolean autoFocus = false, surfaceExists = false;
|
||||
|
||||
private Point cameraResolution;
|
||||
private final Object cameraResolutionLock = new Object();
|
||||
|
||||
public CameraView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
@@ -184,6 +195,24 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
||||
LOG.info("No suitable focus mode");
|
||||
}
|
||||
params.setZoom(0);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
List<Camera.Area> areas = new ArrayList<>();
|
||||
areas.add(new Camera.Area(getFramingRectInSensor(), 1000));
|
||||
if (params.getMaxNumFocusAreas() > 0) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Focus areas supported: " +
|
||||
params.getMaxNumFocusAreas());
|
||||
}
|
||||
params.setFocusAreas(areas);
|
||||
}
|
||||
if (params.getMaxNumMeteringAreas() > 0) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Metering areas supported: " +
|
||||
params.getMaxNumMeteringAreas());
|
||||
}
|
||||
params.setMeteringAreas(areas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setPreviewSize(Parameters params) {
|
||||
@@ -222,6 +251,13 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Best size " + bestSize.width + "x" + bestSize.height);
|
||||
params.setPreviewSize(bestSize.width, bestSize.height);
|
||||
synchronized (cameraResolutionLock) {
|
||||
cameraResolution = new Point(bestSize.width, bestSize.height);
|
||||
}
|
||||
} else {
|
||||
synchronized (cameraResolutionLock) {
|
||||
cameraResolution = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,4 +312,152 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
||||
LOG.log(WARNING, "Error retrying auto focus", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the framing rect which the UI should draw to show the user where to place the
|
||||
* barcode. This target helps with alignment as well as forces the user to hold the device
|
||||
* far enough away to ensure the image will be in focus.
|
||||
*
|
||||
* @return The rectangle to draw on screen in window coordinates.
|
||||
*/
|
||||
@Override
|
||||
public Rect getFramingRect() {
|
||||
if (framingRect == null) {
|
||||
framingRect = calculateFramingRect(true);
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Calculated framing rect: " + framingRect);
|
||||
}
|
||||
return framingRect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the framing rect which the UI should draw to show the user where to place the
|
||||
* barcode. This target helps with alignment as well as forces the user to hold the device
|
||||
* far enough away to ensure the image will be in focus.
|
||||
* <p/>
|
||||
* Adapted from the Zxing Barcode Scanner.
|
||||
*
|
||||
* @return The rectangle to draw on screen in window coordinates.
|
||||
*/
|
||||
private Rect calculateFramingRect(boolean withOrientation) {
|
||||
if (camera == null) {
|
||||
return null;
|
||||
}
|
||||
if (surfaceWidth == 0 || surfaceHeight == 0) {
|
||||
// Called early, before the surface is ready
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean portrait =
|
||||
withOrientation && displayOrientation % 180 == 90;
|
||||
int size = findDesiredDimensionInRange(
|
||||
portrait ? surfaceWidth : surfaceHeight,
|
||||
portrait ? surfaceHeight / 2 : surfaceWidth / 2,
|
||||
MIN_FRAME_SIZE, MAX_FRAME_SIZE);
|
||||
|
||||
int leftOffset = portrait ?
|
||||
(surfaceWidth - size) / 2 :
|
||||
((surfaceWidth / 2) - size) / 2;
|
||||
int topOffset = portrait ?
|
||||
((surfaceHeight / 2) - size) / 2 :
|
||||
(surfaceHeight - size) / 2;
|
||||
return new Rect(leftOffset, topOffset, leftOffset + size,
|
||||
topOffset + size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the square that fits best inside the given region.
|
||||
*/
|
||||
private static int findDesiredDimensionInRange(int side1, int side2,
|
||||
int hardMin, int hardMax) {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Finding framing dimension, side1 = " + side1 +
|
||||
", side2 = " + side2);
|
||||
int minSide = Math.min(side1, side2);
|
||||
int dim = 5 * minSide / 8; // Target 5/8 of smallest side
|
||||
if (dim < hardMin) {
|
||||
if (hardMin > minSide) {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Returning minimum side length: " + minSide);
|
||||
return minSide;
|
||||
} else {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Returning hard minimum: " + hardMin);
|
||||
return hardMin;
|
||||
}
|
||||
}
|
||||
if (dim > hardMax) {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Returning hard maximum: " + hardMax);
|
||||
return hardMax;
|
||||
}
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Returning desired dimension: " + dim);
|
||||
return dim;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #getFramingRect} but coordinates are in terms of the preview
|
||||
* frame, not UI / screen.
|
||||
* <p/>
|
||||
* Adapted from the Zxing Barcode Scanner.
|
||||
*
|
||||
* @return {@link Rect} expressing QR code scan area in terms of the preview size
|
||||
*/
|
||||
@Override
|
||||
public Rect getFramingRectInPreview() {
|
||||
if (framingRectInPreview == null) {
|
||||
Rect framingRect = getFramingRect();
|
||||
if (framingRect == null) {
|
||||
return null;
|
||||
}
|
||||
Rect rect = new Rect(framingRect);
|
||||
Point cameraResolution = getCameraResolution();
|
||||
if (cameraResolution == null || surfaceWidth == 0 ||
|
||||
surfaceHeight == 0) {
|
||||
// Called early, before the surface is ready
|
||||
return null;
|
||||
}
|
||||
rect.left = rect.left * cameraResolution.x / surfaceWidth;
|
||||
rect.right = rect.right * cameraResolution.x / surfaceWidth;
|
||||
rect.top = rect.top * cameraResolution.y / surfaceHeight;
|
||||
rect.bottom = rect.bottom * cameraResolution.y / surfaceHeight;
|
||||
framingRectInPreview = rect;
|
||||
}
|
||||
return framingRectInPreview;
|
||||
}
|
||||
|
||||
private Point getCameraResolution() {
|
||||
Point ret;
|
||||
synchronized (cameraResolutionLock) {
|
||||
ret = new Point(cameraResolution);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #getFramingRect} but coordinates are in terms of the sensor,
|
||||
* not UI / screen (ie. it is independent of orientation)
|
||||
*
|
||||
* @return {@link Rect} expressing QR code scan area in terms of the sensor
|
||||
*/
|
||||
private Rect getFramingRectInSensor() {
|
||||
if (framingRectInSensor == null) {
|
||||
Rect framingRect = calculateFramingRect(false);
|
||||
if (framingRect == null) {
|
||||
return null;
|
||||
}
|
||||
Rect rect = new Rect(framingRect);
|
||||
if (surfaceWidth == 0 || surfaceHeight == 0) {
|
||||
// Called early, before the surface is ready
|
||||
return null;
|
||||
}
|
||||
rect.left = (rect.left * 2000 / surfaceWidth) - 1000;
|
||||
rect.right = (rect.right * 2000 / surfaceWidth) - 1000;
|
||||
rect.top = (rect.top * 2000 / surfaceHeight) - 1000;
|
||||
rect.bottom = (rect.bottom * 2000 / surfaceHeight) - 1000;
|
||||
framingRectInSensor = rect;
|
||||
}
|
||||
return framingRectInSensor;
|
||||
}
|
||||
}
|
||||
@@ -6,14 +6,18 @@ import android.hardware.Camera.Size;
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import com.google.zxing.BinaryBitmap;
|
||||
import com.google.zxing.DecodeHintType;
|
||||
import com.google.zxing.LuminanceSource;
|
||||
import com.google.zxing.PlanarYUVLuminanceSource;
|
||||
import com.google.zxing.Reader;
|
||||
import com.google.zxing.ReaderException;
|
||||
import com.google.zxing.Result;
|
||||
import com.google.zxing.ResultPointCallback;
|
||||
import com.google.zxing.common.HybridBinarizer;
|
||||
import com.google.zxing.qrcode.QRCodeReader;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
@@ -26,11 +30,14 @@ public class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
|
||||
|
||||
private final Reader reader = new QRCodeReader();
|
||||
private final ResultCallback callback;
|
||||
private final ResultPointCallback pointCallback;
|
||||
|
||||
private boolean stopped = false;
|
||||
|
||||
public QrCodeDecoder(ResultCallback callback) {
|
||||
public QrCodeDecoder(ResultCallback callback,
|
||||
ResultPointCallback pointCallback) {
|
||||
this.callback = callback;
|
||||
this.pointCallback = pointCallback;
|
||||
}
|
||||
|
||||
public void start(Camera camera) {
|
||||
@@ -72,9 +79,11 @@ public class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
|
||||
LuminanceSource src = new PlanarYUVLuminanceSource(data, width,
|
||||
height, 0, 0, width, height, false);
|
||||
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(src));
|
||||
Map<DecodeHintType, Object> hints = new HashMap<>();
|
||||
hints.put(DecodeHintType.NEED_RESULT_POINT_CALLBACK, pointCallback);
|
||||
Result result = null;
|
||||
try {
|
||||
result = reader.decode(bitmap);
|
||||
result = reader.decode(bitmap, hints);
|
||||
} catch (ReaderException e) {
|
||||
return null; // No barcode found
|
||||
} catch (RuntimeException e) {
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright (C) 2008 ZXing authors
|
||||
* Copyright (C) 2016 Sublime Software Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.briarproject.android.util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import com.google.zxing.ResultPoint;
|
||||
|
||||
import org.briarproject.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This view is overlaid on top of the camera preview. It adds the viewfinder
|
||||
* rectangle and partial transparency outside it, as well as the laser scanner
|
||||
* animation and result points.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public final class ViewfinderView extends View {
|
||||
|
||||
private static final int[] SCANNER_ALPHA =
|
||||
{0, 64, 128, 192, 255, 192, 128, 64};
|
||||
private static final long ANIMATION_DELAY = 80L;
|
||||
private static final int CURRENT_POINT_OPACITY = 0xA0;
|
||||
private static final int MAX_RESULT_POINTS = 20;
|
||||
private static final int POINT_SIZE = 6;
|
||||
|
||||
private FrameProvider frameProvider;
|
||||
private final Paint paint;
|
||||
private Bitmap resultBitmap;
|
||||
private final int maskColor;
|
||||
private final int resultColor;
|
||||
private final int laserColor;
|
||||
private final int resultPointColor;
|
||||
private int scannerAlpha;
|
||||
private List<ResultPoint> possibleResultPoints;
|
||||
private List<ResultPoint> lastPossibleResultPoints;
|
||||
|
||||
// This constructor is used when the class is built from an XML resource.
|
||||
public ViewfinderView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
if (isInEditMode()) {
|
||||
paint = null;
|
||||
maskColor = 0;
|
||||
resultColor = 0;
|
||||
laserColor = 0;
|
||||
resultPointColor = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize these once for performance rather than calling them every
|
||||
// time in onDraw().
|
||||
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
Resources resources = getResources();
|
||||
maskColor = resources.getColor(R.color.viewfinder_mask);
|
||||
resultColor = resources.getColor(R.color.result_view);
|
||||
laserColor = resources.getColor(R.color.viewfinder_laser);
|
||||
resultPointColor = resources.getColor(R.color.possible_result_points);
|
||||
scannerAlpha = 0;
|
||||
possibleResultPoints = new ArrayList<>(5);
|
||||
lastPossibleResultPoints = null;
|
||||
}
|
||||
|
||||
public void setFrameProvider(FrameProvider frameProvider) {
|
||||
this.frameProvider = frameProvider;
|
||||
}
|
||||
|
||||
@SuppressLint("DrawAllocation")
|
||||
@Override
|
||||
public void onDraw(Canvas canvas) {
|
||||
if (frameProvider == null) {
|
||||
return; // not ready yet, early draw before done configuring
|
||||
}
|
||||
Rect frame = this.frameProvider.getFramingRect();
|
||||
Rect previewFrame = this.frameProvider.getFramingRectInPreview();
|
||||
if (frame == null || previewFrame == null) {
|
||||
return;
|
||||
}
|
||||
int width = canvas.getWidth();
|
||||
int height = canvas.getHeight();
|
||||
|
||||
// Draw the exterior (i.e. outside the framing rect) darkened
|
||||
paint.setColor(resultBitmap != null ? resultColor : maskColor);
|
||||
canvas.drawRect(0, 0, width, frame.top, paint);
|
||||
canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);
|
||||
canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1,
|
||||
paint);
|
||||
canvas.drawRect(0, frame.bottom + 1, width, height, paint);
|
||||
|
||||
if (resultBitmap != null) {
|
||||
// Draw the opaque result bitmap over the scanning rectangle
|
||||
paint.setAlpha(CURRENT_POINT_OPACITY);
|
||||
canvas.drawBitmap(resultBitmap, null, frame, paint);
|
||||
} else {
|
||||
|
||||
// Draw a red "laser scanner" line through the middle to show
|
||||
// decoding is active
|
||||
paint.setColor(laserColor);
|
||||
paint.setAlpha(SCANNER_ALPHA[scannerAlpha]);
|
||||
scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length;
|
||||
int middle = frame.height() / 2 + frame.top;
|
||||
canvas.drawRect(frame.left + 2, middle - 1, frame.right - 1,
|
||||
middle + 2, paint);
|
||||
|
||||
float scaleX = frame.width() / (float) previewFrame.width();
|
||||
float scaleY = frame.height() / (float) previewFrame.height();
|
||||
|
||||
List<ResultPoint> currentPossible = possibleResultPoints;
|
||||
List<ResultPoint> currentLast = lastPossibleResultPoints;
|
||||
int frameLeft = frame.left;
|
||||
int frameTop = frame.top;
|
||||
if (currentPossible.isEmpty()) {
|
||||
lastPossibleResultPoints = null;
|
||||
} else {
|
||||
possibleResultPoints = new ArrayList<>(5);
|
||||
lastPossibleResultPoints = currentPossible;
|
||||
paint.setAlpha(CURRENT_POINT_OPACITY);
|
||||
paint.setColor(resultPointColor);
|
||||
synchronized (currentPossible) {
|
||||
for (ResultPoint point : currentPossible) {
|
||||
canvas.drawCircle(
|
||||
frameLeft + (int) (point.getX() * scaleX),
|
||||
frameTop + (int) (point.getY() * scaleY),
|
||||
POINT_SIZE, paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentLast != null) {
|
||||
paint.setAlpha(CURRENT_POINT_OPACITY / 2);
|
||||
paint.setColor(resultPointColor);
|
||||
synchronized (currentLast) {
|
||||
float radius = POINT_SIZE / 2.0f;
|
||||
for (ResultPoint point : currentLast) {
|
||||
canvas.drawCircle(
|
||||
frameLeft + (int) (point.getX() * scaleX),
|
||||
frameTop + (int) (point.getY() * scaleY),
|
||||
radius, paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Request another update at the animation interval, but only
|
||||
// repaint the laser line, not the entire viewfinder mask.
|
||||
postInvalidateDelayed(ANIMATION_DELAY,
|
||||
frame.left - POINT_SIZE,
|
||||
frame.top - POINT_SIZE,
|
||||
frame.right + POINT_SIZE,
|
||||
frame.bottom + POINT_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
public void drawViewfinder() {
|
||||
Bitmap resultBitmap = this.resultBitmap;
|
||||
this.resultBitmap = null;
|
||||
if (resultBitmap != null) {
|
||||
resultBitmap.recycle();
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a bitmap with the result points highlighted instead of the live
|
||||
* scanning display.
|
||||
*
|
||||
* @param barcode An image of the decoded barcode.
|
||||
*/
|
||||
public void drawResultBitmap(Bitmap barcode) {
|
||||
resultBitmap = barcode;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void addPossibleResultPoint(ResultPoint point) {
|
||||
List<ResultPoint> points = possibleResultPoints;
|
||||
synchronized (points) {
|
||||
points.add(point);
|
||||
int size = points.size();
|
||||
if (size > MAX_RESULT_POINTS) {
|
||||
// trim it
|
||||
points.subList(0, size - MAX_RESULT_POINTS / 2).clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface FrameProvider {
|
||||
|
||||
Rect getFramingRect();
|
||||
Rect getFramingRectInPreview();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user