diff --git a/android/src/com/google/zxing/client/android/CaptureActivity.java b/android/src/com/google/zxing/client/android/CaptureActivity.java index 02437ea9a..da94c95ee 100755 --- a/android/src/com/google/zxing/client/android/CaptureActivity.java +++ b/android/src/com/google/zxing/client/android/CaptureActivity.java @@ -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 PRODUCT_FORMATS; - static final Vector ONE_D_FORMATS; - static final Vector QR_CODE_FORMATS; - static final Vector ALL_FORMATS; - - static { - PRODUCT_FORMATS = new Vector(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(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(1); - QR_CODE_FORMATS.add(BarcodeFormat.QR_CODE); - ALL_FORMATS = new Vector(ONE_D_FORMATS.size() + QR_CODE_FORMATS.size()); - ALL_FORMATS.addAll(ONE_D_FORMATS); - ALL_FORMATS.addAll(QR_CODE_FORMATS); - } - private static final Set DISPLAYABLE_METADATA_TYPES; static { DISPLAYABLE_METADATA_TYPES = new HashSet(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 parseDecodeFormats(Intent intent) { - List 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 parseDecodeFormats(Uri inputUri) { - List 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 parseDecodeFormats(List scanFormats, - String decodeMode) { - if (scanFormats != null) { - Vector formats = new Vector(); - 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 index 000000000..bd883fc88 --- /dev/null +++ b/android/src/com/google/zxing/client/android/DecodeFormatManager.java @@ -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 PRODUCT_FORMATS; + static final Vector ONE_D_FORMATS; + static final Vector QR_CODE_FORMATS; + static final Vector ALL_FORMATS; + static { + PRODUCT_FORMATS = new Vector(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(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(1); + QR_CODE_FORMATS.add(BarcodeFormat.QR_CODE); + ALL_FORMATS = new Vector(ONE_D_FORMATS.size() + QR_CODE_FORMATS.size()); + ALL_FORMATS.addAll(ONE_D_FORMATS); + ALL_FORMATS.addAll(QR_CODE_FORMATS); + } + + private DecodeFormatManager() {} + + static Vector parseDecodeFormats(Intent intent) { + List 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 parseDecodeFormats(Uri inputUri) { + List 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 parseDecodeFormats(Iterable scanFormats, + String decodeMode) { + if (scanFormats != null) { + Vector formats = new Vector(); + 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; + } + +} diff --git a/android/src/com/google/zxing/client/android/DecodeThread.java b/android/src/com/google/zxing/client/android/DecodeThread.java index 75e5bad74..52a5a8d75 100755 --- a/android/src/com/google/zxing/client/android/DecodeThread.java +++ b/android/src/com/google/zxing/client/android/DecodeThread.java @@ -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); diff --git a/android/src/com/google/zxing/client/android/FinishListener.java b/android/src/com/google/zxing/client/android/FinishListener.java index da453ee6b..e8c881348 100644 --- a/android/src/com/google/zxing/client/android/FinishListener.java +++ b/android/src/com/google/zxing/client/android/FinishListener.java @@ -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 index 000000000..01c51ebe1 --- /dev/null +++ b/android/src/com/google/zxing/client/android/InactivityTimer.java @@ -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; + } + } + +}