mirror of
https://github.com/zxing/zxing.git
synced 2025-03-05 20:48:51 -08:00
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:
parent
683a78256d
commit
0198f58150
14
android-m3/AndroidManifest.xml
Normal file
14
android-m3/AndroidManifest.xml
Normal 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
265
android-m3/build.xml
Normal 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>
|
BIN
android-m3/res/drawable/icon.png
Normal file
BIN
android-m3/res/drawable/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.8 KiB |
21
android-m3/res/layout/main.xml
Normal file
21
android-m3/res/layout/main.xml
Normal 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"/>
|
||||||
|
|
20
android-m3/res/values/ids.xml
Normal file
20
android-m3/res/values/ids.xml
Normal 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>
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
148
android-m3/src/com/google/zxing/client/android/RGBMonochromeBitmapSource.java
Executable file
148
android-m3/src/com/google/zxing/client/android/RGBMonochromeBitmapSource.java
Executable 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
158
android-m3/src/com/google/zxing/client/android/ResultHandler.java
Executable file
158
android-m3/src/com/google/zxing/client/android/ResultHandler.java
Executable 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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
123
android-m3/src/com/google/zxing/client/android/WorkerThread.java
Normal file
123
android-m3/src/com/google/zxing/client/android/WorkerThread.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
35
android-m3/strings.xml.template
Normal file
35
android-m3/strings.xml.template
Normal 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>
|
Loading…
Reference in a new issue