Slightly smarter version of last change
[zxing.git] / javame / src / com / google / zxing / client / j2me / SnapshotThread.java
index b24eb78..3777be9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2007 ZXing authors
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -28,72 +28,113 @@ import javax.microedition.media.Player;
 import javax.microedition.media.control.VideoControl;
 
 /**
- * @author Sean Owen (srowen@google.com)
+ * Thread which does the work of capturing a frame and decoding it.
+ *
+ * @author Sean Owen
  */
-final class SnapshotThread extends Thread {
-
-  private static SnapshotThread currentThread;
+final class SnapshotThread implements Runnable {
 
   private final ZXingMIDlet zXingMIDlet;
+  private final Object waitLock;
+  private volatile boolean done;
+  private final MultimediaManager multimediaManager;
+  private String bestEncoding;
 
   SnapshotThread(ZXingMIDlet zXingMIDlet) {
     this.zXingMIDlet = zXingMIDlet;
+    waitLock = new Object();
+    done = false;
+    multimediaManager = ZXingMIDlet.buildMultimediaManager();
+  }
+
+  void continueRun() {
+    synchronized (waitLock) {
+      waitLock.notifyAll();
+    }
   }
 
-  static synchronized void startThread(ZXingMIDlet zXingMIDlet) {
-    if (currentThread == null) {
-      currentThread = new SnapshotThread(zXingMIDlet);
-      currentThread.start();
+  private void waitForSignal() {
+    synchronized (waitLock) {
+      try {
+        waitLock.wait();
+      } catch (InterruptedException ie) {
+        // continue
+      }
     }
   }
 
+  void stop() {
+    done = true;
+    continueRun();
+  }
+
   public void run() {
     Player player = zXingMIDlet.getPlayer();
+    do {
+      waitForSignal();
+      try {
+        multimediaManager.setFocus(player);
+        byte[] snapshot = takeSnapshot();
+        Image capturedImage = Image.createImage(snapshot, 0, snapshot.length);
+        MonochromeBitmapSource source = new LCDUIImageMonochromeBitmapSource(capturedImage);
+        Reader reader = new MultiFormatReader();
+        Result result = reader.decode(source);
+        zXingMIDlet.handleDecodedText(result);
+      } catch (ReaderException re) {
+        // Show a friendlier message on a mere failure to read the barcode
+        zXingMIDlet.showError("Sorry, no barcode was found.");
+      } catch (MediaException me) {
+        zXingMIDlet.showError(me);
+      } catch (RuntimeException re) {
+        zXingMIDlet.showError(re);
+      }
+    } while (!done);
+  }
+
+  private byte[] takeSnapshot() throws MediaException {
+
+    String bestEncoding = guessBestEncoding();
+
+    VideoControl videoControl = zXingMIDlet.getVideoControl();
+    byte[] snapshot = null;
     try {
-      AdvancedMultimediaManager.setFocus(player);
-           try {
-             player.stop();
-           } catch (MediaException me) {
-                   // continue
-           }
-           byte[] snapshot = takeSnapshot();
-      Image capturedImage = Image.createImage(snapshot, 0, snapshot.length);
-      MonochromeBitmapSource source = new LCDUIImageMonochromeBitmapSource(capturedImage);
-      Reader reader = new MultiFormatReader();
-      Result result = reader.decode(source);
-      zXingMIDlet.handleDecodedText(result.getText());
-    } catch (ReaderException re) {
-           // Show a friendlier message on a mere failure to read the barcode
-           zXingMIDlet.showError("No barcode was detected in this image. Try again.");
-    } catch (Throwable t) {
-      zXingMIDlet.showError(t);
-    } finally {
-           try {
-             player.start();
-           } catch (MediaException me) {
-                   // continue
-           }
-      currentThread = null;
+      snapshot = videoControl.getSnapshot("".equals(bestEncoding) ? null : bestEncoding);
+    } catch (MediaException me) {
     }
-
+    if (snapshot == null) {
+      // Fall back on JPEG; seems that some cameras default to PNG even
+      // when PNG isn't supported!
+      snapshot = videoControl.getSnapshot("encoding=jpeg");
+      if (snapshot == null) {
+        throw new MediaException("Can't obtain a snapshot");
+      }
+    }
+    return snapshot;
   }
 
-       private byte[] takeSnapshot() throws MediaException {
-               VideoControl videoControl = zXingMIDlet.getVideoControl();
-               byte[] snapshot = null;
-               try {
-           snapshot = videoControl.getSnapshot(null);
-               } catch (MediaException me) {
-               }
-               if (snapshot == null) {
-                       // Fall back on JPEG; seems that some cameras default to PNG even
-                       // when PNG isn't supported!
-                       snapshot = videoControl.getSnapshot("encoding=jpeg");
-                       if (snapshot == null) {
-                               throw new MediaException("Can't obtain a snapshot");
-                       }
-               }
-               return snapshot;
-       }
+  private synchronized String guessBestEncoding() throws MediaException {
+    if (bestEncoding == null) {
+      // Check this property, present on some Nokias?
+      String supportsVideoCapture = System.getProperty("supports.video.capture");
+      if ("false".equals(supportsVideoCapture)) {
+        throw new MediaException("supports.video.capture is false");
+      }
+
+      bestEncoding = "";
+      String videoSnapshotEncodings = System.getProperty("video.snapshot.encodings");
+      if (videoSnapshotEncodings != null) {
+        // We know explicitly what the camera supports; see if PNG is among them since
+        // Image.createImage() should always support it
+        int pngEncodingStart = videoSnapshotEncodings.indexOf("encoding=png");
+        if (pngEncodingStart >= 0) {
+          int space = videoSnapshotEncodings.indexOf(' ', pngEncodingStart);
+          bestEncoding = space >= 0 ?
+              videoSnapshotEncodings.substring(pngEncodingStart, space) :
+              videoSnapshotEncodings.substring(pngEncodingStart);
+        }
+      }
+    }
+    return bestEncoding;
+  }
 
 }