Branching the current Android client into an M3 directory so we can proceed with M5 development.

git-svn-id: https://zxing.googlecode.com/svn/trunk@319 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
dswitkin 2008-03-28 15:25:18 +00:00
parent 683a78256d
commit 0198f58150
14 changed files with 1543 additions and 0 deletions

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.zxing.client.android">
<application android:icon="@drawable/icon">
<activity class=".BarcodeReaderCaptureActivity" android:label="@string/app_name">
<intent-filter>
<action android:value="android.intent.action.MAIN"/>
<category android:value="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
<uses-permission id="android.permission.READ_CONTACTS"/>
<uses-permission id="android.permission.WRITE_CONTACTS"/>
</manifest>

265
android-m3/build.xml Normal file
View file

@ -0,0 +1,265 @@
<?xml version="1.0" ?>
<!--
Copyright (C) 2008 Google Inc.
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.
-->
<!-- This is a mildly hacked version of the auto-generated project
build.xml file. -->
<project name="BarcodeReader" default="package">
<property file="../build.properties"/>
<property name="sdk-folder" value="${android-home}"/>
<property name="android-tools" value="${sdk-folder}/tools"/>
<!-- The intermediates directory -->
<!-- Eclipse uses "bin" for its own output, so we do the same. -->
<property name="outdir" value="bin"/>
<!-- No user servicable parts below. -->
<!-- Input directories -->
<property name="resource-dir" value="res"/>
<property name="asset-dir" value="assets"/>
<property name="srcdir" value="src"/>
<!-- Output directories -->
<property name="outdir-classes" value="${outdir}/classes"/>
<!-- Create R.java in the source directory -->
<property name="outdir-r" value="src"/>
<!-- Intermediate files -->
<property name="dex-file" value="classes.dex"/>
<property name="intermediate-dex" value="${outdir}/${dex-file}"/>
<!-- The final package file to generate -->
<property name="out-package" value="${outdir}/${ant.project.name}.apk"/>
<!-- Tools -->
<property name="aapt" value="${android-tools}/aapt"/>
<property name="aidl" value="${android-tools}/aidl"/>
<property name="dx" value="${android-tools}/dx"/>
<property name="zip" value="zip"/>
<property name="android-jar" value="${sdk-folder}/android.jar"/>
<!-- Rules -->
<target name="init">
<tstamp/>
<fail message="Please set 'android-home' in build.properties">
<condition>
<not>
<available file="${android-home}" type="dir"/>
</not>
</condition>
</fail>
<fail message="Please build 'core' first">
<condition>
<not>
<available file="../core/core.jar" type="file"/>
</not>
</condition>
</fail>
<fail message="Please put proguard.jar in 'bin' under the WTK install directory">
<condition>
<and>
<not>
<isset property="debug"/>
</not>
<not>
<available file="${WTK-home}/bin/proguard.jar" type="file"/>
</not>
</and>
</condition>
</fail>
</target>
<!-- Create the output directories if they don't exist yet. -->
<target name="dirs">
<mkdir dir="${outdir}"/>
<mkdir dir="${outdir-classes}"/>
</target>
<!-- Generate the R.java file for this project's resources. -->
<target name="resource-src" depends="dirs">
<copy file="strings.xml.template" tofile="res/values/strings.xml" overwrite="true">
<filterset>
<filter token="VERSION" value="${version}"/>
</filterset>
</copy>
<echo>Generating R.java...</echo>
<exec executable="${aapt}" failonerror="true">
<arg value="compile"/>
<arg value="-m"/>
<arg value="-J"/>
<arg value="${outdir-r}"/>
<arg value="-M"/>
<arg value="AndroidManifest.xml"/>
<arg value="-S"/>
<arg value="${resource-dir}"/>
<arg value="-I"/>
<arg value="${android-jar}"/>
</exec>
</target>
<!-- Generate java classes from .aidl files. -->
<target name="aidl" depends="dirs">
<apply executable="${aidl}" failonerror="true">
<fileset dir="${srcdir}">
<include name="**/*.aidl"/>
</fileset>
</apply>
</target>
<!-- Compile this project's .java files into .class files. -->
<target name="compile" depends="init, clean, dirs, resource-src, aidl">
<javac encoding="ascii" target="1.5" debug="true" optimize="true" extdirs=""
srcdir="."
destdir="${outdir-classes}"
bootclasspath="${android-jar}">
<classpath>
<pathelement location="../core/core.jar"/>
</classpath>
</javac>
<unzip src="../core/core.jar" dest="${outdir-classes}"/>
</target>
<target name="optimize" depends="compile" unless="debug">
<jar basedir="${outdir-classes}" destfile="temp.jar"/>
<java jar="${WTK-home}/bin/proguard.jar" fork="true" failonerror="true">
<jvmarg value="-Dmaximum.inlined.code.length=32"/>
<arg value="-injars temp.jar"/>
<arg value="-outjars optimized.jar"/>
<arg value="-libraryjars ${android-jar}"/>
<arg value="-dontpreverify"/>
<arg value="-dontobfuscate"/>
<arg value="-keep public class com.google.zxing.client.android.BarcodeReaderCaptureActivity"/>
<arg value="-optimizationpasses 7"/>
<arg value="-overloadaggressively"/>
<arg value="-verbose"/>
</java>
<delete file="temp.jar"/>
<delete dir="${outdir-classes}"/>
<mkdir dir="${outdir-classes}"/>
<unzip src="optimized.jar" dest="${outdir-classes}"/>
<delete file="optimized.jar"/>
</target>
<!-- Convert this project's .class files into .dex files. -->
<target name="dex" depends="compile, optimize">
<condition property="locals" value="full">
<isset property="debug"/>
</condition>
<condition property="locals" value="none">
<not>
<isset property="debug"/>
</not>
</condition>
<condition property="positions" value="lines">
<isset property="debug"/>
</condition>
<condition property="positions" value="none">
<not>
<isset property="debug"/>
</not>
</condition>
<exec executable="${dx}" failonerror="true">
<arg value="-JXmx384M"/>
<arg value="--dex"/>
<arg value="--output=${intermediate-dex}"/>
<arg value="--locals=${locals}"/>
<arg value="--positions=${positions}"/>
<arg path="${outdir-classes}"/>
</exec>
</target>
<!-- Put the project's resources into the output package file. -->
<target name="package-res-and-assets">
<echo>Packaging resources and assets...</echo>
<exec executable="${aapt}" failonerror="true">
<arg value="package"/>
<arg value="-f"/>
<arg value="-c"/>
<arg value="-M"/>
<arg value="AndroidManifest.xml"/>
<arg value="-S"/>
<arg value="${resource-dir}"/>
<arg value="-A"/>
<arg value="${asset-dir}"/>
<arg value="-I"/>
<arg value="${android-jar}"/>
<arg value="${out-package}"/>
</exec>
</target>
<!-- Same as package-res-and-assets, but without "-A ${asset-dir}" -->
<target name="package-res-no-assets">
<echo>Packaging resources...</echo>
<exec executable="${aapt}" failonerror="true">
<arg value="package"/>
<arg value="-f"/>
<arg value="-c"/>
<arg value="-M"/>
<arg value="AndroidManifest.xml"/>
<arg value="-S"/>
<arg value="${resource-dir}"/>
<!-- No assets directory -->
<arg value="-I"/>
<arg value="${android-jar}"/>
<arg value="${out-package}"/>
</exec>
</target>
<!-- Invoke the proper target depending on whether or not
an assets directory is present. -->
<!-- TODO: find a nicer way to include the "-A ${asset-dir}" argument
only when the assets dir exists. -->
<target name="package-res">
<available file="${asset-dir}" type="dir"
property="res-target" value="and-assets"/>
<property name="res-target" value="no-assets"/>
<antcall target="package-res-${res-target}"/>
</target>
<!-- Put the project's .class files into the output package file. -->
<target name="package-java" depends="compile, package-res">
<echo>Packaging java...</echo>
<jar destfile="${out-package}"
basedir="${outdir-classes}"
update="true"/>
</target>
<!-- Put the project's .dex files into the output package file. -->
<target name="package-dex" depends="dex, package-res">
<echo>Packaging dex...</echo>
<exec executable="${zip}" failonerror="true">
<arg value="-qj"/>
<arg value="${out-package}"/>
<arg value="${intermediate-dex}"/>
</exec>
</target>
<!-- Create the package file for this project from the sources. -->
<target name="package" depends="package-dex"/>
<target name="clean">
<delete dir="${outdir}"/>
</target>
</project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (C) 2008 Google Inc.
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (C) 2008 Google Inc.
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.
-->
<resources>
<item type="id" name="decoding_succeeded_message"/>
<item type="id" name="decoding_failed_message"/>
</resources>

