diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..294a516
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,12 @@
+*.[aod]
+*.DS_Store
+.DS_Store
+*Thumbs.db
+*.iml
+.gradle
+.idea
+node_modules
+npm-debug.log
+/android/build
+/ios/**/*xcuserdata*
+/ios/**/*xcshareddata*
\ No newline at end of file
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..294a516
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,12 @@
+*.[aod]
+*.DS_Store
+.DS_Store
+*Thumbs.db
+*.iml
+.gradle
+.idea
+node_modules
+npm-debug.log
+/android/build
+/ios/**/*xcuserdata*
+/ios/**/*xcshareddata*
\ No newline at end of file
diff --git a/Barcode.js b/Barcode.js
new file mode 100644
index 0000000..33aea36
--- /dev/null
+++ b/Barcode.js
@@ -0,0 +1,74 @@
+
+import React, {
+ PropTypes,
+ Component,
+} from 'react'
+import {
+ View,
+ requireNativeComponent,
+ NativeModules,
+ AppState,
+ Platform,
+} from 'react-native'
+
+const BarcodeManager = Platform.OS == 'ios' ? NativeModules.Barcode : NativeModules.CaptureModule;
+
+
+export default class Barcode extends Component {
+
+ static defaultProps = {
+ barCodeTypes: Object.values(BarcodeManager.barCodeTypes),
+ scannerRectWidth: 255,
+ scannerRectHeight: 255,
+ scannerRectTop: 0,
+ scannerRectLeft: 0,
+ scannerLineInterval: 3000,
+ scannerRectCornerColor: `#09BB0D`,
+ }
+
+ static propTypes = {
+ ...View.propTypes,
+ onBarCodeRead: PropTypes.func.isRequired,
+ barCodeTypes: PropTypes.array,
+ scannerRectWidth: PropTypes.number,
+ scannerRectHeight: PropTypes.number,
+ scannerRectTop: PropTypes.number,
+ scannerRectLeft: PropTypes.number,
+ scannerLineInterval: PropTypes.number,
+ scannerRectCornerColor: PropTypes.string,
+ }
+
+ render() {
+ return (
+
+ )
+ }
+
+ componentDidMount() {
+ AppState.addEventListener('change', this._handleAppStateChange);
+ }
+ componentWillUnmount() {
+ AppState.removeEventListener('change', this._handleAppStateChange);
+ }
+
+ startScan() {
+ BarcodeManager.startSession()
+ }
+
+ stopScan() {
+ BarcodeManager.stopSession()
+ }
+
+ _handleAppStateChange = (currentAppState) => {
+ if(currentAppState !== 'active' ) {
+ this.stopScan()
+ }
+ else {
+ this.startScan()
+ }
+ }
+}
+
+const NativeBarCode = requireNativeComponent(Platform.OS == 'ios' ? 'RCTBarcode' : 'CaptureView', Barcode)
diff --git a/README.md b/README.md
index 113fcb8..f8045d9 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,7 @@
-# react-native-smart-barcode
\ No newline at end of file
+# react-native-smart-barcode
+
+A smart barcode scanner component for React Native app.
+The library uses [https://github.com/zxing/zxing][1] to decode the barcodes for android.
+
+[0]: https://github.com/cyqresig/ReactNativeComponentDemos
+[1]: https://github.com/zxing/zxing
\ No newline at end of file
diff --git a/android/build.gradle b/android/build.gradle
new file mode 100644
index 0000000..75984e0
--- /dev/null
+++ b/android/build.gradle
@@ -0,0 +1,26 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 23
+ buildToolsVersion "24.0.0"
+
+ defaultConfig {
+ minSdkVersion 16
+ targetSdkVersion 23
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ compile 'com.android.support:appcompat-v7:23.4.0'
+ compile 'com.facebook.react:react-native:+'
+ compile 'com.google.zxing:core:3.2.1'
+}
diff --git a/android/proguard-rules.pro b/android/proguard-rules.pro
new file mode 100644
index 0000000..02b46d1
--- /dev/null
+++ b/android/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/cyqresig/Library/Android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..4ddd8fc
--- /dev/null
+++ b/android/src/main/AndroidManifest.xml
@@ -0,0 +1,3 @@
+
+
diff --git a/android/src/main/java/com/reactnativecomponent/barcode/CaptureView.java b/android/src/main/java/com/reactnativecomponent/barcode/CaptureView.java
new file mode 100644
index 0000000..011c8b3
--- /dev/null
+++ b/android/src/main/java/com/reactnativecomponent/barcode/CaptureView.java
@@ -0,0 +1,1090 @@
+package com.reactnativecomponent.barcode;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.hardware.Camera;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.os.Handler;
+import android.os.Vibrator;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.PopupWindow;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.Result;
+import com.reactnativecomponent.barcode.camera.CameraManager;
+import com.reactnativecomponent.barcode.decoding.CaptureActivityHandler;
+import com.reactnativecomponent.barcode.view.LinearGradientView;
+import com.reactnativecomponent.barcode.view.ViewfinderView;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Vector;
+
+
+public class CaptureView extends FrameLayout implements SurfaceHolder.Callback {
+
+
+ private CaptureActivityHandler handler;
+ private ViewfinderView viewfinderView;
+ private boolean hasSurface;
+ private Vector decodeFormats;
+ private String characterSet;
+ //private InactivityTimer inactivityTimer;
+ private MediaPlayer mediaPlayer;
+ private boolean playBeep=true;
+ private static final float BEEP_VOLUME = 0.10f;
+ private boolean vibrate;
+ private Activity activity;
+ private ViewGroup.LayoutParams param;
+ private int ScreenWidth, ScreenHeight;
+ private SurfaceView surfaceView;
+ private long beginTime;
+ private int height;
+ private int width;
+ public boolean decodeFlag = true;
+ /**
+ * 缩放级别拖动条
+ */
+ private Handler mHandler;
+ /**
+ * 当前缩放级别 默认为0
+ */
+ private int mZoom = 0;
+ /**
+ * react-native 设置属性
+ */
+ private int cX;//X轴偏移
+ private int cY;//Y轴偏移
+ private int CORNER_WIDTH = 4;//四个对应角宽度
+ private int MIDDLE_LINE_WIDTH = 3;//扫描线宽度
+ private int CORNER_COLOR = Color.GREEN;
+ private int Min_Frame_Width;//扫描框最小单位
+ //框的颜色
+ private String Text = "";//扫描框显示的文字
+ //扫描框宽
+ private int MAX_FRAME_WIDTH;
+ //扫描框高
+ private int MAX_FRAME_HEIGHT;
+ private float density;
+ /**
+ * s扫码横线的移动时间
+ */
+ public int scanTime = 1000;
+ private long changeTime = 1000;
+ private int focusTime = 1000;
+ private long sleepTime = 2000;
+ public OnEvChangeListener onEvChangeListener;
+
+ private View popupWindowContent;
+ private PopupWindow popupWindow;
+ private LinearGradientView linearGradientView;
+ SurfaceHolder holder;
+ boolean autoStart = true;//是否自动启动扫描
+ String ResultStr="";
+
+
+
+
+ /* private final VerticalSeekBar.OnSeekBarChangeListener onSeekBarChangeListener = new VerticalSeekBar.OnSeekBarChangeListener() {
+
+ @Override
+ public void onProgressChanged(VerticalSeekBar seekBar, int progress,
+ boolean fromUser) {
+ // TODO Auto-generated method stub
+
+ //setZoom(progress);
+
+ mHandler.removeCallbacksAndMessages(progressBar);
+ //ZOOM模式下 在结束四秒后隐藏seekbar 设置token为mZoomSeekBar用以在连续点击时移除前一个定时任务
+ mHandler.postAtTime(new Runnable() {
+
+ @Override
+ public void run() {
+ // TODO Auto-generated method stub
+// progressBar.setVisibility(View.GONE);
+ if (popupWindow.isShowing()) {
+ popupWindow.dismiss();
+ }
+ }
+ }, progressBar, SystemClock.uptimeMillis() + 4000);
+ }
+
+ @Override
+ public void onStartTrackingTouch(VerticalSeekBar VerticalSeekBar) {
+
+ }
+
+ @Override
+ public void onStopTrackingTouch(VerticalSeekBar VerticalSeekBar) {
+
+ }
+
+
+ };*/
+
+
+ public CaptureView(Activity activity, Context context) {
+ super(context);
+ this.activity = activity;
+ CameraManager.init(activity.getApplication());
+ param = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+ density = context.getResources().getDisplayMetrics().density;
+ Min_Frame_Width = (int) (100 * density + 0.5f);
+ Resources resources = activity.getResources();
+ DisplayMetrics dm = resources.getDisplayMetrics();
+ ScreenWidth = dm.widthPixels;
+ ScreenHeight = dm.heightPixels;
+
+ // x=screenResolution.x;
+ // y=screenResolution.y;
+
+ hasSurface = false;
+ this.setOnTouchListener(new TouchListener());
+ }
+
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ height = getHeight();
+ width = getWidth();
+ }
+
+ public ViewfinderView getViewfinderView() {
+ return viewfinderView;
+ }
+
+ public Handler getHandler() {
+ return handler;
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width,
+ int height) {
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+
+// Log.i("Test", "height:" + height + "width:" + width);
+ initCameraManager();
+ this.holder = holder;
+
+ if (autoStart) {
+ startScan();
+ }
+
+ }
+
+ private void initCameraManager() {
+
+ CameraManager.get().x = cX + width;
+ CameraManager.get().y = cY + height;
+ CameraManager.get().MIN_FRAME_WIDTH = MAX_FRAME_WIDTH;
+ CameraManager.get().MIN_FRAME_HEIGHT = MAX_FRAME_HEIGHT;
+ CameraManager.get().MAX_FRAME_WIDTH = MAX_FRAME_WIDTH;
+ CameraManager.get().MAX_FRAME_HEIGHT = MAX_FRAME_HEIGHT;
+ CameraManager.get().setFocusTime(focusTime);
+
+ }
+/*
+ @Override
+ public void onViewAdded(View child) {
+ if (this.viewfinderView == child) return;
+ // remove and readd view to make sure it is in the back.
+ // @TODO figure out why there was a z order issue in the first place and fix accordingly.
+ if (viewfinderView != null) {
+ this.removeView(this.viewfinderView);
+ this.addView(this.viewfinderView);
+ }
+ }*/
+
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+
+ stopScan();
+
+ }
+
+ /**
+ * Activity onResume后调用view的onAttachedToWindow
+ */
+ @Override
+ protected void onAttachedToWindow() {
+ init();
+ super.onAttachedToWindow();
+
+
+ }
+
+ /**
+ * surfaceview 扫码框 声音管理
+ */
+ private void init() {
+ if (mHandler == null) {
+ mHandler = new Handler();
+ }
+ surfaceView = new SurfaceView(activity);
+ surfaceView.setLayoutParams(param);
+ surfaceView.getLayoutParams().height = ScreenHeight;
+ surfaceView.getLayoutParams().width = ScreenWidth;
+ SurfaceHolder surfaceHolder = surfaceView.getHolder();
+ if (hasSurface) {
+ initCamera(surfaceHolder);
+ } else {
+ surfaceHolder.addCallback(this);
+
+ }
+ this.addView(surfaceView);
+ viewfinderView = new ViewfinderView(activity, scanTime, CORNER_COLOR);
+ viewfinderView.CORNER_WIDTH = CORNER_WIDTH;
+ viewfinderView.ShowText = Text;
+ viewfinderView.setLayoutParams(param);
+ viewfinderView.getLayoutParams().height = ScreenHeight;
+ viewfinderView.getLayoutParams().width = ScreenWidth;
+ viewfinderView.setBackgroundColor(getResources().getColor(R.color.transparent));
+ viewfinderView.setMIDDLE_LINE_WIDTH(this.MIDDLE_LINE_WIDTH);
+ this.addView(viewfinderView);
+
+ linearGradientView = new LinearGradientView(activity, activity);
+ linearGradientView.setLayoutParams(param);
+ linearGradientView.setFrameColor(CORNER_COLOR);
+
+
+// decodeFormats = null;
+ characterSet = null;
+
+
+
+ vibrate = true;
+
+ setPlayBeep(true);
+// initProgressBar();
+// progressBar = new VerticalSeekBar(activity);
+ /* popupWindowContent = View.inflate(activity, R.layout.seekbar_layout, null);
+ progressBar = (VerticalSeekBar) popupWindowContent.findViewById(R.id.verticalSeekBar);
+ // 给progressbar准备一个FrameLayout的LayoutParams
+
+
+ progressBar.setIndeterminate(false);
+ progressBar.setThumb(null);*/
+// progressBar.setProgressDrawable(getResources().getDrawable(android.R.drawable.progress_horizontal));
+
+
+//
+
+ /*
+ //渐变色drawable
+ int[] mColors= new int[]{Color.WHITE,Color.BLUE};
+ GradientDrawable drawable=new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT,mColors);
+ drawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);
+ drawable.setCornerRadius(15);
+ drawable.setStroke(10,-1);
+ */
+
+/* LayerDrawable progressDrawable = (LayerDrawable) progressBar
+ .getProgressDrawable();
+ Drawable[] outDrawables = new Drawable[progressDrawable
+ .getNumberOfLayers()];
+ for (int i = 0; i < progressDrawable.getNumberOfLayers(); i++) {
+ switch (progressDrawable.getId(i)) {
+ case android.R.id.background:// 设置进度条背景
+ outDrawables[i] = getResources().getDrawable(R.drawable.seek_bkg);
+ break;
+ case android.R.id.secondaryProgress:// 设置二级进度条
+ outDrawables[i] = getResources().getDrawable(R.drawable.seek);
+ break;
+ case android.R.id.progress:// 设置进度条
+ ClipDrawable oidDrawable = (ClipDrawable) progressDrawable
+ .getDrawable(i);
+ Drawable drawable=getResources().getDrawable(R.drawable.seek);
+ ClipDrawable proDrawable = new ClipDrawable(drawable,
+ Gravity.LEFT, ClipDrawable.HORIZONTAL);
+ proDrawable.setLevel(oidDrawable.getLevel());
+ outDrawables[i] = proDrawable;
+ break;
+ default:
+ break;
+ }
+ }
+ progressDrawable = new LayerDrawable(outDrawables);
+ progressBar.setProgressDrawable(progressDrawable);*/
+
+
+ // progressBar.setBackgroundResource(R.drawable.seek_bkg);
+// progressBar.setSecondaryProgress(R.drawable.seek);
+// progressBar.setThumb(getResources().getDrawable(R.drawable.seek_thumb));
+// progressBar.setMinimumHeight(20);
+
+
+ //获取当前照相机支持的最大缩放级别,值小于0表示不支持缩放。当支持缩放时,加入拖动条。
+ int maxZoom = getMaxZoom();
+ if (maxZoom > 0) {
+// progressBar.setMax(maxZoom);
+// progressBar.setOnSeekBarChangeListener(onSeekBarChangeListener);
+ }
+
+ }
+
+ public void setPlayBeep(boolean b) {
+ playBeep = b;
+ AudioManager audioService = (AudioManager) activity.getSystemService(activity.AUDIO_SERVICE);
+ if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
+ playBeep = false;
+ }
+ initBeepSound();
+ }
+
+
+ private void initProgressBar() {
+/* if (progressBar != null) {
+ LayoutParams progresslp = new LayoutParams(
+ 120,
+ MAX_FRAME_HEIGHT);
+ // 设置对其方式为:屏幕居中
+ int leftMargin = (width / 2) + cX + MAX_FRAME_WIDTH / 2;
+ int topMargin = height / 2 + cY / 2 - MAX_FRAME_HEIGHT;
+// progresslp.gravity = Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL;
+// progressBar.setLayoutParams(param);
+
+ // 创建PopupWindow实例,200,LayoutParams.MATCH_PARENT分别是宽度和高度
+ popupWindow = new PopupWindow(CaptureView.this);
+*//* popupWindow.setWidth((int) (20 * density));
+ popupWindow.setHeight(MAX_FRAME_HEIGHT-CORNER_WIDTH*2);
+ popupWindow.setContentView(popupWindowContent);*//*
+
+ popupWindow.setWidth(MAX_FRAME_WIDTH);
+ popupWindow.setHeight(30);
+ popupWindow.setContentView(linearGradientView);
+
+ popupWindow.setBackgroundDrawable(new BitmapDrawable());
+ popupWindow.setFocusable(false);
+ popupWindow.setOutsideTouchable(false);
+
+ }*/
+ }
+
+
+ public void startScan() {
+ if (!hasSurface) {
+ viewfinderView.drawLine = true;
+ hasSurface = true;
+ CameraManager.get().framingRectInPreview = null;
+ initCamera(holder);
+
+ CameraManager.get().initPreviewCallback();
+ CameraManager.get().startPreview();
+
+ }
+// decodeFormats = null;
+
+ handler = new CaptureActivityHandler(this, decodeFormats,
+ characterSet);
+// handler.restartPreviewAndDecode();
+ }
+
+ public void stopScan() {
+ hasSurface = false;
+ viewfinderView.drawLine = false;
+ if (handler != null) {
+ handler.quitSynchronously();
+ }
+ CameraManager.get().stopPreview();
+ CameraManager.get().closeDriver();
+ }
+
+ public void stopQR() {
+ this.decodeFlag = false;
+ }
+
+ public void startQR() {
+ this.decodeFlag = true;
+ startScan();
+ }
+
+ /**
+ * ondestroy调用,会执行onDetachedFromWindow
+ */
+
+ @Override
+ protected void onDetachedFromWindow() {
+ this.removeView(viewfinderView);
+ this.removeView(surfaceView);
+// if(popupWindow.isShowing()){
+// popupWindow.dismiss();
+// }
+ if (handler != null) {
+ handler.quitSynchronously();
+ }
+
+ super.onDetachedFromWindow();
+ }
+
+
+ private void initCamera(SurfaceHolder surfaceHolder) {
+ try {
+ CameraManager.get().openDriver(surfaceHolder);
+
+
+ } catch (IOException ioe) {
+ return;
+ } catch (RuntimeException e) {
+ return;
+ }
+ if (handler == null) {
+ handler = new CaptureActivityHandler(this, decodeFormats,
+ characterSet);
+ }
+ }
+
+ public void drawViewfinder() {
+ viewfinderView.drawViewfinder();
+
+ }
+
+
+ public void handleDecode(Result obj, Bitmap barcode) {
+
+// viewfinderView.drawResultBitmap(barcode);//画结果图片
+
+ if (obj != null&& this.decodeFlag) {
+ playBeepSoundAndVibrate();
+ String str = obj.getText();//获得扫码的结果
+ /*
+ activity.getCapturePackage().mModuleInstance.sendMsgToRn(str); //发送到RN侧*/
+ onEvChangeListener.getQRCodeResult(str,obj.getBarcodeFormat()); //观察者模式发送到RN侧
+ }
+ stopQR();
+
+
+// viewfinderView.drawResultBitmap(null);//清除结果图片
+
+ /*
+ try {
+ Thread.sleep(sleepTime);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ initCamera(surfaceView.getHolder());
+ if (handler != null) {
+ handler.restartPreviewAndDecode();
+ }*/
+
+ }
+ /* public Handler Scanhandler=new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ // TODO Auto-generated method stub
+ super.handleMessage(msg);
+ // 此处可以更新UI
+ Bundle b = msg.getData();
+ String result = b.getString("result");
+ onEvChangeListener.getQRCodeResult(result); //观察者模式发送到RN侧
+ }
+ };//不加这个分号则不能自动添加代码
+*/
+ /* public void DecodeFromPath(final String path){
+
+
+ Runnable scan_thread =new Runnable() {
+ @Override
+ public void run() {
+ String ResultStr= DecodeUtil.getStringFromQRCode(path);
+ Message msg = new Message();
+ Bundle b = new Bundle();// 存放数据
+ b.putString("result",ResultStr);
+ msg.setData(b);
+
+ Scanhandler.sendMessage(msg);
+ }
+ };
+
+ Scanhandler.post(scan_thread);
+
+
+ }*/
+
+
+ /**
+ * 返回数据
+ *
+ * @param intent
+ * @return
+ */
+ public String ShowResult(Intent intent) {
+ return intent.getData().toString();
+ }
+
+
+ private void initBeepSound() {
+ if (playBeep && mediaPlayer == null) {
+ activity.setVolumeControlStream(AudioManager.STREAM_MUSIC);
+ mediaPlayer = new MediaPlayer();
+ mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ mediaPlayer.setOnCompletionListener(beepListener);
+
+ AssetFileDescriptor file = getResources().openRawResourceFd(
+ R.raw.beep);
+ try {
+ mediaPlayer.setDataSource(file.getFileDescriptor(),
+ file.getStartOffset(), file.getLength());
+ file.close();
+ mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);
+ mediaPlayer.prepare();
+ } catch (IOException e) {
+ mediaPlayer = null;
+ }
+ }
+ }
+
+ private static final long VIBRATE_DURATION = 200L;
+
+
+ private void playBeepSoundAndVibrate() {
+ if (playBeep && mediaPlayer != null) {
+ mediaPlayer.start();
+ }
+ //抖动机身
+ /* if (vibrate) {
+ Vibrator vibrator = (Vibrator) activity.getSystemService(activity.VIBRATOR_SERVICE);
+ vibrator.vibrate(VIBRATE_DURATION);
+ }*/
+ }
+
+ /**
+ * When the beep has finished playing, rewind to queue up another one.
+ */
+ private final MediaPlayer.OnCompletionListener beepListener = new MediaPlayer.OnCompletionListener() {
+ public void onCompletion(MediaPlayer mediaPlayer) {
+ mediaPlayer.seekTo(0);
+ }
+ };
+
+
+ public Activity getActivity() {
+ return activity;
+ }
+
+
+ public void setHandler(CaptureActivityHandler handler) {
+ this.handler = handler;
+
+ }
+
+ public void setAutoStart(boolean autoStart) {
+ this.autoStart = autoStart;
+ }
+
+ public void setChangeTime(long changeTime) {
+ this.changeTime = changeTime;
+ }
+
+ public void setFocusTime(int focusTime) {
+ this.focusTime = focusTime;
+ }
+
+ public void setSleepTime(long sleepTime) {
+ this.sleepTime = sleepTime;
+ }
+
+ public void setcX(int cX) {
+
+ if (width != 0 && ((cX > width / 2 - Min_Frame_Width) || cX < (Min_Frame_Width - width / 2))) {
+ if (cX > 0) {
+ cX = width / 2 - Min_Frame_Width;
+ } else {
+ cX = Min_Frame_Width - width / 2;
+ }
+ }
+
+ this.cX = cX;
+ CameraManager.get().x = cX + width;
+ CameraManager.get().framingRect = null;
+ if (viewfinderView != null) {
+ viewfinderView.invalidate();
+ }
+ initProgressBar();
+ }
+
+ public void setcY(int cY) {
+
+
+ if (height != 0 && ((cY > height / 2 - Min_Frame_Width) || cY < (Min_Frame_Width - height / 2))) {
+ if (cY > 0) {
+ cY = height / 2 - Min_Frame_Width;
+ } else {
+ cY = Min_Frame_Width - height / 2;
+ }
+ }
+
+ this.cY = cY;
+ CameraManager.get().y = cY + height;
+ CameraManager.get().framingRect = null;
+ if (viewfinderView != null) {
+ viewfinderView.invalidate();
+ }
+
+ initProgressBar();
+ }
+
+ public void setMAX_FRAME_WIDTH(int MAX_FRAME_WIDTH) {
+ if (width != 0 && MAX_FRAME_WIDTH > width) {
+ MAX_FRAME_WIDTH = width;
+ }
+ this.MAX_FRAME_WIDTH = MAX_FRAME_WIDTH;
+ CameraManager.get().MIN_FRAME_WIDTH = this.MAX_FRAME_WIDTH;
+
+ CameraManager.get().MAX_FRAME_WIDTH = this.MAX_FRAME_WIDTH;
+ CameraManager.get().framingRect = null;
+ if (viewfinderView != null) {
+ viewfinderView.invalidate();
+ }
+
+ initProgressBar();
+ }
+
+ public void setMAX_FRAME_HEIGHT(int MAX_FRAME_HEIGHT) {
+
+ if (height != 0 && MAX_FRAME_HEIGHT > height) {
+ MAX_FRAME_HEIGHT = width;
+ }
+ this.MAX_FRAME_HEIGHT = MAX_FRAME_HEIGHT;
+
+ CameraManager.get().MIN_FRAME_HEIGHT = this.MAX_FRAME_HEIGHT;
+
+ CameraManager.get().MAX_FRAME_HEIGHT = this.MAX_FRAME_HEIGHT;
+ CameraManager.get().framingRect = null;
+ if (viewfinderView != null) {
+ viewfinderView.invalidate();
+ }
+
+ initProgressBar();
+ }
+
+
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+
+ super.onWindowFocusChanged(hasWindowFocus);
+
+ if (hasWindowFocus) {
+ //对应onresume
+ this.holder = surfaceView.getHolder();
+ startScan();
+ } else {
+ //对应onpause
+ stopScan();
+ }
+
+ }
+
+ public void setText(String text) {
+ Text = text;
+ }
+
+ /**
+ * 设置扫描线运动时间
+ *
+ * @param scanTime
+ */
+ public void setScanTime(int scanTime) {
+ this.scanTime = scanTime;
+ if (viewfinderView != null) {
+ viewfinderView.scanTime = scanTime;
+ }
+ }
+
+ public void setCORNER_COLOR(int CORNER_COLOR) {
+ this.CORNER_COLOR = CORNER_COLOR;
+ if (viewfinderView != null) {
+ viewfinderView.frameColor = this.CORNER_COLOR;
+ viewfinderView.frameBaseColor = reSetColor(this.CORNER_COLOR);
+ }
+
+ }
+
+ /**
+ * 设置四个角的颜色
+ *
+ * @param CORNER_WIDTH
+ */
+ public void setCORNER_WIDTH(int CORNER_WIDTH) {
+ this.CORNER_WIDTH = CORNER_WIDTH;
+
+ if (viewfinderView != null) {
+ viewfinderView.setCORNER_WIDTH(this.CORNER_WIDTH);
+ }
+ }
+
+ public void setMIDDLE_LINE_WIDTH(int MIDDLE_LINE_WIDTH) {
+ this.MIDDLE_LINE_WIDTH = MIDDLE_LINE_WIDTH;
+ if (viewfinderView != null) {
+ viewfinderView.setMIDDLE_LINE_WIDTH(this.MIDDLE_LINE_WIDTH);
+ }
+ }
+
+ /**
+ * 开启闪光灯常亮
+ */
+ public void OpenFlash(){
+ try {
+ Camera.Parameters param =CameraManager.get().getCamera().getParameters();
+
+ param.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
+
+ CameraManager.get().getCamera().setParameters(param);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ } /**
+ * 关闭闪光灯常亮
+ */
+ public void CloseFlash(){
+ try {
+ Camera.Parameters param = CameraManager.get().getCamera().getParameters();
+
+ param.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
+
+
+ CameraManager.get().getCamera().setParameters(param);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * RN调用方法
+ * 改变扫描框大小
+ *
+ * @param WIDTH
+ * @param HEIGHT
+ */
+ public void setCHANGE_WIDTH(final int WIDTH, final int HEIGHT) {
+ //属性动画
+// Toast.makeText(activity, "width" + WIDTH, Toast.LENGTH_SHORT).show();
+ if (viewfinderView != null) {
+
+// if(popupWindow.isShowing()){
+// popupWindow.dismiss();
+// }
+
+ int widthScan = (width / 2 - Math.abs(cX)) - CORNER_WIDTH;
+
+ if (widthScan < Min_Frame_Width) {
+ widthScan = Min_Frame_Width - CORNER_WIDTH;
+ }
+ int heightScan = (height / 2 - Math.abs(cY)) - CORNER_WIDTH;
+
+ if (heightScan < Min_Frame_Width) {
+ heightScan = Min_Frame_Width - CORNER_WIDTH;
+ }
+
+ ObjectAnimator animWidth = ObjectAnimator.ofInt(CaptureView.this, "MAX_FRAME_WIDTH", MAX_FRAME_WIDTH, WIDTH / 2 > widthScan ? widthScan * 2 : WIDTH - CORNER_WIDTH);
+ ObjectAnimator animHeight = ObjectAnimator.ofInt(CaptureView.this, "MAX_FRAME_HEIGHT", MAX_FRAME_HEIGHT, HEIGHT / 2 > heightScan ? heightScan * 2 : HEIGHT - CORNER_WIDTH);
+
+
+ AnimatorSet animSet = new AnimatorSet();
+ animSet.play(animWidth).with(animHeight);
+ animSet.setDuration(changeTime);
+
+ animSet.addListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+
+ viewfinderView.drawLine = false;
+ stopQR();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+// stopScan();
+
+
+ int widthScan = (width / 2 - Math.abs(cX)) - CORNER_WIDTH;
+
+ if (widthScan < Min_Frame_Width) {
+ widthScan = Min_Frame_Width - CORNER_WIDTH;
+ }
+ int heightScan = (height / 2 - Math.abs(cY)) - CORNER_WIDTH;
+
+ if (heightScan < Min_Frame_Width) {
+ heightScan = Min_Frame_Width - CORNER_WIDTH;
+ }
+
+ MAX_FRAME_WIDTH = WIDTH / 2 > widthScan ? widthScan * 2 : WIDTH - CORNER_WIDTH;
+ MAX_FRAME_HEIGHT = HEIGHT / 2 > heightScan ? heightScan * 2 : HEIGHT - CORNER_WIDTH;
+
+ CameraManager.get().MIN_FRAME_WIDTH = MAX_FRAME_WIDTH;
+ CameraManager.get().MIN_FRAME_HEIGHT = MAX_FRAME_HEIGHT;
+ CameraManager.get().MAX_FRAME_WIDTH = MAX_FRAME_WIDTH;
+ CameraManager.get().MAX_FRAME_HEIGHT = MAX_FRAME_HEIGHT;
+
+// Log.i("Test", "width:" + width + ",height:" + height);
+// Log.i("Test", "cX:" + cX + ",cY:" + cY);
+// Log.i("Test", "MAX_FRAME_WIDTH:" + MAX_FRAME_WIDTH + ",MAX_FRAME_HEIGHT:" + MAX_FRAME_HEIGHT);
+
+ CameraManager.get().framingRectInPreview = null;
+// decodeFormats = null;
+ viewfinderView.drawLine = true;
+ holder = surfaceView.getHolder();
+// startScan();
+ startQR();
+ initProgressBar();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ stopScan();
+ CameraManager.get().framingRectInPreview = null;
+// decodeFormats = null;
+ holder = surfaceView.getHolder();
+ startScan();
+ viewfinderView.drawLine = true;
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+
+ }
+ });
+
+ animSet.start();
+
+
+ }
+
+ }
+
+
+ private final class TouchListener implements OnTouchListener {
+
+ /**
+ * 放大缩小照片模式
+ */
+ private static final int MODE_ZOOM = 1;
+ private int mode = MODE_ZOOM;// 初始状态
+
+ /**
+ * 用于记录拖拉图片移动的坐标位置
+ */
+
+ private float startDis;
+
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+
+ /** 通过与运算保留最后八位 MotionEvent.ACTION_MASK = 255 */
+ switch (event.getAction() & MotionEvent.ACTION_MASK) {
+ // 手指压下屏幕
+ case MotionEvent.ACTION_DOWN:
+ mode = MODE_ZOOM;
+ startDis = event.getY();
+// Log.i("Test", "ACTION_DOWN");
+
+
+// int leftMargin = (width / 2) + cX + MAX_FRAME_WIDTH/2-(int)(25*density);
+ int leftMargin = cX / 2 + MAX_FRAME_WIDTH / 2 - (int) (25 * density);
+ int topMargin = cY / 2 - (int) (25 * density);
+/**
+ * 显示seekbar
+ */
+// popupWindow.showAtLocation(v, Gravity.CENTER, leftMargin,topMargin);
+
+ break;
+ /* case MotionEvent.ACTION_POINTER_DOWN:
+ //如果mZoomSeekBar为null 表示该设备不支持缩放 直接跳过设置mode Move指令也无法执行
+ if (seekbar == null) return true;
+ //移除token对象为mZoomSeekBar的延时任务
+ mHandler.removeCallbacksAndMessages(seekbar);
+ seekbar.setVisibility(View.VISIBLE);
+
+ mode = MODE_ZOOM;
+ *//** 计算两个手指间的距离 *//*
+ startDis = distance(event);
+ break;*/
+ case MotionEvent.ACTION_MOVE:
+
+ /**
+ * 控制设置zoom没16毫秒触发,不到时间不触发
+ */
+
+ if (mode == MODE_ZOOM) {
+ /* //只有同时触屏两个点的时候才执行
+ if(event.getPointerCount()<2) return true;*/
+ float endDis = startDis - event.getY();// 结束距离
+
+
+ int scale = (int) (endDis / (ScreenHeight / getMaxZoom()));
+
+ if (scale == 0 && endDis < 0) {
+ scale = -1;
+ } else if (scale == 0 && endDis > 0) {
+ scale = 1;
+ }
+// Log.i("Test", "scale:" + scale);
+
+ /**
+ * 处理时间
+ */
+ long endTime = System.currentTimeMillis();
+
+ long time = endTime - beginTime;
+
+ beginTime = System.currentTimeMillis();
+
+ if (scale >= 1 || scale <= -1) {
+ int zoom = getZoom() + scale;
+ //zoom不能超出范围
+ if (zoom > getMaxZoom()) zoom = getMaxZoom();
+ if (zoom < 0) zoom = 0;
+// Log.i("Test", "zoom:" + zoom + ",Time:" + time);
+ setZoom(zoom);
+// progressBar.setProgress(zoom);
+ //将最后一次的距离设为当前距离
+// startDis = endDis;
+ }
+ }
+ break;
+ // 手指离开屏幕
+ case MotionEvent.ACTION_UP:
+
+ /*if(mode!=MODE_ZOOM){
+ //设置聚焦
+ Point point=new Point((int)event.getX(), (int)event.getY());
+ mCameraView.onFocus(point,autoFocusCallback);
+ mFocusImageView.startFocus(point);
+ }else {*/
+ //ZOOM模式下 在结束两秒后隐藏seekbar 设置token为mZoomSeekBar用以在连续点击时移除前一个定时任务
+
+ /*mHandler.postAtTime(new Runnable() {
+
+ @Override
+ public void run() {
+ // TODO Auto-generated method stub
+ if(popupWindow.isShowing()){
+ popupWindow.dismiss();
+ }
+ }
+ }, progressBar,SystemClock.uptimeMillis()+5000);
+// }*/
+ break;
+ }
+ return true;
+ }
+
+// /**
+// * 计算两个手指间的距离
+// */
+// private float distance(MotionEvent event) {
+// float dx = event.getX(1) - event.getX(0);
+// float dy = event.getY(1) - event.getY(0);
+// /** 使用勾股定理返回两点之间的距离 */
+// return (float) Math.sqrt(dx * dx + dy * dy);
+// }
+
+ }
+
+ /**
+ * 获取最大缩放级别,最大为40
+ *
+ * @return
+ */
+
+ public int getMaxZoom() {
+ if (CameraManager.get().getCamera() != null) {
+ Camera.Parameters parameters = CameraManager.get().getCamera().getParameters();
+ if (!parameters.isZoomSupported()) return -1;
+
+ return parameters.getMaxZoom() > 40 ? 40 : parameters.getMaxZoom();
+ }
+ return 40;
+ }
+
+ /**
+ * 设置相机缩放级别
+ *
+ * @param zoom
+ */
+
+ public void setZoom(int zoom) {
+
+ Camera.Parameters parameters;
+ //注意此处为录像模式下的setZoom方式。在Camera.unlock之后,调用getParameters方法会引起android框架底层的异常
+ //stackoverflow上看到的解释是由于多线程同时访问Camera导致的冲突,所以在此使用录像前保存的mParameters。
+ if (CameraManager.get().getCamera() != null) {
+ parameters = CameraManager.get().getCamera().getParameters();
+ if (!parameters.isZoomSupported()) return;
+ parameters.setZoom(zoom);
+ CameraManager.get().getCamera().setParameters(parameters);
+ mZoom = zoom;
+ }
+ }
+
+ public int getZoom() {
+ return mZoom;
+ }
+
+ public Vector getDecodeFormats() {
+ return decodeFormats;
+ }
+
+ public void setDecodeFormats(List decode) {
+ decodeFormats=new Vector();
+ for(BarcodeFormat format : BarcodeFormat.values()){
+ if(decode.contains(format.toString())){
+ decodeFormats.add(format);
+ }
+ }
+
+ }
+
+ public interface OnEvChangeListener {
+ public void getQRCodeResult(String result, BarcodeFormat format);
+ }
+
+ public void setOnEvChangeListener(OnEvChangeListener onEvChangeListener) {
+ this.onEvChangeListener = onEvChangeListener;
+ }
+
+
+
+ /**
+ * 颜色换算
+ */
+ public int reSetColor(int startInt) {
+
+ int startA = (startInt >> 24) & 0xff;
+ int startR = (startInt >> 16) & 0xff;
+ int startG = (startInt >> 8) & 0xff;
+ int startB = startInt & 0xff;
+
+ int endA = ((startInt / 2) >> 24) & 0xff;
+ return ((startA + (endA - startA)) << 24)
+ | (startR << 16)
+ | (startG << 8)
+ | (startB);
+
+
+ }
+}
+
+
diff --git a/android/src/main/java/com/reactnativecomponent/barcode/QRCodeResultEvent.java b/android/src/main/java/com/reactnativecomponent/barcode/QRCodeResultEvent.java
new file mode 100644
index 0000000..e3abf66
--- /dev/null
+++ b/android/src/main/java/com/reactnativecomponent/barcode/QRCodeResultEvent.java
@@ -0,0 +1,51 @@
+package com.reactnativecomponent.barcode;
+
+import android.util.Log;
+
+import com.facebook.react.bridge.Arguments;
+import com.facebook.react.bridge.WritableMap;
+import com.facebook.react.uimanager.events.Event;
+import com.facebook.react.uimanager.events.RCTEventEmitter;
+import com.google.zxing.BarcodeFormat;
+
+
+public class QRCodeResultEvent extends Event {
+ String result;
+ BarcodeFormat format;
+ public QRCodeResultEvent(int viewTag, long timestampMs,String result,BarcodeFormat format) {
+// super(viewTag, timestampMs);
+ super(viewTag);
+ this.result=result;
+ this.format=format;
+ }
+
+ @Override
+ public String getEventName(){
+ return "QRCodeResult";
+ }
+
+ @Override
+ public void dispatch(RCTEventEmitter rctEventEmitter) {
+ rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData());
+ }
+
+ private WritableMap serializeEventData() {
+ WritableMap eventData = Arguments.createMap();
+ WritableMap data = Arguments.createMap();
+ data.putString("code", getResult());
+ data.putString("type",format.toString());
+// Log.i("Test","code="+getResult());
+ eventData.putMap("data",data);
+
+
+ return eventData;
+ }
+
+ public String getResult() {
+ return result;
+ }
+
+ public BarcodeFormat getFormat() {
+ return format;
+ }
+}
diff --git a/android/src/main/java/com/reactnativecomponent/barcode/RCTCaptureManager.java b/android/src/main/java/com/reactnativecomponent/barcode/RCTCaptureManager.java
new file mode 100644
index 0000000..017b1a7
--- /dev/null
+++ b/android/src/main/java/com/reactnativecomponent/barcode/RCTCaptureManager.java
@@ -0,0 +1,223 @@
+package com.reactnativecomponent.barcode;
+
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.support.annotation.Nullable;
+import android.util.Log;
+
+import com.facebook.react.bridge.ReactMethod;
+import com.facebook.react.bridge.ReadableArray;
+import com.facebook.react.bridge.ReadableMap;
+import com.facebook.react.common.MapBuilder;
+import com.facebook.react.common.SystemClock;
+import com.facebook.react.uimanager.ThemedReactContext;
+import com.facebook.react.uimanager.UIManagerModule;
+import com.facebook.react.uimanager.ViewGroupManager;
+import com.facebook.react.uimanager.annotations.ReactProp;
+import com.google.zxing.BarcodeFormat;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+
+public class RCTCaptureManager extends ViewGroupManager {
+ private static final String REACT_CLASS = "CaptureView";//要与类名一致
+ public static final int CHANGE_SHOW = 0;//用来标记方法的下标
+ Activity activity;
+ CaptureView cap;
+ private float density;
+
+
+ public RCTCaptureManager(Activity activity) {
+ this.activity = activity;
+ density = activity.getResources().getDisplayMetrics().density;
+ }
+
+ @Override
+ public String getName() {
+ return REACT_CLASS;
+ }
+
+ @Override
+ public CaptureView createViewInstance(ThemedReactContext context) {
+ cap = new CaptureView(activity, context);
+// ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+//
+// cap.setLayoutParams(params);
+
+ return cap;
+ }
+ @ReactProp(name = "barCodeTypes")
+ public void setbarCodeTypes(CaptureView view, ReadableArray barCodeTypes) {
+
+ if (barCodeTypes == null) {
+ return;
+ }
+ List result = new ArrayList(barCodeTypes.size());
+ for (int i = 0; i < barCodeTypes.size(); i++) {
+ result.add(barCodeTypes.getString(i));
+ }
+ view.setDecodeFormats(result);
+
+ }
+ @ReactProp(name = "scannerRectLeft", defaultInt = 0)
+ public void setCX(CaptureView view, int cX) {
+ view.setcX((int) (cX* density + 0.5f));
+ }
+
+ @ReactProp(name = "scannerRectTop", defaultInt = 0)
+ public void setCY(CaptureView view, int cY) {
+ view.setcY((int)(cY* density + 0.5f));
+ }
+
+ @ReactProp(name = "scannerRectWidth", defaultInt = 255)
+ public void setMAX_FRAME_WIDTH(CaptureView view, int FRAME_WIDTH) {
+ view.setMAX_FRAME_WIDTH((int) (FRAME_WIDTH * density + 0.5f));
+ }
+
+ @ReactProp(name = "scannerRectHeight", defaultInt = 255)
+ public void setMAX_FRAME_HEIGHT(CaptureView view, int FRAME_HEIGHT) {
+ view.setMAX_FRAME_HEIGHT((int) (FRAME_HEIGHT * density + 0.5f));
+ }
+
+/* @ReactProp(name = "text")
+ public void setText(CaptureView view, String text) {
+ view.setText(text);
+ }*/
+
+
+ /* @ReactProp(name = "scannerRectCornerWidth", defaultInt = 4)
+ public void setCORNER_WIDTH(CaptureView view, int CORNER_WIDTH) {
+ if(CORNER_WIDTH<4){
+ CORNER_WIDTH=4;
+ }
+ view.setCORNER_WIDTH(CORNER_WIDTH);
+ }*/
+
+ /* @ReactProp(name = "scannerLineWidth", defaultInt = 3)
+ public void setMIDDLE_LINE_WIDTH(CaptureView view, int MIDDLE_LINE_WIDTH) {
+ if(MIDDLE_LINE_WIDTH<3){
+ MIDDLE_LINE_WIDTH=3;
+ }
+ view.setMIDDLE_LINE_WIDTH(MIDDLE_LINE_WIDTH);
+ }*/
+
+ //扫描线移动一圈时间
+ @ReactProp(name = "scannerLineInterval", defaultInt = 1000)
+ public void setTime(CaptureView view, int time) {
+ view.setScanTime(time);
+ }
+
+ /* //扫描框尺寸动画持续时间
+ @ReactProp(name = "changeTime", defaultInt = 1000)
+ public void setChangeTime(CaptureView view, int time) {
+ view.setChangeTime(time);
+ }
+
+ //camera聚集时间
+ @ReactProp(name = "focusTime", defaultInt = 1000)
+ public void setfocusTime(CaptureView view, int time) {
+ view.setFocusTime(time);
+ }
+
+ @ReactProp(name = "autoStart", defaultBoolean = true)
+ public void setAutoStart(CaptureView view, boolean start) {
+ view.setAutoStart(start);
+ }*/
+
+ @ReactProp(name = "scannerRectCornerColor")
+ public void setCORNER_COLOR(CaptureView view, String color) {
+ if (color != null && !color.isEmpty()) {
+ view.setCORNER_COLOR(Color.parseColor(color));//转换成16进制
+ }
+ }
+
+ /* //扫码成功提示音
+ @ReactProp(name = "playBeep",defaultBoolean = true)
+ public void setPlayBeep(CaptureView view, boolean isBeep) {
+ view.setPlayBeep(isBeep);
+ }
+*/
+
+ @Override
+ public
+ @Nullable
+ Map getCommandsMap() {
+ return MapBuilder.of(
+ "change",
+ CHANGE_SHOW);//js处发送的方法名字
+ }
+
+ @Override
+ public void receiveCommand(CaptureView root, int commandId, @Nullable ReadableArray config) {
+ // super.receiveCommand(root, commandId, config);
+ if (commandId == CHANGE_SHOW) {
+ this.changeWidthHeight(config.getMap(0));
+ }
+ }
+
+
+ @ReactMethod
+ public void changeWidthHeight(final ReadableMap config) {
+// Log.i("Test", "changeWidthHeight");
+ if (cap != null) {
+ activity.runOnUiThread(new Runnable() {
+ public void run() {
+ int width = config.getInt("FRAME_WIDTH");
+ int height = config.getInt("FRAME_HEIGHT");
+ cap.setCHANGE_WIDTH((int)(width* density + 0.5f), (int)(height* density + 0.5f));
+ }
+ });
+ }
+ }
+
+ @Override
+ protected void addEventEmitters(
+ final ThemedReactContext reactContext,
+ final CaptureView view) {
+ view.setOnEvChangeListener(
+ new CaptureView.OnEvChangeListener() {
+ @Override
+ public void getQRCodeResult(String result,BarcodeFormat format) {
+ reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher()
+ .dispatchEvent(new QRCodeResultEvent(view.getId(), SystemClock.nanoTime(), result,format));
+ }
+
+ });
+ }
+
+ @Override
+ public Map getExportedCustomDirectEventTypeConstants() {
+ return MapBuilder.builder()
+ .put("QRCodeResult", MapBuilder.of("registrationName", "onBarCodeRead"))//registrationName 后的名字,RN中方法也要是这个名字否则不执行
+ .build();
+ }
+
+/*
+
+ @ReactProp(name = "aspect")
+ public void setAspect(CaptureView view, int aspect) {
+ view.setAspect(aspect);
+ }
+
+ @ReactProp(name = "captureMode")
+ public void setCaptureMode(RCTCameraView view, int captureMode) {
+ // TODO - implement video mode
+ }
+
+ @ReactProp(name = "captureTarget")
+ public void setCaptureTarget(RCTCameraView view, int captureTarget) {
+ // No reason to handle this props value here since it's passed again to the RCTCameraModule capture method
+ }
+
+ @ReactProp(name = "type")
+ public void setType(RCTCameraView view, int type) {
+ view.setCameraType(type);
+ }
+*/
+
+
+}
\ No newline at end of file
diff --git a/android/src/main/java/com/reactnativecomponent/barcode/RCTCaptureModule.java b/android/src/main/java/com/reactnativecomponent/barcode/RCTCaptureModule.java
new file mode 100644
index 0000000..924b2ee
--- /dev/null
+++ b/android/src/main/java/com/reactnativecomponent/barcode/RCTCaptureModule.java
@@ -0,0 +1,167 @@
+package com.reactnativecomponent.barcode;
+
+import android.os.Environment;
+import android.support.annotation.Nullable;
+import android.widget.Toast;
+
+import com.facebook.react.bridge.Callback;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.bridge.ReactContextBaseJavaModule;
+import com.facebook.react.bridge.ReactMethod;
+import com.google.zxing.BarcodeFormat;
+import com.reactnativecomponent.barcode.decoding.DecodeUtil;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+
+public class RCTCaptureModule extends ReactContextBaseJavaModule {
+ private ReactApplicationContext mContext;
+ RCTCaptureManager captureManager;
+
+
+ public RCTCaptureModule(ReactApplicationContext reactContext, RCTCaptureManager captureManager) {
+ super(reactContext);
+ mContext = reactContext;
+
+ this.captureManager = captureManager;
+
+ }
+
+ @Override
+ public String getName() {
+ return "CaptureModule";
+ }
+
+// public void sendMsgToRn(String msg) {
+// //将消息msg发送给RN侧
+// mContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("AndroidToRNMessage", msg);
+//
+// }
+
+
+
+ @Nullable
+ @Override
+ public Map getConstants() {
+ return Collections.unmodifiableMap(new HashMap() {
+ {
+ put("barCodeTypes", getBarCodeTypes());
+ }
+ private Map getBarCodeTypes() {
+ return Collections.unmodifiableMap(new HashMap() {
+ {
+ put("upce", BarcodeFormat.UPC_E.toString());
+ put("code39", BarcodeFormat.CODE_39.toString());
+// put("code39mod43",BarcodeFormat. );
+ put("ean13",BarcodeFormat.EAN_13.toString() );
+ put("ean8",BarcodeFormat.EAN_8.toString() );
+ put("code93", BarcodeFormat.CODE_93.toString());
+ put("code128", BarcodeFormat.CODE_128.toString());
+ put("pdf417",BarcodeFormat.PDF_417.toString() );
+ put("qr",BarcodeFormat.QR_CODE.toString() );
+ put("aztec", BarcodeFormat.AZTEC.toString());
+// put("interleaved2of5", BarcodeFormat.);
+ put("itf14",BarcodeFormat.ITF.toString());
+ put("datamatrix", BarcodeFormat.DATA_MATRIX.toString());
+ }
+
+
+ });
+ }
+ });
+ }
+
+
+
+ @ReactMethod
+ public void startSession() {
+
+ if (captureManager.cap != null) {
+ getCurrentActivity().runOnUiThread(new Runnable() {
+ public void run() {
+ captureManager.cap.startQR();
+// captureManager.cap.startScan();
+// Toast.makeText(getCurrentActivity(), "startScan", Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+ }
+
+
+ @ReactMethod
+ public void stopSession() {
+ if (captureManager.cap != null) {
+ getCurrentActivity().runOnUiThread(new Runnable() {
+ public void run() {
+// captureManager.cap.stopQR();
+ captureManager.cap.stopScan();
+// Toast.makeText(getCurrentActivity(), "stopScan", Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+ }
+
+ @ReactMethod
+ public void stopFlash() {
+ if (captureManager.cap != null) {
+ getCurrentActivity().runOnUiThread(new Runnable() {
+ public void run() {
+ captureManager.cap.CloseFlash();
+// Toast.makeText(getCurrentActivity(), "stopFlash", Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+ }
+
+ @ReactMethod
+ public void startFlash() {
+ if (captureManager.cap != null) {
+ getCurrentActivity().runOnUiThread(new Runnable() {
+ public void run() {
+ captureManager.cap.OpenFlash();
+// Toast.makeText(getCurrentActivity(), "startFlash", Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+ }
+
+ @ReactMethod
+ public void DecodeFromPath(final String path,
+ final Callback errorCallback,
+ final Callback successCallback) {
+
+ new Thread(new Runnable() {
+ public void run() {
+ try {
+ String s = Environment.getExternalStorageDirectory()
+ .getAbsolutePath() + "/" + "IMG_20161011_170552.jpg";
+ //不加这个分号则不能自动添加代码
+
+ String ResultStr = DecodeUtil.getStringFromQRCode(s);
+ successCallback.invoke(ResultStr);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ errorCallback.invoke(e.getMessage());
+ }
+ }
+ }).start();
+// Toast.makeText(getCurrentActivity(), "DecodeFromPath:"+path, Toast.LENGTH_SHORT).show();
+
+ }
+
+
+/*
+ @ReactMethod
+ public void changeWidthHeight(final int width,final int height) {
+
+ if (captureManager.cap != null) {
+ activity.runOnUiThread(new Runnable() {
+ public void run() {
+ captureManager.cap.setCHANGE_WIDTH(width, height);
+ }
+ });
+ }
+ }*/
+}
diff --git a/android/src/main/java/com/reactnativecomponent/barcode/RCTCapturePackage.java b/android/src/main/java/com/reactnativecomponent/barcode/RCTCapturePackage.java
new file mode 100644
index 0000000..5b5930a
--- /dev/null
+++ b/android/src/main/java/com/reactnativecomponent/barcode/RCTCapturePackage.java
@@ -0,0 +1,51 @@
+package com.reactnativecomponent.barcode;
+
+import android.app.Activity;
+
+import com.facebook.react.ReactPackage;
+import com.facebook.react.bridge.JavaScriptModule;
+import com.facebook.react.bridge.NativeModule;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.uimanager.ViewManager;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+
+public class RCTCapturePackage implements ReactPackage {
+ Activity activity;
+ RCTCaptureModule mModuleInstance;
+ RCTCaptureManager captureManager;
+// RCTLinearGradientViewManager linearGradientViewManager;
+
+ public RCTCapturePackage(Activity activity) {
+ this.activity = activity;
+ captureManager = new RCTCaptureManager(activity);
+// linearGradientViewManager = new RCTLinearGradientViewManager(activity);
+ }
+
+
+
+ @Override
+ public List createNativeModules(ReactApplicationContext reactApplicationContext) {
+ mModuleInstance = new RCTCaptureModule(reactApplicationContext,captureManager);
+ return Arrays.asList(
+ mModuleInstance
+ );
+ }
+
+ @Override
+ public List> createJSModules() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List createViewManagers(ReactApplicationContext reactApplicationContext) {
+ //noinspection ArraysAsListWithZeroOrOneArgument
+
+// return Arrays.asList(captureManager,linearGradientViewManager);
+ return Arrays.asList(captureManager);
+ }
+
+ }
diff --git a/android/src/main/java/com/reactnativecomponent/barcode/RCTLinearGradientViewManager.java b/android/src/main/java/com/reactnativecomponent/barcode/RCTLinearGradientViewManager.java
new file mode 100644
index 0000000..6caa3ae
--- /dev/null
+++ b/android/src/main/java/com/reactnativecomponent/barcode/RCTLinearGradientViewManager.java
@@ -0,0 +1,87 @@
+package com.reactnativecomponent.barcode;
+
+
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.support.annotation.Nullable;
+import android.util.Log;
+import android.view.ViewGroup;
+
+import com.facebook.react.uimanager.SimpleViewManager;
+import com.facebook.react.uimanager.ThemedReactContext;
+
+import com.facebook.react.uimanager.annotations.ReactProp;
+import com.reactnativecomponent.barcode.view.LinearGradientView;
+
+public class RCTLinearGradientViewManager extends SimpleViewManager{
+
+ private static final String REACT_CLASS = "LinearGradientView";//要与类名一致
+ LinearGradientView linearGradientView;
+ private float density;
+ Activity activity;
+
+
+ public RCTLinearGradientViewManager(Activity activity) {
+ this.activity=activity;
+ density = activity.getResources().getDisplayMetrics().density;
+ }
+
+ @Override
+ public String getName() {
+ return REACT_CLASS;
+ }
+ @Override
+ protected LinearGradientView createViewInstance(ThemedReactContext reactContext) {
+ linearGradientView=new LinearGradientView(reactContext,activity);
+ return linearGradientView;
+
+ }
+
+
+ @ReactProp(name = "size" ,defaultInt = 1)
+ public void setSize(LinearGradientView view,int size) {
+ int num=(int)(size*density+0.5f);
+ if(num<3){
+ num=3;
+ }else if(num >10){
+ num=5;
+ }
+ ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+
+ params.height=num;
+ params.width =view.width;
+ view.setLayoutParams(params);
+ view.size=num;
+
+ }
+
+
+ @ReactProp(name = "width" ,defaultInt = 0)
+ public void setWidth(LinearGradientView view, int width) {
+ ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+if((int) (width * density + 0.5f)>1) {
+ params.width = (int) (width * density + 0.5f);
+ params.height=view.size;
+}
+ view.width=(int)(width*density+0.5f);
+ view.setLayoutParams(params);
+
+
+ }
+
+
+
+ @ReactProp(name = "frameColor")
+ public void setFrameColor(LinearGradientView view, String color) {
+ if (color != null && !color.isEmpty()) {
+ view.setFrameColor(Color.parseColor(color));//转换成16进制
+ }
+ }
+
+
+ @Override
+ public void setBackgroundColor(LinearGradientView view, int backgroundColor) {
+ // super.setBackgroundColor(view, backgroundColor);
+ }
+}
diff --git a/android/src/main/java/com/reactnativecomponent/barcode/camera/AutoFocusCallback.java b/android/src/main/java/com/reactnativecomponent/barcode/camera/AutoFocusCallback.java
new file mode 100644
index 0000000..9ad5a7b
--- /dev/null
+++ b/android/src/main/java/com/reactnativecomponent/barcode/camera/AutoFocusCallback.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.reactnativecomponent.barcode.camera;
+
+import android.hardware.Camera;
+import android.os.Handler;
+import android.os.Message;
+
+final class AutoFocusCallback implements Camera.AutoFocusCallback {
+
+ private static final String TAG = AutoFocusCallback.class.getSimpleName();
+
+ public long AUTOFOCUS_INTERVAL_MS = 500L;//自动聚焦触发 毫秒数
+// private static final long AUTOFOCUS_INTERVAL_MS = 1500L;
+
+ private Handler autoFocusHandler;
+ private int autoFocusMessage;
+
+ 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);
+ // Simulate continuous autofocus by sending a focus request every
+ // AUTOFOCUS_INTERVAL_MS milliseconds.
+ //Log.d(TAG, "Got auto-focus callback; requesting another");
+ autoFocusHandler.sendMessageDelayed(message, AUTOFOCUS_INTERVAL_MS);
+ autoFocusHandler = null;
+ } else {
+// Log.d(TAG, "Got auto-focus callback, but no handler for it");
+ }
+ }
+
+}
diff --git a/android/src/main/java/com/reactnativecomponent/barcode/camera/CameraConfigurationManager.java b/android/src/main/java/com/reactnativecomponent/barcode/camera/CameraConfigurationManager.java
new file mode 100644
index 0000000..fbf1466
--- /dev/null
+++ b/android/src/main/java/com/reactnativecomponent/barcode/camera/CameraConfigurationManager.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.reactnativecomponent.barcode.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.lang.reflect.Method;
+import java.util.regex.Pattern;
+
+final class CameraConfigurationManager {
+
+ private static final String TAG = CameraConfigurationManager.class.getSimpleName();
+
+ private static final int TEN_DESIRED_ZOOM = 27;
+ @SuppressWarnings("unused")
+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;
+ }
+ cameraResolution = getCameraResolution(parameters, screenResolutionForCamera);
+// 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);
+// parameters.set("orientation", "portrait");
+ if (Integer.parseInt(Build.VERSION.SDK) >= 8) {
+ setDisplayOrientation(camera, 90);
+ }
+ camera.setParameters(parameters);
+ }
+
+ protected void setDisplayOrientation(Camera camera, int angle) {
+ Method downPolymorphic;
+ try {
+ downPolymorphic = camera.getClass().getMethod(
+ "setDisplayOrientation", new Class[] { int.class });
+ if (downPolymorphic != null)
+ downPolymorphic.invoke(camera, new Object[] { angle });
+ } catch (Exception e1) {
+ }
+ }
+
+ 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);
+ }
+ }
+
+ /*
+ private void setSharpness(Camera.Parameters parameters) {
+
+ int desiredSharpness = DESIRED_SHARPNESS;
+
+ String maxSharpnessString = parameters.get("sharpness-max");
+ if (maxSharpnessString != null) {
+ try {
+ int maxSharpness = Integer.parseInt(maxSharpnessString);
+ if (desiredSharpness > maxSharpness) {
+ desiredSharpness = maxSharpness;
+ }
+ } catch (NumberFormatException nfe) {
+ Log.w(TAG, "Bad sharpness-max: " + maxSharpnessString);
+ }
+ }
+
+ parameters.set("sharpness", desiredSharpness);
+ }
+ */
+}
diff --git a/android/src/main/java/com/reactnativecomponent/barcode/camera/CameraManager.java b/android/src/main/java/com/reactnativecomponent/barcode/camera/CameraManager.java
new file mode 100644
index 0000000..ac6c7bc
--- /dev/null
+++ b/android/src/main/java/com/reactnativecomponent/barcode/camera/CameraManager.java
@@ -0,0 +1,366 @@
+/*
+ * 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 com.reactnativecomponent.barcode.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.util.Log;
+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.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class CameraManager {
+
+ private static final String TAG ="Test";
+
+/*
+ public static int MIN_FRAME_WIDTH = 300;
+ public static int MIN_FRAME_HEIGHT = 300;
+ public static int MAX_FRAME_WIDTH = 600;
+ public static int MAX_FRAME_HEIGHT = 400; */
+ public static int MIN_FRAME_WIDTH ;
+ public static int MIN_FRAME_HEIGHT ;
+ public static int MAX_FRAME_WIDTH ;
+ public static int MAX_FRAME_HEIGHT;
+ public static int x;
+ public static int y;
+
+ private static CameraManager cameraManager;
+ private int focusTime=500;
+
+ static final int SDK_INT; // Later we can use Build.VERSION.SDK_INT
+ static {
+ int sdkInt;
+ try {
+ sdkInt = Build.VERSION.SDK_INT;
+ } catch (NumberFormatException nfe) {
+ // Just to be safe
+ sdkInt = 10000;
+ }
+ SDK_INT = sdkInt;
+ }
+
+ @SuppressWarnings("unused")
+private final Context context;
+ private final CameraConfigurationManager configManager;
+ private Camera camera;
+ public Rect framingRect;
+ public 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 PreviewCallback previewCallback;
+ /** Autofocus callbacks arrive here, and are dispatched to the Handler which requested them. */
+ private 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;
+ }
+
+ public void setFocusTime(int focusTime) {
+ this.focusTime = focusTime;
+ }
+
+ 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 = Build.VERSION.SDK_INT > 3; // 3 = Cupcake
+
+ initPreviewCallback();
+
+ }
+
+ public void initPreviewCallback() {
+ previewCallback = new PreviewCallback(configManager, useOneShotPreviewCallback);
+ autoFocusCallback = new AutoFocusCallback();
+ autoFocusCallback.AUTOFOCUS_INTERVAL_MS=focusTime;
+ }
+
+ /**
+ * 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);
+
+// SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+// if (prefs.getBoolean(PreferencesActivity.KEY_FRONT_LIGHT, false)) {
+// FlashlightManager.enableFlashlight();
+// }
+ 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() {
+ if (framingRect == null) {
+ Point screenResolution = configManager.getScreenResolution();
+// int x=this.x+screenResolution.x;
+// int y=this.y+screenResolution.y;
+ int x=this.x;
+ int y=this.y;
+// Log.i(TAG, "x: " + this.x+",y:"+this.y);
+
+ if (camera == null) {
+ return null;
+ }
+ int width = x * 3 / 4;
+ if (width < MIN_FRAME_WIDTH) {
+ width = MIN_FRAME_WIDTH;
+ } else if (width > MAX_FRAME_WIDTH) {
+ width = MAX_FRAME_WIDTH;
+ }
+ int height = y * 3 / 4;
+ if (height < MIN_FRAME_HEIGHT) {
+ height = MIN_FRAME_HEIGHT;
+ } else if (height > MAX_FRAME_HEIGHT) {
+ height = MAX_FRAME_HEIGHT;
+ }
+ int leftOffset = (x - width) / 2;
+ int topOffset = (y - height) / 2;
+ // int topOffset = (screenResolution.y - height)/3;
+ framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);
+// Log.i(TAG, "Camera_width: " + width+",Camera_height:"+height);
+ }
+ return framingRect;
+ }
+
+ /**
+ * Like {@link #getFramingRect} but coordinates are in terms of the preview frame,
+ * not UI / screen.
+ * 如果需要重设扫码区域必须重置framingRectInPreview = null
+ */
+ public Rect getFramingRectInPreview() {
+ if (framingRectInPreview == null) {
+
+ Rect rect = new Rect(getFramingRect());
+ Point cameraResolution = configManager.getCameraResolution();
+ Point screenResolution = configManager.getScreenResolution();
+ //横屏
+// 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();
+
+
+// Log.i("Test","PlanarYUVLuminanceSource_width:"+width+",height:"+height+",Rect_left:"+rect.left+"Rect_top:"+rect.top+
+// "Rect_width:"+rect.width()+ "Rect_height:"+rect.height()
+// );
+
+ 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 Camera getCamera() {
+ return camera;
+ }
+
+}
diff --git a/android/src/main/java/com/reactnativecomponent/barcode/camera/FlashlightManager.java b/android/src/main/java/com/reactnativecomponent/barcode/camera/FlashlightManager.java
new file mode 100644
index 0000000..c1208c2
--- /dev/null
+++ b/android/src/main/java/com/reactnativecomponent/barcode/camera/FlashlightManager.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.reactnativecomponent.barcode.camera;
+
+import android.os.IBinder;
+
+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() {
+ }
+
+ 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;
+ }
+ }
+
+ static void enableFlashlight() {
+ setFlashlight(true);
+ }
+
+ static void disableFlashlight() {
+ setFlashlight(false);
+ }
+
+ private static void setFlashlight(boolean active) {
+ if (iHardwareService != null) {
+ invoke(setFlashEnabledMethod, iHardwareService, active);
+ }
+ }
+
+}
diff --git a/android/src/main/java/com/reactnativecomponent/barcode/camera/PlanarYUVLuminanceSource.java b/android/src/main/java/com/reactnativecomponent/barcode/camera/PlanarYUVLuminanceSource.java
new file mode 100644
index 0000000..bbb1828
--- /dev/null
+++ b/android/src/main/java/com/reactnativecomponent/barcode/camera/PlanarYUVLuminanceSource.java
@@ -0,0 +1,154 @@
+/*
+ * 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 com.reactnativecomponent.barcode.camera;
+
+import android.graphics.Bitmap;
+import android.util.Log;
+
+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;
+
+ try {
+ System.arraycopy(yuvData, offset, row, 0, width);
+ } catch (ArrayIndexOutOfBoundsException e) {
+// Log.e("Exception","ArrayIndexOutOfBoundsException:"+e);
+ e.printStackTrace();
+ }
+
+ 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;
+
+ try {
+ System.arraycopy(yuv, inputOffset, matrix, outputOffset, width);
+ } catch (ArrayIndexOutOfBoundsException e) {
+// Log.e("Exception","ArrayIndexOutOfBoundsException:"+e);
+ e.printStackTrace();
+ }
+
+ 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 = 0;
+ try {
+ grey = yuv[inputOffset + x] & 0xff;
+ } catch (ArrayIndexOutOfBoundsException e) {
+// Log.e("Exception","ArrayIndexOutOfBoundsException:"+e);
+ e.printStackTrace();
+ }
+ 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/android/src/main/java/com/reactnativecomponent/barcode/camera/PreviewCallback.java b/android/src/main/java/com/reactnativecomponent/barcode/camera/PreviewCallback.java
new file mode 100644
index 0000000..b5c9500
--- /dev/null
+++ b/android/src/main/java/com/reactnativecomponent/barcode/camera/PreviewCallback.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.reactnativecomponent.barcode.camera;
+
+import android.graphics.Point;
+import android.hardware.Camera;
+import android.os.Handler;
+import android.os.Message;
+
+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;
+ }
+
+ 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/android/src/main/java/com/reactnativecomponent/barcode/decoding/CaptureActivityHandler.java b/android/src/main/java/com/reactnativecomponent/barcode/decoding/CaptureActivityHandler.java
new file mode 100644
index 0000000..982f23e
--- /dev/null
+++ b/android/src/main/java/com/reactnativecomponent/barcode/decoding/CaptureActivityHandler.java
@@ -0,0 +1,153 @@
+/*
+ * 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 com.reactnativecomponent.barcode.decoding;
+
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.Result;
+import com.reactnativecomponent.barcode.CaptureView;
+import com.reactnativecomponent.barcode.R;
+import com.reactnativecomponent.barcode.camera.CameraManager;
+import com.reactnativecomponent.barcode.view.ViewfinderResultPointCallback;
+
+
+import java.util.Vector;
+
+/**
+ * This class handles all the messaging which comprises the state machine for capture.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class CaptureActivityHandler extends Handler {
+
+ private static final String TAG = CaptureActivityHandler.class.getSimpleName();
+
+ private final CaptureView captureView;
+ private final DecodeThread decodeThread;
+ private State state;
+
+ private enum State {
+ PREVIEW,
+ SUCCESS,
+ DONE
+ }
+
+ public CaptureActivityHandler(CaptureView captureView, Vector decodeFormats,
+ String characterSet) {
+ this.captureView = captureView;
+ decodeThread = new DecodeThread(captureView, decodeFormats, characterSet,
+ new ViewfinderResultPointCallback(captureView.getViewfinderView()));
+ 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) {// case 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) {// case R.id.restart_preview:
+// Log.d(TAG, "Got restart preview message");
+ restartPreviewAndDecode();
+
+ } else if (message.what == R.id.decode_succeeded) {// case 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);*/
+ captureView.handleDecode((Result) message.obj, null);
+
+// case R.id.decode_failed:
+ } 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");
+ captureView.ShowResult((Intent) message.obj);
+
+
+ /* case 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);
+ activity.startActivity(intent);
+ break;*/
+ }
+ }
+
+
+
+
+ public void quitSynchronously() {
+ if (state != State.DONE) {
+ state = State.DONE;
+ CameraManager.get().stopPreview();
+ Message quit = Message.obtain(decodeThread.getHandler(), R.id.quit);
+ quit.sendToTarget();
+ decodeThread.flag=false;
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ decodeThread.interrupt();
+ try {
+ decodeThread.join();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ // continue
+ }
+
+ // Be absolutely sure we don't send any queued up messages
+ removeMessages(R.id.decode_succeeded);
+ removeMessages(R.id.decode_failed);
+ }
+ }
+
+ public void restartPreviewAndDecode() {
+ if (state == State.SUCCESS) {
+ state = State.PREVIEW;
+ decodeThread.flag=true;
+ CameraManager.get().requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
+ CameraManager.get().requestAutoFocus(this, R.id.auto_focus);
+ captureView.drawViewfinder();
+ }
+ }
+
+
+}
diff --git a/android/src/main/java/com/reactnativecomponent/barcode/decoding/DecodeFormatManager.java b/android/src/main/java/com/reactnativecomponent/barcode/decoding/DecodeFormatManager.java
new file mode 100644
index 0000000..821d788
--- /dev/null
+++ b/android/src/main/java/com/reactnativecomponent/barcode/decoding/DecodeFormatManager.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.reactnativecomponent.barcode.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(",");
+
+ static final Vector PRODUCT_FORMATS;
+ static final Vector ONE_D_FORMATS;
+ static final Vector QR_CODE_FORMATS;
+ 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.RSS_14);
+ 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/android/src/main/java/com/reactnativecomponent/barcode/decoding/DecodeHandler.java b/android/src/main/java/com/reactnativecomponent/barcode/decoding/DecodeHandler.java
new file mode 100644
index 0000000..cd74e29
--- /dev/null
+++ b/android/src/main/java/com/reactnativecomponent/barcode/decoding/DecodeHandler.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.reactnativecomponent.barcode.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 com.reactnativecomponent.barcode.CaptureView;
+import com.reactnativecomponent.barcode.R;
+import com.reactnativecomponent.barcode.camera.CameraManager;
+import com.reactnativecomponent.barcode.camera.PlanarYUVLuminanceSource;
+
+
+import java.util.Hashtable;
+
+final class
+DecodeHandler extends Handler {
+
+// private static final String TAG = DecodeHandler.class.getSimpleName();
+ private static final String TAG ="Test";
+
+
+ private final CaptureView captureView;
+ private final MultiFormatReader multiFormatReader;
+
+ DecodeHandler(CaptureView captureView, Hashtable hints) {
+
+ multiFormatReader = new MultiFormatReader();
+ multiFormatReader.setHints(hints);
+ this.captureView = captureView;
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ int id=message.what;
+ if(id== R.id.decode) {
+ if(captureView.decodeFlag) {
+ decode((byte[]) message.obj, message.arg1, message.arg2);
+ }
+ }else if(id==R.id.quit) {
+// Log.i(TAG, "decode 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) {
+ // Log.v("DecodeHandler", data.length + "");
+// {
+// StringBuilder tmp;
+// // if the data width is 480
+// {
+// int x = 40;
+// tmp = new StringBuilder();
+// for (int y = 0; y < height; y += 20)
+// tmp.append(Integer.toHexString(data[y * width + x]) + "_");
+// Log.v("DecodeHandler", tmp.toString());
+// }
+// // if the data width is 320
+// {
+// int x = 40;
+// tmp = new StringBuilder();
+// for (int y = 0; y < width; y += 20)
+// tmp.append(Integer.toHexString(data[y * height + x]) + "_");
+// Log.v("DecodeHandler", tmp.toString());
+// }
+// }
+ // rotate the data 90 degree clockwise.
+ // note that on a HTC G2, data length is 230400, but 480*320=153600,
+ // which means u and v channels are not touched.
+ // from the rotated data you will get a wrong image, whatever, only Y
+ // channel is used.
+ // check PlanarYUVLuminanceSource for more.
+ // it says: "It works for any pixel format where
+ // the Y channel is planar and appears first, including
+ // YCbCr_420_SP and YCbCr_422_SP."
+ 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];
+ }
+
+ long start = System.currentTimeMillis();
+ Result rawResult = null;
+// PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(data, width, height);
+// PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(data, height, width);
+ // switch width and height
+// Log.i("Test","DecodeHandler_height:"+height+",width:"+width);
+ PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(rotatedData, height, width);
+
+ 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(captureView.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(captureView.getHandler(), R.id.decode_failed);
+ message.sendToTarget();
+ }
+ }
+
+}
diff --git a/android/src/main/java/com/reactnativecomponent/barcode/decoding/DecodeThread.java b/android/src/main/java/com/reactnativecomponent/barcode/decoding/DecodeThread.java
new file mode 100644
index 0000000..bd34b11
--- /dev/null
+++ b/android/src/main/java/com/reactnativecomponent/barcode/decoding/DecodeThread.java
@@ -0,0 +1,113 @@
+/*
+ * 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 com.reactnativecomponent.barcode.decoding;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.ResultPointCallback;
+import com.reactnativecomponent.barcode.CaptureView;
+import com.reactnativecomponent.barcode.R;
+
+
+import java.util.Hashtable;
+import java.util.Vector;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * This thread does all the heavy lifting of decoding the images.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+final class DecodeThread extends Thread {
+
+ public static final String BARCODE_BITMAP = "barcode_bitmap";
+
+ private final CaptureView captureView;
+ private final Hashtable hints;
+ private DecodeHandler handler;
+ private final CountDownLatch handlerInitLatch;//到计数的锁
+ public boolean flag=true;
+
+ DecodeThread(CaptureView captureView,
+ Vector decodeFormats,
+ String characterSet,
+ ResultPointCallback resultPointCallback) {
+// Log.i("Test", "DecodeThread create");
+ this.captureView = captureView;
+ handlerInitLatch = new CountDownLatch(1);//从1开始到计数
+
+ hints = new Hashtable(3);
+
+// // The prefs can't change while the thread is running, so pick them up once here.
+// if (decodeFormats == null || decodeFormats.isEmpty()) {
+// SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
+// decodeFormats = new Vector();
+// if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_1D, true)) {
+// decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS);
+// }
+// if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_QR, true)) {
+// decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
+// }
+// if (prefs.getBoolean(PreferencesActivity.KEY_DECODE_DATA_MATRIX, true)) {
+// decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);
+// }
+// }
+ 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();//阻塞先等handler被初始化了才能返回结果。改计数锁即等countdown-->0。
+ } catch (InterruptedException ie) {
+ // continue?
+ }
+ return handler;
+ }
+
+ @Override
+ public void run() {
+
+ Looper.prepare();
+ handler = new DecodeHandler(captureView, hints);
+ handlerInitLatch.countDown();//启动到计数,countdown-1 变成0;
+// Log.i("Test","The worker thread id = " + Thread.currentThread().getId()); //判断线程ID
+ Looper.loop();
+
+ }
+
+
+
+}
diff --git a/android/src/main/java/com/reactnativecomponent/barcode/decoding/DecodeUtil.java b/android/src/main/java/com/reactnativecomponent/barcode/decoding/DecodeUtil.java
new file mode 100644
index 0000000..f254367
--- /dev/null
+++ b/android/src/main/java/com/reactnativecomponent/barcode/decoding/DecodeUtil.java
@@ -0,0 +1,168 @@
+package com.reactnativecomponent.barcode.decoding;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Drawable;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.BinaryBitmap;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.PlanarYUVLuminanceSource;
+import com.google.zxing.Result;
+import com.google.zxing.common.HybridBinarizer;
+import com.google.zxing.qrcode.QRCodeReader;
+
+import java.lang.ref.WeakReference;
+import java.util.Hashtable;
+
+
+public class DecodeUtil {
+
+ public static Bitmap convertToBitmap(String path) {
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+ // 设置为ture只获取图片大小
+ opts.inJustDecodeBounds = true;
+ opts.inPreferredConfig = Bitmap.Config.RGB_565;
+ // 返回为空
+ BitmapFactory.decodeFile(path, opts);
+ int width = opts.outWidth;
+ int height = opts.outHeight;
+ /* float scaleWidth = 0.f, scaleHeight = 0.f;
+ if (width > w || height > h){
+ // 缩放
+ scaleWidth = ((float) width) / w;
+ scaleHeight = ((float) height) / h;
+ }*/
+ opts.inJustDecodeBounds = false;
+// float scale = Math.max(scaleWidth, scaleHeight);
+ float scale = 1.5f;
+ opts.inSampleSize = (int)scale;
+ WeakReference weak = new WeakReference(BitmapFactory.decodeFile(path, opts));
+ return Bitmap.createScaledBitmap(weak.get(), (int)(width/scale), (int)(height/scale), true);
+ }
+
+ public static String getStringFromQRCode(String path) {
+ String httpString = null;
+
+ Bitmap bmp = convertToBitmap(path);
+ byte[] data = getYUV420sp(bmp.getWidth(), bmp.getHeight(), bmp);
+ // 处理
+ try {
+ Hashtable hints = new Hashtable();
+// hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
+ hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
+ hints.put(DecodeHintType.POSSIBLE_FORMATS, BarcodeFormat.QR_CODE);
+ PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(data,
+ bmp.getWidth(),
+ bmp.getHeight(),
+ 0, 0,
+ bmp.getWidth(),
+ bmp.getHeight(),
+ false);
+ BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source));
+ QRCodeReader reader2= new QRCodeReader();
+ Result result = reader2.decode(bitmap1, hints);
+
+ httpString = result.getText();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ bmp.recycle();
+ bmp = null;
+
+ return httpString;
+ }
+
+
+ /**
+ * YUV420sp
+ *
+ * @param inputWidth
+ * @param inputHeight
+ * @param scaled
+ * @return
+ */
+ public static byte[] getYUV420sp(int inputWidth, int inputHeight,
+ Bitmap scaled) {
+ int[] argb = new int[inputWidth * inputHeight];
+
+ scaled.getPixels(argb, 0, inputWidth, 0, 0, inputWidth, inputHeight);
+
+ byte[] yuv = new byte[inputWidth * inputHeight * 3 / 2];
+
+ encodeYUV420SP(yuv, argb, inputWidth, inputHeight);
+
+ scaled.recycle();
+
+ return yuv;
+ }
+
+ /**
+ * RGB转YUV420sp
+ *
+ * @param yuv420sp
+ * inputWidth * inputHeight * 3 / 2
+ * @param argb
+ * inputWidth * inputHeight
+ * @param width
+ * @param height
+ */
+ private static void encodeYUV420SP(byte[] yuv420sp, int[] argb, int width,
+ int height) {
+ // 帧图片的像素大小
+ final int frameSize = width * height;
+ // ---YUV数据---
+ int Y, U, V;
+ // Y的index从0开始
+ int yIndex = 0;
+ // UV的index从frameSize开始
+ int uvIndex = frameSize;
+
+ // ---颜色数据---
+// int a, R, G, B;
+ int R, G, B;
+ //
+ int argbIndex = 0;
+ //
+
+ // ---循环所有像素点,RGB转YUV---
+ for (int j = 0; j < height; j++) {
+ for (int i = 0; i < width; i++) {
+
+ // a is not used obviously
+// a = (argb[argbIndex] & 0xff000000) >> 24;
+ R = (argb[argbIndex] & 0xff0000) >> 16;
+ G = (argb[argbIndex] & 0xff00) >> 8;
+ B = (argb[argbIndex] & 0xff);
+ //
+ argbIndex++;
+
+ // well known RGB to YUV algorithm
+ Y = ((66 * R + 129 * G + 25 * B + 128) >> 8) + 16;
+ U = ((-38 * R - 74 * G + 112 * B + 128) >> 8) + 128;
+ V = ((112 * R - 94 * G - 18 * B + 128) >> 8) + 128;
+
+ //
+ Y = Math.max(0, Math.min(Y, 255));
+ U = Math.max(0, Math.min(U, 255));
+ V = Math.max(0, Math.min(V, 255));
+
+ // NV21 has a plane of Y and interleaved planes of VU each
+ // sampled by a factor of 2
+ // meaning for every 4 Y pixels there are 1 V and 1 U. Note the
+ // sampling is every other
+ // pixel AND every other scanline.
+ // ---Y---
+ yuv420sp[yIndex++] = (byte) Y;
+ // ---UV---
+ if ((j % 2 == 0) && (i % 2 == 0)) {
+ //
+ yuv420sp[uvIndex++] = (byte) V;
+ //
+ yuv420sp[uvIndex++] = (byte) U;
+ }
+ }
+ }
+ }
+}
diff --git a/android/src/main/java/com/reactnativecomponent/barcode/decoding/FinishListener.java b/android/src/main/java/com/reactnativecomponent/barcode/decoding/FinishListener.java
new file mode 100644
index 0000000..db89d3d
--- /dev/null
+++ b/android/src/main/java/com/reactnativecomponent/barcode/decoding/FinishListener.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 com.reactnativecomponent.barcode.decoding;
+
+import android.app.Activity;
+import android.content.DialogInterface;
+
+/**
+ * Simple listener used to exit the app in a few cases.
+ *
+ * @author Sean Owen
+ */
+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/android/src/main/java/com/reactnativecomponent/barcode/decoding/InactivityTimer.java b/android/src/main/java/com/reactnativecomponent/barcode/decoding/InactivityTimer.java
new file mode 100644
index 0000000..40e5948
--- /dev/null
+++ b/android/src/main/java/com/reactnativecomponent/barcode/decoding/InactivityTimer.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.reactnativecomponent.barcode.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/android/src/main/java/com/reactnativecomponent/barcode/decoding/Intents.java b/android/src/main/java/com/reactnativecomponent/barcode/decoding/Intents.java
new file mode 100644
index 0000000..6c3495a
--- /dev/null
+++ b/android/src/main/java/com/reactnativecomponent/barcode/decoding/Intents.java
@@ -0,0 +1,192 @@
+/*
+ * 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 com.reactnativecomponent.barcode.decoding;
+
+/**
+ * This class provides the constants to use when sending an Intent to Barcode Scanner.
+ * These strings are effectively API and cannot be changed.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+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/android/src/main/java/com/reactnativecomponent/barcode/view/LinearGradientView.java b/android/src/main/java/com/reactnativecomponent/barcode/view/LinearGradientView.java
new file mode 100644
index 0000000..0f072aa
--- /dev/null
+++ b/android/src/main/java/com/reactnativecomponent/barcode/view/LinearGradientView.java
@@ -0,0 +1,186 @@
+package com.reactnativecomponent.barcode.view;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.Shader;
+import android.graphics.drawable.GradientDrawable;
+import android.view.View;
+import android.view.ViewGroup;
+
+
+public class LinearGradientView extends View {
+
+// /* *//**
+// * 中间滑动线的最顶端位置
+// *//*
+// private int slideTop;
+//
+// *//**
+// * 中间那条线每次刷新移动的距离
+// *//*
+// private static int SPEEN_DISTANCE = 3;
+//
+// *//**
+// * 中间滑动线的最底端位置
+// *//*
+// private int slideBottom;
+//
+// *//**
+// * 四个蓝色边角对应的宽度
+// *//*
+// public int CORNER_WIDTH = 3;*/
+
+ /**
+ * 框架颜色
+ */
+ public int frameColor=Color.GREEN;
+
+ /**
+ * 扫描线渐变色中间色
+ */
+ public int frameBaseColor;
+
+ /**
+ * 线的厚度
+ */
+ public int size;
+ /**
+ * 线的宽度
+ */
+ public int width;
+
+ Activity activity;
+
+// /**
+// * 扫描框中的中间线的宽度
+// */
+// private static final int MIDDLE_LINE_WIDTH = 3;
+
+
+// public int top,left,right;
+
+
+// private Paint paintLine;
+
+
+ public LinearGradientView(Context context,Activity activity) {
+ super(context);
+ this.activity=activity;
+
+// paintLine=new Paint();
+ }
+
+ public void setFrameColor(int frameColor) {
+
+ this.frameColor = frameColor;
+
+ this.frameBaseColor = reSetColor(frameColor);
+ //渐变色drawable
+ int[] mColors = new int[]{Color.TRANSPARENT, frameBaseColor, frameColor, frameColor, frameColor, frameColor, frameColor, frameBaseColor, Color.TRANSPARENT};
+
+ GradientDrawable drawable = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, mColors);
+ drawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);
+// drawable.setCornerRadius(15);
+// drawable.setStroke(10,-1);
+ setBackground(drawable);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ ViewGroup.LayoutParams params= getLayoutParams();
+ if(size>1) {
+ params.height = size;
+ }
+ if(width>1){
+ params.width=width;
+ }
+ setLayoutParams(params);
+
+ super.onAttachedToWindow();
+
+ }
+
+
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+
+ super.onWindowFocusChanged(hasWindowFocus);
+
+
+
+ }
+
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ final ViewGroup.LayoutParams params= getLayoutParams();
+ int height = getHeight();
+ if (height < 3) {
+ params.height = 3;
+ } else if (height > 10) {
+ params.height = 10;
+ }
+
+ activity.runOnUiThread(new Runnable() {
+ public void run() {
+ LinearGradientView.this.setLayoutParams(params);
+ }
+ });
+
+
+ super.onLayout(changed, left, top, right, bottom);
+
+ }
+
+
+
+
+/* @Override
+ protected void onDraw(Canvas canvas) {
+ paintLine.setColor(frameColor);
+
+
+ slideTop += SPEEN_DISTANCE;
+ if (slideTop >= slideBottom) {
+ slideTop = top + CORNER_WIDTH;
+ }
+ //自己画
+ paintLine.setColor(frameColor);
+
+// 0x8800FF00
+ Shader mShader = new LinearGradient(left + CORNER_WIDTH, slideTop, right
+ - CORNER_WIDTH, slideTop + MIDDLE_LINE_WIDTH,new int[] {Color.TRANSPARENT,frameBaseColor,frameColor,frameColor,frameColor,frameColor,frameColor,frameBaseColor,Color.TRANSPARENT},null, Shader.TileMode.CLAMP);
+//新建一个线性渐变,前两个参数是渐变开始的点坐标,第三四个参数是渐变结束的点的坐标。
+// 连接这2个点就拉出一条渐变线了,玩过PS的都懂。然后那个数组是渐变的颜色。下一个参数是渐变颜色的分布,如果为空,每个颜色就是均匀分布的。
+// 最后是模式,这里设置的是Clamp渐变
+ paintLine.setShader(mShader);
+ canvas.drawRect(left + CORNER_WIDTH, slideTop, right
+ - CORNER_WIDTH, slideTop + MIDDLE_LINE_WIDTH, paintLine);
+
+ }*/
+
+ /**
+ * 中间色颜色换算
+ */
+ public int reSetColor(int startInt) {
+
+ int startA = (startInt >> 24) & 0xff;
+ int startR = (startInt >> 16) & 0xff;
+ int startG = (startInt >> 8) & 0xff;
+ int startB = startInt & 0xff;
+
+ int endA = startA / 2;// 转化后可以设置 2:半透明度 4:4分之一透明度
+
+
+ return ((startA + (endA - startA)) << 24)
+ | (startR << 16)
+ | (startG << 8)
+ | (startB);
+
+
+ }
+}
diff --git a/android/src/main/java/com/reactnativecomponent/barcode/view/VerticalSeekBar.java b/android/src/main/java/com/reactnativecomponent/barcode/view/VerticalSeekBar.java
new file mode 100644
index 0000000..fc4c901
--- /dev/null
+++ b/android/src/main/java/com/reactnativecomponent/barcode/view/VerticalSeekBar.java
@@ -0,0 +1,179 @@
+package com.reactnativecomponent.barcode.view;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.AbsSeekBar;
+
+public class VerticalSeekBar extends AbsSeekBar {
+
+ private Drawable mThumb;
+
+ public interface OnSeekBarChangeListener {
+ void onProgressChanged(VerticalSeekBar VerticalSeekBar, int progress, boolean fromUser);
+
+ void onStartTrackingTouch(VerticalSeekBar VerticalSeekBar);
+
+ void onStopTrackingTouch(VerticalSeekBar VerticalSeekBar);
+ }
+
+ private OnSeekBarChangeListener mOnSeekBarChangeListener;
+
+ public VerticalSeekBar(Context context) {
+ this(context, null);
+ }
+
+ public VerticalSeekBar(Context context, AttributeSet attrs) {
+ this(context, attrs, android.R.attr.seekBarStyle);
+ }
+
+ public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public void setOnSeekBarChangeListener(OnSeekBarChangeListener l) {
+ mOnSeekBarChangeListener = l;
+ }
+
+ void onStartTrackingTouch() {
+ if (mOnSeekBarChangeListener != null) {
+ mOnSeekBarChangeListener.onStartTrackingTouch(this);
+ }
+ }
+
+ void onStopTrackingTouch() {
+ if (mOnSeekBarChangeListener != null) {
+ mOnSeekBarChangeListener.onStopTrackingTouch(this);
+ }
+ }
+
+ void onProgressRefresh(float scale, boolean fromUser) {
+ Drawable thumb = mThumb;
+ if (thumb != null) {
+ setThumbPos(getHeight(), thumb, scale, Integer.MIN_VALUE);
+ invalidate();
+ }
+ if (mOnSeekBarChangeListener != null) {
+ mOnSeekBarChangeListener.onProgressChanged(this, getProgress(), isPressed());
+ }
+ }
+
+ private void setThumbPos(int w, Drawable thumb, float scale, int gap) {
+ int available = w - getPaddingLeft() - getPaddingRight();
+ int thumbWidth = thumb.getIntrinsicWidth();
+ int thumbHeight = thumb.getIntrinsicHeight();
+ available -= thumbWidth;
+
+ // The extra space for the thumb to move on the track
+ available += getThumbOffset() * 2;
+
+ int thumbPos = (int) (scale * available);
+
+ int topBound, bottomBound;
+ if (gap == Integer.MIN_VALUE) {
+ Rect oldBounds = thumb.getBounds();
+ topBound = oldBounds.top;
+ bottomBound = oldBounds.bottom;
+ } else {
+ topBound = gap;
+ bottomBound = gap + thumbHeight;
+ }
+ thumb.setBounds(thumbPos, topBound, thumbPos + thumbWidth, bottomBound);
+ }
+
+ @Override
+ protected void onDraw(Canvas c) {
+ c.rotate(-90);// 反转90度,将水平SeekBar竖起来
+ c.translate(-getHeight(), 0);// 将经过旋转后得到的VerticalSeekBar移到正确的位置,注意经旋转后宽高值互换
+ super.onDraw(c);
+ }
+
+ @Override
+ protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(heightMeasureSpec, widthMeasureSpec);
+ setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());// 宽高值互换
+ }
+
+ @Override
+ public void setThumb(Drawable thumb) {
+ mThumb = thumb;
+ super.setThumb(thumb);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(h, w, oldw, oldh);// 宽高值互换
+ }
+
+ // 与源码完全相同,仅为调用宽高值互换处理的onStartTrackingTouch()方法
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (!isEnabled()) {
+ return false;
+ }
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN: {
+ setPressed(true);
+ onStartTrackingTouch();
+ trackTouchEvent(event);
+ break;
+ }
+
+ case MotionEvent.ACTION_MOVE: {
+ trackTouchEvent(event);
+ attemptClaimDrag();
+ break;
+ }
+
+ case MotionEvent.ACTION_UP: {
+ trackTouchEvent(event);
+ onStopTrackingTouch();
+ setPressed(false);
+ // ProgressBar doesn't know to repaint the thumb drawable
+ // in its inactive state when the touch stops (because the
+ // value has not apparently changed)
+ invalidate();
+ break;
+ }
+
+ case MotionEvent.ACTION_CANCEL: {
+ onStopTrackingTouch();
+ setPressed(false);
+ invalidate(); // see above explanation
+ break;
+ }
+
+ default:
+ break;
+ }
+ return true;
+ }
+
+ // 宽高值互换处理
+ private void trackTouchEvent(MotionEvent event) {
+ final int height = getHeight();
+ final int available = height - getPaddingBottom() - getPaddingTop();
+ int Y = (int) event.getY();
+ float scale;
+ float progress = 0;
+ if (Y > height - getPaddingBottom()) {
+ scale = 0.0f;
+ } else if (Y < getPaddingTop()) {
+ scale = 1.0f;
+ } else {
+ scale = (float) (height - getPaddingBottom() - Y) / (float) available;
+ }
+ final int max = getMax();
+ progress = scale * max;
+ setProgress((int) progress);
+ }
+
+ private void attemptClaimDrag() {
+ if (getParent() != null) {
+ getParent().requestDisallowInterceptTouchEvent(true);
+ }
+ }
+}
diff --git a/android/src/main/java/com/reactnativecomponent/barcode/view/ViewfinderResultPointCallback.java b/android/src/main/java/com/reactnativecomponent/barcode/view/ViewfinderResultPointCallback.java
new file mode 100644
index 0000000..2cd4a55
--- /dev/null
+++ b/android/src/main/java/com/reactnativecomponent/barcode/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 com.reactnativecomponent.barcode.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/android/src/main/java/com/reactnativecomponent/barcode/view/ViewfinderView.java b/android/src/main/java/com/reactnativecomponent/barcode/view/ViewfinderView.java
new file mode 100644
index 0000000..54b0a31
--- /dev/null
+++ b/android/src/main/java/com/reactnativecomponent/barcode/view/ViewfinderView.java
@@ -0,0 +1,402 @@
+/*
+ * 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 com.reactnativecomponent.barcode.view;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Shader;
+import android.graphics.Typeface;
+import android.util.Log;
+import android.view.View;
+
+import com.google.zxing.ResultPoint;
+import com.reactnativecomponent.barcode.R;
+import com.reactnativecomponent.barcode.camera.CameraManager;
+
+
+import java.util.Collection;
+import java.util.HashSet;
+
+/**
+ * This view is overlaid on top of the camera preview. It adds the viewfinder rectangle and partial
+ * transparency outside it, as well as the laser scanner animation and result points.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class ViewfinderView extends View
+{
+ /**扫描页面透明度*/
+ private static final int[] SCANNER_ALPHA = { 0, 64, 128, 192, 255, 192,
+ 128, 64 };
+ /**动画延迟*/
+ private static final long ANIMATION_DELAY = 10L;
+ private static final int OPAQUE = 0xFF;//不透明
+
+ /**
+ * 四个蓝色边角对应的长度
+ */
+ private int ScreenRate;
+
+ /**
+ * 四个蓝色边角对应的宽度
+ */
+ public int CORNER_WIDTH = 3;
+ /**
+ * 扫描框中的中间线的宽度
+ */
+ private int MIDDLE_LINE_WIDTH = 3;
+
+ /**
+ * 扫描框中的中间线的与扫描框左右的间隙
+ */
+ private static final int MIDDLE_LINE_PADDING = 5;
+
+ /**
+ * 中间那条线每次刷新移动的距离
+ */
+ private static int SPEEN_DISTANCE = 3;
+
+ /**
+ * 手机的屏幕密度
+ */
+ private static float density;
+ /**
+ * 字体大小
+ */
+ private static final int TEXT_SIZE = 16;
+
+ public String ShowText;
+ /**
+ * 字体距离扫描框下面的距离
+ */
+ private static final int TEXT_PADDING_TOP = 30;
+
+ private final Paint paint;
+ private final Paint paintLine;
+ /**返回的照片*/
+ private Bitmap resultBitmap;
+ /**遮盖物颜色*/
+ private final int maskColor;
+ /**结果颜色*/
+ private final int resultColor;
+ /**框架颜色*/
+ public int frameColor;
+ /**
+ * 扫描线渐变色中间色
+ */
+ public int frameBaseColor;
+ /**扫描线颜色*/
+ private final int laserColor;
+ /**结果点的颜色*/
+ private final int resultPointColor;
+ private int scannerAlpha;//扫描透明度
+ /**可能的结果点数*/
+ private Collection possibleResultPoints;
+ /**最后的结果点数*/
+ private Collection lastPossibleResultPoints;
+ /**
+ * 是否画中间线
+ */
+ public boolean drawLine = false;
+
+ /**
+ * 中间滑动线的最顶端位置
+ */
+ private int slideTop;
+
+ /**
+ * 中间滑动线的最底端位置
+ */
+ private int slideBottom;
+ private boolean isFirst;
+
+ /**
+ *s扫码横线的移动时间
+ */
+ public int scanTime;
+
+
+ // This constructor is used when the class is built from an XML resource.
+ public ViewfinderView(Context context,int time,int color)
+ {
+ super(context);
+ density = context.getResources().getDisplayMetrics().density;
+ //将像素转化成dp
+ ScreenRate = (int) (25 * density);
+
+ // Initialize these once for performance rather than calling them every time in onDraw().
+ paint = new Paint();
+ paintLine=new Paint();
+ Resources resources = getResources();
+ maskColor = resources.getColor(R.color.viewfinder_mask);
+ resultColor = resources.getColor(R.color.backgroud);
+ frameColor = color;//resources.getColor(R.color.viewfinder_frame);
+ frameBaseColor=reSetColor(frameColor);
+// Log.i("Test","reSetColor"+frameBaseColor);
+ laserColor = resources.getColor(R.color.viewfinder_laser);
+ resultPointColor = resources.getColor(R.color.possible_result_points);
+ scannerAlpha = 0;
+ possibleResultPoints = new HashSet(5);
+ drawLine=true;
+ scanTime=time;
+ }
+
+ public void setCORNER_WIDTH(int CORNER_WIDTH) {
+ this.CORNER_WIDTH = CORNER_WIDTH;
+ }
+
+ public void setMIDDLE_LINE_WIDTH(int MIDDLE_LINE_WIDTH) {
+ this.MIDDLE_LINE_WIDTH = MIDDLE_LINE_WIDTH;
+// Log.i("Test","MIDDLE_LINE_WIDTH:"+MIDDLE_LINE_WIDTH);
+ }
+
+ @Override
+ public void onDraw(Canvas canvas)
+ {
+ Rect frame = CameraManager.get().getFramingRect();
+ if (frame == null)
+ {
+ return;
+ }
+
+ if (!isFirst)
+ {
+ isFirst = true;
+ slideTop = frame.top + CORNER_WIDTH;
+ slideBottom = frame.bottom - CORNER_WIDTH;
+
+ SPEEN_DISTANCE= (slideBottom-slideTop)/((scanTime/16)+2);
+
+
+ }
+
+ 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
+ {
+
+ // Draw a two pixel solid black border inside the framing rect
+ //画框架
+ paint.setColor(frameColor);
+ // canvas.drawRect(frame.left, frame.top, frame.right + 1, frame.top + 2, paint);
+ // canvas.drawRect(frame.left, frame.top + 2, frame.left + 2, frame.bottom - 1, paint);
+ // canvas.drawRect(frame.right - 1, frame.top, frame.right + 1, frame.bottom - 1, paint);
+ // canvas.drawRect(frame.left, frame.bottom - 1, frame.right + 1, frame.bottom + 1, paint);
+ //public void drawRect (float left, float top, float right, float bottom, Paint paint)
+ //自己画(扫描框边上的角,共8个部分)
+ canvas.drawRect(frame.left - CORNER_WIDTH/2 , frame.top
+ - CORNER_WIDTH /2, frame.left + ScreenRate, frame.top
+ + CORNER_WIDTH /2, paint);//左上角横线
+ canvas.drawRect(frame.left - CORNER_WIDTH/2 , frame.top
+ - CORNER_WIDTH/2, frame.left + CORNER_WIDTH/2,
+ frame.top + ScreenRate, paint);//左上角竖线
+
+ paint.setColor(Color.WHITE);
+ //画白线
+ canvas.drawRect(frame.left - CORNER_WIDTH/2 , frame.top
+ + ScreenRate, frame.left,
+ frame.bottom - ScreenRate, paint);//左白线
+ canvas.drawRect(frame.right , frame.top
+ + ScreenRate, frame.right+ CORNER_WIDTH/2,
+ frame.bottom - ScreenRate, paint);//右白线
+ canvas.drawRect(frame.left+ScreenRate , frame.top - CORNER_WIDTH/2,
+ frame.right- ScreenRate,frame.top, paint);//上白线
+ canvas.drawRect(frame.left+ScreenRate , frame.bottom ,
+ frame.right- ScreenRate,frame.bottom + CORNER_WIDTH/2, paint);//下白线
+
+
+ paint.setColor(frameColor);
+
+ canvas.drawRect(frame.left - CORNER_WIDTH/2 , frame.bottom
+ - ScreenRate, frame.left + CORNER_WIDTH/2 , frame.bottom
+ + CORNER_WIDTH/2 , paint);//左下角竖线
+ canvas.drawRect(frame.left - CORNER_WIDTH/2 , frame.bottom
+ - CORNER_WIDTH/2 , frame.left + ScreenRate, frame.bottom
+ + CORNER_WIDTH/2 , paint);//左下角横线
+ canvas.drawRect(frame.right - ScreenRate, frame.top - CORNER_WIDTH/2
+ , frame.right + CORNER_WIDTH/2 , frame.top
+ + CORNER_WIDTH/2 , paint);//右上横线
+ canvas.drawRect(frame.right - CORNER_WIDTH / 2, frame.top
+ - CORNER_WIDTH / 2, frame.right + CORNER_WIDTH / 2,
+ frame.top + ScreenRate, paint);//右上竖线
+ canvas.drawRect(frame.right - CORNER_WIDTH/2 , frame.bottom
+ - ScreenRate, frame.right + CORNER_WIDTH /2, frame.bottom
+ + CORNER_WIDTH /2, paint);//右下竖线
+ canvas.drawRect(frame.right - ScreenRate, frame.bottom
+ - CORNER_WIDTH / 2, frame.right + CORNER_WIDTH / 2,
+ frame.bottom + CORNER_WIDTH / 2, paint);//右下横线
+ //直接用图片
+ // Rect bigRect = new Rect();
+ // bigRect.left = frame.left;
+ // bigRect.right = frame.right;
+ // bigRect.top = frame.top;
+ // bigRect.bottom = frame.bottom;
+ // Drawable drawable = getResources().getDrawable(R.drawable.qr_mask);
+ // BitmapDrawable b= (BitmapDrawable) drawable;
+ // canvas.drawBitmap(b.getBitmap(), null, bigRect, paint);
+
+ // Draw a red "laser scanner" line through the middle to show decoding is active
+ // paint.setColor(laserColor);
+ // paint.setAlpha(SCANNER_ALPHA[scannerAlpha]);
+ // scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length;
+ // int middle = frame.height() / 2 + frame.top;
+ // canvas.drawRect(frame.left + 2, middle - 1, frame.right - 1, middle + 2, paint);
+
+ //画中间移动的线 (int)(SPEEN_DISTANCE*density+0.5f)
+ if(drawLine) {
+ slideTop += SPEEN_DISTANCE;
+ if (slideTop >= slideBottom) {
+ slideTop = frame.top + CORNER_WIDTH;
+ }
+ //自己画
+ paintLine.setColor(frameColor);
+
+// 0x8800FF00
+ Shader mShader = new LinearGradient(frame.left + CORNER_WIDTH, slideTop, frame.right
+ - CORNER_WIDTH, slideTop + MIDDLE_LINE_WIDTH,new int[] {Color.TRANSPARENT,frameBaseColor,frameColor,frameColor,frameColor,frameColor,frameColor,frameBaseColor,Color.TRANSPARENT},null, Shader.TileMode.CLAMP);
+//新建一个线性渐变,前两个参数是渐变开始的点坐标,第三四个参数是渐变结束的点的坐标。
+// 连接这2个点就拉出一条渐变线了,玩过PS的都懂。然后那个数组是渐变的颜色。下一个参数是渐变颜色的分布,如果为空,每个颜色就是均匀分布的。
+// 最后是模式,这里设置的是Clamp渐变
+ paintLine.setShader(mShader);
+ canvas.drawRect(frame.left + CORNER_WIDTH, slideTop, frame.right
+ - CORNER_WIDTH, slideTop + MIDDLE_LINE_WIDTH, paintLine);
+
+
+
+ }
+ //用图片
+ // Rect lineRect = new Rect();
+ // lineRect.left = frame.left;
+ // lineRect.right = frame.right;
+ // lineRect.top = slideTop;
+ // lineRect.bottom = slideTop + MIDDLE_LINE_PADDING;
+ // canvas.drawBitmap(((BitmapDrawable)(getResources().getDrawable(R.drawable.qrcode_scan_line))).getBitmap(), null, lineRect, paint);
+
+ //画扫描框下面的字
+ paint.setColor(Color.WHITE);
+ paint.setTextSize(TEXT_SIZE * density);
+ paint.setAlpha(0x40);
+ paint.setTypeface(Typeface.create("System", Typeface.BOLD));
+ paint.setTextAlign(Paint.Align.CENTER);//文字居中,X,Y 对应文字坐标中心
+ canvas.drawText(
+ ShowText,
+ width/2, frame.bottom + TEXT_PADDING_TOP * density,
+ paint);
+
+ Collection currentPossible = possibleResultPoints;
+ Collection currentLast = lastPossibleResultPoints;
+ if (currentPossible.isEmpty())
+ {
+ lastPossibleResultPoints = null;
+ }
+ else
+ {
+ possibleResultPoints = new HashSet(5);
+ lastPossibleResultPoints = currentPossible;
+ paint.setAlpha(OPAQUE);
+ paint.setColor(resultPointColor);
+ 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);
+ for (ResultPoint point : currentLast)
+ {
+ canvas.drawCircle(frame.left + point.getX(), frame.top
+ + point.getY(), 3.0f, paint);
+ }
+ }
+
+ // Request another update at the animation interval, but only repaint the laser line,
+ // not the entire viewfinder mask.
+ postInvalidateDelayed(ANIMATION_DELAY, frame.left, frame.top,
+ frame.right, frame.bottom);
+ }
+ }
+
+ public void drawViewfinder()
+ {
+ resultBitmap = null;
+ invalidate();
+ }
+
+ /**
+ * Draw a bitmap with the result points highlighted instead of the live scanning display.
+ *
+ * @param barcode An image of the decoded barcode.
+ */
+ public void drawResultBitmap(Bitmap barcode)
+ {
+ resultBitmap = barcode;
+ invalidate();
+ }
+
+ public void addPossibleResultPoint(ResultPoint point)
+ {
+ possibleResultPoints.add(point);
+ }
+
+
+ /**
+ * 中间色颜色换算
+ */
+ public int reSetColor(int startInt) {
+
+ int startA = (startInt >> 24) & 0xff;
+ int startR = (startInt >> 16) & 0xff;
+ int startG = (startInt >> 8) & 0xff;
+ int startB = startInt & 0xff;
+
+ int endA = startA/2;
+
+
+ return ((startA + (endA - startA)) << 24)
+ | (startR << 16)
+ | (startG << 8)
+ | (startB );
+
+
+ }
+
+}
diff --git a/android/src/main/res/drawable/po_seekbar.xml b/android/src/main/res/drawable/po_seekbar.xml
new file mode 100644
index 0000000..4cca522
--- /dev/null
+++ b/android/src/main/res/drawable/po_seekbar.xml
@@ -0,0 +1,23 @@
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/src/main/res/drawable/seek.9.png b/android/src/main/res/drawable/seek.9.png
new file mode 100644
index 0000000..0869d8d
Binary files /dev/null and b/android/src/main/res/drawable/seek.9.png differ
diff --git a/android/src/main/res/drawable/seek_bkg.9.png b/android/src/main/res/drawable/seek_bkg.9.png
new file mode 100644
index 0000000..6dad8d1
Binary files /dev/null and b/android/src/main/res/drawable/seek_bkg.9.png differ
diff --git a/android/src/main/res/drawable/seek_thumb.png b/android/src/main/res/drawable/seek_thumb.png
new file mode 100644
index 0000000..753b8f0
Binary files /dev/null and b/android/src/main/res/drawable/seek_thumb.png differ
diff --git a/android/src/main/res/drawable/seekbar_horizontal.xml b/android/src/main/res/drawable/seekbar_horizontal.xml
new file mode 100644
index 0000000..1df04b7
--- /dev/null
+++ b/android/src/main/res/drawable/seekbar_horizontal.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
diff --git a/android/src/main/res/drawable/seekbar_thumb.xml b/android/src/main/res/drawable/seekbar_thumb.xml
new file mode 100644
index 0000000..70bd183
--- /dev/null
+++ b/android/src/main/res/drawable/seekbar_thumb.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/src/main/res/drawable/seekbar_thumb_normal.png b/android/src/main/res/drawable/seekbar_thumb_normal.png
new file mode 100644
index 0000000..91b9362
Binary files /dev/null and b/android/src/main/res/drawable/seekbar_thumb_normal.png differ
diff --git a/android/src/main/res/drawable/seekbar_thumb_pressed.png b/android/src/main/res/drawable/seekbar_thumb_pressed.png
new file mode 100644
index 0000000..b897d22
Binary files /dev/null and b/android/src/main/res/drawable/seekbar_thumb_pressed.png differ
diff --git a/android/src/main/res/layout/seekbar_layout.xml b/android/src/main/res/layout/seekbar_layout.xml
new file mode 100644
index 0000000..2b12dac
--- /dev/null
+++ b/android/src/main/res/layout/seekbar_layout.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
diff --git a/android/src/main/res/raw/beep.wav b/android/src/main/res/raw/beep.wav
new file mode 100644
index 0000000..120affd
Binary files /dev/null and b/android/src/main/res/raw/beep.wav differ
diff --git a/android/src/main/res/values/colors.xml b/android/src/main/res/values/colors.xml
new file mode 100644
index 0000000..4510125
--- /dev/null
+++ b/android/src/main/res/values/colors.xml
@@ -0,0 +1,15 @@
+
+
+
+ #c0ffbd21
+ #ff000000
+
+ #00000000
+ #ffcc0000
+ #cb000000
+ #ff4EEE94
+ #ffffffff
+ #ffffffff
+
+
+
diff --git a/android/src/main/res/values/dimens.xml b/android/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..55c1e59
--- /dev/null
+++ b/android/src/main/res/values/dimens.xml
@@ -0,0 +1,7 @@
+
+
+
+ 16dp
+ 16dp
+
+
diff --git a/android/src/main/res/values/ids.xml b/android/src/main/res/values/ids.xml
new file mode 100644
index 0000000..3500cc5
--- /dev/null
+++ b/android/src/main/res/values/ids.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml
new file mode 100644
index 0000000..f6437f0
--- /dev/null
+++ b/android/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ QRCode_Zxing
+
diff --git a/android/src/main/res/values/styles.xml b/android/src/main/res/values/styles.xml
new file mode 100644
index 0000000..319eb0c
--- /dev/null
+++ b/android/src/main/res/values/styles.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/ios/RCTBarcode/RCTBarCode.xcodeproj/project.pbxproj b/ios/RCTBarcode/RCTBarCode.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..eba07b7
--- /dev/null
+++ b/ios/RCTBarcode/RCTBarCode.xcodeproj/project.pbxproj
@@ -0,0 +1,298 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 9F0396621DB5F98F00393F03 /* UIColor+Hex.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F0396611DB5F98F00393F03 /* UIColor+Hex.m */; };
+ 9F924BA81DAE17A5002340CC /* RectView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F924BA71DAE17A5002340CC /* RectView.m */; };
+ 9FCCD75D1DAC8DA800025BFD /* RCTBarcode.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FCCD75C1DAC8DA800025BFD /* RCTBarcode.m */; };
+ 9FCCD75E1DAC8DA800025BFD /* RCTBarcode.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9FCCD75B1DAC8DA800025BFD /* RCTBarcode.h */; };
+ 9FCCD7931DACC77A00025BFD /* LineView.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FCCD7921DACC77A00025BFD /* LineView.m */; };
+ 9FCCD7C01DACCCD500025BFD /* RCTBarcodeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FCCD7BF1DACCCD500025BFD /* RCTBarcodeManager.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 9FCCD7561DAC8DA800025BFD /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "include/$(PRODUCT_NAME)";
+ dstSubfolderSpec = 16;
+ files = (
+ 9FCCD75E1DAC8DA800025BFD /* RCTBarcode.h in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 9F0396601DB5F98F00393F03 /* UIColor+Hex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIColor+Hex.h"; sourceTree = ""; };
+ 9F0396611DB5F98F00393F03 /* UIColor+Hex.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIColor+Hex.m"; sourceTree = ""; };
+ 9F924BA61DAE17A5002340CC /* RectView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RectView.h; sourceTree = ""; };
+ 9F924BA71DAE17A5002340CC /* RectView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RectView.m; sourceTree = ""; };
+ 9FCCD7581DAC8DA800025BFD /* libRCTBarcode.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTBarcode.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 9FCCD75B1DAC8DA800025BFD /* RCTBarcode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTBarcode.h; sourceTree = ""; };
+ 9FCCD75C1DAC8DA800025BFD /* RCTBarcode.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTBarcode.m; sourceTree = ""; };
+ 9FCCD7911DACC77A00025BFD /* LineView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LineView.h; sourceTree = ""; };
+ 9FCCD7921DACC77A00025BFD /* LineView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LineView.m; sourceTree = ""; };
+ 9FCCD7BE1DACCCD500025BFD /* RCTBarcodeManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBarcodeManager.h; sourceTree = ""; };
+ 9FCCD7BF1DACCCD500025BFD /* RCTBarcodeManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBarcodeManager.m; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 9FCCD7551DAC8DA800025BFD /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 9FCCD74F1DAC8DA800025BFD = {
+ isa = PBXGroup;
+ children = (
+ 9FCCD75A1DAC8DA800025BFD /* RCTBarCode */,
+ 9FCCD7591DAC8DA800025BFD /* Products */,
+ );
+ sourceTree = "";
+ };
+ 9FCCD7591DAC8DA800025BFD /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 9FCCD7581DAC8DA800025BFD /* libRCTBarcode.a */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 9FCCD75A1DAC8DA800025BFD /* RCTBarCode */ = {
+ isa = PBXGroup;
+ children = (
+ 9FCCD7911DACC77A00025BFD /* LineView.h */,
+ 9FCCD7921DACC77A00025BFD /* LineView.m */,
+ 9FCCD75B1DAC8DA800025BFD /* RCTBarcode.h */,
+ 9FCCD75C1DAC8DA800025BFD /* RCTBarcode.m */,
+ 9FCCD7BE1DACCCD500025BFD /* RCTBarcodeManager.h */,
+ 9FCCD7BF1DACCCD500025BFD /* RCTBarcodeManager.m */,
+ 9F924BA61DAE17A5002340CC /* RectView.h */,
+ 9F924BA71DAE17A5002340CC /* RectView.m */,
+ 9F0396601DB5F98F00393F03 /* UIColor+Hex.h */,
+ 9F0396611DB5F98F00393F03 /* UIColor+Hex.m */,
+ );
+ name = RCTBarCode;
+ path = RCTBarcode;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 9FCCD7571DAC8DA800025BFD /* RCTBarcode */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 9FCCD7611DAC8DA800025BFD /* Build configuration list for PBXNativeTarget "RCTBarcode" */;
+ buildPhases = (
+ 9FCCD7541DAC8DA800025BFD /* Sources */,
+ 9FCCD7551DAC8DA800025BFD /* Frameworks */,
+ 9FCCD7561DAC8DA800025BFD /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = RCTBarcode;
+ productName = RCTBarcode;
+ productReference = 9FCCD7581DAC8DA800025BFD /* libRCTBarcode.a */;
+ productType = "com.apple.product-type.library.static";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 9FCCD7501DAC8DA800025BFD /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0800;
+ ORGANIZATIONNAME = "react-native-component";
+ TargetAttributes = {
+ 9FCCD7571DAC8DA800025BFD = {
+ CreatedOnToolsVersion = 8.0;
+ ProvisioningStyle = Automatic;
+ };
+ };
+ };
+ buildConfigurationList = 9FCCD7531DAC8DA800025BFD /* Build configuration list for PBXProject "RCTBarCode" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ );
+ mainGroup = 9FCCD74F1DAC8DA800025BFD;
+ productRefGroup = 9FCCD7591DAC8DA800025BFD /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 9FCCD7571DAC8DA800025BFD /* RCTBarcode */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 9FCCD7541DAC8DA800025BFD /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 9FCCD75D1DAC8DA800025BFD /* RCTBarcode.m in Sources */,
+ 9F0396621DB5F98F00393F03 /* UIColor+Hex.m in Sources */,
+ 9FCCD7C01DACCCD500025BFD /* RCTBarcodeManager.m in Sources */,
+ 9F924BA81DAE17A5002340CC /* RectView.m in Sources */,
+ 9FCCD7931DACC77A00025BFD /* LineView.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ 9FCCD75F1DAC8DA800025BFD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_SUSPICIOUS_MOVES = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 10.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ };
+ name = Debug;
+ };
+ 9FCCD7601DAC8DA800025BFD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_SUSPICIOUS_MOVES = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 10.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 9FCCD7621DAC8DA800025BFD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ HEADER_SEARCH_PATHS = (
+ "$(inherited)",
+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+ "$(SRCROOT)/../../../react-native/React/**",
+ );
+ OTHER_LDFLAGS = "-ObjC";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ };
+ name = Debug;
+ };
+ 9FCCD7631DAC8DA800025BFD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ HEADER_SEARCH_PATHS = (
+ "$(inherited)",
+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+ "$(SRCROOT)/../../../react-native/React/**",
+ );
+ OTHER_LDFLAGS = "-ObjC";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 9FCCD7531DAC8DA800025BFD /* Build configuration list for PBXProject "RCTBarCode" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 9FCCD75F1DAC8DA800025BFD /* Debug */,
+ 9FCCD7601DAC8DA800025BFD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 9FCCD7611DAC8DA800025BFD /* Build configuration list for PBXNativeTarget "RCTBarcode" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 9FCCD7621DAC8DA800025BFD /* Debug */,
+ 9FCCD7631DAC8DA800025BFD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 9FCCD7501DAC8DA800025BFD /* Project object */;
+}
diff --git a/ios/RCTBarcode/RCTBarCode.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/RCTBarcode/RCTBarCode.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..ce5bbb1
--- /dev/null
+++ b/ios/RCTBarcode/RCTBarCode.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/ios/RCTBarcode/RCTBarcode/LineView.h b/ios/RCTBarcode/RCTBarcode/LineView.h
new file mode 100644
index 0000000..c8852ba
--- /dev/null
+++ b/ios/RCTBarcode/RCTBarcode/LineView.h
@@ -0,0 +1,10 @@
+
+#import
+
+@interface LineView : UIView
+
+@property (nonatomic, copy) NSString *scannerLineColor;
+
+- (id)initWithScannerLineColor:(NSString*)scannerLineColor frame:(CGRect)frame;
+
+@end
diff --git a/ios/RCTBarcode/RCTBarcode/LineView.m b/ios/RCTBarcode/RCTBarcode/LineView.m
new file mode 100644
index 0000000..17298fb
--- /dev/null
+++ b/ios/RCTBarcode/RCTBarcode/LineView.m
@@ -0,0 +1,73 @@
+
+#import "LineView.h"
+#import "UIColor+Hex.h"
+//#define UIColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
+
+@implementation LineView
+
+- (id)initWithScannerLineColor:(NSString*)scannerLineColor frame:(CGRect)frame
+{
+
+ if ((self = [super initWithFrame:frame])) {
+ self.scannerLineColor = scannerLineColor;
+ }
+ return self;
+
+}
+
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+
+ CGContextRef context = UIGraphicsGetCurrentContext();
+ self.backgroundColor = [UIColor clearColor];
+// NSArray *colors = @[@0x00000000, @0x8800FF00, @0xFF00FF00, @0xFF00FF00, @0xFF00FF00, @0xFF00FF00, @0xFF00FF00, @0x8800FF00, @0x00000000];
+// NSLog(@"@0xFF00FF00 = %@", @0xFF00FF00);
+ UIColor *scannerLineColor = [UIColor colorWithHexString:self.scannerLineColor];
+ NSArray *colors = @[scannerLineColor, scannerLineColor, scannerLineColor, scannerLineColor, scannerLineColor, scannerLineColor, scannerLineColor, scannerLineColor, scannerLineColor];
+ [self _drawGradientColor:context
+ rect:rect
+ options:kCGGradientDrawsBeforeStartLocation
+ colors:colors];
+}
+
+/**
+ * 绘制背景色渐变的矩形,p_colors渐变颜色设置,集合中存储UIColor对象(创建Color时一定用三原色来创建)
+ **/
+- (void)_drawGradientColor:(CGContextRef)p_context rect:(CGRect)p_clipRect options:(CGGradientDrawingOptions)p_options colors:(NSArray *)p_colors {
+ CGContextSaveGState(p_context);// 保持住现在的context
+ CGContextClipToRect(p_context, p_clipRect);// 截取对应的context
+ long colorCount = p_colors.count;
+ int numOfComponents = 4;
+ CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
+ CGFloat colorComponents[colorCount * numOfComponents];
+ CGColorRef temcolorRef = nil;
+ for (int i = 0; i < colorCount; i++) {
+
+ if(i==0){
+ temcolorRef = [UIColor clearColor].CGColor;
+ }else if (i==colorCount-1) {
+ temcolorRef = [UIColor clearColor].CGColor;
+ }else{
+// temcolorRef = UIColorFromRGB([p_colors[i] integerValue]).CGColor;
+ temcolorRef = ((UIColor *)p_colors[i]).CGColor;
+ }
+
+ const CGFloat *components = CGColorGetComponents(temcolorRef);
+ for (int j = 0; j < numOfComponents; ++j) {
+ colorComponents[i * numOfComponents + j] = components[j];
+ }
+ }
+ CGGradientRef gradient = CGGradientCreateWithColorComponents(rgb, colorComponents, NULL, colorCount);
+ CGColorSpaceRelease(rgb);
+ CGPoint startPoint = CGPointMake(0, 0);
+ CGPoint endPoint = CGPointMake(p_clipRect.size.width, p_clipRect.size.height);
+ CGContextDrawLinearGradient(p_context, gradient, startPoint, endPoint, p_options);
+
+
+
+ CGGradientRelease(gradient);
+ CGContextRestoreGState(p_context);// 恢复到之前的context
+}
+
+@end
diff --git a/ios/RCTBarcode/RCTBarcode/QRCScanner.h b/ios/RCTBarcode/RCTBarcode/QRCScanner.h
new file mode 100644
index 0000000..a51c32b
--- /dev/null
+++ b/ios/RCTBarcode/RCTBarcode/QRCScanner.h
@@ -0,0 +1,53 @@
+
+
+#import
+#import "LineView.h"
+
+@protocol QRCodeScanneDelegate
+/**
+ * 扫描成功后返回扫描结果
+ *
+ * @param result 扫描结果
+ */
+- (void)didFinshedScanningQRCode:(NSString *)result;
+
+@end
+
+@interface QRCScanner : UIView
+/**
+ * 提示语
+ */
+@property (nonatomic,strong)UILabel *noticeInfoLable;
+/**
+ * 扫描线的颜色,默认红色
+ */
+@property (nonatomic,strong)UIColor *scanningLieColor;
+/**
+ * 扫描框边角的颜色,默认红色
+ */
+@property (nonatomic,strong)UIColor *cornerLineColor;
+/**
+ * 扫描框的宽高区域,默认(200,200)
+ */
+@property (nonatomic,assign)CGSize transparentAreaSize;
+/**
+ * 代理
+ */
+@property (nonatomic,assign) iddelegate;
+/**
+ * 初始化方法
+ *
+ * @param QRCScannerView的父view
+ *
+ * @return QRCScanner实例
+ */
+- (instancetype)initQRCScannerWithView:(UIView *)view;
+/**
+ * 从图片中读取二维码
+ *
+ * @param qrimage 一张二维码图片
+ *
+ * @return 二维码信息
+ */
++ (NSString *)scQRReaderForImage:(UIImage *)qrimage NS_AVAILABLE_IOS(8_0);
+@end
diff --git a/ios/RCTBarcode/RCTBarcode/QRCScanner.m b/ios/RCTBarcode/RCTBarcode/QRCScanner.m
new file mode 100644
index 0000000..fac9856
--- /dev/null
+++ b/ios/RCTBarcode/RCTBarcode/QRCScanner.m
@@ -0,0 +1,352 @@
+//
+// QRCScanner.m
+// SweepView
+//
+// Created by chen on 16/8/17.
+// Copyright © 2016年 defan. All rights reserved.
+//
+
+#import "QRCScanner.h"
+#import
+
+#define LINE_SCAN_TIME 2.0 // 扫描线从上到下扫描所历时间(s)
+
+@interface QRCScanner()
+
+@property (nonatomic,strong)NSTimer *scanLineTimer;
+@property (nonatomic,strong)LineView *scanLine;
+@property (nonatomic,strong)UIButton *lightButton;
+
+@property (nonatomic,assign)CGRect clearDrawRect;
+@property (nonatomic,assign)BOOL isOn;
+
+@property (nonatomic,strong)AVCaptureSession *session;
+@property (nonatomic,strong)AVCaptureVideoPreviewLayer *preview;
+@property (nonatomic,strong)AVCaptureDeviceInput * input;
+@property (nonatomic,strong)AVCaptureMetadataOutput * output;
+@property (nonatomic,strong)AVCaptureDevice * device;
+@property (nonatomic,assign)CGSize parentSize;
+
+@end
+@implementation QRCScanner
+#pragma mark - 初始化
+- (instancetype)initQRCScannerWithView:(UIView *)view{
+ QRCScanner *qrcView = [[QRCScanner alloc]initWithFrame:view.frame];
+ _parentSize = view.frame.size;
+ [qrcView initDataWithView:view];
+
+// UIImagePickerController *picker = [[UIImagePickerController alloc] init];
+// picker.delegate = self;
+// picker.sourceType = UIImagePickerControllerSourceTypeCamera;
+ return qrcView;
+}
+
+- (instancetype)initWithFrame:(CGRect)frame{
+ self = [super initWithFrame:frame];
+ if(self){
+ self.backgroundColor = [UIColor clearColor];
+ _transparentAreaSize = CGSizeMake(200, 100);
+ _cornerLineColor = [UIColor redColor];
+ _scanningLieColor = [UIColor redColor];
+ }
+ return self;
+}
+
+- (void)drawRect:(CGRect)rect {
+ _parentSize = rect.size;
+ [self updateLayout];
+}
+
+#pragma mark - 从图片中读取二维码
++ (NSString *)scQRReaderForImage:(UIImage *)qrimage{
+ CIContext *context = [CIContext contextWithOptions:nil];
+ CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:context options:@{CIDetectorAccuracy:CIDetectorAccuracyHigh}];
+ CIImage *image = [CIImage imageWithCGImage:qrimage.CGImage];
+ NSArray *features = [detector featuresInImage:image];
+ CIQRCodeFeature *feature = [features firstObject];
+ NSString *result = feature.messageString;
+ return result;
+}
+#pragma mark - setter and getter
+- (void)setTransparentAreaSize:(CGSize)transparentAreaSize{
+ _transparentAreaSize = transparentAreaSize;
+ [self setNeedsLayout];
+ [self setNeedsDisplay];
+}
+
+- (void)setScanningLieColor:(UIColor *)scanningLieColor{
+ _scanningLieColor = scanningLieColor;
+ [self setNeedsLayout];
+ [self setNeedsDisplay];
+}
+
+- (void)setCornerLineColor:(UIColor *)cornerLineColor{
+ _cornerLineColor = cornerLineColor;
+ [self setNeedsLayout];
+ [self setNeedsDisplay];
+}
+#pragma mark - UI
+#pragma mark 私有方法
+- (void)updateLayout{
+ //整个二维码扫描界面的颜色
+ CGSize screenSize = _parentSize;
+ CGRect screenDrawRect =CGRectMake(0, 0, screenSize.width,screenSize.height);
+
+ CGSize transparentArea = _transparentAreaSize;
+ //中间清空的矩形框
+ _clearDrawRect = CGRectMake(screenDrawRect.size.width / 2 - transparentArea.width / 2,
+ screenDrawRect.size.height / 2 - transparentArea.height / 2,
+ transparentArea.width,transparentArea.height);
+
+ CGContextRef ctx = UIGraphicsGetCurrentContext();
+ [self addScreenFillRect:ctx rect:screenDrawRect];
+ [self addCenterClearRect:ctx rect:_clearDrawRect];
+ [self addWhiteRect:ctx rect:_clearDrawRect];
+ [self addCornerLineWithContext:ctx rect:_clearDrawRect];
+ [self addScanLine:_clearDrawRect];
+ [self addNoticeInfoLable:_clearDrawRect];
+ [self addLightButton:_clearDrawRect];
+ if (self.scanLineTimer == nil) {
+ [self moveUpAndDownLine];
+ [self createTimer];
+ }
+}
+#pragma mark 添加提示提心Lable
+- (void)addNoticeInfoLable:(CGRect)rect{
+ _noticeInfoLable = [[UILabel alloc]initWithFrame:CGRectMake(0, (rect.origin.y + rect.size.height+10), self.bounds.size.width, 20)];
+// [_noticeInfoLable setText:@"将二维码/条形码放入取景框中即可自动扫描"];
+ _noticeInfoLable.font = [UIFont systemFontOfSize:15];
+ [_noticeInfoLable setTextColor:[UIColor whiteColor]];
+ _noticeInfoLable.textAlignment = NSTextAlignmentCenter;
+ [self addSubview:_noticeInfoLable];
+}
+#pragma mark 添加手电筒功能按钮
+- (void)addLightButton:(CGRect)rect{
+ _lightButton = [[UIButton alloc]initWithFrame:CGRectMake((self.bounds.size.width - 80)/2, (rect.origin.y + rect.size.height+40), 80, 30)];
+ [_lightButton setTitle:@"打开照明" forState:UIControlStateNormal];
+ [_lightButton addTarget:self action:@selector(torchSwitch:) forControlEvents:UIControlEventTouchUpInside];
+ [_lightButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
+ _isOn = NO;
+ [self addSubview:_lightButton];
+}
+#pragma mark 画背景
+- (void)addScreenFillRect:(CGContextRef)ctx rect:(CGRect)rect {
+ CGContextSetRGBFillColor(ctx, 40 / 255.0,40 / 255.0,40 / 255.0,0.5);
+ CGContextFillRect(ctx, rect); //draw the transparent layer
+}
+#pragma mark 扣扫描框
+- (void)addCenterClearRect :(CGContextRef)ctx rect:(CGRect)rect {
+ CGContextClearRect(ctx, rect); //clear the center rect of the layer
+}
+#pragma mark 画框的白线
+- (void)addWhiteRect:(CGContextRef)ctx rect:(CGRect)rect {
+ CGContextStrokeRect(ctx, rect);
+ CGContextSetRGBStrokeColor(ctx, 1, 1, 1, 1);
+ CGContextSetLineWidth(ctx, 0.8);
+ CGContextAddRect(ctx, rect);
+ CGContextStrokePath(ctx);
+}
+#pragma mark 画扫描线
+- (void)addScanLine:(CGRect)rect{
+// self.scanLine = [[UIImageView alloc]initWithFrame:CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, 2)];
+
+ self.scanLine = [[LineView alloc] initWithFrame:CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, 2)];
+
+ self.scanLine.backgroundColor = [UIColor clearColor];
+// [self.scanLine setImage:[UIImage imageNamed:@"zbar-line"]];
+ [self addSubview:self.scanLine];
+}
+#pragma mark 画框的四个角
+- (void)addCornerLineWithContext:(CGContextRef)ctx rect:(CGRect)rect{
+
+ //画四个边角
+ CGContextSetLineWidth(ctx, 2);
+ [self setStrokeColor:_cornerLineColor withContext:ctx];
+
+ //左上角
+ CGPoint poinsTopLeftA[] = {
+ CGPointMake(rect.origin.x+0.7, rect.origin.y),
+ CGPointMake(rect.origin.x+0.7 , rect.origin.y + 15)
+ };
+
+ CGPoint poinsTopLeftB[] = {CGPointMake(rect.origin.x, rect.origin.y +0.7),CGPointMake(rect.origin.x + 15, rect.origin.y+0.7)};
+ [self addLine:poinsTopLeftA pointB:poinsTopLeftB ctx:ctx];
+
+ //左下角
+ CGPoint poinsBottomLeftA[] = {CGPointMake(rect.origin.x+ 0.7, rect.origin.y + rect.size.height - 15),CGPointMake(rect.origin.x +0.7,rect.origin.y + rect.size.height)};
+ CGPoint poinsBottomLeftB[] = {CGPointMake(rect.origin.x , rect.origin.y + rect.size.height - 0.7) ,CGPointMake(rect.origin.x+0.7 +15, rect.origin.y + rect.size.height - 0.7)};
+ [self addLine:poinsBottomLeftA pointB:poinsBottomLeftB ctx:ctx];
+
+ //右上角
+ CGPoint poinsTopRightA[] = {CGPointMake(rect.origin.x+ rect.size.width - 15, rect.origin.y+0.7),CGPointMake(rect.origin.x + rect.size.width,rect.origin.y +0.7 )};
+ CGPoint poinsTopRightB[] = {CGPointMake(rect.origin.x+ rect.size.width-0.7, rect.origin.y),CGPointMake(rect.origin.x + rect.size.width-0.7,rect.origin.y + 15 +0.7 )};
+ [self addLine:poinsTopRightA pointB:poinsTopRightB ctx:ctx];
+
+ CGPoint poinsBottomRightA[] = {CGPointMake(rect.origin.x+ rect.size.width -0.7 , rect.origin.y+rect.size.height+ -15),CGPointMake(rect.origin.x-0.7 + rect.size.width,rect.origin.y +rect.size.height )};
+ CGPoint poinsBottomRightB[] = {CGPointMake(rect.origin.x+ rect.size.width - 15 , rect.origin.y + rect.size.height-0.7),CGPointMake(rect.origin.x + rect.size.width,rect.origin.y + rect.size.height - 0.7 )};
+ [self addLine:poinsBottomRightA pointB:poinsBottomRightB ctx:ctx];
+ CGContextStrokePath(ctx);
+}
+- (void)addLine:(CGPoint[])pointA pointB:(CGPoint[])pointB ctx:(CGContextRef)ctx {
+ CGContextAddLines(ctx, pointA, 2);
+ CGContextAddLines(ctx, pointB, 2);
+}
+#pragma mark - 功能方法
+#pragma mark 定时器
+- (void)createTimer {
+ self.scanLineTimer =
+ [NSTimer scheduledTimerWithTimeInterval:LINE_SCAN_TIME
+ target:self
+ selector:@selector(moveUpAndDownLine)
+ userInfo:nil
+ repeats:YES];
+}
+#pragma mark 移动扫描线
+- (void)moveUpAndDownLine {
+ CGRect readerFrame = self.frame;
+ CGSize viewFinderSize = _clearDrawRect.size;
+ CGRect scanLineframe = self.scanLine.frame;
+ scanLineframe.origin.y = (readerFrame.size.height - viewFinderSize.height)/2;
+ self.scanLine.frame = scanLineframe;
+ self.scanLine.hidden = NO;
+ __weak __typeof(self) weakSelf = self;
+ [UIView animateWithDuration:LINE_SCAN_TIME - 0.05
+ animations:^{
+ CGRect scanLineframe = weakSelf.scanLine.frame;
+ scanLineframe.origin.y =
+ (readerFrame.size.height + viewFinderSize.height)/2 -
+ weakSelf.scanLine.frame.size.height;
+ weakSelf.scanLine.frame = scanLineframe;
+ }
+ completion:^(BOOL finished) {
+ weakSelf.scanLine.hidden = YES;
+ }];
+
+}
+//设置画笔颜色
+- (void)setStrokeColor:(UIColor *)color withContext:(CGContextRef)ctx{
+ NSMutableArray *rgbColorArray = [self changeUIColorToRGB:color];
+ CGFloat r = [rgbColorArray[0] floatValue];
+ CGFloat g = [rgbColorArray[1] floatValue];
+ CGFloat b = [rgbColorArray[2] floatValue];
+ CGContextSetRGBStrokeColor(ctx,r,g,b,1);
+}
+#pragma mark 照明灯切换
+- (void)torchSwitch:(id)sender {
+ if (!_isOn) {
+ [_lightButton setTitle:@"关闭照明" forState:UIControlStateNormal];
+ _isOn = YES;
+ }else{
+ [_lightButton setTitle:@"打开照明" forState:UIControlStateNormal];
+ _isOn = NO;
+ }
+ AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
+ NSError *error;
+ if (device.hasTorch) { // 判断设备是否有闪光灯
+ BOOL b = [device lockForConfiguration:&error];
+ if (!b) {
+ if (error) {
+ NSLog(@"lock torch configuration error:%@", error.localizedDescription);
+ }
+ return;
+ }
+ device.torchMode =
+ (device.torchMode == AVCaptureTorchModeOff ? AVCaptureTorchModeOn : AVCaptureTorchModeOff);
+ [device unlockForConfiguration];
+ }
+}
+
+
+
+
+#pragma mark - 扫描
+- (void)initDataWithView:(UIView *)parentView{
+ _device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
+
+ _input = [AVCaptureDeviceInput deviceInputWithDevice:_device error:nil];
+
+ _output = [[AVCaptureMetadataOutput alloc]init];
+ [_output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
+ CGRect screenDrawRect =CGRectMake(0, 0, parentView.frame.size.width,parentView.frame.size.height);
+ CGSize transparentArea = _transparentAreaSize;
+
+
+ // *设置聚焦区域
+ _output.rectOfInterest = CGRectMake((screenDrawRect.size.height / 2 - transparentArea.height / 2)/parentView.frame.size.height,
+ (screenDrawRect.size.width / 2 - transparentArea.width / 2)/parentView.frame.size.width,
+ transparentArea.height/parentView.frame.size.height,
+ transparentArea.width/parentView.frame.size.width);
+
+
+ // Session
+ _session = [[AVCaptureSession alloc]init];
+ [_session setSessionPreset:AVCaptureSessionPresetHigh];
+ if ([_session canAddInput:_input])
+ {
+ [_session addInput:_input];
+ }
+
+ if ([_session canAddOutput:_output])
+ {
+ [_session addOutput:_output];
+ }
+
+ // 条码类型 AVMetadataObjectTypeQRCode
+ _output.metadataObjectTypes =@[AVMetadataObjectTypeQRCode];
+
+ //增加条形码扫描
+ _output.metadataObjectTypes = @[AVMetadataObjectTypeEAN13Code,AVMetadataObjectTypeEAN8Code,AVMetadataObjectTypeCode128Code,AVMetadataObjectTypeQRCode];
+
+ // Preview
+ _preview =[AVCaptureVideoPreviewLayer layerWithSession:_session];
+ _preview.videoGravity =AVLayerVideoGravityResize;
+ [_preview setFrame:parentView.bounds];
+ [parentView.layer insertSublayer:_preview atIndex:0];
+
+ [_session startRunning];
+}
+
+#pragma mark - AVCaptureMetadataOutputObjectsDelegate
+- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
+{
+// [self.session stopRunning];
+// [self.preview removeFromSuperlayer];
+
+ //设置界面显示扫描结果
+ if (metadataObjects.count > 0) {
+ AVMetadataMachineReadableCodeObject *obj = metadataObjects[0];
+ if ([self.delegate respondsToSelector:@selector(didFinshedScanningQRCode:)]) {
+ [self.delegate didFinshedScanningQRCode:obj.stringValue];
+ }
+ else{
+ NSLog(@"没有收到扫描结果,看看是不是没有实现协议!");
+ }
+ }
+// [self removeFromSuperview];
+}
+#pragma mark - 辅助方法
+//将UIColor转换为RGB值
+- (NSMutableArray *) changeUIColorToRGB:(UIColor *)color
+{
+ NSMutableArray *RGBStrValueArr = [[NSMutableArray alloc] init];
+ NSString *RGBStr = nil;
+ //获得RGB值描述
+ NSString *RGBValue = [NSString stringWithFormat:@"%@",color];
+ //将RGB值描述分隔成字符串
+ NSArray *RGBArr = [RGBValue componentsSeparatedByString:@" "];
+ //获取红色值
+ float r = [[RGBArr objectAtIndex:1] floatValue] * 255;
+ RGBStr = [NSString stringWithFormat:@"%f",r];
+ [RGBStrValueArr addObject:RGBStr];
+ //获取绿色值
+ float g = [[RGBArr objectAtIndex:2] intValue] * 255;
+ RGBStr = [NSString stringWithFormat:@"%f",g];
+ [RGBStrValueArr addObject:RGBStr];
+ //获取蓝色值
+ float b = [[RGBArr objectAtIndex:3] intValue] * 255;
+ RGBStr = [NSString stringWithFormat:@"%f",b];
+ [RGBStrValueArr addObject:RGBStr];
+ //返回保存RGB值的数组
+ return RGBStrValueArr;
+}
+@end
diff --git a/ios/RCTBarcode/RCTBarcode/RCTBarcode.h b/ios/RCTBarcode/RCTBarcode/RCTBarcode.h
new file mode 100644
index 0000000..5afd1af
--- /dev/null
+++ b/ios/RCTBarcode/RCTBarcode/RCTBarcode.h
@@ -0,0 +1,25 @@
+#import
+#import
+#import "RCTBarcodeManager.h"
+#import "LineView.h"
+#import "RectView.h";
+
+
+@interface RCTBarcode : UIView
+
+@property (nonatomic,strong)NSTimer *scanLineTimer;
+@property (nonatomic,strong)LineView *scanLine;
+@property (nonatomic,assign)CGRect scannerRect;
+@property (nonatomic, copy) RCTBubblingEventBlock onBarCodeRead;
+@property (nonatomic, assign) NSInteger scannerRectWidth;
+@property (nonatomic, assign) NSInteger scannerRectHeight;
+@property (nonatomic, assign) NSInteger scannerRectTop;
+@property (nonatomic, assign) NSInteger scannerRectLeft;
+@property (nonatomic, assign) NSInteger scannerLineInterval;
+@property (nonatomic, copy) NSString *scannerRectCornerColor;
+
+- (id)initWithManager:(RCTBarcodeManager*)manager;
+- (void)moveUpAndDownLine;
+- (void)createTimer;
+
+@end
diff --git a/ios/RCTBarcode/RCTBarcode/RCTBarcode.m b/ios/RCTBarcode/RCTBarcode/RCTBarcode.m
new file mode 100644
index 0000000..5937e48
--- /dev/null
+++ b/ios/RCTBarcode/RCTBarcode/RCTBarcode.m
@@ -0,0 +1,225 @@
+
+#import "RCTBarcode.h"
+#import "UIView+React.h"
+#import "LineView.h"
+//#define WIDTH [UIScreen mainScreen].bounds.size.width
+//#define HEIGHT [UIScreen mainScreen].bounds.size.height
+//#define LINE_SCAN_INTERVAL 3.0 // 扫描线从上到下扫描所历时间(s)
+
+@interface RCTBarcode()
+
+@property (nonatomic, weak) RCTBarcodeManager *manager;
+
+@end
+
+@implementation RCTBarcode
+
+- (id)initWithManager:(RCTBarcodeManager*)manager
+{
+
+ if ((self = [super init])) {
+ self.manager = manager;
+ [self.manager initializeCaptureSessionInput:AVMediaTypeVideo];
+ [self.manager startSession];
+ }
+ return self;
+
+}
+
+- (void)drawRect:(CGRect)rect {
+// NSLog(@"drawRect--------------");
+
+ [super drawRect:rect];
+
+ [self updateLayout];
+}
+
+
+- (void)layoutSubviews
+{
+// NSLog(@"layoutSubviews...");
+
+ [super layoutSubviews];
+ self.manager.previewLayer.frame = self.bounds;
+ self.manager.previewLayer.transform = CATransform3DScale(self.manager.previewLayer.transform, 1.5, 1.5, 1);
+ self.manager.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
+
+ [self setBackgroundColor:[UIColor blackColor]];
+ [self.layer insertSublayer:self.manager.previewLayer atIndex:0];
+}
+
+- (void)removeFromSuperview
+{
+// NSLog(@"removeFromSuperview...");
+
+ [self.scanLineTimer invalidate];
+ self.scanLineTimer = nil;
+
+ [self.manager endSession];
+ [super removeFromSuperview];
+}
+
+
+- (void)updateLayout{
+
+// NSLog(@"updateLayout...");
+
+ int scannerRectWidth = self.scannerRectWidth;//300;
+ int scannerRectHeight = self.scannerRectHeight;//300;
+
+ CGRect cameraRect = self.bounds;
+ //中间的矩形框
+ self.scannerRect = CGRectMake( (cameraRect.size.width - scannerRectWidth) / 2, (cameraRect.size.height - scannerRectHeight) / 2, scannerRectWidth, scannerRectHeight);
+
+ RectView *view = [[RectView alloc] initWithScannerRect:self.scannerRect frame:self.bounds scannerRectCornerColor:self.scannerRectCornerColor];
+// RectView *view = [[RectView alloc] initWithFrame:self.bounds];
+
+ view.backgroundColor = [UIColor clearColor];
+
+ [self addSubview:view];
+
+ [self addScanLine:self.scannerRect];
+
+ if (self.scanLineTimer == nil) {
+// [self moveUpAndDownLine];
+ [self createTimer];
+ }
+}
+
+#pragma mark 画背景
+- (void)addScreenFillRect:(CGContextRef)ctx rect:(CGRect)rect {
+ CGContextSetRGBFillColor(ctx, 40 / 255.0,40 / 255.0,40 / 255.0,0.5);
+ CGContextFillRect(ctx, rect); //draw the transparent layer
+}
+#pragma mark 扣扫描框
+- (void)addCenterClearRect :(CGContextRef)ctx rect:(CGRect)rect {
+ CGContextClearRect(ctx, rect); //clear the center rect of the layer
+}
+#pragma mark 画框的白线
+- (void)addWhiteRect:(CGContextRef)ctx rect:(CGRect)rect {
+ CGContextStrokeRect(ctx, rect);
+ CGContextSetRGBStrokeColor(ctx, 1, 1, 1, 1);
+ CGContextSetLineWidth(ctx, 0.8);
+ CGContextAddRect(ctx, rect);
+ CGContextStrokePath(ctx);
+}
+#pragma mark 画扫描线
+- (void)addScanLine:(CGRect)rect{
+// self.scanLine = [[LineView alloc] initWithFrame:CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, 1.25)];
+ self.scanLine = [[LineView alloc] initWithScannerLineColor:self.scannerRectCornerColor frame:CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, 1.25)];
+
+ self.scanLine.backgroundColor = [UIColor clearColor];
+
+ [self addSubview:self.scanLine];
+}
+//#pragma mark 画框的四个角
+//- (void)addCornerLineWithContext:(CGContextRef)ctx rect:(CGRect)rect{
+//
+// UIColor *cornerLineColor = [UIColor redColor];
+//
+// //画四个边角
+// CGContextSetLineWidth(ctx, 2);
+// [self setStrokeColor:cornerLineColor withContext:ctx];
+//
+// //左上角
+// CGPoint poinsTopLeftA[] = {
+// CGPointMake(rect.origin.x+0.7, rect.origin.y),
+// CGPointMake(rect.origin.x+0.7 , rect.origin.y + 15)
+// };
+//
+// CGPoint poinsTopLeftB[] = {CGPointMake(rect.origin.x, rect.origin.y +0.7),CGPointMake(rect.origin.x + 15, rect.origin.y+0.7)};
+// [self addLine:poinsTopLeftA pointB:poinsTopLeftB ctx:ctx];
+//
+// //左下角
+// CGPoint poinsBottomLeftA[] = {CGPointMake(rect.origin.x+ 0.7, rect.origin.y + rect.size.height - 15),CGPointMake(rect.origin.x +0.7,rect.origin.y + rect.size.height)};
+// CGPoint poinsBottomLeftB[] = {CGPointMake(rect.origin.x , rect.origin.y + rect.size.height - 0.7) ,CGPointMake(rect.origin.x+0.7 +15, rect.origin.y + rect.size.height - 0.7)};
+// [self addLine:poinsBottomLeftA pointB:poinsBottomLeftB ctx:ctx];
+//
+// //右上角
+// CGPoint poinsTopRightA[] = {CGPointMake(rect.origin.x+ rect.size.width - 15, rect.origin.y+0.7),CGPointMake(rect.origin.x + rect.size.width,rect.origin.y +0.7 )};
+// CGPoint poinsTopRightB[] = {CGPointMake(rect.origin.x+ rect.size.width-0.7, rect.origin.y),CGPointMake(rect.origin.x + rect.size.width-0.7,rect.origin.y + 15 +0.7 )};
+// [self addLine:poinsTopRightA pointB:poinsTopRightB ctx:ctx];
+//
+// CGPoint poinsBottomRightA[] = {CGPointMake(rect.origin.x+ rect.size.width -0.7 , rect.origin.y+rect.size.height+ -15),CGPointMake(rect.origin.x-0.7 + rect.size.width,rect.origin.y +rect.size.height )};
+// CGPoint poinsBottomRightB[] = {CGPointMake(rect.origin.x+ rect.size.width - 15 , rect.origin.y + rect.size.height-0.7),CGPointMake(rect.origin.x + rect.size.width,rect.origin.y + rect.size.height - 0.7 )};
+// [self addLine:poinsBottomRightA pointB:poinsBottomRightB ctx:ctx];
+// CGContextStrokePath(ctx);
+//}
+//- (void)addLine:(CGPoint[])pointA pointB:(CGPoint[])pointB ctx:(CGContextRef)ctx {
+// CGContextAddLines(ctx, pointA, 2);
+// CGContextAddLines(ctx, pointB, 2);
+//}
+#pragma mark - 功能方法
+#pragma mark 定时器
+- (void)createTimer {
+
+ self.scanLineTimer =
+ [NSTimer scheduledTimerWithTimeInterval:self.scannerLineInterval / 1000
+ target:self
+ selector:@selector(moveUpAndDownLine)
+ userInfo:nil
+ repeats:YES];
+}
+#pragma mark 移动扫描线
+- (void)moveUpAndDownLine {
+// NSLog(@"moveUpAndDownLine");
+
+ CGRect readerFrame = self.frame;
+ CGSize viewFinderSize = self.scannerRect.size;
+ CGRect scanLineframe = self.scanLine.frame;
+ scanLineframe.origin.y = (readerFrame.size.height - viewFinderSize.height)/2;
+ self.scanLine.frame = scanLineframe;
+ self.scanLine.hidden = NO;
+ __weak __typeof(self) weakSelf = self;
+ [UIView animateWithDuration:self.scannerLineInterval / 1000 - 0.05
+ animations:^{
+ CGRect scanLineframe = weakSelf.scanLine.frame;
+ scanLineframe.origin.y =
+ (readerFrame.size.height + viewFinderSize.height)/2 -
+ weakSelf.scanLine.frame.size.height;
+ weakSelf.scanLine.frame = scanLineframe;
+ }
+ completion:^(BOOL finished) {
+ weakSelf.scanLine.hidden = YES;
+ }];
+
+}
+////设置画笔颜色
+//- (void)setStrokeColor:(UIColor *)color withContext:(CGContextRef)ctx{
+// NSMutableArray *rgbColorArray = [self changeUIColorToRGB:color];
+// CGFloat r = [rgbColorArray[0] floatValue];
+// CGFloat g = [rgbColorArray[1] floatValue];
+// CGFloat b = [rgbColorArray[2] floatValue];
+// CGContextSetRGBStrokeColor(ctx,r,g,b,1);
+//}
+//#pragma mark - 辅助方法
+////将UIColor转换为RGB值
+//- (NSMutableArray *) changeUIColorToRGB:(UIColor *)color
+//{
+// NSMutableArray *RGBStrValueArr = [[NSMutableArray alloc] init];
+// NSString *RGBStr = nil;
+// //获得RGB值描述
+// NSString *RGBValue = [NSString stringWithFormat:@"%@",color];
+// //将RGB值描述分隔成字符串
+// NSArray *RGBArr = [RGBValue componentsSeparatedByString:@" "];
+// //获取红色值
+// float r = [[RGBArr objectAtIndex:1] floatValue] * 255;
+// RGBStr = [NSString stringWithFormat:@"%f",r];
+// [RGBStrValueArr addObject:RGBStr];
+// //获取绿色值
+// float g = [[RGBArr objectAtIndex:2] intValue] * 255;
+// RGBStr = [NSString stringWithFormat:@"%f",g];
+// [RGBStrValueArr addObject:RGBStr];
+// //获取蓝色值
+// float b = [[RGBArr objectAtIndex:3] intValue] * 255;
+// RGBStr = [NSString stringWithFormat:@"%f",b];
+// [RGBStrValueArr addObject:RGBStr];
+// //返回保存RGB值的数组
+// return RGBStrValueArr;
+//}
+
+
+
+@end
+
+
diff --git a/ios/RCTBarcode/RCTBarcode/RCTBarcodeManager.h b/ios/RCTBarcode/RCTBarcode/RCTBarcodeManager.h
new file mode 100644
index 0000000..578564c
--- /dev/null
+++ b/ios/RCTBarcode/RCTBarcode/RCTBarcodeManager.h
@@ -0,0 +1,24 @@
+
+#import "RCTViewManager.h"
+#import
+
+@class RCTBarcode;
+
+@interface RCTBarcodeManager : RCTViewManager
+
+@property (nonatomic, strong) dispatch_queue_t sessionQueue;
+@property (nonatomic, strong) AVCaptureSession *session;
+@property (nonatomic, strong) AVCaptureDeviceInput *videoCaptureDeviceInput;
+@property (nonatomic, strong) AVCaptureMetadataOutput *metadataOutput;
+@property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer;
+@property (nonatomic, strong) RCTBarcode *barcode;
+@property (nonatomic, strong) NSArray* barCodeTypes;
+@property (nonatomic, assign) SystemSoundID beep_sound_id;
+
+- (void)initializeCaptureSessionInput:(NSString*)type;
+
+- (void)startSession;
+- (void)stopSession;
+- (void)endSession;
+
+@end
diff --git a/ios/RCTBarcode/RCTBarcode/RCTBarcodeManager.m b/ios/RCTBarcode/RCTBarcode/RCTBarcodeManager.m
new file mode 100644
index 0000000..c1ff9df
--- /dev/null
+++ b/ios/RCTBarcode/RCTBarcode/RCTBarcodeManager.m
@@ -0,0 +1,288 @@
+
+#import "RCTBarcode.h"
+#import "RCTBarcodeManager.h"
+
+
+
+@interface RCTBarcodeManager ()
+
+@end
+
+@implementation RCTBarcodeManager
+
+RCT_EXPORT_MODULE(RCTBarcode)
+
+RCT_EXPORT_VIEW_PROPERTY(scannerRectWidth, NSInteger)
+
+RCT_EXPORT_VIEW_PROPERTY(scannerRectHeight, NSInteger)
+
+RCT_EXPORT_VIEW_PROPERTY(scannerRectTop, NSInteger)
+
+RCT_EXPORT_VIEW_PROPERTY(scannerRectLeft, NSInteger)
+
+RCT_EXPORT_VIEW_PROPERTY(scannerLineInterval, NSInteger)
+
+RCT_EXPORT_VIEW_PROPERTY(scannerRectCornerColor, NSString)
+
+RCT_EXPORT_VIEW_PROPERTY(onBarCodeRead, RCTBubblingEventBlock)
+
+RCT_CUSTOM_VIEW_PROPERTY(barCodeTypes, NSArray, RCTBarcode) {
+// NSLog(@"custom barCodeTypes -> %@", self.barCodeTypes);
+ self.barCodeTypes = [RCTConvert NSArray:json];
+}
+
+- (UIView *)view
+{
+ self.session = [[AVCaptureSession alloc]init];
+#if !(TARGET_IPHONE_SIMULATOR)
+ self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
+// self.previewLayer.needsDisplayOnBoundsChange = YES;
+ #endif
+
+ if(!self.barcode){
+ self.barcode = [[RCTBarcode alloc] initWithManager:self];
+ }
+
+ SystemSoundID beep_sound_id;
+ NSString *path = [[NSBundle mainBundle] pathForResource:@"beep" ofType:@"wav"];
+ if (path) {
+ AudioServicesCreateSystemSoundID((__bridge CFURLRef)[NSURL fileURLWithPath:path],&beep_sound_id);
+ self.beep_sound_id = beep_sound_id;
+ }
+
+ return self.barcode;
+}
+
+- (id)init {
+ if ((self = [super init])) {
+ self.sessionQueue = dispatch_queue_create("barCodeManagerQueue", DISPATCH_QUEUE_SERIAL);
+ }
+ return self;
+}
+
+- (void)initializeCaptureSessionInput:(NSString *)type {
+ dispatch_async(self.sessionQueue, ^{
+
+ [self.session beginConfiguration];
+
+ NSError *error = nil;
+
+ AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
+
+ if (captureDevice == nil) {
+ return;
+ }
+
+ AVCaptureDeviceInput *captureDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];
+
+ if (error || captureDeviceInput == nil) {
+// NSLog(@"%@", error);
+ return;
+ }
+
+ [self.session removeInput:self.videoCaptureDeviceInput];
+
+
+ if ([self.session canAddInput:captureDeviceInput]) {
+ [self.session addInput:captureDeviceInput];
+
+ self.videoCaptureDeviceInput = captureDeviceInput;
+
+// self.metadataOutput.rectOfInterest = self.barcode.bounds;
+// [self.metadataOutput setMetadataObjectTypes:self.metadataOutput.availableMetadataObjectTypes];
+// [self.metadataOutput setMetadataObjectTypes:self.barCodeTypes];
+
+ }
+
+ [self.session commitConfiguration];
+ });
+}
+
+RCT_EXPORT_METHOD(startSession) {
+ #if TARGET_IPHONE_SIMULATOR
+ return;
+ #endif
+ dispatch_async(self.sessionQueue, ^{
+
+// NSLog(@"self.metadataOutput = %@", self.metadataOutput);
+
+ if(self.metadataOutput == nil) {
+ AVCaptureMetadataOutput *metadataOutput = [[AVCaptureMetadataOutput alloc] init];
+ self.metadataOutput = metadataOutput;
+
+ if ([self.session canAddOutput:self.metadataOutput]) {
+ [self.metadataOutput setMetadataObjectsDelegate:self queue:self.sessionQueue];
+ [self.session addOutput:self.metadataOutput];
+// [metadataOutput setMetadataObjectTypes:self.metadataOutput.availableMetadataObjectTypes];
+ [self.metadataOutput setMetadataObjectTypes:self.barCodeTypes];
+ }
+
+// [metadataOutput setMetadataObjectTypes:@[AVMetadataObjectTypeEAN13Code,AVMetadataObjectTypeEAN8Code,AVMetadataObjectTypeCode128Code,AVMetadataObjectTypeQRCode]];
+
+ // [[NSNotificationCenter defaultCenter] addObserverForName:AVCaptureInputPortFormatDescriptionDidChangeNotification
+ // object:nil
+ // queue:[NSOperationQueue currentQueue]
+ // usingBlock: ^(NSNotification *_Nonnull note) {
+// metadataOutput.rectOfInterest = [self.previewLayer metadataOutputRectOfInterestForRect:CGRectMake(80, 80, 160, 160)];
+ // }];
+
+// NSLog(@"startSession set metadataOutput...");
+ }
+
+ [self.session startRunning];
+
+ if(self.barcode.scanLineTimer != nil) {
+ //设回当前时间模拟继续效果
+ [self.barcode.scanLineTimer setFireDate:[NSDate date]];
+ }
+
+ });
+}
+
+RCT_EXPORT_METHOD(stopSession) {
+ #if TARGET_IPHONE_SIMULATOR
+ return;
+ #endif
+ dispatch_async(self.sessionQueue, ^{
+
+ [self.session commitConfiguration];
+ [self.session stopRunning];
+
+
+ //设置大时刻来模拟暂停效果
+ [self.barcode.scanLineTimer setFireDate:[NSDate distantFuture]];
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0),
+ dispatch_get_main_queue(),
+ ^{
+// NSLog(@"self.barcode.scanLine remove animation");
+ [self.barcode.scanLine.layer removeAllAnimations];
+ });
+
+ });
+}
+
+- (void)endSession {
+ #if TARGET_IPHONE_SIMULATOR
+ return;
+ #endif
+ dispatch_async(self.sessionQueue, ^{
+ self.barcode = nil;
+ [self.previewLayer removeFromSuperlayer];
+ [self.session commitConfiguration];
+ [self.session stopRunning];
+ [self.barcode.scanLineTimer invalidate];
+ for(AVCaptureInput *input in self.session.inputs) {
+ [self.session removeInput:input];
+ }
+
+ for(AVCaptureOutput *output in self.session.outputs) {
+ [self.session removeOutput:output];
+ }
+ });
+}
+
+//- (void)startSession {
+//#if TARGET_IPHONE_SIMULATOR
+// return;
+//#endif
+// dispatch_async(self.sessionQueue, ^{
+//
+// AVCaptureMetadataOutput *metadataOutput = [[AVCaptureMetadataOutput alloc] init];
+// if ([self.session canAddOutput:metadataOutput]) {
+// [metadataOutput setMetadataObjectsDelegate:self queue:self.sessionQueue];
+// [self.session addOutput:metadataOutput];
+//// [metadataOutput setMetadataObjectTypes:self.barCodeTypes];
+// [metadataOutput setMetadataObjectTypes:@[AVMetadataObjectTypeEAN13Code,AVMetadataObjectTypeEAN8Code,AVMetadataObjectTypeCode128Code,AVMetadataObjectTypeQRCode]];
+//
+//// [[NSNotificationCenter defaultCenter] addObserverForName:AVCaptureInputPortFormatDescriptionDidChangeNotification
+//// object:nil
+//// queue:[NSOperationQueue currentQueue]
+//// usingBlock: ^(NSNotification *_Nonnull note) {
+//// metadataOutput.rectOfInterest = [self.previewLayer metadataOutputRectOfInterestForRect:CGRectMake(80, 80, 160, 160)];
+//// }];
+//
+// self.metadataOutput = metadataOutput;
+// NSLog(@"startSession set metadataOutput...");
+// }
+//
+// [self.session startRunning];
+// });
+//}
+
+//- (void)stopSession {
+//#if TARGET_IPHONE_SIMULATOR
+// return;
+//#endif
+// dispatch_async(self.sessionQueue, ^{
+// self.barcode = nil;
+// [self.previewLayer removeFromSuperlayer];
+// [self.session commitConfiguration];
+// [self.session stopRunning];
+// for(AVCaptureInput *input in self.session.inputs) {
+// [self.session removeInput:input];
+// }
+//
+// for(AVCaptureOutput *output in self.session.outputs) {
+// [self.session removeOutput:output];
+// }
+// });
+//}
+
+- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection {
+// NSLog(@"captureOutput!!!");
+ for (AVMetadataMachineReadableCodeObject *metadata in metadataObjects) {
+// NSLog(@"AVMetadataMachineReadableCodeObject!!!");
+// NSLog(@"type = %@, data = %@", metadata.type, metadata.stringValue);
+ for (id barcodeType in self.barCodeTypes) {
+ if ([metadata.type isEqualToString:barcodeType]) {
+ if (!self.barcode.onBarCodeRead) {
+ return;
+ }
+
+ AudioServicesPlaySystemSound(self.beep_sound_id);
+
+// NSLog(@"type = %@, data = %@", metadata.type, metadata.stringValue);
+ self.barcode.onBarCodeRead(@{
+ @"data": @{
+ @"type": metadata.type,
+ @"code": metadata.stringValue,
+ },
+ });
+ }
+ }
+ }
+}
+
+
+
+
+
+- (NSDictionary *)constantsToExport
+{
+ return @{
+ @"barCodeTypes": @{
+ @"upce": AVMetadataObjectTypeUPCECode,
+ @"code39": AVMetadataObjectTypeCode39Code,
+ @"code39mod43": AVMetadataObjectTypeCode39Mod43Code,
+ @"ean13": AVMetadataObjectTypeEAN13Code,
+ @"ean8": AVMetadataObjectTypeEAN8Code,
+ @"code93": AVMetadataObjectTypeCode93Code,
+ @"code128": AVMetadataObjectTypeCode128Code,
+ @"pdf417": AVMetadataObjectTypePDF417Code,
+ @"qr": AVMetadataObjectTypeQRCode,
+ @"aztec": AVMetadataObjectTypeAztecCode
+ #ifdef AVMetadataObjectTypeInterleaved2of5Code
+ ,@"interleaved2of5": AVMetadataObjectTypeInterleaved2of5Code
+ # endif
+ #ifdef AVMetadataObjectTypeITF14Code
+ ,@"itf14": AVMetadataObjectTypeITF14Code
+ # endif
+ #ifdef AVMetadataObjectTypeDataMatrixCode
+ ,@"datamatrix": AVMetadataObjectTypeDataMatrixCode
+ # endif
+ }
+ };
+}
+
+
+@end
diff --git a/ios/RCTBarcode/RCTBarcode/RectView.h b/ios/RCTBarcode/RCTBarcode/RectView.h
new file mode 100644
index 0000000..ba6e080
--- /dev/null
+++ b/ios/RCTBarcode/RCTBarcode/RectView.h
@@ -0,0 +1,11 @@
+
+#import
+
+@interface RectView : UIView
+
+@property (nonatomic,assign)CGRect scannerRect;
+@property (nonatomic, copy) NSString *scannerRectCornerColor;
+
+- (id)initWithScannerRect:(CGRect)scannerRect frame:(CGRect)frame scannerRectCornerColor:(NSString*)scannerRectCornerColor;
+
+@end
diff --git a/ios/RCTBarcode/RCTBarcode/RectView.m b/ios/RCTBarcode/RCTBarcode/RectView.m
new file mode 100644
index 0000000..49e1f0d
--- /dev/null
+++ b/ios/RCTBarcode/RCTBarcode/RectView.m
@@ -0,0 +1,152 @@
+
+#import "RectView.h"
+#import "UIColor+Hex.h"
+
+@implementation RectView
+
+
+- (id)initWithScannerRect:(CGRect)scannerRect frame:(CGRect)frame scannerRectCornerColor:(NSString*)scannerRectCornerColor
+{
+
+ if ((self = [super initWithFrame:frame])) {
+ self.scannerRect = scannerRect;
+ self.scannerRectCornerColor = scannerRectCornerColor;
+ }
+ return self;
+
+}
+
+- (void)drawRect:(CGRect)rect {
+
+ CGContextRef ctx = UIGraphicsGetCurrentContext();
+ [self addScreenFillRect:ctx rect:rect];
+ [self addCenterClearRect:ctx rect:self.scannerRect];
+ [self addWhiteRect:ctx rect:self.scannerRect];
+ [self addCornerLineWithContext:ctx rect:self.scannerRect];
+}
+
+
+#pragma mark 画背景
+- (void)addScreenFillRect:(CGContextRef)ctx rect:(CGRect)rect {
+ CGContextSetRGBFillColor(ctx, 40 / 255.0,40 / 255.0,40 / 255.0,0.5);
+ CGContextFillRect(ctx, rect); //draw the transparent layer
+}
+#pragma mark 扣扫描框
+- (void)addCenterClearRect :(CGContextRef)ctx rect:(CGRect)rect {
+ CGContextClearRect(ctx, rect); //clear the center rect of the layer
+}
+#pragma mark 画框的白线
+- (void)addWhiteRect:(CGContextRef)ctx rect:(CGRect)rect {
+ CGContextStrokeRect(ctx, rect);
+ CGContextSetRGBStrokeColor(ctx, 1, 1, 1, 1);
+ CGContextSetLineWidth(ctx, 0.8);
+ CGContextAddRect(ctx, rect);
+ CGContextStrokePath(ctx);
+}
+#pragma mark 画框的四个角
+- (void)addCornerLineWithContext:(CGContextRef)ctx rect:(CGRect)rect{
+
+// UIColor *cornerLineColor = [UIColor redColor];
+// NSLog(@"self.scannerRectCornerColor=%@", self.scannerRectCornerColor);
+ UIColor *cornerLineColor = [UIColor colorWithHexString:self.scannerRectCornerColor];
+
+ //画四个边角
+ CGContextSetLineWidth(ctx, 2);
+ [self setStrokeColor:cornerLineColor withContext:ctx];
+
+ //左上角
+ CGPoint poinsTopLeftA[] = {
+ CGPointMake(rect.origin.x+0.7, rect.origin.y),
+ CGPointMake(rect.origin.x+0.7 , rect.origin.y + 15)
+ };
+
+ CGPoint poinsTopLeftB[] = {CGPointMake(rect.origin.x, rect.origin.y +0.7),CGPointMake(rect.origin.x + 15, rect.origin.y+0.7)};
+ [self addLine:poinsTopLeftA pointB:poinsTopLeftB ctx:ctx];
+
+ //左下角
+ CGPoint poinsBottomLeftA[] = {CGPointMake(rect.origin.x+ 0.7, rect.origin.y + rect.size.height - 15),CGPointMake(rect.origin.x +0.7,rect.origin.y + rect.size.height)};
+ CGPoint poinsBottomLeftB[] = {CGPointMake(rect.origin.x , rect.origin.y + rect.size.height - 0.7) ,CGPointMake(rect.origin.x+0.7 +15, rect.origin.y + rect.size.height - 0.7)};
+ [self addLine:poinsBottomLeftA pointB:poinsBottomLeftB ctx:ctx];
+
+ //右上角
+ CGPoint poinsTopRightA[] = {CGPointMake(rect.origin.x+ rect.size.width - 15, rect.origin.y+0.7),CGPointMake(rect.origin.x + rect.size.width,rect.origin.y +0.7 )};
+ CGPoint poinsTopRightB[] = {CGPointMake(rect.origin.x+ rect.size.width-0.7, rect.origin.y),CGPointMake(rect.origin.x + rect.size.width-0.7,rect.origin.y + 15 +0.7 )};
+ [self addLine:poinsTopRightA pointB:poinsTopRightB ctx:ctx];
+
+ CGPoint poinsBottomRightA[] = {CGPointMake(rect.origin.x+ rect.size.width -0.7 , rect.origin.y+rect.size.height+ -15),CGPointMake(rect.origin.x-0.7 + rect.size.width,rect.origin.y +rect.size.height )};
+ CGPoint poinsBottomRightB[] = {CGPointMake(rect.origin.x+ rect.size.width - 15 , rect.origin.y + rect.size.height-0.7),CGPointMake(rect.origin.x + rect.size.width,rect.origin.y + rect.size.height - 0.7 )};
+ [self addLine:poinsBottomRightA pointB:poinsBottomRightB ctx:ctx];
+ CGContextStrokePath(ctx);
+}
+- (void)addLine:(CGPoint[])pointA pointB:(CGPoint[])pointB ctx:(CGContextRef)ctx {
+ CGContextAddLines(ctx, pointA, 2);
+ CGContextAddLines(ctx, pointB, 2);
+}
+#pragma mark - 功能方法
+//#pragma mark 定时器
+//- (void)createTimer {
+// self.scanLineTimer =
+// [NSTimer scheduledTimerWithTimeInterval:LINE_SCAN_TIME
+// target:self
+// selector:@selector(moveUpAndDownLine)
+// userInfo:nil
+// repeats:YES];
+//}
+//#pragma mark 移动扫描线
+//- (void)moveUpAndDownLine:(CGRect)rect {
+// CGRect readerFrame = self.frame;
+// CGSize viewFinderSize = rect.size;
+// CGRect scanLineframe = self.scanLine.frame;
+// scanLineframe.origin.y = (readerFrame.size.height - viewFinderSize.height)/2;
+// self.scanLine.frame = scanLineframe;
+// self.scanLine.hidden = NO;
+// __weak __typeof(self) weakSelf = self;
+// [UIView animateWithDuration:LINE_SCAN_TIME - 0.05
+// animations:^{
+// CGRect scanLineframe = weakSelf.scanLine.frame;
+// scanLineframe.origin.y =
+// (readerFrame.size.height + viewFinderSize.height)/2 -
+// weakSelf.scanLine.frame.size.height;
+// weakSelf.scanLine.frame = scanLineframe;
+// }
+// completion:^(BOOL finished) {
+// weakSelf.scanLine.hidden = YES;
+// }];
+//
+//}
+//设置画笔颜色
+- (void)setStrokeColor:(UIColor *)color withContext:(CGContextRef)ctx{
+ NSMutableArray *rgbColorArray = [UIColor colorArrayWithHexString:self.scannerRectCornerColor];
+ CGFloat r = [rgbColorArray[0] floatValue];
+ CGFloat g = [rgbColorArray[1] floatValue];
+ CGFloat b = [rgbColorArray[2] floatValue];
+ CGContextSetRGBStrokeColor(ctx,r,g,b,1);
+}
+#pragma mark - 辅助方法
+//将UIColor转换为RGB值
+- (NSMutableArray *) changeUIColorToRGB:(UIColor *)color
+{
+ NSMutableArray *RGBStrValueArr = [[NSMutableArray alloc] init];
+ NSString *RGBStr = nil;
+ //获得RGB值描述
+ NSString *RGBValue = [NSString stringWithFormat:@"%@",color];
+ //将RGB值描述分隔成字符串
+ NSArray *RGBArr = [RGBValue componentsSeparatedByString:@" "];
+ //获取红色值
+ float r = [[RGBArr objectAtIndex:1] floatValue] * 255;
+ RGBStr = [NSString stringWithFormat:@"%f",r];
+ [RGBStrValueArr addObject:RGBStr];
+ //获取绿色值
+ float g = [[RGBArr objectAtIndex:2] intValue] * 255;
+ RGBStr = [NSString stringWithFormat:@"%f",g];
+ [RGBStrValueArr addObject:RGBStr];
+ //获取蓝色值
+ float b = [[RGBArr objectAtIndex:3] intValue] * 255;
+ RGBStr = [NSString stringWithFormat:@"%f",b];
+ [RGBStrValueArr addObject:RGBStr];
+ //返回保存RGB值的数组
+ return RGBStrValueArr;
+}
+
+
+@end
diff --git a/ios/RCTBarcode/RCTBarcode/ScannerRect.h b/ios/RCTBarcode/RCTBarcode/ScannerRect.h
new file mode 100644
index 0000000..1d78470
--- /dev/null
+++ b/ios/RCTBarcode/RCTBarcode/ScannerRect.h
@@ -0,0 +1,13 @@
+//
+// ScannerRect.h
+// RCTBarcode
+//
+// Created by cyqresig on 16/10/12.
+// Copyright © 2016年 react-native-component. All rights reserved.
+//
+
+#import
+
+@interface ScannerRect : NSObject
+
+@end
diff --git a/ios/RCTBarcode/RCTBarcode/ScannerRect.m b/ios/RCTBarcode/RCTBarcode/ScannerRect.m
new file mode 100644
index 0000000..2d36914
--- /dev/null
+++ b/ios/RCTBarcode/RCTBarcode/ScannerRect.m
@@ -0,0 +1,112 @@
+
+#import "ScannerRect.h"
+#import
+
+@implementation ScannerRect
+
+#pragma mark 画背景
+- (void)addScreenFillRect:(CGContextRef)ctx rect:(CGRect)rect {
+ CGContextSetRGBFillColor(ctx, 40 / 255.0,40 / 255.0,40 / 255.0,0.5);
+ CGContextFillRect(ctx, rect); //draw the transparent layer
+}
+#pragma mark 扣扫描框
+- (void)addCenterClearRect :(CGContextRef)ctx rect:(CGRect)rect {
+ CGContextClearRect(ctx, rect); //clear the center rect of the layer
+}
+#pragma mark 画框的白线
+- (void)addWhiteRect:(CGContextRef)ctx rect:(CGRect)rect {
+ CGContextStrokeRect(ctx, rect);
+ CGContextSetRGBStrokeColor(ctx, 1, 1, 1, 1);
+ CGContextSetLineWidth(ctx, 0.8);
+ CGContextAddRect(ctx, rect);
+ CGContextStrokePath(ctx);
+}
+#pragma mark 画扫描线
+- (void)addScanLine:(CGRect)rect{
+ // self.scanLine = [[UIImageView alloc]initWithFrame:CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, 2)];
+
+ self.scanLine = [[LineView alloc] initWithFrame:CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, 2)];
+
+ self.scanLine.backgroundColor = [UIColor clearColor];
+ // [self.scanLine setImage:[UIImage imageNamed:@"zbar-line"]];
+ [self addSubview:self.scanLine];
+}
+#pragma mark 画框的四个角
+- (void)addCornerLineWithContext:(CGContextRef)ctx rect:(CGRect)rect{
+
+ //画四个边角
+ CGContextSetLineWidth(ctx, 2);
+ [self setStrokeColor:_cornerLineColor withContext:ctx];
+
+ //左上角
+ CGPoint poinsTopLeftA[] = {
+ CGPointMake(rect.origin.x+0.7, rect.origin.y),
+ CGPointMake(rect.origin.x+0.7 , rect.origin.y + 15)
+ };
+
+ CGPoint poinsTopLeftB[] = {CGPointMake(rect.origin.x, rect.origin.y +0.7),CGPointMake(rect.origin.x + 15, rect.origin.y+0.7)};
+ [self addLine:poinsTopLeftA pointB:poinsTopLeftB ctx:ctx];
+
+ //左下角
+ CGPoint poinsBottomLeftA[] = {CGPointMake(rect.origin.x+ 0.7, rect.origin.y + rect.size.height - 15),CGPointMake(rect.origin.x +0.7,rect.origin.y + rect.size.height)};
+ CGPoint poinsBottomLeftB[] = {CGPointMake(rect.origin.x , rect.origin.y + rect.size.height - 0.7) ,CGPointMake(rect.origin.x+0.7 +15, rect.origin.y + rect.size.height - 0.7)};
+ [self addLine:poinsBottomLeftA pointB:poinsBottomLeftB ctx:ctx];
+
+ //右上角
+ CGPoint poinsTopRightA[] = {CGPointMake(rect.origin.x+ rect.size.width - 15, rect.origin.y+0.7),CGPointMake(rect.origin.x + rect.size.width,rect.origin.y +0.7 )};
+ CGPoint poinsTopRightB[] = {CGPointMake(rect.origin.x+ rect.size.width-0.7, rect.origin.y),CGPointMake(rect.origin.x + rect.size.width-0.7,rect.origin.y + 15 +0.7 )};
+ [self addLine:poinsTopRightA pointB:poinsTopRightB ctx:ctx];
+
+ CGPoint poinsBottomRightA[] = {CGPointMake(rect.origin.x+ rect.size.width -0.7 , rect.origin.y+rect.size.height+ -15),CGPointMake(rect.origin.x-0.7 + rect.size.width,rect.origin.y +rect.size.height )};
+ CGPoint poinsBottomRightB[] = {CGPointMake(rect.origin.x+ rect.size.width - 15 , rect.origin.y + rect.size.height-0.7),CGPointMake(rect.origin.x + rect.size.width,rect.origin.y + rect.size.height - 0.7 )};
+ [self addLine:poinsBottomRightA pointB:poinsBottomRightB ctx:ctx];
+ CGContextStrokePath(ctx);
+}
+- (void)addLine:(CGPoint[])pointA pointB:(CGPoint[])pointB ctx:(CGContextRef)ctx {
+ CGContextAddLines(ctx, pointA, 2);
+ CGContextAddLines(ctx, pointB, 2);
+}
+#pragma mark - 功能方法
+#pragma mark 定时器
+- (void)createTimer {
+ self.scanLineTimer =
+ [NSTimer scheduledTimerWithTimeInterval:LINE_SCAN_TIME
+ target:self
+ selector:@selector(moveUpAndDownLine)
+ userInfo:nil
+ repeats:YES];
+}
+#pragma mark 移动扫描线
+- (void)moveUpAndDownLine {
+ CGRect readerFrame = self.frame;
+ CGSize viewFinderSize = _clearDrawRect.size;
+ CGRect scanLineframe = self.scanLine.frame;
+ scanLineframe.origin.y = (readerFrame.size.height - viewFinderSize.height)/2;
+ self.scanLine.frame = scanLineframe;
+ self.scanLine.hidden = NO;
+ __weak __typeof(self) weakSelf = self;
+ [UIView animateWithDuration:LINE_SCAN_TIME - 0.05
+ animations:^{
+ CGRect scanLineframe = weakSelf.scanLine.frame;
+ scanLineframe.origin.y =
+ (readerFrame.size.height + viewFinderSize.height)/2 -
+ weakSelf.scanLine.frame.size.height;
+ weakSelf.scanLine.frame = scanLineframe;
+ }
+ completion:^(BOOL finished) {
+ weakSelf.scanLine.hidden = YES;
+ }];
+
+}
+//设置画笔颜色
+- (void)setStrokeColor:(UIColor *)color withContext:(CGContextRef)ctx{
+ NSMutableArray *rgbColorArray = [self changeUIColorToRGB:color];
+ CGFloat r = [rgbColorArray[0] floatValue];
+ CGFloat g = [rgbColorArray[1] floatValue];
+ CGFloat b = [rgbColorArray[2] floatValue];
+ CGContextSetRGBStrokeColor(ctx,r,g,b,1);
+}
+
+
+
+@end
diff --git a/ios/RCTBarcode/RCTBarcode/UIColor+Hex.h b/ios/RCTBarcode/RCTBarcode/UIColor+Hex.h
new file mode 100644
index 0000000..7e23e89
--- /dev/null
+++ b/ios/RCTBarcode/RCTBarcode/UIColor+Hex.h
@@ -0,0 +1,15 @@
+#import
+
+#define RGBA_COLOR(R, G, B, A) [UIColor colorWithRed:((R) / 255.0f) green:((G) / 255.0f) blue:((B) / 255.0f) alpha:A]
+#define RGB_COLOR(R, G, B) [UIColor colorWithRed:((R) / 255.0f) green:((G) / 255.0f) blue:((B) / 255.0f) alpha:1.0f]
+
+@interface UIColor (Hex)
+
++ (UIColor *)colorWithHexString:(NSString *)color;
++ (NSMutableArray *)colorArrayWithHexString:(NSString *)color;
+
+//从十六进制字符串获取颜色,
+//color:支持@“#123456”、 @“0X123456”、 @“123456”三种格式
++ (UIColor *)colorWithHexString:(NSString *)color alpha:(CGFloat)alpha;
++ (NSMutableArray *)colorArrayWithHexString:(NSString *)color alpha:(CGFloat)alpha;
+@end
diff --git a/ios/RCTBarcode/RCTBarcode/UIColor+Hex.m b/ios/RCTBarcode/RCTBarcode/UIColor+Hex.m
new file mode 100644
index 0000000..405f3a2
--- /dev/null
+++ b/ios/RCTBarcode/RCTBarcode/UIColor+Hex.m
@@ -0,0 +1,122 @@
+#import "UIColor+Hex.h"
+
+@implementation UIColor (Hex)
+
++ (UIColor *)colorWithHexString:(NSString *)color alpha:(CGFloat)alpha
+{
+ //删除字符串中的空格
+ NSString *cString = [[color stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] uppercaseString];
+ // String should be 6 or 8 characters
+ if ([cString length] < 6)
+ {
+ return [UIColor clearColor];
+ }
+ // strip 0X if it appears
+ //如果是0x开头的,那么截取字符串,字符串从索引为2的位置开始,一直到末尾
+ if ([cString hasPrefix:@"0X"])
+ {
+ cString = [cString substringFromIndex:2];
+ }
+ //如果是#开头的,那么截取字符串,字符串从索引为1的位置开始,一直到末尾
+ if ([cString hasPrefix:@"#"])
+ {
+ cString = [cString substringFromIndex:1];
+ }
+ if ([cString length] != 6)
+ {
+ return [UIColor clearColor];
+ }
+
+ // Separate into r, g, b substrings
+ NSRange range;
+ range.location = 0;
+ range.length = 2;
+ //r
+ NSString *rString = [cString substringWithRange:range];
+ //g
+ range.location = 2;
+ NSString *gString = [cString substringWithRange:range];
+ //b
+ range.location = 4;
+ NSString *bString = [cString substringWithRange:range];
+
+ // Scan values
+ unsigned int r, g, b;
+ [[NSScanner scannerWithString:rString] scanHexInt:&r];
+ [[NSScanner scannerWithString:gString] scanHexInt:&g];
+ [[NSScanner scannerWithString:bString] scanHexInt:&b];
+ return [UIColor colorWithRed:((float)r / 255.0f) green:((float)g / 255.0f) blue:((float)b / 255.0f) alpha:alpha];
+}
+
+//默认alpha值为1
++ (UIColor *)colorWithHexString:(NSString *)color
+{
+ return [self colorWithHexString:color alpha:1.0f];
+}
+
++ (NSMutableArray *)colorArrayWithHexString:(NSString *)color alpha:(CGFloat)alpha
+{
+ //删除字符串中的空格
+ NSString *cString = [[color stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] uppercaseString];
+ // String should be 6 or 8 characters
+ if ([cString length] < 6)
+ {
+ return [UIColor clearColor];
+ }
+ // strip 0X if it appears
+ //如果是0x开头的,那么截取字符串,字符串从索引为2的位置开始,一直到末尾
+ if ([cString hasPrefix:@"0X"])
+ {
+ cString = [cString substringFromIndex:2];
+ }
+ //如果是#开头的,那么截取字符串,字符串从索引为1的位置开始,一直到末尾
+ if ([cString hasPrefix:@"#"])
+ {
+ cString = [cString substringFromIndex:1];
+ }
+ if ([cString length] != 6)
+ {
+ return [UIColor clearColor];
+ }
+
+ // Separate into r, g, b substrings
+ NSRange range;
+ range.location = 0;
+ range.length = 2;
+ //r
+ NSString *rString = [cString substringWithRange:range];
+ //g
+ range.location = 2;
+ NSString *gString = [cString substringWithRange:range];
+ //b
+ range.location = 4;
+ NSString *bString = [cString substringWithRange:range];
+
+ // Scan values
+ unsigned int r, g, b;
+ [[NSScanner scannerWithString:rString] scanHexInt:&r];
+ [[NSScanner scannerWithString:gString] scanHexInt:&g];
+ [[NSScanner scannerWithString:bString] scanHexInt:&b];
+ NSMutableArray *RGBStrValueArr = [[NSMutableArray alloc] init];
+
+ NSString *RGBStr = nil;
+ RGBStr = [NSString stringWithFormat:@"%f",((float)r / 255.0f)];
+ [RGBStrValueArr addObject:RGBStr];
+
+ RGBStr = [NSString stringWithFormat:@"%f",((float)g / 255.0f)];
+ [RGBStrValueArr addObject:RGBStr];
+
+ RGBStr = [NSString stringWithFormat:@"%f",((float)b / 255.0f)];
+ [RGBStrValueArr addObject:RGBStr];
+ //返回保存RGB值的数组
+ return RGBStrValueArr;
+// return [UIColor colorWithRed:((float)r / 255.0f) green:((float)g / 255.0f) blue:((float)b / 255.0f) alpha:alpha];
+}
+
+//默认alpha值为1
++ (NSMutableArray *)colorArrayWithHexString:(NSString *)color
+{
+ return [self colorArrayWithHexString:color alpha:1.0f];
+}
+
+@end
diff --git a/ios/raw/beep.wav b/ios/raw/beep.wav
new file mode 100644
index 0000000..120affd
Binary files /dev/null and b/ios/raw/beep.wav differ
diff --git a/note.md b/note.md
new file mode 100644
index 0000000..dada01c
--- /dev/null
+++ b/note.md
@@ -0,0 +1,5 @@
+
+* ios 使用自带支持的扫码解析功能, 但扫条码的有效范围是矩形范围内的十字架范围, 而不是整个矩形范围
+* android 使用zxing库的扫码解析功能
+* 要注意应用切换到后台时, 高频执行的扫码解析操作应控制终止
+* 要注意扫码View生命周期结束后, 相应的资源应控制终止并释放, 比如: 定时器, 动画, 线程等
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..c4a0e08
--- /dev/null
+++ b/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "react-native-smart-barcode",
+ "version": "1.0.0",
+ "description": "A smart barcode scanner component for React Native app",
+ "main": "Barcode.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/react-native-component/react-native-smart-barcode.git"
+ },
+ "keywords": [
+ "react-native",
+ "smart",
+ "barcode",
+ "scan"
+ ],
+ "author": "HISAME SHIZUMARU",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/react-native-component/react-native-smart-barcode/issues"
+ },
+ "homepage": "https://github.com/react-native-component/react-native-smart-barcode#readme"
+}