diff --git a/doric-android/devkit/build.gradle b/doric-android/devkit/build.gradle
index 78479401..91639f06 100644
--- a/doric-android/devkit/build.gradle
+++ b/doric-android/devkit/build.gradle
@@ -36,7 +36,8 @@ dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
- implementation 'cn.yipianfengye.android:zxing-library:2.2'
+ implementation 'com.google.zxing:core:3.3.3'
+ implementation 'com.google.zxing:android-core:3.3.0'
}
diff --git a/doric-android/devkit/src/main/AndroidManifest.xml b/doric-android/devkit/src/main/AndroidManifest.xml
index aab69485..a6d467f8 100644
--- a/doric-android/devkit/src/main/AndroidManifest.xml
+++ b/doric-android/devkit/src/main/AndroidManifest.xml
@@ -7,5 +7,6 @@
+
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/DisplayUtil.java b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/DisplayUtil.java
new file mode 100644
index 00000000..0032846b
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/DisplayUtil.java
@@ -0,0 +1,43 @@
+package pub.doric.devkit.qrcode;
+
+import android.content.Context;
+import android.util.DisplayMetrics;
+
+
+public class DisplayUtil
+{
+
+ public static int screenWidthPx; //屏幕宽 px
+ public static int screenHeightPx; //屏幕高 px
+ public static float density;//屏幕密度
+ public static int densityDPI;//屏幕密度
+ public static float screenWidthDip;// dp单位
+ public static float screenHeightDip;// dp单位
+
+ public static void initDisplayOpinion(Context context) {
+ DisplayMetrics dm =context.getResources().getDisplayMetrics();
+ DisplayUtil.density = dm.density;
+ DisplayUtil.densityDPI = dm.densityDpi;
+ DisplayUtil.screenWidthPx = dm.widthPixels;
+ DisplayUtil.screenHeightPx = dm.heightPixels;
+ DisplayUtil.screenWidthDip = DisplayUtil.px2dip(context, dm.widthPixels);
+ DisplayUtil.screenHeightDip = DisplayUtil.px2dip(context, dm.heightPixels);
+ }
+
+ /**
+ * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
+ */
+ public static int dip2px(Context context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+
+ /**
+ * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
+ */
+ public static int px2dip(Context context, float pxValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (pxValue / scale + 0.5f);
+ }
+
+}
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/activity/CaptureActivity.java b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/activity/CaptureActivity.java
new file mode 100644
index 00000000..5b3a8c6a
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/activity/CaptureActivity.java
@@ -0,0 +1,68 @@
+package pub.doric.devkit.qrcode.activity;
+
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.util.Log;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import pub.doric.devkit.R;
+
+
+/**
+ * Initial the camera
+ *
+ * 默认的二维码扫描Activity
+ */
+public class CaptureActivity extends AppCompatActivity {
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.camera);
+ CaptureFragment captureFragment = new CaptureFragment();
+ captureFragment.setAnalyzeCallback(analyzeCallback);
+ getSupportFragmentManager().beginTransaction().replace(R.id.fl_zxing_container, captureFragment).commit();
+ captureFragment.setCameraInitCallBack(new CaptureFragment.CameraInitCallBack() {
+ @Override
+ public void callBack(Exception e) {
+ if (e == null) {
+
+ } else {
+ Log.e("TAG", "callBack: ", e);
+ }
+ }
+ });
+
+ }
+
+ /**
+ * 二维码解析回调函数
+ */
+ CodeUtils.AnalyzeCallback analyzeCallback = new CodeUtils.AnalyzeCallback() {
+ @Override
+ public void onAnalyzeSuccess(Bitmap mBitmap, String result) {
+ Intent resultIntent = new Intent();
+ Bundle bundle = new Bundle();
+ bundle.putInt(CodeUtils.RESULT_TYPE, CodeUtils.RESULT_SUCCESS);
+ bundle.putString(CodeUtils.RESULT_STRING, result);
+ resultIntent.putExtras(bundle);
+ CaptureActivity.this.setResult(RESULT_OK, resultIntent);
+ CaptureActivity.this.finish();
+ }
+
+ @Override
+ public void onAnalyzeFailed() {
+ Intent resultIntent = new Intent();
+ Bundle bundle = new Bundle();
+ bundle.putInt(CodeUtils.RESULT_TYPE, CodeUtils.RESULT_FAILED);
+ bundle.putString(CodeUtils.RESULT_STRING, "");
+ resultIntent.putExtras(bundle);
+ CaptureActivity.this.setResult(RESULT_OK, resultIntent);
+ CaptureActivity.this.finish();
+ }
+ };
+}
\ No newline at end of file
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/activity/CaptureFragment.java b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/activity/CaptureFragment.java
new file mode 100644
index 00000000..96ee21ad
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/activity/CaptureFragment.java
@@ -0,0 +1,230 @@
+package pub.doric.devkit.qrcode.activity;
+
+import android.graphics.Bitmap;
+import android.hardware.Camera;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Vibrator;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.Result;
+
+import java.util.Vector;
+
+import pub.doric.devkit.R;
+import pub.doric.devkit.qrcode.camera.CameraManager;
+import pub.doric.devkit.qrcode.decoding.CaptureActivityHandler;
+import pub.doric.devkit.qrcode.decoding.InactivityTimer;
+import pub.doric.devkit.qrcode.view.ViewfinderView;
+
+/**
+ * 自定义实现的扫描Fragment
+ */
+public class CaptureFragment extends Fragment implements SurfaceHolder.Callback {
+
+ private CaptureActivityHandler handler;
+ private ViewfinderView viewfinderView;
+ private boolean hasSurface;
+ private Vector decodeFormats;
+ private String characterSet;
+ private InactivityTimer inactivityTimer;
+ private static final float BEEP_VOLUME = 0.10f;
+ private boolean vibrate=false;
+ private SurfaceView surfaceView;
+ private SurfaceHolder surfaceHolder;
+ private CodeUtils.AnalyzeCallback analyzeCallback;
+ private Camera camera;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+
+ CameraManager.init(getActivity().getApplication());
+
+ hasSurface = false;
+ inactivityTimer = new InactivityTimer(this.getActivity());
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+
+ Bundle bundle = getArguments();
+ View view = null;
+ if (bundle != null) {
+ int layoutId = bundle.getInt(CodeUtils.LAYOUT_ID);
+ if (layoutId != -1) {
+ view = inflater.inflate(layoutId, null);
+ }
+ }
+
+ if (view == null) {
+ view = inflater.inflate(R.layout.fragment_capture, null);
+ }
+
+ viewfinderView = (ViewfinderView) view.findViewById(R.id.viewfinder_view);
+ surfaceView = (SurfaceView) view.findViewById(R.id.preview_view);
+ surfaceHolder = surfaceView.getHolder();
+
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (hasSurface) {
+ initCamera(surfaceHolder);
+ } else {
+ surfaceHolder.addCallback(this);
+ surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
+ }
+ decodeFormats = null;
+ characterSet = null;
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ if (handler != null) {
+ handler.quitSynchronously();
+ handler = null;
+ }
+ CameraManager.get().closeDriver();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ inactivityTimer.shutdown();
+ }
+
+
+ /**
+ * Handler scan result
+ *
+ * @param result
+ * @param barcode
+ */
+ public void handleDecode(Result result, Bitmap barcode) {
+ inactivityTimer.onActivity();
+ playVibrate();
+
+ if (result == null || TextUtils.isEmpty(result.getText())) {
+ if (analyzeCallback != null) {
+ analyzeCallback.onAnalyzeFailed();
+ }
+ } else {
+ if (analyzeCallback != null) {
+ analyzeCallback.onAnalyzeSuccess(barcode, result.getText());
+ }
+ }
+ }
+
+ private void initCamera(SurfaceHolder surfaceHolder) {
+ try {
+ CameraManager.get().openDriver(surfaceHolder);
+ camera = CameraManager.get().getCamera();
+ } catch (Exception e) {
+ if (callBack != null) {
+ callBack.callBack(e);
+ }
+ return;
+ }
+ if (callBack != null) {
+ callBack.callBack(null);
+ }
+ if (handler == null) {
+ handler = new CaptureActivityHandler(this, decodeFormats, characterSet, viewfinderView);
+ }
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width,
+ int height) {
+
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ if (!hasSurface) {
+ hasSurface = true;
+ initCamera(holder);
+ }
+
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ hasSurface = false;
+ if (camera != null) {
+ if (camera != null && CameraManager.get().isPreviewing()) {
+ if (!CameraManager.get().isUseOneShotPreviewCallback()) {
+ camera.setPreviewCallback(null);
+ }
+ camera.stopPreview();
+ CameraManager.get().getPreviewCallback().setHandler(null, 0);
+ CameraManager.get().getAutoFocusCallback().setHandler(null, 0);
+ CameraManager.get().setPreviewing(false);
+ }
+ }
+ }
+
+ public Handler getHandler() {
+ return handler;
+ }
+
+ public void drawViewfinder() {
+ viewfinderView.drawViewfinder();
+
+ }
+
+
+ private static final long VIBRATE_DURATION = 200L;
+
+ private void playVibrate() {
+ if (vibrate) {
+ Vibrator vibrator = (Vibrator) getActivity().getSystemService(getActivity().VIBRATOR_SERVICE);
+ vibrator.vibrate(VIBRATE_DURATION);
+ }
+ }
+
+
+
+ public CodeUtils.AnalyzeCallback getAnalyzeCallback() {
+ return analyzeCallback;
+ }
+
+ public void setAnalyzeCallback(CodeUtils.AnalyzeCallback analyzeCallback) {
+ this.analyzeCallback = analyzeCallback;
+ }
+
+ @Nullable
+ CameraInitCallBack callBack;
+
+ /**
+ * Set callback for Camera check whether Camera init success or not.
+ */
+ public void setCameraInitCallBack(CameraInitCallBack callBack) {
+ this.callBack = callBack;
+ }
+
+ interface CameraInitCallBack {
+ /**
+ * Callback for Camera init result.
+ * @param e If is's null,means success.otherwise Camera init failed with the Exception.
+ */
+ void callBack(Exception e);
+ }
+
+
+}
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/activity/CodeUtils.java b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/activity/CodeUtils.java
new file mode 100644
index 00000000..1747f77a
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/activity/CodeUtils.java
@@ -0,0 +1,220 @@
+package pub.doric.devkit.qrcode.activity;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Matrix;
+import android.hardware.Camera;
+import android.os.Bundle;
+import android.text.TextUtils;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.BinaryBitmap;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.EncodeHintType;
+import com.google.zxing.MultiFormatReader;
+import com.google.zxing.Result;
+import com.google.zxing.WriterException;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.common.HybridBinarizer;
+import com.google.zxing.qrcode.QRCodeWriter;
+import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
+
+import java.util.Hashtable;
+import java.util.Vector;
+
+import pub.doric.devkit.qrcode.camera.BitmapLuminanceSource;
+import pub.doric.devkit.qrcode.camera.CameraManager;
+import pub.doric.devkit.qrcode.decoding.DecodeFormatManager;
+
+/**
+ * 二维码扫描工具类
+ */
+public class CodeUtils {
+
+ public static final String RESULT_TYPE = "result_type";
+ public static final String RESULT_STRING = "result_string";
+ public static final int RESULT_SUCCESS = 1;
+ public static final int RESULT_FAILED = 2;
+
+ public static final String LAYOUT_ID = "layout_id";
+
+
+
+ /**
+ * 解析二维码图片工具类
+ * @param analyzeCallback
+ */
+ public static void analyzeBitmap(String path, AnalyzeCallback analyzeCallback) {
+
+ /**
+ * 首先判断图片的大小,若图片过大,则执行图片的裁剪操作,防止OOM
+ */
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true; // 先获取原大小
+ Bitmap mBitmap = BitmapFactory.decodeFile(path, options);
+ options.inJustDecodeBounds = false; // 获取新的大小
+
+ int sampleSize = (int) (options.outHeight / (float) 400);
+
+ if (sampleSize <= 0)
+ sampleSize = 1;
+ options.inSampleSize = sampleSize;
+ mBitmap = BitmapFactory.decodeFile(path, options);
+
+ MultiFormatReader multiFormatReader = new MultiFormatReader();
+
+ // 解码的参数
+ Hashtable hints = new Hashtable(2);
+ // 可以解析的编码类型
+ Vector decodeFormats = new Vector();
+ if (decodeFormats == null || decodeFormats.isEmpty()) {
+ decodeFormats = new Vector();
+
+ // 这里设置可扫描的类型,我这里选择了都支持
+ decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS);
+ decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
+ decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);
+ }
+ hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
+ // 设置继续的字符编码格式为UTF8
+ // hints.put(DecodeHintType.CHARACTER_SET, "UTF8");
+ // 设置解析配置参数
+ multiFormatReader.setHints(hints);
+
+ // 开始对图像资源解码
+ Result rawResult = null;
+ try {
+ rawResult = multiFormatReader.decodeWithState(new BinaryBitmap(new HybridBinarizer(new BitmapLuminanceSource(mBitmap))));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ if (rawResult != null) {
+ if (analyzeCallback != null) {
+ analyzeCallback.onAnalyzeSuccess(mBitmap, rawResult.getText());
+ }
+ } else {
+ if (analyzeCallback != null) {
+ analyzeCallback.onAnalyzeFailed();
+ }
+ }
+ }
+
+ /**
+ * 生成二维码图片
+ * @param text
+ * @param w
+ * @param h
+ * @param logo
+ * @return
+ */
+ public static Bitmap createImage(String text,int w,int h,Bitmap logo) {
+ if (TextUtils.isEmpty(text)) {
+ return null;
+ }
+ try {
+ Bitmap scaleLogo = getScaleLogo(logo,w,h);
+
+ int offsetX = w / 2;
+ int offsetY = h / 2;
+
+ int scaleWidth = 0;
+ int scaleHeight = 0;
+ if (scaleLogo != null) {
+ scaleWidth = scaleLogo.getWidth();
+ scaleHeight = scaleLogo.getHeight();
+ offsetX = (w - scaleWidth) / 2;
+ offsetY = (h - scaleHeight) / 2;
+ }
+ Hashtable hints = new Hashtable();
+ hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
+ //容错级别
+ hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
+ //设置空白边距的宽度
+ hints.put(EncodeHintType.MARGIN, 0);
+ BitMatrix bitMatrix = new QRCodeWriter().encode(text, BarcodeFormat.QR_CODE, w, h, hints);
+ int[] pixels = new int[w * h];
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ if(x >= offsetX && x < offsetX + scaleWidth && y>= offsetY && y < offsetY + scaleHeight){
+ int pixel = scaleLogo.getPixel(x-offsetX,y-offsetY);
+ if(pixel == 0){
+ if(bitMatrix.get(x, y)){
+ pixel = 0xff000000;
+ }else{
+ pixel = 0xffffffff;
+ }
+ }
+ pixels[y * w + x] = pixel;
+ }else{
+ if (bitMatrix.get(x, y)) {
+ pixels[y * w + x] = 0xff000000;
+ } else {
+ pixels[y * w + x] = 0xffffffff;
+ }
+ }
+ }
+ }
+ Bitmap bitmap = Bitmap.createBitmap(w, h,
+ Bitmap.Config.ARGB_8888);
+ bitmap.setPixels(pixels, 0, w, 0, 0, w, h);
+ return bitmap;
+ } catch (WriterException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ private static Bitmap getScaleLogo(Bitmap logo,int w,int h){
+ if(logo == null)return null;
+ Matrix matrix = new Matrix();
+ float scaleFactor = Math.min(w * 1.0f / 5 / logo.getWidth(), h * 1.0f / 5 /logo.getHeight());
+ matrix.postScale(scaleFactor,scaleFactor);
+ Bitmap result = Bitmap.createBitmap(logo, 0, 0, logo.getWidth(), logo.getHeight(), matrix, true);
+ return result;
+ }
+
+ /**
+ * 解析二维码结果
+ */
+ public interface AnalyzeCallback{
+
+ public void onAnalyzeSuccess(Bitmap mBitmap, String result);
+
+ public void onAnalyzeFailed();
+ }
+
+
+ /**
+ * 为CaptureFragment设置layout参数
+ * @param captureFragment
+ * @param layoutId
+ */
+ public static void setFragmentArgs(CaptureFragment captureFragment, int layoutId) {
+ if (captureFragment == null || layoutId == -1) {
+ return;
+ }
+
+ Bundle bundle = new Bundle();
+ bundle.putInt(LAYOUT_ID, layoutId);
+ captureFragment.setArguments(bundle);
+ }
+
+ public static void isLightEnable(boolean isEnable) {
+ if (isEnable) {
+ Camera camera = CameraManager.get().getCamera();
+ if (camera != null) {
+ Camera.Parameters parameter = camera.getParameters();
+ parameter.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
+ camera.setParameters(parameter);
+ }
+ } else {
+ Camera camera = CameraManager.get().getCamera();
+ if (camera != null) {
+ Camera.Parameters parameter = camera.getParameters();
+ parameter.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
+ camera.setParameters(parameter);
+ }
+ }
+ }
+}
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/camera/AutoFocusCallback.java b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/camera/AutoFocusCallback.java
new file mode 100644
index 00000000..ba9f452b
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/camera/AutoFocusCallback.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package pub.doric.devkit.qrcode.camera;
+
+import android.hardware.Camera;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+public final class AutoFocusCallback implements Camera.AutoFocusCallback {
+
+ private static final String TAG = AutoFocusCallback.class.getSimpleName();
+
+ private static final long AUTOFOCUS_INTERVAL_MS = 1500L;
+
+ private Handler autoFocusHandler;
+ private int autoFocusMessage;
+
+ public void setHandler(Handler autoFocusHandler, int autoFocusMessage) {
+ this.autoFocusHandler = autoFocusHandler;
+ this.autoFocusMessage = autoFocusMessage;
+ }
+
+ public void onAutoFocus(boolean success, Camera camera) {
+ if (autoFocusHandler != null) {
+ Message message = autoFocusHandler.obtainMessage(autoFocusMessage, success);
+ autoFocusHandler.sendMessageDelayed(message, AUTOFOCUS_INTERVAL_MS);
+ autoFocusHandler = null;
+ } else {
+ Log.d(TAG, "Got auto-focus callback, but no handler for it");
+ }
+ }
+
+}
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/camera/BitmapLuminanceSource.java b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/camera/BitmapLuminanceSource.java
new file mode 100644
index 00000000..edde3dfb
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/camera/BitmapLuminanceSource.java
@@ -0,0 +1,41 @@
+package pub.doric.devkit.qrcode.camera;
+
+import android.graphics.Bitmap;
+
+import com.google.zxing.LuminanceSource;
+
+/**
+ * Created by aaron on 16/7/27.
+ * 自定义解析Bitmap LuminanceSource
+ */
+public class BitmapLuminanceSource extends LuminanceSource {
+
+ private byte bitmapPixels[];
+
+ public BitmapLuminanceSource(Bitmap bitmap) {
+ super(bitmap.getWidth(), bitmap.getHeight());
+
+ // 首先,要取得该图片的像素数组内容
+ int[] data = new int[bitmap.getWidth() * bitmap.getHeight()];
+ this.bitmapPixels = new byte[bitmap.getWidth() * bitmap.getHeight()];
+ bitmap.getPixels(data, 0, getWidth(), 0, 0, getWidth(), getHeight());
+
+ // 将int数组转换为byte数组,也就是取像素值中蓝色值部分作为辨析内容
+ for (int i = 0; i < data.length; i++) {
+ this.bitmapPixels[i] = (byte) data[i];
+ }
+ }
+
+ @Override
+ public byte[] getMatrix() {
+ // 返回我们生成好的像素数据
+ return bitmapPixels;
+ }
+
+ @Override
+ public byte[] getRow(int y, byte[] row) {
+ // 这里要得到指定行的像素数据
+ System.arraycopy(bitmapPixels, y * getWidth(), row, 0, getWidth());
+ return row;
+ }
+}
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/camera/CameraConfigurationManager.java b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/camera/CameraConfigurationManager.java
new file mode 100644
index 00000000..276d1029
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/camera/CameraConfigurationManager.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package pub.doric.devkit.qrcode.camera;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.hardware.Camera;
+import android.os.Build;
+import android.util.Log;
+import android.view.Display;
+import android.view.WindowManager;
+
+import java.util.regex.Pattern;
+
+final class CameraConfigurationManager {
+
+ private static final String TAG = CameraConfigurationManager.class.getSimpleName();
+
+ private static final int TEN_DESIRED_ZOOM = 27;
+ private static final int DESIRED_SHARPNESS = 30;
+
+ private static final Pattern COMMA_PATTERN = Pattern.compile(",");
+
+ private final Context context;
+ private Point screenResolution;
+ private Point cameraResolution;
+ private int previewFormat;
+ private String previewFormatString;
+
+ CameraConfigurationManager(Context context) {
+ this.context = context;
+ }
+
+ /**
+ * Reads, one time, values from the camera that are needed by the app.
+ */
+ void initFromCameraParameters(Camera camera) {
+ Camera.Parameters parameters = camera.getParameters();
+ previewFormat = parameters.getPreviewFormat();
+ previewFormatString = parameters.get("preview-format");
+ Log.d(TAG, "Default preview format: " + previewFormat + '/' + previewFormatString);
+ WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ Display display = manager.getDefaultDisplay();
+ screenResolution = new Point(display.getWidth(), display.getHeight());
+ Log.d(TAG, "Screen resolution: " + screenResolution);
+
+ Point screenResolutionForCamera = new Point();
+ screenResolutionForCamera.x = screenResolution.x;
+ screenResolutionForCamera.y = screenResolution.y;
+ // preview size is always something like 480*320, other 320*480
+ if (screenResolution.x < screenResolution.y) {
+ screenResolutionForCamera.x = screenResolution.y;
+ screenResolutionForCamera.y = screenResolution.x;
+ }
+ Log.i("#########", "screenX:" + screenResolutionForCamera.x + " screenY:" + screenResolutionForCamera.y);
+ cameraResolution = getCameraResolution(parameters, screenResolutionForCamera);
+
+ // cameraResolution = getCameraResolution(parameters, screenResolution);
+ Log.d(TAG, "Camera resolution: " + screenResolution);
+ }
+
+ /**
+ * Sets the camera up to take preview images which are used for both preview and decoding.
+ * We detect the preview format here so that buildLuminanceSource() can build an appropriate
+ * LuminanceSource subclass. In the future we may want to force YUV420SP as it's the smallest,
+ * and the planar Y can be used for barcode scanning without a copy in some cases.
+ */
+ void setDesiredCameraParameters(Camera camera) {
+ Camera.Parameters parameters = camera.getParameters();
+ Log.d(TAG, "Setting preview size: " + cameraResolution);
+ parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);
+ setFlash(parameters);
+ setZoom(parameters);
+ //setSharpness(parameters);
+ //modify here
+ camera.setDisplayOrientation(90);
+ camera.setParameters(parameters);
+ }
+
+ Point getCameraResolution() {
+ return cameraResolution;
+ }
+
+ Point getScreenResolution() {
+ return screenResolution;
+ }
+
+ int getPreviewFormat() {
+ return previewFormat;
+ }
+
+ String getPreviewFormatString() {
+ return previewFormatString;
+ }
+
+ private static Point getCameraResolution(Camera.Parameters parameters, Point screenResolution) {
+
+ String previewSizeValueString = parameters.get("preview-size-values");
+ // saw this on Xperia
+ if (previewSizeValueString == null) {
+ previewSizeValueString = parameters.get("preview-size-value");
+ }
+
+ Point cameraResolution = null;
+
+ if (previewSizeValueString != null) {
+ Log.d(TAG, "preview-size-values parameter: " + previewSizeValueString);
+ cameraResolution = findBestPreviewSizeValue(previewSizeValueString, screenResolution);
+ }
+
+ if (cameraResolution == null) {
+ // Ensure that the camera resolution is a multiple of 8, as the screen may not be.
+ cameraResolution = new Point(
+ (screenResolution.x >> 3) << 3,
+ (screenResolution.y >> 3) << 3);
+ }
+
+ return cameraResolution;
+ }
+
+ private static Point findBestPreviewSizeValue(CharSequence previewSizeValueString, Point screenResolution) {
+ int bestX = 0;
+ int bestY = 0;
+ int diff = Integer.MAX_VALUE;
+ for (String previewSize : COMMA_PATTERN.split(previewSizeValueString)) {
+
+ previewSize = previewSize.trim();
+ int dimPosition = previewSize.indexOf('x');
+ if (dimPosition < 0) {
+ Log.w(TAG, "Bad preview-size: " + previewSize);
+ continue;
+ }
+
+ int newX;
+ int newY;
+ try {
+ newX = Integer.parseInt(previewSize.substring(0, dimPosition));
+ newY = Integer.parseInt(previewSize.substring(dimPosition + 1));
+ } catch (NumberFormatException nfe) {
+ Log.w(TAG, "Bad preview-size: " + previewSize);
+ continue;
+ }
+
+ int newDiff = Math.abs(newX - screenResolution.x) + Math.abs(newY - screenResolution.y);
+ if (newDiff == 0) {
+ bestX = newX;
+ bestY = newY;
+ break;
+ } else if (newDiff < diff) {
+ bestX = newX;
+ bestY = newY;
+ diff = newDiff;
+ }
+
+ }
+
+ if (bestX > 0 && bestY > 0) {
+ return new Point(bestX, bestY);
+ }
+ return null;
+ }
+
+ private static int findBestMotZoomValue(CharSequence stringValues, int tenDesiredZoom) {
+ int tenBestValue = 0;
+ for (String stringValue : COMMA_PATTERN.split(stringValues)) {
+ stringValue = stringValue.trim();
+ double value;
+ try {
+ value = Double.parseDouble(stringValue);
+ } catch (NumberFormatException nfe) {
+ return tenDesiredZoom;
+ }
+ int tenValue = (int) (10.0 * value);
+ if (Math.abs(tenDesiredZoom - value) < Math.abs(tenDesiredZoom - tenBestValue)) {
+ tenBestValue = tenValue;
+ }
+ }
+ return tenBestValue;
+ }
+
+ private void setFlash(Camera.Parameters parameters) {
+ // FIXME: This is a hack to turn the flash off on the Samsung Galaxy.
+ // And this is a hack-hack to work around a different value on the Behold II
+ // Restrict Behold II check to Cupcake, per Samsung's advice
+ //if (Build.MODEL.contains("Behold II") &&
+ // CameraManager.SDK_INT == Build.VERSION_CODES.CUPCAKE) {
+ if (Build.MODEL.contains("Behold II") && CameraManager.SDK_INT == 3) { // 3 = Cupcake
+ parameters.set("flash-value", 1);
+ } else {
+ parameters.set("flash-value", 2);
+ }
+ // This is the standard setting to turn the flash off that all devices should honor.
+ parameters.set("flash-mode", "off");
+ }
+
+ private void setZoom(Camera.Parameters parameters) {
+
+ String zoomSupportedString = parameters.get("zoom-supported");
+ if (zoomSupportedString != null && !Boolean.parseBoolean(zoomSupportedString)) {
+ return;
+ }
+
+ int tenDesiredZoom = TEN_DESIRED_ZOOM;
+
+ String maxZoomString = parameters.get("max-zoom");
+ if (maxZoomString != null) {
+ try {
+ int tenMaxZoom = (int) (10.0 * Double.parseDouble(maxZoomString));
+ if (tenDesiredZoom > tenMaxZoom) {
+ tenDesiredZoom = tenMaxZoom;
+ }
+ } catch (NumberFormatException nfe) {
+ Log.w(TAG, "Bad max-zoom: " + maxZoomString);
+ }
+ }
+
+ String takingPictureZoomMaxString = parameters.get("taking-picture-zoom-max");
+ if (takingPictureZoomMaxString != null) {
+ try {
+ int tenMaxZoom = Integer.parseInt(takingPictureZoomMaxString);
+ if (tenDesiredZoom > tenMaxZoom) {
+ tenDesiredZoom = tenMaxZoom;
+ }
+ } catch (NumberFormatException nfe) {
+ Log.w(TAG, "Bad taking-picture-zoom-max: " + takingPictureZoomMaxString);
+ }
+ }
+
+ String motZoomValuesString = parameters.get("mot-zoom-values");
+ if (motZoomValuesString != null) {
+ tenDesiredZoom = findBestMotZoomValue(motZoomValuesString, tenDesiredZoom);
+ }
+
+ String motZoomStepString = parameters.get("mot-zoom-step");
+ if (motZoomStepString != null) {
+ try {
+ double motZoomStep = Double.parseDouble(motZoomStepString.trim());
+ int tenZoomStep = (int) (10.0 * motZoomStep);
+ if (tenZoomStep > 1) {
+ tenDesiredZoom -= tenDesiredZoom % tenZoomStep;
+ }
+ } catch (NumberFormatException nfe) {
+ // continue
+ }
+ }
+
+ // Set zoom. This helps encourage the user to pull back.
+ // Some devices like the Behold have a zoom parameter
+ if (maxZoomString != null || motZoomValuesString != null) {
+ parameters.set("zoom", String.valueOf(tenDesiredZoom / 10.0));
+ }
+
+ // Most devices, like the Hero, appear to expose this zoom parameter.
+ // It takes on values like "27" which appears to mean 2.7x zoom
+ if (takingPictureZoomMaxString != null) {
+ parameters.set("taking-picture-zoom", tenDesiredZoom);
+ }
+ }
+
+ public static int getDesiredSharpness() {
+ return DESIRED_SHARPNESS;
+ }
+
+}
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/camera/CameraManager.java b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/camera/CameraManager.java
new file mode 100644
index 00000000..6b1df354
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/camera/CameraManager.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package pub.doric.devkit.qrcode.camera;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.Camera;
+import android.os.Build;
+import android.os.Handler;
+import android.view.SurfaceHolder;
+
+import java.io.IOException;
+
+/**
+ * This object wraps the Camera service object and expects to be the only one talking to it. The
+ * implementation encapsulates the steps needed to take preview-sized images, which are used for
+ * both preview and decoding.
+ */
+public final class CameraManager {
+
+ private static final String TAG = CameraManager.class.getSimpleName();
+
+ public static int FRAME_WIDTH = -1;
+ public static int FRAME_HEIGHT = -1;
+ public static int FRAME_MARGINTOP = -1;
+
+ private static CameraManager cameraManager;
+
+ static final int SDK_INT; // Later we can use Build.VERSION.SDK_INT
+
+ static {
+ int sdkInt;
+ try {
+ sdkInt = Integer.parseInt(Build.VERSION.SDK);
+ } catch (NumberFormatException nfe) {
+ // Just to be safe
+ sdkInt = 10000;
+ }
+ SDK_INT = sdkInt;
+ }
+
+ private final Context context;
+ private final CameraConfigurationManager configManager;
+ private Camera camera;
+ private Rect framingRect;
+ private Rect framingRectInPreview;
+ private boolean initialized;
+ private boolean previewing;
+ private final boolean useOneShotPreviewCallback;
+ /**
+ * Preview frames are delivered here, which we pass on to the registered handler. Make sure to
+ * clear the handler so it will only receive one message.
+ */
+ private final PreviewCallback previewCallback;
+ /**
+ * Autofocus callbacks arrive here, and are dispatched to the Handler which requested them.
+ */
+ private final AutoFocusCallback autoFocusCallback;
+
+ /**
+ * Initializes this static object with the Context of the calling Activity.
+ *
+ * @param context The Activity which wants to use the camera.
+ */
+ public static void init(Context context) {
+ if (cameraManager == null) {
+ cameraManager = new CameraManager(context);
+ }
+ }
+
+ /**
+ * Gets the CameraManager singleton instance.
+ *
+ * @return A reference to the CameraManager singleton.
+ */
+ public static CameraManager get() {
+ return cameraManager;
+ }
+
+ private CameraManager(Context context) {
+
+ this.context = context;
+ this.configManager = new CameraConfigurationManager(context);
+
+ // Camera.setOneShotPreviewCallback() has a race condition in Cupcake, so we use the older
+ // Camera.setPreviewCallback() on 1.5 and earlier. For Donut and later, we need to use
+ // the more efficient one shot callback, as the older one can swamp the system and cause it
+ // to run out of memory. We can't use SDK_INT because it was introduced in the Donut SDK.
+ //useOneShotPreviewCallback = Integer.parseInt(Build.VERSION.SDK) > Build.VERSION_CODES.CUPCAKE;
+ useOneShotPreviewCallback = Integer.parseInt(Build.VERSION.SDK) > 3; // 3 = Cupcake
+
+ previewCallback = new PreviewCallback(configManager, useOneShotPreviewCallback);
+ autoFocusCallback = new AutoFocusCallback();
+ }
+
+ /**
+ * Opens the camera driver and initializes the hardware parameters.
+ *
+ * @param holder The surface object which the camera will draw preview frames into.
+ * @throws IOException Indicates the camera driver failed to open.
+ */
+ public void openDriver(SurfaceHolder holder) throws IOException {
+ if (camera == null) {
+ camera = Camera.open();
+ if (camera == null) {
+ throw new IOException();
+ }
+ camera.setPreviewDisplay(holder);
+
+ if (!initialized) {
+ initialized = true;
+ configManager.initFromCameraParameters(camera);
+ }
+ configManager.setDesiredCameraParameters(camera);
+
+ FlashlightManager.enableFlashlight();
+ }
+ }
+
+ /**
+ * Closes the camera driver if still in use.
+ */
+ public void closeDriver() {
+ if (camera != null) {
+ FlashlightManager.disableFlashlight();
+ camera.release();
+ camera = null;
+ }
+ }
+
+ /**
+ * Asks the camera hardware to begin drawing preview frames to the screen.
+ */
+ public void startPreview() {
+ if (camera != null && !previewing) {
+ camera.startPreview();
+ previewing = true;
+ }
+ }
+
+ /**
+ * Tells the camera to stop drawing preview frames.
+ */
+ public void stopPreview() {
+ if (camera != null && previewing) {
+ if (!useOneShotPreviewCallback) {
+ camera.setPreviewCallback(null);
+ }
+ camera.stopPreview();
+ previewCallback.setHandler(null, 0);
+ autoFocusCallback.setHandler(null, 0);
+ previewing = false;
+ }
+ }
+
+ /**
+ * A single preview frame will be returned to the handler supplied. The data will arrive as byte[]
+ * in the message.obj field, with width and height encoded as message.arg1 and message.arg2,
+ * respectively.
+ *
+ * @param handler The handler to send the message to.
+ * @param message The what field of the message to be sent.
+ */
+ public void requestPreviewFrame(Handler handler, int message) {
+ if (camera != null && previewing) {
+ previewCallback.setHandler(handler, message);
+ if (useOneShotPreviewCallback) {
+ camera.setOneShotPreviewCallback(previewCallback);
+ } else {
+ camera.setPreviewCallback(previewCallback);
+ }
+ }
+ }
+
+ /**
+ * Asks the camera hardware to perform an autofocus.
+ *
+ * @param handler The Handler to notify when the autofocus completes.
+ * @param message The message to deliver.
+ */
+ public void requestAutoFocus(Handler handler, int message) {
+ if (camera != null && previewing) {
+ autoFocusCallback.setHandler(handler, message);
+ //Log.d(TAG, "Requesting auto-focus callback");
+ camera.autoFocus(autoFocusCallback);
+ }
+ }
+
+ /**
+ * Calculates the framing rect which the UI should draw to show the user where to place the
+ * barcode. 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() {
+ try {
+ Point screenResolution = configManager.getScreenResolution();
+ // if (framingRect == null) {
+ if (camera == null) {
+ return null;
+ }
+
+ int leftOffset = (screenResolution.x - FRAME_WIDTH) / 2;
+
+ int topOffset;
+ if (FRAME_MARGINTOP != -1) {
+ topOffset = FRAME_MARGINTOP;
+ } else {
+ topOffset = (screenResolution.y - FRAME_HEIGHT) / 2;
+ }
+ framingRect = new Rect(leftOffset, topOffset, leftOffset + FRAME_WIDTH, topOffset + FRAME_HEIGHT);
+ // }
+ return framingRect;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * Like {@link #getFramingRect} but coordinates are in terms of the preview frame,
+ * not UI / screen.
+ */
+ public Rect getFramingRectInPreview() {
+ if (framingRectInPreview == null) {
+ Rect rect = new Rect(getFramingRect());
+ Point cameraResolution = configManager.getCameraResolution();
+ Point screenResolution = configManager.getScreenResolution();
+ //modify here
+// rect.left = rect.left * cameraResolution.x / screenResolution.x;
+// rect.right = rect.right * cameraResolution.x / screenResolution.x;
+// rect.top = rect.top * cameraResolution.y / screenResolution.y;
+// rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;
+ rect.left = rect.left * cameraResolution.y / screenResolution.x;
+ rect.right = rect.right * cameraResolution.y / screenResolution.x;
+ rect.top = rect.top * cameraResolution.x / screenResolution.y;
+ rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;
+ framingRectInPreview = rect;
+ }
+ return framingRectInPreview;
+ }
+
+ /**
+ * 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 = getFramingRectInPreview();
+ 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() + 0.5f);
+ output[x].y = frame.top + (int) (points[x].getY() + 0.5f);
+ }
+ return output;
+ }
+ */
+
+ /**
+ * A factory method to build the appropriate LuminanceSource object based on the format
+ * of the preview buffers, as described by Camera.Parameters.
+ *
+ * @param data A preview frame.
+ * @param width The width of the image.
+ * @param height The height of the image.
+ * @return A PlanarYUVLuminanceSource instance.
+ */
+ public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {
+ Rect rect = getFramingRectInPreview();
+ int previewFormat = configManager.getPreviewFormat();
+ String previewFormatString = configManager.getPreviewFormatString();
+ switch (previewFormat) {
+ // This is the standard Android format which all devices are REQUIRED to support.
+ // In theory, it's the only one we should ever care about.
+ case PixelFormat.YCbCr_420_SP:
+ // This format has never been seen in the wild, but is compatible as we only care
+ // about the Y channel, so allow it.
+ case PixelFormat.YCbCr_422_SP:
+ return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,
+ rect.width(), rect.height());
+ default:
+ // The Samsung Moment incorrectly uses this variant instead of the 'sp' version.
+ // Fortunately, it too has all the Y data up front, so we can read it.
+ if ("yuv420p".equals(previewFormatString)) {
+ return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,
+ rect.width(), rect.height());
+ }
+ }
+ throw new IllegalArgumentException("Unsupported picture format: " +
+ previewFormat + '/' + previewFormatString);
+ }
+
+ public Context getContext() {
+ return context;
+ }
+
+ public Camera getCamera() {
+ return camera;
+ }
+
+ public boolean isPreviewing() {
+ return previewing;
+ }
+
+ public boolean isUseOneShotPreviewCallback() {
+ return useOneShotPreviewCallback;
+ }
+
+ public PreviewCallback getPreviewCallback() {
+ return previewCallback;
+ }
+
+ public AutoFocusCallback getAutoFocusCallback() {
+ return autoFocusCallback;
+ }
+
+ public void setPreviewing(boolean previewing) {
+ this.previewing = previewing;
+ }
+}
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/camera/FlashlightManager.java b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/camera/FlashlightManager.java
new file mode 100644
index 00000000..64f02206
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/camera/FlashlightManager.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package pub.doric.devkit.qrcode.camera;
+
+import android.os.IBinder;
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * This class is used to activate the weak light on some camera phones (not flash)
+ * in order to illuminate surfaces for scanning. There is no official way to do this,
+ * but, classes which allow access to this function still exist on some devices.
+ * This therefore proceeds through a great deal of reflection.
+ *
+ * See
+ * http://almondmendoza.com/2009/01/05/changing-the-screen-brightness-programatically/ and
+ *
+ * http://code.google.com/p/droidled/source/browse/trunk/src/com/droidled/demo/DroidLED.java.
+ * Thanks to Ryan Alford for pointing out the availability of this class.
+ */
+final class FlashlightManager {
+
+ private static final String TAG = FlashlightManager.class.getSimpleName();
+
+ private static final Object iHardwareService;
+ private static final Method setFlashEnabledMethod;
+
+ static {
+ iHardwareService = getHardwareService();
+ setFlashEnabledMethod = getSetFlashEnabledMethod(iHardwareService);
+ if (iHardwareService == null) {
+ Log.v(TAG, "This device does supports control of a flashlight");
+ } else {
+ Log.v(TAG, "This device does not support control of a flashlight");
+ }
+ }
+
+ private FlashlightManager() {
+ }
+
+ /**
+ * �����������ƿ���
+ */
+ //FIXME
+ static void enableFlashlight() {
+ setFlashlight(false);
+ }
+
+ static void disableFlashlight() {
+ setFlashlight(false);
+ }
+
+ private static Object getHardwareService() {
+ Class> serviceManagerClass = maybeForName("android.os.ServiceManager");
+ if (serviceManagerClass == null) {
+ return null;
+ }
+
+ Method getServiceMethod = maybeGetMethod(serviceManagerClass, "getService", String.class);
+ if (getServiceMethod == null) {
+ return null;
+ }
+
+ Object hardwareService = invoke(getServiceMethod, null, "hardware");
+ if (hardwareService == null) {
+ return null;
+ }
+
+ Class> iHardwareServiceStubClass = maybeForName("android.os.IHardwareService$Stub");
+ if (iHardwareServiceStubClass == null) {
+ return null;
+ }
+
+ Method asInterfaceMethod = maybeGetMethod(iHardwareServiceStubClass, "asInterface", IBinder.class);
+ if (asInterfaceMethod == null) {
+ return null;
+ }
+
+ return invoke(asInterfaceMethod, null, hardwareService);
+ }
+
+ private static Method getSetFlashEnabledMethod(Object iHardwareService) {
+ if (iHardwareService == null) {
+ return null;
+ }
+ Class> proxyClass = iHardwareService.getClass();
+ return maybeGetMethod(proxyClass, "setFlashlightEnabled", boolean.class);
+ }
+
+ private static Class> maybeForName(String name) {
+ try {
+ return Class.forName(name);
+ } catch (ClassNotFoundException cnfe) {
+ // OK
+ return null;
+ } catch (RuntimeException re) {
+ Log.w(TAG, "Unexpected error while finding class " + name, re);
+ return null;
+ }
+ }
+
+ private static Method maybeGetMethod(Class> clazz, String name, Class>... argClasses) {
+ try {
+ return clazz.getMethod(name, argClasses);
+ } catch (NoSuchMethodException nsme) {
+ // OK
+ return null;
+ } catch (RuntimeException re) {
+ Log.w(TAG, "Unexpected error while finding method " + name, re);
+ return null;
+ }
+ }
+
+ private static Object invoke(Method method, Object instance, Object... args) {
+ try {
+ return method.invoke(instance, args);
+ } catch (IllegalAccessException e) {
+ Log.w(TAG, "Unexpected error while invoking " + method, e);
+ return null;
+ } catch (InvocationTargetException e) {
+ Log.w(TAG, "Unexpected error while invoking " + method, e.getCause());
+ return null;
+ } catch (RuntimeException re) {
+ Log.w(TAG, "Unexpected error while invoking " + method, re);
+ return null;
+ }
+ }
+
+ private static void setFlashlight(boolean active) {
+ if (iHardwareService != null) {
+ invoke(setFlashEnabledMethod, iHardwareService, active);
+ }
+ }
+
+}
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/camera/PlanarYUVLuminanceSource.java b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/camera/PlanarYUVLuminanceSource.java
new file mode 100644
index 00000000..51075f13
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/camera/PlanarYUVLuminanceSource.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2009 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package pub.doric.devkit.qrcode.camera;
+
+import android.graphics.Bitmap;
+
+import com.google.zxing.LuminanceSource;
+
+/**
+ * This object extends LuminanceSource around an array of YUV data returned from the camera driver,
+ * with the option to crop to a rectangle within the full data. This can be used to exclude
+ * superfluous pixels around the perimeter and speed up decoding.
+ *
+ * It works for any pixel format where the Y channel is planar and appears first, including
+ * YCbCr_420_SP and YCbCr_422_SP.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class PlanarYUVLuminanceSource extends LuminanceSource {
+ private final byte[] yuvData;
+ private final int dataWidth;
+ private final int dataHeight;
+ private final int left;
+ private final int top;
+
+ public PlanarYUVLuminanceSource(byte[] yuvData, int dataWidth, int dataHeight, int left, int top,
+ int width, int height) {
+ super(width, height);
+
+ if (left + width > dataWidth || top + height > dataHeight) {
+ throw new IllegalArgumentException("Crop rectangle does not fit within image data.");
+ }
+
+ this.yuvData = yuvData;
+ this.dataWidth = dataWidth;
+ this.dataHeight = dataHeight;
+ this.left = left;
+ this.top = top;
+ }
+
+ @Override
+ public byte[] getRow(int y, byte[] row) {
+ if (y < 0 || y >= getHeight()) {
+ throw new IllegalArgumentException("Requested row is outside the image: " + y);
+ }
+ int width = getWidth();
+ if (row == null || row.length < width) {
+ row = new byte[width];
+ }
+ int offset = (y + top) * dataWidth + left;
+ System.arraycopy(yuvData, offset, row, 0, width);
+ return row;
+ }
+
+ @Override
+ public byte[] getMatrix() {
+ int width = getWidth();
+ int height = getHeight();
+
+ // If the caller asks for the entire underlying image, save the copy and give them the
+ // original data. The docs specifically warn that result.length must be ignored.
+ if (width == dataWidth && height == dataHeight) {
+ return yuvData;
+ }
+
+ int area = width * height;
+ byte[] matrix = new byte[area];
+ int inputOffset = top * dataWidth + left;
+
+ // If the width matches the full width of the underlying data, perform a single copy.
+ if (width == dataWidth) {
+ System.arraycopy(yuvData, inputOffset, matrix, 0, area);
+ return matrix;
+ }
+
+ // Otherwise copy one cropped row at a time.
+ byte[] yuv = yuvData;
+ for (int y = 0; y < height; y++) {
+ int outputOffset = y * width;
+ System.arraycopy(yuv, inputOffset, matrix, outputOffset, width);
+ inputOffset += dataWidth;
+ }
+ return matrix;
+ }
+
+ @Override
+ public boolean isCropSupported() {
+ return true;
+ }
+
+ public int getDataWidth() {
+ return dataWidth;
+ }
+
+ public int getDataHeight() {
+ return dataHeight;
+ }
+
+ public Bitmap renderCroppedGreyscaleBitmap() {
+ int width = getWidth();
+ int height = getHeight();
+ int[] pixels = new int[width * height];
+ byte[] yuv = yuvData;
+ int inputOffset = top * dataWidth + left;
+
+ for (int y = 0; y < height; y++) {
+ int outputOffset = y * width;
+ for (int x = 0; x < width; x++) {
+ int grey = yuv[inputOffset + x] & 0xff;
+ pixels[outputOffset + x] = 0xFF000000 | (grey * 0x00010101);
+ }
+ inputOffset += dataWidth;
+ }
+
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
+ return bitmap;
+ }
+}
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/camera/PreviewCallback.java b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/camera/PreviewCallback.java
new file mode 100644
index 00000000..f4bbec08
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/camera/PreviewCallback.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package pub.doric.devkit.qrcode.camera;
+
+import android.graphics.Point;
+import android.hardware.Camera;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+public final class PreviewCallback implements Camera.PreviewCallback {
+
+ private static final String TAG = PreviewCallback.class.getSimpleName();
+
+ private final CameraConfigurationManager configManager;
+ private final boolean useOneShotPreviewCallback;
+ private Handler previewHandler;
+ private int previewMessage;
+
+ PreviewCallback(CameraConfigurationManager configManager, boolean useOneShotPreviewCallback) {
+ this.configManager = configManager;
+ this.useOneShotPreviewCallback = useOneShotPreviewCallback;
+ }
+
+ public void setHandler(Handler previewHandler, int previewMessage) {
+ this.previewHandler = previewHandler;
+ this.previewMessage = previewMessage;
+ }
+
+ public void onPreviewFrame(byte[] data, Camera camera) {
+ Point cameraResolution = configManager.getCameraResolution();
+ if (!useOneShotPreviewCallback) {
+ camera.setPreviewCallback(null);
+ }
+ if (previewHandler != null) {
+ Message message = previewHandler.obtainMessage(previewMessage, cameraResolution.x,
+ cameraResolution.y, data);
+ message.sendToTarget();
+ previewHandler = null;
+ } else {
+ Log.d(TAG, "Got preview callback, but no handler for it");
+ }
+ }
+
+}
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/decoding/CaptureActivityHandler.java b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/decoding/CaptureActivityHandler.java
new file mode 100644
index 00000000..b2515bf8
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/decoding/CaptureActivityHandler.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package pub.doric.devkit.qrcode.decoding;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.Result;
+
+import java.util.Vector;
+
+import pub.doric.devkit.R;
+import pub.doric.devkit.qrcode.activity.CaptureFragment;
+import pub.doric.devkit.qrcode.camera.CameraManager;
+import pub.doric.devkit.qrcode.view.ViewfinderResultPointCallback;
+import pub.doric.devkit.qrcode.view.ViewfinderView;
+
+/**
+ * This class handles all the messaging which comprises the state machine for capture.
+ */
+public final class CaptureActivityHandler extends Handler {
+
+ private static final String TAG = CaptureActivityHandler.class.getSimpleName();
+
+ private final CaptureFragment fragment;
+ private final DecodeThread decodeThread;
+ private State state;
+
+ private enum State {
+ PREVIEW,
+ SUCCESS,
+ DONE
+ }
+
+ public CaptureActivityHandler(CaptureFragment fragment, Vector decodeFormats,
+ String characterSet, ViewfinderView viewfinderView) {
+ this.fragment = fragment;
+ decodeThread = new DecodeThread(fragment, decodeFormats, characterSet,
+ new ViewfinderResultPointCallback(viewfinderView));
+ decodeThread.start();
+ state = State.SUCCESS;
+ // Start ourselves capturing previews and decoding.
+ CameraManager.get().startPreview();
+ restartPreviewAndDecode();
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == R.id.auto_focus) {
+ //Log.d(TAG, "Got auto-focus message");
+ // When one auto focus pass finishes, start another. This is the closest thing to
+ // continuous AF. It does seem to hunt a bit, but I'm not sure what else to do.
+ if (state == State.PREVIEW) {
+ CameraManager.get().requestAutoFocus(this, R.id.auto_focus);
+ }
+ } else if (message.what == R.id.restart_preview) {
+ Log.d(TAG, "Got restart preview message");
+ restartPreviewAndDecode();
+ } else if (message.what == R.id.decode_succeeded) {
+ Log.d(TAG, "Got decode succeeded message");
+ state = State.SUCCESS;
+ Bundle bundle = message.getData();
+
+ /***********************************************************************/
+ Bitmap barcode = bundle == null ? null :
+ (Bitmap) bundle.getParcelable(DecodeThread.BARCODE_BITMAP);
+
+ fragment.handleDecode((Result) message.obj, barcode);
+ /***********************************************************************/
+ } else if (message.what == R.id.decode_failed) {
+ // We're decoding as fast as possible, so when one decode fails, start another.
+ state = State.PREVIEW;
+ CameraManager.get().requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
+ } else if (message.what == R.id.return_scan_result) {
+ Log.d(TAG, "Got return scan result message");
+ fragment.getActivity().setResult(Activity.RESULT_OK, (Intent) message.obj);
+ fragment.getActivity().finish();
+ } else if (message.what == R.id.launch_product_query) {
+ Log.d(TAG, "Got product query message");
+ String url = (String) message.obj;
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+ fragment.getActivity().startActivity(intent);
+ }
+ }
+
+ public void quitSynchronously() {
+ state = State.DONE;
+ CameraManager.get().stopPreview();
+ Message quit = Message.obtain(decodeThread.getHandler(), R.id.quit);
+ quit.sendToTarget();
+ try {
+ decodeThread.join();
+ } catch (InterruptedException e) {
+ // continue
+ }
+
+ // Be absolutely sure we don't send any queued up messages
+ removeMessages(R.id.decode_succeeded);
+ removeMessages(R.id.decode_failed);
+ }
+
+ private void restartPreviewAndDecode() {
+ if (state == State.SUCCESS) {
+ state = State.PREVIEW;
+ CameraManager.get().requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
+ CameraManager.get().requestAutoFocus(this, R.id.auto_focus);
+ fragment.drawViewfinder();
+ }
+ }
+
+}
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/decoding/DecodeFormatManager.java b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/decoding/DecodeFormatManager.java
new file mode 100644
index 00000000..2d7ad430
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/decoding/DecodeFormatManager.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package pub.doric.devkit.qrcode.decoding;
+
+import android.content.Intent;
+import android.net.Uri;
+
+import com.google.zxing.BarcodeFormat;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Vector;
+import java.util.regex.Pattern;
+
+public class DecodeFormatManager {
+
+ private static final Pattern COMMA_PATTERN = Pattern.compile(",");
+
+ public static final Vector PRODUCT_FORMATS;
+ public static final Vector ONE_D_FORMATS;
+ public static final Vector QR_CODE_FORMATS;
+ public static final Vector DATA_MATRIX_FORMATS;
+
+ static {
+ PRODUCT_FORMATS = new Vector(5);
+ PRODUCT_FORMATS.add(BarcodeFormat.UPC_A);
+ PRODUCT_FORMATS.add(BarcodeFormat.UPC_E);
+ PRODUCT_FORMATS.add(BarcodeFormat.EAN_13);
+ PRODUCT_FORMATS.add(BarcodeFormat.EAN_8);
+ // PRODUCT_FORMATS.add(BarcodeFormat.RSS14);
+ ONE_D_FORMATS = new Vector(PRODUCT_FORMATS.size() + 4);
+ ONE_D_FORMATS.addAll(PRODUCT_FORMATS);
+ ONE_D_FORMATS.add(BarcodeFormat.CODE_39);
+ ONE_D_FORMATS.add(BarcodeFormat.CODE_93);
+ ONE_D_FORMATS.add(BarcodeFormat.CODE_128);
+ ONE_D_FORMATS.add(BarcodeFormat.ITF);
+ QR_CODE_FORMATS = new Vector(1);
+ QR_CODE_FORMATS.add(BarcodeFormat.QR_CODE);
+ DATA_MATRIX_FORMATS = new Vector(1);
+ DATA_MATRIX_FORMATS.add(BarcodeFormat.DATA_MATRIX);
+ }
+
+ private DecodeFormatManager() {
+ }
+
+ static Vector parseDecodeFormats(Intent intent) {
+ List scanFormats = null;
+ String scanFormatsString = intent.getStringExtra(Intents.Scan.SCAN_FORMATS);
+ if (scanFormatsString != null) {
+ scanFormats = Arrays.asList(COMMA_PATTERN.split(scanFormatsString));
+ }
+ return parseDecodeFormats(scanFormats, intent.getStringExtra(Intents.Scan.MODE));
+ }
+
+ static Vector parseDecodeFormats(Uri inputUri) {
+ List formats = inputUri.getQueryParameters(Intents.Scan.SCAN_FORMATS);
+ if (formats != null && formats.size() == 1 && formats.get(0) != null) {
+ formats = Arrays.asList(COMMA_PATTERN.split(formats.get(0)));
+ }
+ return parseDecodeFormats(formats, inputUri.getQueryParameter(Intents.Scan.MODE));
+ }
+
+ private static Vector parseDecodeFormats(Iterable scanFormats,
+ String decodeMode) {
+ if (scanFormats != null) {
+ Vector formats = new Vector();
+ try {
+ for (String format : scanFormats) {
+ formats.add(BarcodeFormat.valueOf(format));
+ }
+ return formats;
+ } catch (IllegalArgumentException iae) {
+ // ignore it then
+ }
+ }
+ if (decodeMode != null) {
+ if (Intents.Scan.PRODUCT_MODE.equals(decodeMode)) {
+ return PRODUCT_FORMATS;
+ }
+ if (Intents.Scan.QR_CODE_MODE.equals(decodeMode)) {
+ return QR_CODE_FORMATS;
+ }
+ if (Intents.Scan.DATA_MATRIX_MODE.equals(decodeMode)) {
+ return DATA_MATRIX_FORMATS;
+ }
+ if (Intents.Scan.ONE_D_MODE.equals(decodeMode)) {
+ return ONE_D_FORMATS;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/decoding/DecodeHandler.java b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/decoding/DecodeHandler.java
new file mode 100644
index 00000000..71bc825d
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/decoding/DecodeHandler.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package pub.doric.devkit.qrcode.decoding;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+import com.google.zxing.BinaryBitmap;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.MultiFormatReader;
+import com.google.zxing.ReaderException;
+import com.google.zxing.Result;
+import com.google.zxing.common.HybridBinarizer;
+
+import java.util.Hashtable;
+
+import pub.doric.devkit.R;
+import pub.doric.devkit.qrcode.activity.CaptureFragment;
+import pub.doric.devkit.qrcode.camera.CameraManager;
+import pub.doric.devkit.qrcode.camera.PlanarYUVLuminanceSource;
+
+final class DecodeHandler extends Handler {
+
+ private static final String TAG = DecodeHandler.class.getSimpleName();
+
+ private final CaptureFragment fragment;
+ private final MultiFormatReader multiFormatReader;
+
+ DecodeHandler(CaptureFragment fragment, Hashtable hints) {
+ multiFormatReader = new MultiFormatReader();
+ multiFormatReader.setHints(hints);
+ this.fragment = fragment;
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == R.id.decode) {
+ decode((byte[]) message.obj, message.arg1, message.arg2);
+ } else if (message.what == R.id.quit) {
+ Looper.myLooper().quit();
+ }
+ }
+
+ /**
+ * Decode the data within the viewfinder rectangle, and time how long it took. For efficiency,
+ * reuse the same reader objects from one decode to the next.
+ *
+ * @param data The YUV preview frame.
+ * @param width The width of the preview frame.
+ * @param height The height of the preview frame.
+ */
+ private void decode(byte[] data, int width, int height) {
+ long start = System.currentTimeMillis();
+ Result rawResult = null;
+
+ //modify here
+ byte[] rotatedData = new byte[data.length];
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++)
+ rotatedData[x * height + height - y - 1] = data[x + y * width];
+ }
+ int tmp = width; // Here we are swapping, that's the difference to #11
+ width = height;
+ height = tmp;
+
+ PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(rotatedData, width, height);
+ BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
+ try {
+ rawResult = multiFormatReader.decodeWithState(bitmap);
+ } catch (ReaderException re) {
+ // continue
+ } finally {
+ multiFormatReader.reset();
+ }
+
+ if (rawResult != null) {
+ long end = System.currentTimeMillis();
+ Log.d(TAG, "Found barcode (" + (end - start) + " ms):\n" + rawResult.toString());
+ Message message = Message.obtain(fragment.getHandler(), R.id.decode_succeeded, rawResult);
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(DecodeThread.BARCODE_BITMAP, source.renderCroppedGreyscaleBitmap());
+ message.setData(bundle);
+ //Log.d(TAG, "Sending decode succeeded message...");
+ message.sendToTarget();
+ } else {
+ Message message = Message.obtain(fragment.getHandler(), R.id.decode_failed);
+ message.sendToTarget();
+ }
+ }
+
+}
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/decoding/DecodeThread.java b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/decoding/DecodeThread.java
new file mode 100644
index 00000000..376f0f34
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/decoding/DecodeThread.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package pub.doric.devkit.qrcode.decoding;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.ResultPointCallback;
+
+import java.util.Hashtable;
+import java.util.Vector;
+import java.util.concurrent.CountDownLatch;
+
+import pub.doric.devkit.qrcode.activity.CaptureFragment;
+
+/**
+ * This thread does all the heavy lifting of decoding the images.
+ */
+final class DecodeThread extends Thread {
+
+ public static final String BARCODE_BITMAP = "barcode_bitmap";
+ private final CaptureFragment fragment;
+ private final Hashtable hints;
+ private Handler handler;
+ private final CountDownLatch handlerInitLatch;
+
+ DecodeThread(CaptureFragment fragment,
+ Vector decodeFormats,
+ String characterSet,
+ ResultPointCallback resultPointCallback) {
+
+ this.fragment = fragment;
+ handlerInitLatch = new CountDownLatch(1);
+
+ hints = new Hashtable(3);
+
+ if (decodeFormats == null || decodeFormats.isEmpty()) {
+ decodeFormats = new Vector();
+ decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS);
+ decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
+ decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);
+ }
+
+ hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
+
+ if (characterSet != null) {
+ hints.put(DecodeHintType.CHARACTER_SET, characterSet);
+ }
+
+ hints.put(DecodeHintType.NEED_RESULT_POINT_CALLBACK, resultPointCallback);
+ }
+
+ Handler getHandler() {
+ try {
+ handlerInitLatch.await();
+ } catch (InterruptedException ie) {
+ // continue?
+ }
+ return handler;
+ }
+
+ @Override
+ public void run() {
+ Looper.prepare();
+ handler = new DecodeHandler(fragment, hints);
+ handlerInitLatch.countDown();
+ Looper.loop();
+ }
+
+}
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/decoding/FinishListener.java b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/decoding/FinishListener.java
new file mode 100644
index 00000000..69f1b5d5
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/decoding/FinishListener.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package pub.doric.devkit.qrcode.decoding;
+
+import android.app.Activity;
+import android.content.DialogInterface;
+
+/**
+ * Simple listener used to exit the app in a few cases.
+ */
+public final class FinishListener
+ implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener, Runnable {
+
+ private final Activity activityToFinish;
+
+ public FinishListener(Activity activityToFinish) {
+ this.activityToFinish = activityToFinish;
+ }
+
+ public void onCancel(DialogInterface dialogInterface) {
+ run();
+ }
+
+ public void onClick(DialogInterface dialogInterface, int i) {
+ run();
+ }
+
+ public void run() {
+ activityToFinish.finish();
+ }
+
+}
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/decoding/InactivityTimer.java b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/decoding/InactivityTimer.java
new file mode 100644
index 00000000..0c2bf389
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/decoding/InactivityTimer.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package pub.doric.devkit.qrcode.decoding;
+
+import android.app.Activity;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Finishes an activity after a period of inactivity.
+ */
+public final class InactivityTimer {
+
+ private static final int INACTIVITY_DELAY_SECONDS = 5 * 60;
+
+ private final ScheduledExecutorService inactivityTimer =
+ Executors.newSingleThreadScheduledExecutor(new DaemonThreadFactory());
+ private final Activity activity;
+ private ScheduledFuture> inactivityFuture = null;
+
+ public InactivityTimer(Activity activity) {
+ this.activity = activity;
+ onActivity();
+ }
+
+ public void onActivity() {
+ cancel();
+ inactivityFuture = inactivityTimer.schedule(new FinishListener(activity),
+ INACTIVITY_DELAY_SECONDS,
+ TimeUnit.SECONDS);
+ }
+
+ private void cancel() {
+ if (inactivityFuture != null) {
+ inactivityFuture.cancel(true);
+ inactivityFuture = null;
+ }
+ }
+
+ public void shutdown() {
+ cancel();
+ inactivityTimer.shutdown();
+ }
+
+ private static final class DaemonThreadFactory implements ThreadFactory {
+ public Thread newThread(Runnable runnable) {
+ Thread thread = new Thread(runnable);
+ thread.setDaemon(true);
+ return thread;
+ }
+ }
+
+}
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/decoding/Intents.java b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/decoding/Intents.java
new file mode 100644
index 00000000..e11bf214
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/decoding/Intents.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package pub.doric.devkit.qrcode.decoding;
+
+/**
+ * This class provides the constants to use when sending an Intent to Barcode Scanner.
+ * These strings are effectively API and cannot be changed.
+ */
+public final class Intents {
+ private Intents() {
+ }
+
+ public static final class Scan {
+ /**
+ * Send this intent to open the Barcodes app in scanning mode, find a barcode, and return
+ * the results.
+ */
+ public static final String ACTION = "com.google.zxing.client.android.SCAN";
+
+ /**
+ * By default, sending Scan.ACTION will decode all barcodes that we understand. However it
+ * may be useful to limit scanning to certain formats. Use Intent.putExtra(MODE, value) with
+ * one of the values below ({@link #PRODUCT_MODE}, {@link #ONE_D_MODE}, {@link #QR_CODE_MODE}).
+ * Optional.
+ *
+ * Setting this is effectively shorthnad for setting explicit formats with {@link #SCAN_FORMATS}.
+ * It is overridden by that setting.
+ */
+ public static final String MODE = "SCAN_MODE";
+
+ /**
+ * Comma-separated list of formats to scan for. The values must match the names of
+ * {@link com.google.zxing.BarcodeFormat}s, such as {@link com.google.zxing.BarcodeFormat#EAN_13}.
+ * Example: "EAN_13,EAN_8,QR_CODE"
+ *
+ * This overrides {@link #MODE}.
+ */
+ public static final String SCAN_FORMATS = "SCAN_FORMATS";
+
+ /**
+ * @see com.google.zxing.DecodeHintType#CHARACTER_SET
+ */
+ public static final String CHARACTER_SET = "CHARACTER_SET";
+
+ /**
+ * Decode only UPC and EAN barcodes. This is the right choice for shopping apps which get
+ * prices, reviews, etc. for products.
+ */
+ public static final String PRODUCT_MODE = "PRODUCT_MODE";
+
+ /**
+ * Decode only 1D barcodes (currently UPC, EAN, Code 39, and Code 128).
+ */
+ public static final String ONE_D_MODE = "ONE_D_MODE";
+
+ /**
+ * Decode only QR codes.
+ */
+ public static final String QR_CODE_MODE = "QR_CODE_MODE";
+
+ /**
+ * Decode only Data Matrix codes.
+ */
+ public static final String DATA_MATRIX_MODE = "DATA_MATRIX_MODE";
+
+ /**
+ * If a barcode is found, Barcodes returns RESULT_OK to onActivityResult() of the app which
+ * requested the scan via startSubActivity(). The barcodes contents can be retrieved with
+ * intent.getStringExtra(RESULT). If the user presses Back, the result code will be
+ * RESULT_CANCELED.
+ */
+ public static final String RESULT = "SCAN_RESULT";
+
+ /**
+ * Call intent.getStringExtra(RESULT_FORMAT) to determine which barcode format was found.
+ * See Contents.Format for possible values.
+ */
+ public static final String RESULT_FORMAT = "SCAN_RESULT_FORMAT";
+
+ /**
+ * Setting this to false will not save scanned codes in the history.
+ */
+ public static final String SAVE_HISTORY = "SAVE_HISTORY";
+
+ private Scan() {
+ }
+ }
+
+ public static final class Encode {
+ /**
+ * Send this intent to encode a piece of data as a QR code and display it full screen, so
+ * that another person can scan the barcode from your screen.
+ */
+ public static final String ACTION = "com.google.zxing.client.android.ENCODE";
+
+ /**
+ * The data to encode. Use Intent.putExtra(DATA, data) where data is either a String or a
+ * Bundle, depending on the type and format specified. Non-QR Code formats should
+ * just use a String here. For QR Code, see Contents for details.
+ */
+ public static final String DATA = "ENCODE_DATA";
+
+ /**
+ * The type of data being supplied if the format is QR Code. Use
+ * Intent.putExtra(TYPE, type) with one of Contents.Type.
+ */
+ public static final String TYPE = "ENCODE_TYPE";
+
+ /**
+ * The barcode format to be displayed. If this isn't specified or is blank,
+ * it defaults to QR Code. Use Intent.putExtra(FORMAT, format), where
+ * format is one of Contents.Format.
+ */
+ public static final String FORMAT = "ENCODE_FORMAT";
+
+ private Encode() {
+ }
+ }
+
+ public static final class SearchBookContents {
+ /**
+ * Use Google Book Search to search the contents of the book provided.
+ */
+ public static final String ACTION = "com.google.zxing.client.android.SEARCH_BOOK_CONTENTS";
+
+ /**
+ * The book to search, identified by ISBN number.
+ */
+ public static final String ISBN = "ISBN";
+
+ /**
+ * An optional field which is the text to search for.
+ */
+ public static final String QUERY = "QUERY";
+
+ private SearchBookContents() {
+ }
+ }
+
+ public static final class WifiConnect {
+ /**
+ * Internal intent used to trigger connection to a wi-fi network.
+ */
+ public static final String ACTION = "com.google.zxing.client.android.WIFI_CONNECT";
+
+ /**
+ * The network to connect to, all the configuration provided here.
+ */
+ public static final String SSID = "SSID";
+
+ /**
+ * The network to connect to, all the configuration provided here.
+ */
+ public static final String TYPE = "TYPE";
+
+ /**
+ * The network to connect to, all the configuration provided here.
+ */
+ public static final String PASSWORD = "PASSWORD";
+
+ private WifiConnect() {
+ }
+ }
+
+
+ public static final class Share {
+ /**
+ * Give the user a choice of items to encode as a barcode, then render it as a QR Code and
+ * display onscreen for a friend to scan with their phone.
+ */
+ public static final String ACTION = "com.google.zxing.client.android.SHARE";
+
+ private Share() {
+ }
+ }
+}
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/view/ViewfinderResultPointCallback.java b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/view/ViewfinderResultPointCallback.java
new file mode 100644
index 00000000..a7a7d688
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/view/ViewfinderResultPointCallback.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package pub.doric.devkit.qrcode.view;
+
+import com.google.zxing.ResultPoint;
+import com.google.zxing.ResultPointCallback;
+
+public final class ViewfinderResultPointCallback implements ResultPointCallback {
+
+ private final ViewfinderView viewfinderView;
+
+ public ViewfinderResultPointCallback(ViewfinderView viewfinderView) {
+ this.viewfinderView = viewfinderView;
+ }
+
+ public void foundPossibleResultPoint(ResultPoint point) {
+ viewfinderView.addPossibleResultPoint(point);
+ }
+
+}
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/view/ViewfinderView.java b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/view/ViewfinderView.java
new file mode 100644
index 00000000..eb7e9dff
--- /dev/null
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/qrcode/view/ViewfinderView.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package pub.doric.devkit.qrcode.view;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.google.zxing.ResultPoint;
+
+import java.util.Collection;
+import java.util.HashSet;
+
+import pub.doric.devkit.R;
+import pub.doric.devkit.qrcode.DisplayUtil;
+import pub.doric.devkit.qrcode.camera.CameraManager;
+
+
+public final class ViewfinderView extends View {
+
+ private static final long ANIMATION_DELAY = 100L;
+ private static final int OPAQUE = 0xFF;
+
+ private final Paint paint;
+ private Bitmap resultBitmap;
+ private final int maskColor;
+ private final int resultColor;
+ private final int resultPointColor;
+ private Collection possibleResultPoints;
+ private Collection lastPossibleResultPoints;
+
+ public ViewfinderView(Context context) {
+ this(context, null);
+ }
+
+ public ViewfinderView(Context context, AttributeSet attrs) {
+ this(context, attrs, -1);
+
+ }
+
+ public ViewfinderView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ paint = new Paint();
+ Resources resources = getResources();
+ maskColor = resources.getColor(R.color.viewfinder_mask);
+ resultColor = resources.getColor(R.color.result_view);
+ resultPointColor = resources.getColor(R.color.possible_result_points);
+ possibleResultPoints = new HashSet<>(5);
+
+ scanLight = BitmapFactory.decodeResource(resources,
+ R.drawable.scan_light);
+
+ initInnerRect(context, attrs);
+ }
+
+ /**
+ * 初始化内部框的大小
+ *
+ * @param context
+ * @param attrs
+ */
+ private void initInnerRect(Context context, AttributeSet attrs) {
+ TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ViewfinderView);
+
+ // 扫描框距离顶部
+ float innerMarginTop = ta.getDimension(R.styleable.ViewfinderView_inner_margintop, -1);
+ if (innerMarginTop != -1) {
+ CameraManager.FRAME_MARGINTOP = (int) innerMarginTop;
+ }
+
+ // 扫描框的宽度
+ CameraManager.FRAME_WIDTH = (int) ta.getDimension(R.styleable.ViewfinderView_inner_width, DisplayUtil.screenWidthPx / 2);
+
+ // 扫描框的高度
+ CameraManager.FRAME_HEIGHT = (int) ta.getDimension(R.styleable.ViewfinderView_inner_height, DisplayUtil.screenWidthPx / 2);
+
+ // 扫描框边角颜色
+ innercornercolor = ta.getColor(R.styleable.ViewfinderView_inner_corner_color, Color.parseColor("#45DDDD"));
+ // 扫描框边角长度
+ innercornerlength = (int) ta.getDimension(R.styleable.ViewfinderView_inner_corner_length, 65);
+ // 扫描框边角宽度
+ innercornerwidth = (int) ta.getDimension(R.styleable.ViewfinderView_inner_corner_width, 15);
+
+ // 扫描bitmap
+ Drawable drawable = ta.getDrawable(R.styleable.ViewfinderView_inner_scan_bitmap);
+ if (drawable != null) {
+ }
+
+ // 扫描控件
+ scanLight = BitmapFactory.decodeResource(getResources(), ta.getResourceId(R.styleable.ViewfinderView_inner_scan_bitmap, R.drawable.scan_light));
+ // 扫描速度
+ SCAN_VELOCITY = ta.getInt(R.styleable.ViewfinderView_inner_scan_speed, 5);
+
+ isCircle = ta.getBoolean(R.styleable.ViewfinderView_inner_scan_iscircle, true);
+
+ ta.recycle();
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ Rect frame = CameraManager.get().getFramingRect();
+ if (frame == null) {
+ return;
+ }
+ int width = canvas.getWidth();
+ int height = canvas.getHeight();
+
+ // Draw the exterior (i.e. outside the framing rect) darkened
+ paint.setColor(resultBitmap != null ? resultColor : maskColor);
+ canvas.drawRect(0, 0, width, frame.top, paint);
+ canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);
+ canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, paint);
+ canvas.drawRect(0, frame.bottom + 1, width, height, paint);
+
+ if (resultBitmap != null) {
+ // Draw the opaque result bitmap over the scanning rectangle
+ paint.setAlpha(OPAQUE);
+ canvas.drawBitmap(resultBitmap, frame.left, frame.top, paint);
+ } else {
+
+ drawFrameBounds(canvas, frame);
+
+ drawScanLight(canvas, frame);
+
+ Collection currentPossible = possibleResultPoints;
+ Collection currentLast = lastPossibleResultPoints;
+ if (currentPossible.isEmpty()) {
+ lastPossibleResultPoints = null;
+ } else {
+ possibleResultPoints = new HashSet(5);
+ lastPossibleResultPoints = currentPossible;
+ paint.setAlpha(OPAQUE);
+ paint.setColor(resultPointColor);
+
+ if (isCircle) {
+ for (ResultPoint point : currentPossible) {
+ canvas.drawCircle(frame.left + point.getX(), frame.top + point.getY(), 6.0f, paint);
+ }
+ }
+ }
+ if (currentLast != null) {
+ paint.setAlpha(OPAQUE / 2);
+ paint.setColor(resultPointColor);
+
+ if (isCircle) {
+ for (ResultPoint point : currentLast) {
+ canvas.drawCircle(frame.left + point.getX(), frame.top + point.getY(), 3.0f, paint);
+ }
+ }
+ }
+
+ postInvalidateDelayed(ANIMATION_DELAY, frame.left, frame.top, frame.right, frame.bottom);
+ }
+ }
+
+ // 扫描线移动的y
+ private int scanLineTop;
+ // 扫描线移动速度
+ private int SCAN_VELOCITY;
+ // 扫描线
+ private Bitmap scanLight;
+ // 是否展示小圆点
+ private boolean isCircle;
+
+ /**
+ * 绘制移动扫描线
+ *
+ * @param canvas
+ * @param frame
+ */
+ private void drawScanLight(Canvas canvas, Rect frame) {
+
+ if (scanLineTop == 0) {
+ scanLineTop = frame.top;
+ }
+
+ if (scanLineTop >= frame.bottom - 30) {
+ scanLineTop = frame.top;
+ } else {
+ scanLineTop += SCAN_VELOCITY;
+ }
+ Rect scanRect = new Rect(frame.left, scanLineTop, frame.right,
+ scanLineTop + 30);
+ canvas.drawBitmap(scanLight, null, scanRect, paint);
+ }
+
+
+ // 扫描框边角颜色
+ private int innercornercolor;
+ // 扫描框边角长度
+ private int innercornerlength;
+ // 扫描框边角宽度
+ private int innercornerwidth;
+
+ /**
+ * 绘制取景框边框
+ *
+ * @param canvas
+ * @param frame
+ */
+ private void drawFrameBounds(Canvas canvas, Rect frame) {
+
+ /*paint.setColor(Color.WHITE);
+ paint.setStrokeWidth(2);
+ paint.setStyle(Paint.Style.STROKE);
+
+ canvas.drawRect(frame, paint);*/
+
+ paint.setColor(innercornercolor);
+ paint.setStyle(Paint.Style.FILL);
+
+ int corWidth = innercornerwidth;
+ int corLength = innercornerlength;
+
+ // 左上角
+ canvas.drawRect(frame.left, frame.top, frame.left + corWidth, frame.top
+ + corLength, paint);
+ canvas.drawRect(frame.left, frame.top, frame.left
+ + corLength, frame.top + corWidth, paint);
+ // 右上角
+ canvas.drawRect(frame.right - corWidth, frame.top, frame.right,
+ frame.top + corLength, paint);
+ canvas.drawRect(frame.right - corLength, frame.top,
+ frame.right, frame.top + corWidth, paint);
+ // 左下角
+ canvas.drawRect(frame.left, frame.bottom - corLength,
+ frame.left + corWidth, frame.bottom, paint);
+ canvas.drawRect(frame.left, frame.bottom - corWidth, frame.left
+ + corLength, frame.bottom, paint);
+ // 右下角
+ canvas.drawRect(frame.right - corWidth, frame.bottom - corLength,
+ frame.right, frame.bottom, paint);
+ canvas.drawRect(frame.right - corLength, frame.bottom - corWidth,
+ frame.right, frame.bottom, paint);
+ }
+
+
+ public void drawViewfinder() {
+ resultBitmap = null;
+ invalidate();
+ }
+
+ public void addPossibleResultPoint(ResultPoint point) {
+ possibleResultPoints.add(point);
+ }
+
+
+ /**
+ * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
+ */
+ public static int dip2px(Context context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale + 0.5f);
+ }
+
+
+}
diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/ui/DoricDevActivity.java b/doric-android/devkit/src/main/java/pub/doric/devkit/ui/DoricDevActivity.java
index aafe5446..b5900ecd 100644
--- a/doric-android/devkit/src/main/java/pub/doric/devkit/ui/DoricDevActivity.java
+++ b/doric-android/devkit/src/main/java/pub/doric/devkit/ui/DoricDevActivity.java
@@ -18,9 +18,6 @@ import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.google.gson.JsonObject;
-import com.uuzuche.lib_zxing.activity.CaptureActivity;
-import com.uuzuche.lib_zxing.activity.CodeUtils;
-import com.uuzuche.lib_zxing.activity.ZXingLibrary;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
@@ -36,14 +33,18 @@ import pub.doric.devkit.event.ConnectExceptionEvent;
import pub.doric.devkit.event.EOFExceptionEvent;
import pub.doric.devkit.event.OpenEvent;
import pub.doric.devkit.event.StartDebugEvent;
+import pub.doric.devkit.qrcode.DisplayUtil;
+import pub.doric.devkit.qrcode.activity.CaptureActivity;
+import pub.doric.devkit.qrcode.activity.CodeUtils;
public class DoricDevActivity extends AppCompatActivity {
- private int REQUEST_CODE=100;
+ private int REQUEST_CODE = 100;
+
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventBus.getDefault().register(this);
- ZXingLibrary.initDisplayOpinion(this);
+ DisplayUtil.initDisplayOpinion(getApplicationContext());
setContentView(R.layout.layout_debug_context);
initDisconnect();
if (DoricDev.getInstance().isInDevMode()) {
@@ -122,14 +123,14 @@ public class DoricDevActivity extends AppCompatActivity {
finish();
}
- private void initDisconnect(){
+ private void initDisconnect() {
LinearLayout container = findViewById(R.id.container);
- Button button=new Button(this);
+ Button button = new Button(this);
button.setText("断开连接");
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- if(DoricDev.getInstance().isInDevMode()){
+ if (DoricDev.getInstance().isInDevMode()) {
DoricDev.getInstance().closeDevMode();
}
finish();
@@ -137,6 +138,7 @@ public class DoricDevActivity extends AppCompatActivity {
});
container.addView(button);
}
+
private void initViews() {
LinearLayout container = findViewById(R.id.container);
LayoutInflater inflater = LayoutInflater.from(this);
diff --git a/doric-android/devkit/src/main/res/drawable/scan_light.png b/doric-android/devkit/src/main/res/drawable/scan_light.png
new file mode 100644
index 00000000..0ed22ab3
Binary files /dev/null and b/doric-android/devkit/src/main/res/drawable/scan_light.png differ
diff --git a/doric-android/devkit/src/main/res/layout/camera.xml b/doric-android/devkit/src/main/res/layout/camera.xml
new file mode 100644
index 00000000..6f4ac3fe
--- /dev/null
+++ b/doric-android/devkit/src/main/res/layout/camera.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/doric-android/devkit/src/main/res/layout/fragment_capture.xml b/doric-android/devkit/src/main/res/layout/fragment_capture.xml
new file mode 100644
index 00000000..93ccdba7
--- /dev/null
+++ b/doric-android/devkit/src/main/res/layout/fragment_capture.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/doric-android/devkit/src/main/res/values/attrs.xml b/doric-android/devkit/src/main/res/values/attrs.xml
new file mode 100644
index 00000000..5821e1b1
--- /dev/null
+++ b/doric-android/devkit/src/main/res/values/attrs.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/doric-android/devkit/src/main/res/values/colors.xml b/doric-android/devkit/src/main/res/values/colors.xml
new file mode 100644
index 00000000..bc00646e
--- /dev/null
+++ b/doric-android/devkit/src/main/res/values/colors.xml
@@ -0,0 +1,32 @@
+
+
+ #59a9ff
+
+ #EDEDED
+ #ff000000
+ #ffffffff
+ #ffcccccc
+ #ff404040
+ #c0ffff00
+ #ffffffff
+ #ffc0c0c0
+ #c000ff00
+ #ffffffff
+ #b0000000
+ #ff808080
+ #ffffffff
+ #fffff0e0
+ #ffffffff
+ #ff000000
+ #ff4b4b4b
+ #ff000000
+ #ffffffff
+ #50000000
+ #ffffffff
+ #00000000
+ #ff000000
+ #ffff0000
+ #60000000
+ #58567D
+
+
diff --git a/doric-android/devkit/src/main/res/values/ids.xml b/doric-android/devkit/src/main/res/values/ids.xml
new file mode 100644
index 00000000..fb5c1c02
--- /dev/null
+++ b/doric-android/devkit/src/main/res/values/ids.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+