View file

@ -0,0 +1,82 @@
/*
* Copyright 2007 Google Inc.
*
* 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 android.graphics.Matrix;
import com.google.zxing.MonochromeBitmapSource;
import com.google.zxing.ReaderException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.GridSampler;
/**
* Implementation based on Android's
* {@link Matrix#setPolyToPoly(float[], int, float[], int, int)}
* class, which should offer faster performance for these matrix
* operations.
*
* @author srowen@google.com (Sean Owen)
*/
public final class AndroidGraphicsGridSampler extends GridSampler {
@Override
public BitMatrix sampleGrid(MonochromeBitmapSource image,
int dimension,
float p1ToX, float p1ToY,
float p2ToX, float p2ToY,
float p3ToX, float p3ToY,
float p4ToX, float p4ToY,
float p1FromX, float p1FromY,
float p2FromX, float p2FromY,
float p3FromX, float p3FromY,
float p4FromX, float p4FromY) throws ReaderException {
Matrix transformMatrix = new Matrix();
boolean succeeded = transformMatrix.setPolyToPoly(
new float[] { p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY },
0,
new float[] { p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY },
0,
4
);
if (!succeeded) {
throw new ReaderException("Could not establish transformation matrix");
}
BitMatrix bits = new BitMatrix(dimension);
float[] points = new float[dimension << 1];
for (int i = 0; i < dimension; i++) {
int max = points.length;
float iValue = (float) i + 0.5f;
for (int j = 0; j < max; j += 2) {
points[j] = (float) (j >> 1) + 0.5f;
points[j + 1] = iValue;
}
transformMatrix.mapPoints(points);
// Quick check to see if points transformed to something inside the image;
// sufficent to check the endpoints
checkAndNudgePoints(image, points);
for (int j = 0; j < max; j += 2) {
if (image.isBlack((int) points[j], (int) points[j + 1])) {
// Black(-ish) pixel
bits.set(i, j >> 1);
}
}
}
return bits;
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright 2008 Google Inc.
*
* 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 android.content.Intent;
import com.google.zxing.client.result.ParsedReaderResult;
import com.google.zxing.client.result.ParsedReaderResultType;
import java.net.URISyntaxException;
/**
* A {@link ParsedReaderResult} derived from a URI that encodes an Android
* {@link Intent}, and which should presumably trigger that intent on Android.
*
* @author srowen@google.com (Sean Owen)
*/
public final class AndroidIntentParsedResult extends ParsedReaderResult {
private final Intent intent;
private AndroidIntentParsedResult(Intent intent) {
super(ParsedReaderResultType.ANDROID_INTENT);
this.intent = intent;
}
public static AndroidIntentParsedResult parse(String rawText) {
try {
return new AndroidIntentParsedResult(Intent.getIntent(rawText));
} catch (URISyntaxException urise) {
return null;
} catch (IllegalArgumentException iae) {
return null;
}
}
public Intent getIntent() {
return intent;
}
@Override
public String getDisplayResult() {
return intent.toString();
}
}

View file

@ -0,0 +1,217 @@
/*
* Copyright (C) 2008 Google Inc.
*
* 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 android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.Window;
import android.view.WindowManager.LayoutParams;
import com.google.zxing.Result;
import com.google.zxing.ResultPoint;
import com.google.zxing.client.result.ParsedReaderResult;
import com.google.zxing.client.result.ParsedReaderResultType;
/**
* The barcode reader activity itself. This is loosely based on the CameraPreview
* example included in the Android SDK.
*
* @author dswitkin@google.com (Daniel Switkin)
* @author Android Team (for CameraPreview example)
*/
public final class BarcodeReaderCaptureActivity extends Activity {
private CameraManager cameraManager;
private CameraSurfaceView surfaceView;
private WorkerThread workerThread;
private static final int ABOUT_ID = Menu.FIRST;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
requestWindowFeature(Window.FEATURE_NO_TITLE);
// Make sure to create a TRANSLUCENT window. This is required for SurfaceView to work.
// Eventually this'll be done by the system automatically.
getWindow().setAttributes(new LayoutParams(LayoutParams.APPLICATION_TYPE,
LayoutParams.NO_STATUS_BAR_FLAG));
getWindow().setFormat(PixelFormat.TRANSLUCENT);
cameraManager = new CameraManager(getApplication());
surfaceView = new CameraSurfaceView(getApplication(), cameraManager);
setContentView(surfaceView);
workerThread = new WorkerThread(surfaceView, cameraManager, messageHandler);
workerThread.requestPreviewLoop();
workerThread.start();
// TODO re-enable this when issues with Matrix.setPolyToPoly() are resolved
//GridSampler.setGridSampler(new AndroidGraphicsGridSampler());
}
@Override
protected boolean isFullscreenOpaque() {
// Our main window is set to translucent, but we know that we will
// fill it with opaque data. Tell the system that so it can perform
// some important optimizations.
return true;
}
@Override
protected void onResume() {
super.onResume();
cameraManager.openDriver();
if (workerThread == null) {
workerThread = new WorkerThread(surfaceView, cameraManager, messageHandler);
workerThread.requestPreviewLoop();
workerThread.start();
}
}
@Override
protected void onPause() {
super.onPause();
if (workerThread != null) {
workerThread.requestExitAndWait();
workerThread = null;
}
cameraManager.closeDriver();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
workerThread.requestStillAndDecode();
return true;
} else {
return super.onKeyDown(keyCode, event);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(0, ABOUT_ID, R.string.menu_about);
return true;
}
@Override
public boolean onOptionsItemSelected(Menu.Item item) {
switch (item.getId()) {
case ABOUT_ID:
Context context = getApplication();
showAlert(context.getString(R.string.title_about),
context.getString(R.string.msg_about),
context.getString(R.string.button_ok), null, true, null);
break;
}
return super.onOptionsItemSelected(item);
}
private final Handler messageHandler = new Handler() {
@Override
public void handleMessage(Message message) {
switch (message.what) {
case R.id.decoding_succeeded_message:
handleDecode((Result) message.obj);
break;
case R.id.decoding_failed_message:
Context context = getApplication();
showAlert(context.getString(R.string.title_no_barcode_detected),
context.getString(R.string.msg_no_barcode_detected),
context.getString(R.string.button_ok), null, true, null);
break;
}
}
};
public void restartPreview() {
workerThread.requestPreviewLoop();
}
// TODO(dswitkin): These deprecated showAlert calls need to be updated.
private void handleDecode(Result rawResult) {
ResultPoint[] points = rawResult.getResultPoints();
if (points != null && points.length > 0) {
surfaceView.drawResultPoints(points);
}
Context context = getApplication();
ParsedReaderResult readerResult = parseReaderResult(rawResult);
ResultHandler handler = new ResultHandler(this, readerResult);
if (handler.getIntent() != null) {
// Can be handled by some external app; ask if the user wants to
// proceed first though
Message yesMessage = handler.obtainMessage(R.string.button_yes);
Message noMessage = handler.obtainMessage(R.string.button_no);
showAlert(context.getString(getDialogTitleID(readerResult.getType())),
readerResult.getDisplayResult(), context.getString(R.string.button_yes),
yesMessage, context.getString(R.string.button_no), noMessage, true, noMessage);
} else {
// Just show information to user
Message okMessage = handler.obtainMessage(R.string.button_ok);
showAlert(context.getString(R.string.title_barcode_detected),
readerResult.getDisplayResult(), context.getString(R.string.button_ok), okMessage, null,
null, true, okMessage);
}
}
private static ParsedReaderResult parseReaderResult(Result rawResult) {
ParsedReaderResult readerResult = ParsedReaderResult.parseReaderResult(rawResult);
if (readerResult.getType().equals(ParsedReaderResultType.TEXT)) {
String rawText = rawResult.getText();
AndroidIntentParsedResult androidResult = AndroidIntentParsedResult.parse(rawText);
if (androidResult != null) {
Intent intent = androidResult.getIntent();
if (!Intent.VIEW_ACTION.equals(intent.getAction())) {
// For now, don't take anything that just parses as a View action. A lot
// of things are accepted as a View action by default.
readerResult = androidResult;
}
}
}
return readerResult;
}
private static int getDialogTitleID(ParsedReaderResultType type) {
if (type.equals(ParsedReaderResultType.ADDRESSBOOK)) {
return R.string.title_add_contact;
} else if (type.equals(ParsedReaderResultType.URI) ||
type.equals(ParsedReaderResultType.BOOKMARK) ||
type.equals(ParsedReaderResultType.URLTO)) {
return R.string.title_open_url;
} else if (type.equals(ParsedReaderResultType.EMAIL) ||
type.equals(ParsedReaderResultType.EMAIL_ADDRESS)) {
return R.string.title_compose_email;
} else if (type.equals(ParsedReaderResultType.UPC)) {
return R.string.title_lookup_barcode;
} else if (type.equals(ParsedReaderResultType.TEL)) {
return R.string.title_dial;
} else if (type.equals(ParsedReaderResultType.GEO)) {
return R.string.title_view_maps;
} else {
return R.string.title_barcode_detected;
}
}
}

View file

@ -0,0 +1,246 @@
/*
* Copyright (C) 2008 Google Inc.
*
* 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 android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.CameraDevice;
import android.util.Log;
import android.view.Display;
import android.view.WindowManager;
import com.google.zxing.ResultPoint;
/**
* This object wraps the CameraDevice and expects to be the only one talking to it. The
* implementation encapsulates the steps needed to take preview-sized images and well as high
* resolution stills.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
final class CameraManager {
private static final String TAG = "CameraManager";
private final Context context;
private Point cameraResolution;
private Point stillResolution;
private int stillMultiplier;
private Point screenResolution;
private Rect framingRect;
private final Bitmap bitmap;
private CameraDevice camera;
private final CameraDevice.CaptureParams params;
private boolean previewMode;
CameraManager(Context context) {
this.context = context;
calculateStillResolution();
getScreenResolution();
bitmap = Bitmap.createBitmap(stillResolution.x, stillResolution.y, false);
camera = CameraDevice.open();
params = new CameraDevice.CaptureParams();
previewMode = false;
setPreviewMode(true);
}
public void openDriver() {
if (camera == null) {
camera = CameraDevice.open();
}
}
public void closeDriver() {
if (camera != null) {
camera.close();
camera = null;
}
}
public void capturePreview(Canvas canvas) {
setPreviewMode(true);
camera.capture(canvas);
}
public Bitmap captureStill() {
setPreviewMode(false);
Canvas canvas = new Canvas(bitmap);
camera.capture(canvas);
return bitmap;
}
/**
* Calculates the framing rect which the UI should draw to show the user where to place the
* barcode. The actual captured image should be a bit larger than indicated because they might
* frame the shot too tightly. This target helps with alignment as well as forces the user to hold
* the device far enough away to ensure the image will be in focus.
*
* @return The rectangle to draw on screen in window coordinates.
*/
public Rect getFramingRect() {
if (framingRect == null) {
int size = stillResolution.x * screenResolution.x / cameraResolution.x;
int leftOffset = (screenResolution.x - size) / 2;
int topOffset = (screenResolution.y - size) / 2;
framingRect = new Rect(leftOffset, topOffset, leftOffset + size, topOffset + size);
}
return framingRect;
}
/**
* Converts the result points from still resolution coordinates to screen coordinates.
*
* @param points The points returned by the Reader subclass through Result.getResultPoints().
* @return An array of Points scaled to the size of the framing rect and offset appropriately
* so they can be drawn in screen coordinates.
*/
public Point[] convertResultPoints(ResultPoint[] points) {
Rect frame = getFramingRect();
int frameSize = frame.width();
int count = points.length;
Point[] output = new Point[count];
for (int x = 0; x < count; x++) {
output[x] = new Point();
output[x].x = frame.left + (int) (points[x].getX() * frameSize / stillResolution.x + 0.5f);
output[x].y = frame.top + (int) (points[x].getY() * frameSize / stillResolution.y + 0.5f);
}
return output;
}
/**
* Images for the live preview are taken at low resolution in RGB. The final stills for the
* decoding step are taken in YUV, since we only need the luminance channel. Other code depends
* on the ability to call this method for free if the correct mode is already set.
*
* @param on Setting on true will engage preview mode, setting it false will request still mode.
*/
private void setPreviewMode(boolean on) {
if (on != previewMode) {
if (on) {
params.type = 1; // preview
if (cameraResolution.x / (float) cameraResolution.y <
screenResolution.x / (float) screenResolution.y) {
params.srcWidth = cameraResolution.x;
params.srcHeight = cameraResolution.x * screenResolution.y / screenResolution.x;
params.leftPixel = 0;
params.topPixel = (cameraResolution.y - params.srcHeight) / 2;
} else {
params.srcWidth = cameraResolution.y * screenResolution.x / screenResolution.y;
params.srcHeight = cameraResolution.y;
params.leftPixel = (cameraResolution.x - params.srcWidth) / 2;
params.topPixel = 0;
}
params.outputWidth = screenResolution.x;
params.outputHeight = screenResolution.y;
params.dataFormat = 2; // RGB565
} else {
params.type = 0; // still
params.srcWidth = stillResolution.x * stillMultiplier;
params.srcHeight = stillResolution.y * stillMultiplier;
params.leftPixel = (cameraResolution.x - params.srcWidth) / 2;
params.topPixel = (cameraResolution.y - params.srcHeight) / 2;
params.outputWidth = stillResolution.x;
params.outputHeight = stillResolution.y;
params.dataFormat = 2; // RGB565
}
String captureType = on ? "preview" : "still";
Log.v(TAG, "Setting params for " + captureType + ": srcWidth " + params.srcWidth +
" srcHeight " + params.srcHeight + " leftPixel " + params.leftPixel + " topPixel " +
params.topPixel + " outputWidth " + params.outputWidth + " outputHeight " +
params.outputHeight);
camera.setCaptureParams(params);
previewMode = on;
}
}
/**
* This method determines how to take the highest quality image (i.e. the one which has the best
* chance of being decoded) given the capabilities of the camera. It is a balancing act between
* having enough resolution to read UPCs and having few enough pixels to keep the QR Code
* processing fast. The result is the dimensions of the rectangle to capture from the center of
* the sensor, plus a stillMultiplier which indicates whether we'll ask the driver to downsample
* for us. This has the added benefit of keeping the memory footprint of the bitmap as small as
* possible.
*/
private void calculateStillResolution() {
cameraResolution = getMaximumCameraResolution();
int minDimension = (cameraResolution.x < cameraResolution.y) ? cameraResolution.x :
cameraResolution.y;
int diagonalResolution = (int) Math.sqrt(cameraResolution.x * cameraResolution.x +
cameraResolution.y * cameraResolution.y);
float diagonalFov = getFieldOfView();
// Determine the field of view in the smaller dimension, then calculate how large an object
// would be at the minimum focus distance.
float fov = diagonalFov * minDimension / diagonalResolution;
double objectSize = Math.tan(Math.toRadians(fov / 2.0)) * getMinimumFocusDistance() * 2;
// Let's assume the largest barcode we might photograph at this distance is 3 inches across. By
// cropping to this size, we can avoid processing surrounding pixels, which helps with speed and
// accuracy.
// TODO(dswitkin): Handle a device with a great macro mode where objectSize < 4 inches.
double crop = 3.0 / objectSize;
int nativeResolution = (int) (minDimension * crop);
// The camera driver can only capture images which are a multiple of eight, so it's necessary to
// round up.
nativeResolution = ((nativeResolution + 7) >> 3) << 3;
if (nativeResolution > minDimension) {
nativeResolution = minDimension;
}
// There's no point in capturing too much detail, so ask the driver to downsample. I haven't
// tried a non-integer multiple, but it seems unlikely to work.
double dpi = nativeResolution / objectSize;
stillMultiplier = 1;
if (dpi > 200) {
stillMultiplier = (int) (dpi / 200 + 1);
}
stillResolution = new Point(nativeResolution, nativeResolution);
Log.v(TAG, "FOV " + fov + " objectSize " + objectSize + " crop " + crop + " dpi " + dpi +
" nativeResolution " + nativeResolution + " stillMultiplier " + stillMultiplier);
}
// FIXME(dswitkin): These three methods have temporary constants until the new Camera API can
// provide the real values for the current device.
// Temporary: the camera's maximum resolution in pixels.
private static Point getMaximumCameraResolution() {
return new Point(1280, 1024);
}
// Temporary: the diagonal field of view in degrees.
private static float getFieldOfView() {
return 60.0f;
}
// Temporary: the minimum focus distance in inches.
private static float getMinimumFocusDistance() {
return 12.0f;
}
private Point getScreenResolution() {
if (screenResolution == null) {
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = manager.getDefaultDisplay();
screenResolution = new Point(display.getWidth(), display.getHeight());
}
return screenResolution;
}
}

