mirror of
https://github.com/zxing/zxing.git
synced 2025-01-12 19:57:27 -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