Draft of 'thinking' visualization for barcode scanning. Works for 1D and QR codes.
[zxing.git] / android / src / com / google / zxing / client / android / ViewfinderView.java
index 3b73753..b972077 100755 (executable)
@@ -24,6 +24,10 @@ import android.graphics.Paint;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.View;
+import com.google.zxing.ResultPoint;
+
+import java.util.Collection;
+import java.util.HashSet;
 
 /**
  * This view is overlaid on top of the camera preview. It adds the viewfinder rectangle and partial
@@ -34,15 +38,18 @@ import android.view.View;
 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 = 100L;
+  private static final int OPAQUE = 0xFF;
 
   private final Paint paint;
-  private final Rect box;
   private Bitmap resultBitmap;
   private final int maskColor;
   private final int resultColor;
   private final int frameColor;
   private final int laserColor;
+  private final int resultPointColor;
   private int scannerAlpha;
+  private Collection<ResultPoint> possibleResultPoints;
+  private Collection<ResultPoint> lastPossibleResultPoints;
 
   // This constructor is used when the class is built from an XML resource.
   public ViewfinderView(Context context, AttributeSet attrs) {
@@ -50,13 +57,14 @@ public final class ViewfinderView extends View {
 
     // Initialize these once for performance rather than calling them every time in onDraw().
     paint = new Paint();
-    box = new Rect();
     Resources resources = getResources();
     maskColor = resources.getColor(R.color.viewfinder_mask);
     resultColor = resources.getColor(R.color.result_view);
     frameColor = resources.getColor(R.color.viewfinder_frame);
     laserColor = resources.getColor(R.color.viewfinder_laser);
+    resultPointColor = resources.getColor(R.color.possible_result_points);
     scannerAlpha = 0;
+    possibleResultPoints = new HashSet<ResultPoint>(5);
   }
 
   @Override
@@ -70,42 +78,55 @@ public final class ViewfinderView extends View {
 
     // Draw the exterior (i.e. outside the framing rect) darkened
     paint.setColor(resultBitmap != null ? resultColor : maskColor);
-    box.set(0, 0, width, frame.top);
-    canvas.drawRect(box, paint);
-    box.set(0, frame.top, frame.left, frame.bottom + 1);
-    canvas.drawRect(box, paint);
-    box.set(frame.right + 1, frame.top, width, frame.bottom + 1);
-    canvas.drawRect(box, paint);
-    box.set(0, frame.bottom + 1, width, height);
-    canvas.drawRect(box, paint);
+    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(255);
+      paint.setAlpha(OPAQUE);
       canvas.drawBitmap(resultBitmap, frame.left, frame.top, paint);
     } else {
+
       // Draw a two pixel solid black border inside the framing rect
       paint.setColor(frameColor);
-      box.set(frame.left, frame.top, frame.right + 1, frame.top + 2);
-      canvas.drawRect(box, paint);
-      box.set(frame.left, frame.top + 2, frame.left + 2, frame.bottom - 1);
-      canvas.drawRect(box, paint);
-      box.set(frame.right - 1, frame.top, frame.right + 1, frame.bottom - 1);
-      canvas.drawRect(box, paint);
-      box.set(frame.left, frame.bottom - 1, frame.right + 1, frame.bottom + 1);
-      canvas.drawRect(box, paint);
+      canvas.drawRect(frame.left, frame.top, frame.right + 1, frame.top + 2, paint);
+      canvas.drawRect(frame.left, frame.top + 2, frame.left + 2, frame.bottom - 1, paint);
+      canvas.drawRect(frame.right - 1, frame.top, frame.right + 1, frame.bottom - 1, paint);
+      canvas.drawRect(frame.left, frame.bottom - 1, frame.right + 1, frame.bottom + 1, paint);
 
       // 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;
-      box.set(frame.left + 2, middle - 1, frame.right - 1, middle + 2);
-      canvas.drawRect(box, paint);
+      canvas.drawRect(frame.left + 2, middle - 1, frame.right - 1, middle + 2, paint);
+
+      Collection<ResultPoint> currentPossible = possibleResultPoints;
+      Collection<ResultPoint> currentLast = lastPossibleResultPoints;
+      if (currentPossible.isEmpty()) {
+        lastPossibleResultPoints = null;
+      } else {
+        possibleResultPoints = new HashSet<ResultPoint>(5);
+        lastPossibleResultPoints = currentPossible;
+        paint.setAlpha(OPAQUE);
+        paint.setColor(resultPointColor);
+        for (ResultPoint point : currentPossible) {
+          canvas.drawCircle(frame.left + point.getX(), frame.top + point.getY(), 6.0f, paint);
+        }
+      }
+      if (currentLast != null) {
+        paint.setAlpha(OPAQUE / 2);
+        paint.setColor(resultPointColor);
+        for (ResultPoint point : currentLast) {
+          canvas.drawCircle(frame.left + point.getX(), frame.top + point.getY(), 3.0f, paint);
+        }
+      }
 
       // Request another update at the animation interval, but only repaint the laser line,
       // not the entire viewfinder mask.
-      postInvalidateDelayed(ANIMATION_DELAY, box.left, box.top, box.right, box.bottom);
+      postInvalidateDelayed(ANIMATION_DELAY, frame.left, frame.top, frame.right, frame.bottom);
     }
   }
 
@@ -123,4 +144,9 @@ public final class ViewfinderView extends View {
     resultBitmap = barcode;
     invalidate();
   }
+
+  public void addPossibleResultPoint(ResultPoint point) {
+    possibleResultPoints.add(point);
+  }
+
 }