View file

@ -0,0 +1,155 @@
/*
* Copyright (C) 2008 Google Inc.
*
* 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 android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import com.google.zxing.ResultPoint;
/**
* @author dswitkin@google.com (Daniel Switkin)
*/
final class CameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private static final int[] SCANNER_ALPHA = {0, 64, 128, 192, 255, 192, 128, 64};
private final CameraManager cameraManager;
private final SurfaceHolder surfaceHolder;
private boolean hasSurface;
private int scannerAlpha;
CameraSurfaceView(Context context, CameraManager cameraManager) {
super(context);
this.cameraManager = cameraManager;
// Install a SurfaceHolder.Callback so we get notified when the underlying surface is created
// and destroyed.
surfaceHolder = getHolder();
surfaceHolder.setCallback(this);
hasSurface = false;
scannerAlpha = 0;
surfaceHolder.setSizeFromLayout();
}
public boolean surfaceCreated(SurfaceHolder holder) {
hasSurface = true;
// Tell the system that we filled the surface in this call. This is a lie to prevent the system
// from filling the surface for us automatically. THIS IS REQUIRED because otherwise we'll
// access the Surface object from 2 different threads which is not allowed.
return true;
}
public void surfaceDestroyed(SurfaceHolder holder) {
// FIXME(dswitkin): The docs say this surface will be destroyed when this method returns. In
// practice this has not been a problem so far. I need to investigate.
hasSurface = false;
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// Surface size or format has changed. This won't happen because of the setFixedSize() call.
}
/**
* This method is only called from the WorkerThread. It's job is to grab the next preview frame
* from the camera, draw the framing rectangle, and blit everything to the screen.
*/
public void capturePreviewAndDraw() {
if (hasSurface) {
Canvas canvas = surfaceHolder.lockCanvas();
cameraManager.capturePreview(canvas);
Rect frame = cameraManager.getFramingRect();
int width = canvas.getBitmapWidth();
int height = canvas.getBitmapHeight();
// Draw the exterior (i.e. outside the framing rect) as half darkened
Paint paint = new Paint();
paint.setColor(Color.BLACK);
paint.setAlpha(96);
Rect box = new Rect(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);
// Draw a two pixel solid black border inside the framing rect
paint.setAlpha(255);
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);
// Draw a red "laser scanner" line through the middle
paint.setColor(Color.RED);
paint.setAlpha(SCANNER_ALPHA[scannerAlpha]);
int middle = frame.height() / 2 + frame.top;
box.set(frame.left + 2, middle - 1, frame.right - 1, middle + 2);
canvas.drawRect(box, paint);
surfaceHolder.unlockCanvasAndPost(canvas);
// This cheap animation is tied to the rate at which we pull previews from the camera.
scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length;
}
}
/**
* Draw a line for 1D barcodes (which return two points) or otherwise a set of points returned
* from the decoder to indicate what we found.
* TODO(dswitkin): It might be nice to clear the framing rect and zoom in on the actual still that
* was captured, then paint the green points on it. This would also clear the red scanner line
* which doesn't make sense after the capture.
*
* @param resultPoints An array of points from the decoder, whose coordinates are expressed
* relative to the still image from the camera.
*/
public void drawResultPoints(ResultPoint[] resultPoints) {
if (hasSurface) {
Canvas canvas = surfaceHolder.lockCanvas();
Paint paint = new Paint();
paint.setColor(Color.GREEN);
paint.setAlpha(128);
Point[] points = cameraManager.convertResultPoints(resultPoints);
if (points.length == 2) {
paint.setStrokeWidth(4);
canvas.drawLine(points[0].x, points[0].y, points[1].x, points[1].y, paint);
} else {
paint.setStrokeWidth(10);
for (int x = 0; x < points.length; x++) {
canvas.drawPoint(points[x].x, points[x].y, paint);
}
}
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}

View file

@ -0,0 +1,148 @@
/*
* Copyright (C) 2008 Google Inc.
*
* 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 android.graphics.Bitmap;
import com.google.zxing.BlackPointEstimationMethod;
import com.google.zxing.MonochromeBitmapSource;
import com.google.zxing.ReaderException;
import com.google.zxing.common.BitArray;
import com.google.zxing.common.BlackPointEstimator;
/**
* This object implements MonochromeBitmapSource around an Android Bitmap. Rather than capturing an
* RGB image and calculating the grey value at each pixel, we ask the camera driver for YUV data and
* strip out the luminance channel directly. This should be faster but provides fewer bits, i.e.
* fewer grey levels.
*
* @author dswitkin@google.com (Daniel Switkin)
* @author srowen@google.com (Sean Owen)
*/
final class RGBMonochromeBitmapSource implements MonochromeBitmapSource {
private final Bitmap image;
private int blackPoint;
private BlackPointEstimationMethod lastMethod;
private int lastArgument;
private static final int LUMINANCE_BITS = 5;
private static final int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS;
private static final int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS;
RGBMonochromeBitmapSource(Bitmap image) {
this.image = image;
blackPoint = 0x7F;
lastMethod = null;
lastArgument = 0;
}
public boolean isBlack(int x, int y) {
return computeRGBLuminance(image.getPixel(x, y)) < blackPoint;
}
public BitArray getBlackRow(int y, BitArray row, int startX, int getWidth) {
if (row == null) {
row = new BitArray(getWidth);
} else {
row.clear();
}
int[] pixelRow = new int[getWidth];
image.getPixels(pixelRow, 0, getWidth, startX, y, getWidth, 1);
for (int i = 0; i < getWidth; i++) {
if (computeRGBLuminance(pixelRow[i]) < blackPoint) {
row.set(i);
}
}
return row;
}
public int getHeight() {
return image.height();
}
public int getWidth() {
return image.width();
}
public void estimateBlackPoint(BlackPointEstimationMethod method, int argument) throws ReaderException {
if (!method.equals(lastMethod) || argument != lastArgument) {
int width = image.width();
int height = image.height();
int[] histogram = new int[LUMINANCE_BUCKETS];
if (method.equals(BlackPointEstimationMethod.TWO_D_SAMPLING)) {
int minDimension = width < height ? width : height;
int startI = height == minDimension ? 0 : (height - width) >> 1;
int startJ = width == minDimension ? 0 : (width - height) >> 1;
for (int n = 0; n < minDimension; n++) {
int pixel = image.getPixel(startJ + n, startI + n);
histogram[computeRGBLuminance(pixel) >> LUMINANCE_SHIFT]++;
}
} else if (method.equals(BlackPointEstimationMethod.ROW_SAMPLING)) {
if (argument < 0 || argument >= height) {
throw new IllegalArgumentException("Row is not within the image: " + argument);
}
int[] pixelRow = new int[width];
image.getPixels(pixelRow, 0, width, 0, argument, width, 1);
for (int x = 0; x < width; x++) {
histogram[computeRGBLuminance(pixelRow[x]) >> LUMINANCE_SHIFT]++;
}
} else {
throw new IllegalArgumentException("Unknown method: " + method);
}
blackPoint = BlackPointEstimator.estimate(histogram) << LUMINANCE_SHIFT;
lastMethod = method;
lastArgument = argument;
}
}
public BlackPointEstimationMethod getLastEstimationMethod() {
return lastMethod;
}
public MonochromeBitmapSource rotateCounterClockwise() {
throw new IllegalStateException("Rotate not supported");
}
public boolean isRotateSupported() {
return false;
}
/**
* An optimized approximation of a more proper conversion from RGB to luminance which
* only uses shifts. See BufferedImageMonochromeBitmapSource for an original version.
*/
private static int computeRGBLuminance(int pixel) {
// Instead of multiplying by 306, 601, 117, we multiply by 256, 512, 256, so that
// the multiplies can be implemented as shifts.
//
// Really, it's:
//
// return ((((pixel >> 16) & 0xFF) << 8) +
// (((pixel >> 8) & 0xFF) << 9) +
// (( pixel & 0xFF) << 8)) >> 10;
//
// That is, we're replacing the coefficients in the original with powers of two,
// which can be implemented as shifts, even though changing the coefficients slightly
// corrupts the conversion. Not significant for our purposes.
//
// But we can get even cleverer and eliminate a few shifts:
return (((pixel & 0x00FF0000) >> 8) +
((pixel & 0x0000FF00) << 1) +
((pixel & 0x000000FF) << 8)) >> 10;
}
}

View file

@ -0,0 +1,158 @@
/*
* Copyright (C) 2008 Google Inc.
*
* 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 android.content.Intent;
import android.net.ContentURI;
import android.os.Handler;
import android.os.Message;
import android.provider.Contacts;
import com.google.zxing.client.result.AddressBookAUParsedResult;
import com.google.zxing.client.result.AddressBookDoCoMoParsedResult;
import com.google.zxing.client.result.BookmarkDoCoMoParsedResult;
import com.google.zxing.client.result.EmailAddressParsedResult;
import com.google.zxing.client.result.EmailDoCoMoParsedResult;
import com.google.zxing.client.result.GeoParsedResult;
import com.google.zxing.client.result.ParsedReaderResult;
import com.google.zxing.client.result.ParsedReaderResultType;
import com.google.zxing.client.result.TelParsedResult;
import com.google.zxing.client.result.UPCParsedResult;
import com.google.zxing.client.result.URIParsedResult;
import com.google.zxing.client.result.URLTOParsedResult;
import java.net.URISyntaxException;
/**
* Handles the result of barcode decoding in the context of the Android platform,
* by dispatching the proper intents and so on.
*
* @author srowen@google.com (Sean Owen)
* @author dswitkin@google.com (Daniel Switkin)
*/
final class ResultHandler extends Handler {
private final Intent intent;
private final BarcodeReaderCaptureActivity captureActivity;
ResultHandler(BarcodeReaderCaptureActivity captureActivity, ParsedReaderResult result) {
this.captureActivity = captureActivity;
this.intent = resultToIntent(result);
}
private static Intent resultToIntent(ParsedReaderResult result) {
Intent intent = null;
ParsedReaderResultType type = result.getType();
if (type.equals(ParsedReaderResultType.ADDRESSBOOK)) {
AddressBookDoCoMoParsedResult addressResult = (AddressBookDoCoMoParsedResult) result;
intent = new Intent(Contacts.Intents.Insert.ACTION, Contacts.People.CONTENT_URI);
putExtra(intent, Contacts.Intents.Insert.NAME, addressResult.getName());
putExtra(intent, Contacts.Intents.Insert.PHONE, addressResult.getPhoneNumbers());
putExtra(intent, Contacts.Intents.Insert.EMAIL, addressResult.getEmail());
putExtra(intent, Contacts.Intents.Insert.NOTES, addressResult.getNote());
putExtra(intent, Contacts.Intents.Insert.POSTAL, addressResult.getAddress());
} else if (type.equals(ParsedReaderResultType.ADDRESSBOOK_AU)) {
AddressBookAUParsedResult addressResult = (AddressBookAUParsedResult) result;
intent = new Intent(Contacts.Intents.Insert.ACTION, Contacts.People.CONTENT_URI);
putExtra(intent, Contacts.Intents.Insert.NAME, addressResult.getNames());
putExtra(intent, Contacts.Intents.Insert.PHONE, addressResult.getPhoneNumbers());
putExtra(intent, Contacts.Intents.Insert.EMAIL, addressResult.getEmails());
putExtra(intent, Contacts.Intents.Insert.NOTES, addressResult.getNote());
putExtra(intent, Contacts.Intents.Insert.POSTAL, addressResult.getAddress());
} else if (type.equals(ParsedReaderResultType.BOOKMARK)) {
// For now, we can only open the browser, and not actually add a bookmark
try {
intent = new Intent(Intent.VIEW_ACTION, new ContentURI(((BookmarkDoCoMoParsedResult) result).getURI()));
} catch (URISyntaxException e) {
}
} else if (type.equals(ParsedReaderResultType.URLTO)) {
try {
intent = new Intent(Intent.VIEW_ACTION, new ContentURI(((URLTOParsedResult) result).getURI()));
} catch (URISyntaxException e) {
}
} else if (type.equals(ParsedReaderResultType.EMAIL)) {
EmailDoCoMoParsedResult emailResult = (EmailDoCoMoParsedResult) result;
try {
intent = new Intent(Intent.SENDTO_ACTION, new ContentURI(emailResult.getTo()));
} catch (URISyntaxException e) {
}
putExtra(intent, "subject", emailResult.getSubject());
putExtra(intent, "body", emailResult.getBody());
} else if (type.equals(ParsedReaderResultType.EMAIL_ADDRESS)) {
EmailAddressParsedResult emailResult = (EmailAddressParsedResult) result;
try {
intent = new Intent(Intent.SENDTO_ACTION, new ContentURI(emailResult.getEmailAddress()));
} catch (URISyntaxException e) {
}
} else if (type.equals(ParsedReaderResultType.TEL)) {
TelParsedResult telResult = (TelParsedResult) result;
try {
intent = new Intent(Intent.DIAL_ACTION, new ContentURI("tel:" + telResult.getNumber()));
} catch (URISyntaxException e) {
}
} else if (type.equals(ParsedReaderResultType.GEO)) {
GeoParsedResult geoResult = (GeoParsedResult) result;
try {
intent = new Intent(Intent.VIEW_ACTION, new ContentURI(geoResult.getGeoURI()));
} catch (URISyntaxException e) {
}
} else if (type.equals(ParsedReaderResultType.UPC)) {
UPCParsedResult upcResult = (UPCParsedResult) result;
try {
ContentURI uri = new ContentURI("http://www.upcdatabase.com/item.asp?upc=" + upcResult.getUPC());
intent = new Intent(Intent.VIEW_ACTION, uri);
} catch (URISyntaxException e) {
}
} else if (type.equals(ParsedReaderResultType.URI)) {
URIParsedResult uriResult = (URIParsedResult) result;
try {
intent = new Intent(Intent.VIEW_ACTION, new ContentURI(uriResult.getURI()));
} catch (URISyntaxException e) {
}
} else if (type.equals(ParsedReaderResultType.ANDROID_INTENT)) {
intent = ((AndroidIntentParsedResult) result).getIntent();
}
return intent;
}
@Override
public void handleMessage(Message message) {
if (message.what == R.string.button_yes) {
if (intent != null) {
captureActivity.startActivity(intent);
}
} else {
captureActivity.restartPreview();
}
}
Intent getIntent() {
return intent;
}
private static void putExtra(Intent intent, String key, String value) {
if (value != null && value.length() > 0) {
intent.putExtra(key, value);
}
}
private static void putExtra(Intent intent, String key, String[] value) {
if (value != null && value.length > 0) {
putExtra(intent, key, value[0]);
}
}
}

View file

@ -0,0 +1,123 @@
/*
* Copyright (C) 2008 Google Inc.
*
* 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 android.graphics.Bitmap;
import android.os.Handler;
import android.os.Message;
import com.google.zxing.MonochromeBitmapSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.ReaderException;
import com.google.zxing.Result;
/**
* This thread does all the heavy lifting, both during preview and for the final capture and
* decoding. That leaves the main thread free to handle UI tasks.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
final class WorkerThread extends Thread {
private final CameraSurfaceView surfaceView;
private final CameraManager cameraManager;
private final Handler handler;
private final Object idleLock;
private State state;
private enum State {
IDLE,
PREVIEW_LOOP,
STILL_AND_DECODE,
DONE
}
WorkerThread(CameraSurfaceView surfaceView, CameraManager cameraManager, Handler handler) {
this.surfaceView = surfaceView;
this.cameraManager = cameraManager;
this.handler = handler;
this.idleLock = new Object();
state = State.IDLE;
}
@Override
public void run() {
while (true) {
switch (state) {
case IDLE:
idle();
break;
case PREVIEW_LOOP:
surfaceView.capturePreviewAndDraw();
break;
case STILL_AND_DECODE:
Bitmap bitmap = cameraManager.captureStill();
Result rawResult;
try {
MonochromeBitmapSource source = new RGBMonochromeBitmapSource(bitmap);
rawResult = new MultiFormatReader().decode(source);
} catch (ReaderException e) {
Message message = Message.obtain(handler, R.id.decoding_failed_message);
message.sendToTarget();
state = State.PREVIEW_LOOP;
break;
}
Message message = Message.obtain(handler, R.id.decoding_succeeded_message, rawResult);
message.sendToTarget();
state = State.IDLE;
break;
case DONE:
return;
}
}
}
public void requestPreviewLoop() {
state = State.PREVIEW_LOOP;
wakeFromIdle();
}
public void requestStillAndDecode() {
state = State.STILL_AND_DECODE;
wakeFromIdle();
}
public void requestExitAndWait() {
state = State.DONE;
wakeFromIdle();
try {
join();
} catch (InterruptedException e) {
}
}
private void idle() {
try {
synchronized (idleLock) {
idleLock.wait();
}
} catch (InterruptedException ie) {
// continue
}
}
private void wakeFromIdle() {
synchronized (idleLock) {
idleLock.notifyAll();
}
}
}

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (C) 2008 Google Inc.
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.
-->
<resources>
<string name="app_name">Barcode Reader</string>
<string name="button_no">No</string>
<string name="button_ok">OK</string>
<string name="button_yes">Yes</string>
<string name="menu_about">About...</string>
<string name="msg_about">ZXing Barcode Reader v@VERSION@\nhttp://code.google.com/p/zxing</string>
<string name="msg_no_barcode_detected">Sorry, no barcode was found.</string>
<string name="title_about">About</string>
<string name="title_barcode_detected">Barcode Detected</string>
<string name="title_no_barcode_detected">No Barcode Detected</string>
<string name="title_error">Error</string>
<string name="title_open_url">Open Web Page?</string>
<string name="title_add_contact">Add Contact?</string>
<string name="title_compose_email">Compose E-mail?</string>
<string name="title_lookup_barcode">Look Up Barcode Online?</string>
<string name="title_dial">Dial Number?</string>
<string name="title_view_maps">View In Google Maps?</string>
</resources>