17620644768ffc235607b3a94671e49518c18686fBryan Mawhinney/* 27620644768ffc235607b3a94671e49518c18686fBryan Mawhinney * Copyright (C) 2008 ZXing authors 37620644768ffc235607b3a94671e49518c18686fBryan Mawhinney * 47620644768ffc235607b3a94671e49518c18686fBryan Mawhinney * Licensed under the Apache License, Version 2.0 (the "License"); 57620644768ffc235607b3a94671e49518c18686fBryan Mawhinney * you may not use this file except in compliance with the License. 67620644768ffc235607b3a94671e49518c18686fBryan Mawhinney * You may obtain a copy of the License at 77620644768ffc235607b3a94671e49518c18686fBryan Mawhinney * 87620644768ffc235607b3a94671e49518c18686fBryan Mawhinney * http://www.apache.org/licenses/LICENSE-2.0 97620644768ffc235607b3a94671e49518c18686fBryan Mawhinney * 107620644768ffc235607b3a94671e49518c18686fBryan Mawhinney * Unless required by applicable law or agreed to in writing, software 117620644768ffc235607b3a94671e49518c18686fBryan Mawhinney * distributed under the License is distributed on an "AS IS" BASIS, 127620644768ffc235607b3a94671e49518c18686fBryan Mawhinney * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 137620644768ffc235607b3a94671e49518c18686fBryan Mawhinney * See the License for the specific language governing permissions and 147620644768ffc235607b3a94671e49518c18686fBryan Mawhinney * limitations under the License. 157620644768ffc235607b3a94671e49518c18686fBryan Mawhinney */ 167620644768ffc235607b3a94671e49518c18686fBryan Mawhinney 177620644768ffc235607b3a94671e49518c18686fBryan Mawhinneypackage com.google.zxing.client.android; 187620644768ffc235607b3a94671e49518c18686fBryan Mawhinney 197620644768ffc235607b3a94671e49518c18686fBryan Mawhinneyimport com.google.zxing.ResultPoint; 207620644768ffc235607b3a94671e49518c18686fBryan Mawhinneyimport com.google.zxing.client.android.camera.CameraManager; 217620644768ffc235607b3a94671e49518c18686fBryan Mawhinney 227620644768ffc235607b3a94671e49518c18686fBryan Mawhinneyimport android.content.Context; 237620644768ffc235607b3a94671e49518c18686fBryan Mawhinneyimport android.content.res.Resources; 247620644768ffc235607b3a94671e49518c18686fBryan Mawhinneyimport android.graphics.Bitmap; 257620644768ffc235607b3a94671e49518c18686fBryan Mawhinneyimport android.graphics.Canvas; 267620644768ffc235607b3a94671e49518c18686fBryan Mawhinneyimport android.graphics.Paint; 277620644768ffc235607b3a94671e49518c18686fBryan Mawhinneyimport android.graphics.Rect; 287620644768ffc235607b3a94671e49518c18686fBryan Mawhinneyimport android.util.AttributeSet; 297620644768ffc235607b3a94671e49518c18686fBryan Mawhinneyimport android.view.View; 307620644768ffc235607b3a94671e49518c18686fBryan Mawhinney 317620644768ffc235607b3a94671e49518c18686fBryan Mawhinneyimport java.util.ArrayList; 327620644768ffc235607b3a94671e49518c18686fBryan Mawhinneyimport java.util.List; 337620644768ffc235607b3a94671e49518c18686fBryan Mawhinney 347620644768ffc235607b3a94671e49518c18686fBryan Mawhinney/** 357620644768ffc235607b3a94671e49518c18686fBryan Mawhinney * This view is overlaid on top of the camera preview. It adds the viewfinder rectangle and partial 367620644768ffc235607b3a94671e49518c18686fBryan Mawhinney * transparency outside it, as well as the laser scanner animation and result points. 377620644768ffc235607b3a94671e49518c18686fBryan Mawhinney * 387620644768ffc235607b3a94671e49518c18686fBryan Mawhinney * @author dswitkin@google.com (Daniel Switkin) 397620644768ffc235607b3a94671e49518c18686fBryan Mawhinney */ 407620644768ffc235607b3a94671e49518c18686fBryan Mawhinneypublic final class ViewfinderView extends View { 417620644768ffc235607b3a94671e49518c18686fBryan Mawhinney 427620644768ffc235607b3a94671e49518c18686fBryan Mawhinney private static final int[] SCANNER_ALPHA = {0, 64, 128, 192, 255, 192, 128, 64}; 437620644768ffc235607b3a94671e49518c18686fBryan Mawhinney private static final long ANIMATION_DELAY = 80L; 447620644768ffc235607b3a94671e49518c18686fBryan Mawhinney private static final int CURRENT_POINT_OPACITY = 0xA0; 457620644768ffc235607b3a94671e49518c18686fBryan Mawhinney private static final int MAX_RESULT_POINTS = 20; 467620644768ffc235607b3a94671e49518c18686fBryan Mawhinney private static final int POINT_SIZE = 6; 477620644768ffc235607b3a94671e49518c18686fBryan Mawhinney 487620644768ffc235607b3a94671e49518c18686fBryan Mawhinney private CameraManager cameraManager; 497620644768ffc235607b3a94671e49518c18686fBryan Mawhinney private final Paint paint; 507620644768ffc235607b3a94671e49518c18686fBryan Mawhinney private Bitmap resultBitmap; 517620644768ffc235607b3a94671e49518c18686fBryan Mawhinney private final int maskColor; 527620644768ffc235607b3a94671e49518c18686fBryan Mawhinney private final int resultColor; 537620644768ffc235607b3a94671e49518c18686fBryan Mawhinney private final int frameColor; 547620644768ffc235607b3a94671e49518c18686fBryan Mawhinney private final int laserColor; 557620644768ffc235607b3a94671e49518c18686fBryan Mawhinney private final int resultPointColor; 567620644768ffc235607b3a94671e49518c18686fBryan Mawhinney private int scannerAlpha; 577620644768ffc235607b3a94671e49518c18686fBryan Mawhinney private List<ResultPoint> possibleResultPoints; 587620644768ffc235607b3a94671e49518c18686fBryan Mawhinney private List<ResultPoint> lastPossibleResultPoints; 597620644768ffc235607b3a94671e49518c18686fBryan Mawhinney 607620644768ffc235607b3a94671e49518c18686fBryan Mawhinney // This constructor is used when the class is built from an XML resource. 617620644768ffc235607b3a94671e49518c18686fBryan Mawhinney public ViewfinderView(Context context, AttributeSet attrs) { 627620644768ffc235607b3a94671e49518c18686fBryan Mawhinney super(context, attrs); 637620644768ffc235607b3a94671e49518c18686fBryan Mawhinney 647620644768ffc235607b3a94671e49518c18686fBryan Mawhinney // Initialize these once for performance rather than calling them every time in onDraw(). 657620644768ffc235607b3a94671e49518c18686fBryan Mawhinney paint = new Paint(Paint.ANTI_ALIAS_FLAG); 667620644768ffc235607b3a94671e49518c18686fBryan Mawhinney Resources resources = getResources(); 677620644768ffc235607b3a94671e49518c18686fBryan Mawhinney maskColor = resources.getColor(R.color.viewfinder_mask); 687620644768ffc235607b3a94671e49518c18686fBryan Mawhinney resultColor = resources.getColor(R.color.result_view); 697620644768ffc235607b3a94671e49518c18686fBryan Mawhinney frameColor = resources.getColor(R.color.viewfinder_frame); 707620644768ffc235607b3a94671e49518c18686fBryan Mawhinney laserColor = resources.getColor(R.color.viewfinder_laser); 717620644768ffc235607b3a94671e49518c18686fBryan Mawhinney resultPointColor = resources.getColor(R.color.possible_result_points); 727620644768ffc235607b3a94671e49518c18686fBryan Mawhinney scannerAlpha = 0; 737620644768ffc235607b3a94671e49518c18686fBryan Mawhinney possibleResultPoints = new ArrayList<ResultPoint>(5); 747620644768ffc235607b3a94671e49518c18686fBryan Mawhinney lastPossibleResultPoints = null; 757620644768ffc235607b3a94671e49518c18686fBryan Mawhinney } 767620644768ffc235607b3a94671e49518c18686fBryan Mawhinney 777620644768ffc235607b3a94671e49518c18686fBryan Mawhinney public void setCameraManager(CameraManager cameraManager) { 787620644768ffc235607b3a94671e49518c18686fBryan Mawhinney this.cameraManager = cameraManager; 797620644768ffc235607b3a94671e49518c18686fBryan Mawhinney } 807620644768ffc235607b3a94671e49518c18686fBryan Mawhinney 817620644768ffc235607b3a94671e49518c18686fBryan Mawhinney @Override 827620644768ffc235607b3a94671e49518c18686fBryan Mawhinney public void onDraw(Canvas canvas) { 837620644768ffc235607b3a94671e49518c18686fBryan Mawhinney Rect frame = cameraManager.getFramingRect(); 847620644768ffc235607b3a94671e49518c18686fBryan Mawhinney if (frame == null) { 857620644768ffc235607b3a94671e49518c18686fBryan Mawhinney return; 867620644768ffc235607b3a94671e49518c18686fBryan Mawhinney } 877620644768ffc235607b3a94671e49518c18686fBryan Mawhinney int width = canvas.getWidth(); 887620644768ffc235607b3a94671e49518c18686fBryan Mawhinney int height = canvas.getHeight(); 897620644768ffc235607b3a94671e49518c18686fBryan Mawhinney 907620644768ffc235607b3a94671e49518c18686fBryan Mawhinney // Draw the exterior (i.e. outside the framing rect) darkened 917620644768ffc235607b3a94671e49518c18686fBryan Mawhinney paint.setColor(resultBitmap != null ? resultColor : maskColor); 927620644768ffc235607b3a94671e49518c18686fBryan Mawhinney canvas.drawRect(0, 0, width, frame.top, paint); 937620644768ffc235607b3a94671e49518c18686fBryan Mawhinney canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint); 947620644768ffc235607b3a94671e49518c18686fBryan Mawhinney canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, paint); 957620644768ffc235607b3a94671e49518c18686fBryan Mawhinney canvas.drawRect(0, frame.bottom + 1, width, height, paint); 967620644768ffc235607b3a94671e49518c18686fBryan Mawhinney 977620644768ffc235607b3a94671e49518c18686fBryan Mawhinney if (resultBitmap != null) { 987620644768ffc235607b3a94671e49518c18686fBryan Mawhinney // Draw the opaque result bitmap over the scanning rectangle 997620644768ffc235607b3a94671e49518c18686fBryan Mawhinney paint.setAlpha(CURRENT_POINT_OPACITY); 1007620644768ffc235607b3a94671e49518c18686fBryan Mawhinney canvas.drawBitmap(resultBitmap, null, frame, paint); 1017620644768ffc235607b3a94671e49518c18686fBryan Mawhinney } else { 1027620644768ffc235607b3a94671e49518c18686fBryan Mawhinney 1037620644768ffc235607b3a94671e49518c18686fBryan Mawhinney // Draw a two pixel solid black border inside the framing rect 1047620644768ffc235607b3a94671e49518c18686fBryan Mawhinney paint.setColor(frameColor); 1057620644768ffc235607b3a94671e49518c18686fBryan Mawhinney canvas.drawRect(frame.left, frame.top, frame.right + 1, frame.top + 2, paint); 1067620644768ffc235607b3a94671e49518c18686fBryan Mawhinney canvas.drawRect(frame.left, frame.top + 2, frame.left + 2, frame.bottom - 1, paint); 1077620644768ffc235607b3a94671e49518c18686fBryan Mawhinney canvas.drawRect(frame.right - 1, frame.top, frame.right + 1, frame.bottom - 1, paint); 1087620644768ffc235607b3a94671e49518c18686fBryan Mawhinney canvas.drawRect(frame.left, frame.bottom - 1, frame.right + 1, frame.bottom + 1, paint); 1097620644768ffc235607b3a94671e49518c18686fBryan Mawhinney 1107620644768ffc235607b3a94671e49518c18686fBryan Mawhinney // Draw a red "laser scanner" line through the middle to show decoding is active 1117620644768ffc235607b3a94671e49518c18686fBryan Mawhinney paint.setColor(laserColor); 1127620644768ffc235607b3a94671e49518c18686fBryan Mawhinney paint.setAlpha(SCANNER_ALPHA[scannerAlpha]); 1137620644768ffc235607b3a94671e49518c18686fBryan Mawhinney scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length; 1147620644768ffc235607b3a94671e49518c18686fBryan Mawhinney int middle = frame.height() / 2 + frame.top; 1157620644768ffc235607b3a94671e49518c18686fBryan Mawhinney canvas.drawRect(frame.left + 2, middle - 1, frame.right - 1, middle + 2, paint); 1167620644768ffc235607b3a94671e49518c18686fBryan Mawhinney 1177620644768ffc235607b3a94671e49518c18686fBryan Mawhinney Rect previewFrame = cameraManager.getFramingRectInPreview(); 1187620644768ffc235607b3a94671e49518c18686fBryan Mawhinney float scaleX = frame.width() / (float) previewFrame.width(); 1197620644768ffc235607b3a94671e49518c18686fBryan Mawhinney float scaleY = frame.height() / (float) previewFrame.height(); 1207620644768ffc235607b3a94671e49518c18686fBryan Mawhinney 1217620644768ffc235607b3a94671e49518c18686fBryan Mawhinney List<ResultPoint> currentPossible = possibleResultPoints; 1227620644768ffc235607b3a94671e49518c18686fBryan Mawhinney List<ResultPoint> currentLast = lastPossibleResultPoints; 1237620644768ffc235607b3a94671e49518c18686fBryan Mawhinney int frameLeft = frame.left; 1247620644768ffc235607b3a94671e49518c18686fBryan Mawhinney int frameTop = frame.top; 1257620644768ffc235607b3a94671e49518c18686fBryan Mawhinney if (currentPossible.isEmpty()) { 1267620644768ffc235607b3a94671e49518c18686fBryan Mawhinney lastPossibleResultPoints = null; 1277620644768ffc235607b3a94671e49518c18686fBryan Mawhinney } else { 1287620644768ffc235607b3a94671e49518c18686fBryan Mawhinney possibleResultPoints = new ArrayList<ResultPoint>(5); 1297620644768ffc235607b3a94671e49518c18686fBryan Mawhinney lastPossibleResultPoints = currentPossible; 1307620644768ffc235607b3a94671e49518c18686fBryan Mawhinney paint.setAlpha(CURRENT_POINT_OPACITY); 1317620644768ffc235607b3a94671e49518c18686fBryan Mawhinney paint.setColor(resultPointColor); 1327620644768ffc235607b3a94671e49518c18686fBryan Mawhinney synchronized (currentPossible) { 1337620644768ffc235607b3a94671e49518c18686fBryan Mawhinney for (ResultPoint point : currentPossible) { 1347620644768ffc235607b3a94671e49518c18686fBryan Mawhinney canvas.drawCircle(frameLeft + (int) (point.getX() * scaleX), 1357620644768ffc235607b3a94671e49518c18686fBryan Mawhinney frameTop + (int) (point.getY() * scaleY), 1367620644768ffc235607b3a94671e49518c18686fBryan Mawhinney POINT_SIZE, paint); 1377620644768ffc235607b3a94671e49518c18686fBryan Mawhinney } 1387620644768ffc235607b3a94671e49518c18686fBryan Mawhinney } 1397620644768ffc235607b3a94671e49518c18686fBryan Mawhinney } 1407620644768ffc235607b3a94671e49518c18686fBryan Mawhinney if (currentLast != null) { 1417620644768ffc235607b3a94671e49518c18686fBryan Mawhinney paint.setAlpha(CURRENT_POINT_OPACITY / 2); 1427620644768ffc235607b3a94671e49518c18686fBryan Mawhinney paint.setColor(resultPointColor); 1437620644768ffc235607b3a94671e49518c18686fBryan Mawhinney synchronized (currentLast) { 1447620644768ffc235607b3a94671e49518c18686fBryan Mawhinney for (ResultPoint point : currentLast) { 1457620644768ffc235607b3a94671e49518c18686fBryan Mawhinney canvas.drawCircle(frameLeft + (int) (point.getX() * scaleX), 1467620644768ffc235607b3a94671e49518c18686fBryan Mawhinney frameTop + (int) (point.getY() * scaleY), 1477620644768ffc235607b3a94671e49518c18686fBryan Mawhinney POINT_SIZE / 2, paint); 1487620644768ffc235607b3a94671e49518c18686fBryan Mawhinney } 1497620644768ffc235607b3a94671e49518c18686fBryan Mawhinney } 1507620644768ffc235607b3a94671e49518c18686fBryan Mawhinney } 1517620644768ffc235607b3a94671e49518c18686fBryan Mawhinney 1527620644768ffc235607b3a94671e49518c18686fBryan Mawhinney // Request another update at the animation interval, but only repaint the laser line, 1537620644768ffc235607b3a94671e49518c18686fBryan Mawhinney // not the entire viewfinder mask. 1547620644768ffc235607b3a94671e49518c18686fBryan Mawhinney postInvalidateDelayed(ANIMATION_DELAY, 1557620644768ffc235607b3a94671e49518c18686fBryan Mawhinney frame.left - POINT_SIZE, 1567620644768ffc235607b3a94671e49518c18686fBryan Mawhinney frame.top - POINT_SIZE, 1577620644768ffc235607b3a94671e49518c18686fBryan Mawhinney frame.right + POINT_SIZE, 1587620644768ffc235607b3a94671e49518c18686fBryan Mawhinney frame.bottom + POINT_SIZE); 1597620644768ffc235607b3a94671e49518c18686fBryan Mawhinney } 1607620644768ffc235607b3a94671e49518c18686fBryan Mawhinney } 1617620644768ffc235607b3a94671e49518c18686fBryan Mawhinney 1627620644768ffc235607b3a94671e49518c18686fBryan Mawhinney public void drawViewfinder() { 1637620644768ffc235607b3a94671e49518c18686fBryan Mawhinney Bitmap resultBitmap = this.resultBitmap; 1647620644768ffc235607b3a94671e49518c18686fBryan Mawhinney this.resultBitmap = null; 1657620644768ffc235607b3a94671e49518c18686fBryan Mawhinney if (resultBitmap != null) { 1667620644768ffc235607b3a94671e49518c18686fBryan Mawhinney resultBitmap.recycle(); 1677620644768ffc235607b3a94671e49518c18686fBryan Mawhinney } 1687620644768ffc235607b3a94671e49518c18686fBryan Mawhinney invalidate(); 1697620644768ffc235607b3a94671e49518c18686fBryan Mawhinney } 1707620644768ffc235607b3a94671e49518c18686fBryan Mawhinney 1717620644768ffc235607b3a94671e49518c18686fBryan Mawhinney /** 1727620644768ffc235607b3a94671e49518c18686fBryan Mawhinney * Draw a bitmap with the result points highlighted instead of the live scanning display. 1737620644768ffc235607b3a94671e49518c18686fBryan Mawhinney * 1747620644768ffc235607b3a94671e49518c18686fBryan Mawhinney * @param barcode An image of the decoded barcode. 1757620644768ffc235607b3a94671e49518c18686fBryan Mawhinney */ 1767620644768ffc235607b3a94671e49518c18686fBryan Mawhinney public void drawResultBitmap(Bitmap barcode) { 1777620644768ffc235607b3a94671e49518c18686fBryan Mawhinney resultBitmap = barcode; 1787620644768ffc235607b3a94671e49518c18686fBryan Mawhinney invalidate(); 1797620644768ffc235607b3a94671e49518c18686fBryan Mawhinney } 1807620644768ffc235607b3a94671e49518c18686fBryan Mawhinney 1817620644768ffc235607b3a94671e49518c18686fBryan Mawhinney public void addPossibleResultPoint(ResultPoint point) { 1827620644768ffc235607b3a94671e49518c18686fBryan Mawhinney List<ResultPoint> points = possibleResultPoints; 1837620644768ffc235607b3a94671e49518c18686fBryan Mawhinney synchronized (point) { 1847620644768ffc235607b3a94671e49518c18686fBryan Mawhinney points.add(point); 1857620644768ffc235607b3a94671e49518c18686fBryan Mawhinney int size = points.size(); 1867620644768ffc235607b3a94671e49518c18686fBryan Mawhinney if (size > MAX_RESULT_POINTS) { 1877620644768ffc235607b3a94671e49518c18686fBryan Mawhinney // trim it 1887620644768ffc235607b3a94671e49518c18686fBryan Mawhinney points.subList(0, size - MAX_RESULT_POINTS / 2).clear(); 1897620644768ffc235607b3a94671e49518c18686fBryan Mawhinney } 1907620644768ffc235607b3a94671e49518c18686fBryan Mawhinney } 1917620644768ffc235607b3a94671e49518c18686fBryan Mawhinney } 1927620644768ffc235607b3a94671e49518c18686fBryan Mawhinney 1937620644768ffc235607b3a94671e49518c18686fBryan Mawhinney} 194