Issue 460, auto timeout of CaptureActivity after inactivity, for testing. Also break...
authorsrowen <srowen@59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Fri, 13 Aug 2010 17:18:53 +0000 (17:18 +0000)
committersrowen <srowen@59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Fri, 13 Aug 2010 17:18:53 +0000 (17:18 +0000)
git-svn-id: http://zxing.googlecode.com/svn/trunk@1529 59b500cc-1b3d-0410-9834-0bbf25fbcc57

android/src/com/google/zxing/client/android/CaptureActivity.java
android/src/com/google/zxing/client/android/DecodeFormatManager.java [new file with mode: 0644]
android/src/com/google/zxing/client/android/DecodeThread.java
android/src/com/google/zxing/client/android/FinishListener.java
android/src/com/google/zxing/client/android/InactivityTimer.java [new file with mode: 0644]

index 02437ea..da94c95 100755 (executable)
@@ -68,14 +68,11 @@ import android.widget.Toast;
 
 import java.io.IOException;
 import java.text.DateFormat;
-import java.util.Arrays;
 import java.util.Date;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.Vector;
-import java.util.regex.Pattern;
 
 /**
  * The barcode reader activity itself. This is loosely based on the CameraPreview
@@ -88,8 +85,6 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
 
   private static final String TAG = CaptureActivity.class.getSimpleName();
 
-  private static final Pattern COMMA_PATTERN = Pattern.compile(",");
-
   private static final int SHARE_ID = Menu.FIRST;
   private static final int HISTORY_ID = Menu.FIRST + 1;
   private static final int SETTINGS_ID = Menu.FIRST + 2;
@@ -108,31 +103,6 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
   private static final String RETURN_CODE_PLACEHOLDER = "{CODE}";
   private static final String RETURN_URL_PARAM = "ret";
 
-  static final Vector<BarcodeFormat> PRODUCT_FORMATS;
-  static final Vector<BarcodeFormat> ONE_D_FORMATS;
-  static final Vector<BarcodeFormat> QR_CODE_FORMATS;
-  static final Vector<BarcodeFormat> ALL_FORMATS;
-
-  static {
-    PRODUCT_FORMATS = new Vector<BarcodeFormat>(5);
-    PRODUCT_FORMATS.add(BarcodeFormat.UPC_A);
-    PRODUCT_FORMATS.add(BarcodeFormat.UPC_E);
-    PRODUCT_FORMATS.add(BarcodeFormat.EAN_13);
-    PRODUCT_FORMATS.add(BarcodeFormat.EAN_8);
-    PRODUCT_FORMATS.add(BarcodeFormat.RSS14);
-    ONE_D_FORMATS = new Vector<BarcodeFormat>(PRODUCT_FORMATS.size() + 4);
-    ONE_D_FORMATS.addAll(PRODUCT_FORMATS);
-    ONE_D_FORMATS.add(BarcodeFormat.CODE_39);
-    ONE_D_FORMATS.add(BarcodeFormat.CODE_93);
-    ONE_D_FORMATS.add(BarcodeFormat.CODE_128);
-    ONE_D_FORMATS.add(BarcodeFormat.ITF);
-    QR_CODE_FORMATS = new Vector<BarcodeFormat>(1);
-    QR_CODE_FORMATS.add(BarcodeFormat.QR_CODE);
-    ALL_FORMATS = new Vector<BarcodeFormat>(ONE_D_FORMATS.size() + QR_CODE_FORMATS.size());
-    ALL_FORMATS.addAll(ONE_D_FORMATS);
-    ALL_FORMATS.addAll(QR_CODE_FORMATS);
-  }
-
   private static final Set<ResultMetadataType> DISPLAYABLE_METADATA_TYPES;
   static {
     DISPLAYABLE_METADATA_TYPES = new HashSet<ResultMetadataType>(5);
@@ -167,6 +137,7 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
   private String characterSet;
   private String versionName;
   private HistoryManager historyManager;
+  private InactivityTimer inactivityTimer;
 
   /**
    * When the beep has finished playing, rewind to queue up another one.
@@ -211,6 +182,7 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
     hasSurface = false;
     historyManager = new HistoryManager(this);
     historyManager.trimHistory();
+    inactivityTimer = new InactivityTimer(this);
 
     showHelpOnFirstLaunch();
   }
@@ -239,13 +211,13 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
       if (action.equals(Intents.Scan.ACTION)) {
         // Scan the formats the intent requested, and return the result to the calling activity.
         source = Source.NATIVE_APP_INTENT;
-        decodeFormats = parseDecodeFormats(intent);
+        decodeFormats = DecodeFormatManager.parseDecodeFormats(intent);
       } else if (dataString != null && dataString.contains(PRODUCT_SEARCH_URL_PREFIX) &&
           dataString.contains(PRODUCT_SEARCH_URL_SUFFIX)) {
         // Scan only products and send the result to mobile Product Search.
         source = Source.PRODUCT_SEARCH_LINK;
         sourceUrl = dataString;
-        decodeFormats = PRODUCT_FORMATS;
+        decodeFormats = DecodeFormatManager.PRODUCT_FORMATS;
       } else if (dataString != null && dataString.startsWith(ZXING_URL)) {
         // Scan formats requested in query string (all formats if none specified).
         // If a return URL is specified, send the results there. Otherwise, handle it ourselves.
@@ -253,7 +225,7 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
         sourceUrl = dataString;
         Uri inputUri = Uri.parse(sourceUrl);
         returnUrlTemplate = inputUri.getQueryParameter(RETURN_URL_PARAM);
-        decodeFormats = parseDecodeFormats(inputUri);
+        decodeFormats = DecodeFormatManager.parseDecodeFormats(inputUri);
       } else {
         // Scan all formats and handle the results ourselves (launched from Home).
         source = Source.NONE;
@@ -280,50 +252,6 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
     initBeepSound();
   }
 
-  private static Vector<BarcodeFormat> parseDecodeFormats(Intent intent) {
-    List<String> scanFormats = null;
-    String scanFormatsString = intent.getStringExtra(Intents.Scan.SCAN_FORMATS);
-    if (scanFormatsString != null) {
-      scanFormats = Arrays.asList(COMMA_PATTERN.split(scanFormatsString));
-    }
-    return parseDecodeFormats(scanFormats, intent.getStringExtra(Intents.Scan.MODE));
-  }
-
-  private static Vector<BarcodeFormat> parseDecodeFormats(Uri inputUri) {
-    List<String> formats = inputUri.getQueryParameters(Intents.Scan.SCAN_FORMATS);
-    if (formats != null && formats.size() == 1 && formats.get(0) != null){
-      formats = Arrays.asList(COMMA_PATTERN.split(formats.get(0)));
-    }
-    return parseDecodeFormats(formats, inputUri.getQueryParameter(Intents.Scan.MODE));
-  }
-
-  private static Vector<BarcodeFormat> parseDecodeFormats(List<String> scanFormats,
-                                                          String decodeMode) {
-    if (scanFormats != null) {
-      Vector<BarcodeFormat> formats = new Vector<BarcodeFormat>();
-      try {
-        for (String format : scanFormats) {
-          formats.add(BarcodeFormat.valueOf(format));
-        }
-        return formats;
-      } catch (IllegalArgumentException iae) {
-        // ignore it then
-      }
-    }
-    if (decodeMode != null) {
-      if (Intents.Scan.PRODUCT_MODE.equals(decodeMode)) {
-        return PRODUCT_FORMATS;
-      }
-      if (Intents.Scan.QR_CODE_MODE.equals(decodeMode)) {
-        return QR_CODE_FORMATS;
-      }
-      if (Intents.Scan.ONE_D_MODE.equals(decodeMode)) {
-        return ONE_D_FORMATS;
-      }
-    }
-    return null;
-  }
-
   @Override
   protected void onPause() {
     super.onPause();
@@ -334,6 +262,12 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
     CameraManager.get().closeDriver();
   }
 
+  @Override
+  protected void onDestroy() {
+    inactivityTimer.shutdown();
+    super.onDestroy();
+  }
+
   @Override
   public boolean onKeyDown(int keyCode, KeyEvent event) {
     if (keyCode == KeyEvent.KEYCODE_BACK) {
@@ -449,6 +383,7 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
    * @param barcode   A greyscale bitmap of the camera data which was decoded.
    */
   public void handleDecode(Result rawResult, Bitmap barcode) {
+    inactivityTimer.onActivity();
     lastResult = rawResult;
     historyManager.addHistoryItem(rawResult);
     if (barcode == null) {
diff --git a/android/src/com/google/zxing/client/android/DecodeFormatManager.java b/android/src/com/google/zxing/client/android/DecodeFormatManager.java
new file mode 100644 (file)
index 0000000..bd883fc
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * 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 com.google.zxing.client.android;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Vector;
+import java.util.regex.Pattern;
+
+import android.content.Intent;
+import android.net.Uri;
+import com.google.zxing.BarcodeFormat;
+
+final class DecodeFormatManager {
+
+  private static final Pattern COMMA_PATTERN = Pattern.compile(",");
+
+  static final Vector<BarcodeFormat> PRODUCT_FORMATS;
+  static final Vector<BarcodeFormat> ONE_D_FORMATS;
+  static final Vector<BarcodeFormat> QR_CODE_FORMATS;
+  static final Vector<BarcodeFormat> ALL_FORMATS;
+  static {
+    PRODUCT_FORMATS = new Vector<BarcodeFormat>(5);
+    PRODUCT_FORMATS.add(BarcodeFormat.UPC_A);
+    PRODUCT_FORMATS.add(BarcodeFormat.UPC_E);
+    PRODUCT_FORMATS.add(BarcodeFormat.EAN_13);
+    PRODUCT_FORMATS.add(BarcodeFormat.EAN_8);
+    PRODUCT_FORMATS.add(BarcodeFormat.RSS14);
+    ONE_D_FORMATS = new Vector<BarcodeFormat>(PRODUCT_FORMATS.size() + 4);
+    ONE_D_FORMATS.addAll(PRODUCT_FORMATS);
+    ONE_D_FORMATS.add(BarcodeFormat.CODE_39);
+    ONE_D_FORMATS.add(BarcodeFormat.CODE_93);
+    ONE_D_FORMATS.add(BarcodeFormat.CODE_128);
+    ONE_D_FORMATS.add(BarcodeFormat.ITF);
+    QR_CODE_FORMATS = new Vector<BarcodeFormat>(1);
+    QR_CODE_FORMATS.add(BarcodeFormat.QR_CODE);
+    ALL_FORMATS = new Vector<BarcodeFormat>(ONE_D_FORMATS.size() + QR_CODE_FORMATS.size());
+    ALL_FORMATS.addAll(ONE_D_FORMATS);
+    ALL_FORMATS.addAll(QR_CODE_FORMATS);
+  }
+
+  private DecodeFormatManager() {}
+
+  static Vector<BarcodeFormat> parseDecodeFormats(Intent intent) {
+    List<String> scanFormats = null;
+    String scanFormatsString = intent.getStringExtra(Intents.Scan.SCAN_FORMATS);
+    if (scanFormatsString != null) {
+      scanFormats = Arrays.asList(COMMA_PATTERN.split(scanFormatsString));
+    }
+    return parseDecodeFormats(scanFormats, intent.getStringExtra(Intents.Scan.MODE));
+  }
+
+  static Vector<BarcodeFormat> parseDecodeFormats(Uri inputUri) {
+    List<String> formats = inputUri.getQueryParameters(Intents.Scan.SCAN_FORMATS);
+    if (formats != null && formats.size() == 1 && formats.get(0) != null){
+      formats = Arrays.asList(COMMA_PATTERN.split(formats.get(0)));
+    }
+    return parseDecodeFormats(formats, inputUri.getQueryParameter(Intents.Scan.MODE));
+  }
+
+  private static Vector<BarcodeFormat> parseDecodeFormats(Iterable<String> scanFormats,
+                                                          String decodeMode) {
+    if (scanFormats != null) {
+      Vector<BarcodeFormat> formats = new Vector<BarcodeFormat>();
+      try {
+        for (String format : scanFormats) {
+          formats.add(BarcodeFormat.valueOf(format));
+        }
+        return formats;
+      } catch (IllegalArgumentException iae) {
+        // ignore it then
+      }
+    }
+    if (decodeMode != null) {
+      if (Intents.Scan.PRODUCT_MODE.equals(decodeMode)) {
+        return PRODUCT_FORMATS;
+      }
+      if (Intents.Scan.QR_CODE_MODE.equals(decodeMode)) {
+        return QR_CODE_FORMATS;
+      }
+      if (Intents.Scan.ONE_D_MODE.equals(decodeMode)) {
+        return ONE_D_FORMATS;
+      }
+    }
+    return null;
+  }
+
+}
index 75e5bad..52a5a8d 100755 (executable)
@@ -59,11 +59,11 @@ final class DecodeThread extends Thread {
       boolean decode1D = prefs.getBoolean(PreferencesActivity.KEY_DECODE_1D, true);
       boolean decodeQR = prefs.getBoolean(PreferencesActivity.KEY_DECODE_QR, true);
       if (decode1D && decodeQR) {
-        hints.put(DecodeHintType.POSSIBLE_FORMATS, CaptureActivity.ALL_FORMATS);
+        hints.put(DecodeHintType.POSSIBLE_FORMATS, DecodeFormatManager.ALL_FORMATS);
       } else if (decode1D) {
-        hints.put(DecodeHintType.POSSIBLE_FORMATS, CaptureActivity.ONE_D_FORMATS);
+        hints.put(DecodeHintType.POSSIBLE_FORMATS, DecodeFormatManager.ONE_D_FORMATS);
       } else if (decodeQR) {
-        hints.put(DecodeHintType.POSSIBLE_FORMATS, CaptureActivity.QR_CODE_FORMATS);
+        hints.put(DecodeHintType.POSSIBLE_FORMATS, DecodeFormatManager.QR_CODE_FORMATS);
       }
     } else {
       hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
index da453ee..e8c8813 100644 (file)
@@ -24,7 +24,8 @@ import android.content.DialogInterface;
  *
  * @author Sean Owen
  */
-public final class FinishListener implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
+public final class FinishListener
+    implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener, Runnable {
 
   private final Activity activityToFinish;
 
@@ -33,10 +34,14 @@ public final class FinishListener implements DialogInterface.OnClickListener, Di
   }
 
   public void onCancel(DialogInterface dialogInterface) {
-    activityToFinish.finish();
+    run();
   }
 
   public void onClick(DialogInterface dialogInterface, int i) {
+    run();
+  }
+
+  public void run() {
     activityToFinish.finish();
   }
 
diff --git a/android/src/com/google/zxing/client/android/InactivityTimer.java b/android/src/com/google/zxing/client/android/InactivityTimer.java
new file mode 100644 (file)
index 0000000..01c51eb
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * 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 com.google.zxing.client.android;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+import android.app.Activity;
+
+/**
+ * Finishes an activity after a period of inactivity.
+ */
+final class InactivityTimer {
+
+  private static final int INACTIVITY_DELAY_MINUTES = 3;
+
+  private final ScheduledExecutorService inactivityTimer =
+      Executors.newSingleThreadScheduledExecutor(new DaemonThreadFactory());
+  private final Activity activity;
+  private ScheduledFuture<?> inactivityFuture = null;
+
+  InactivityTimer(Activity activity) {
+    this.activity = activity;
+    onActivity();
+  }
+
+  void onActivity() {
+    cancel();
+    inactivityFuture = inactivityTimer.schedule(new FinishListener(activity),
+                                                INACTIVITY_DELAY_MINUTES,
+                                                TimeUnit.MINUTES);
+  }
+
+  private void cancel() {
+    if (inactivityFuture != null) {
+      inactivityFuture.cancel(true);
+      inactivityFuture = null;
+    }
+  }
+
+  void shutdown() {
+    cancel();
+    inactivityTimer.shutdown();
+  }
+
+  private static final class DaemonThreadFactory implements ThreadFactory {
+    public Thread newThread(Runnable runnable) {
+      Thread thread = new Thread(runnable);
+      thread.setDaemon(true);
+      return thread;
+    }
+  }
+
+}