Skip to content

Commit

Permalink
1.0.3 update
Browse files Browse the repository at this point in the history
  • Loading branch information
kongzue committed Jun 7, 2023
1 parent 8b055d5 commit aca1ef0
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 51 deletions.
1 change: 0 additions & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions CameraXQRDecoder/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ android {
defaultConfig {
minSdk 21
targetSdk 30
versionCode 2
versionName "1.0.2"
versionCode 3
versionName "1.0.3"
consumerProguardFiles "consumer-rules.pro"
}

Expand All @@ -36,4 +36,6 @@ dependencies {
api "androidx.camera:camera-view:1.0.0-alpha25"
api "androidx.camera:camera-extensions:1.0.0-alpha25"
api 'com.google.zxing:core:3.5.1'

api 'com.google.mlkit:text-recognition-chinese:16.0.0'
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import androidx.camera.core.AspectRatio;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.ImageProxy;
import androidx.camera.core.Preview;
import androidx.camera.core.impl.CameraInternal;
import androidx.camera.lifecycle.ProcessCameraProvider;
Expand All @@ -29,6 +30,8 @@
import com.kongzue.cameraxqrdecoder.util.QRcodeAnalyzerImpl;
import com.kongzue.cameraxqrdecoder.util.QrDecoderPermissionUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
Expand All @@ -41,111 +44,137 @@
* @createTime: 2021/8/28 9:32
*/
public class QrDecoderView extends FrameLayout {

public static boolean DEBUGMODE = true;
public static final String ERROR_NO_PERMISSION = "无法初始化:请检查权限,未获得权限:android.permission.CAMERA";

boolean isFlashOpen;
ExecutorService cameraExecutor;
boolean finish;
CameraInternal cameraInternal;
OnWorkFinish<String> onWorkFinish;
boolean keepScan;

List<ImageAnalysis.Analyzer> analyzeImageInterfaceList;

public QrDecoderView(@NonNull Context context) {
super(context);
}

public QrDecoderView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}

public QrDecoderView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

public QrDecoderView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}


public void start() {
if (QrDecoderPermissionUtil.checkPermissions(getContext(), new String[]{"android.permission.CAMERA"})) {
addScanQRView();
} else {
error(ERROR_NO_PERMISSION);
}
}

public void start(OnWorkFinish<String> onWorkFinish) {
if (QrDecoderPermissionUtil.checkPermissions(getContext(), new String[]{"android.permission.CAMERA"})) {
this.onWorkFinish = onWorkFinish;
if (analyzeImageInterfaceList == null) {
analyzeImageInterfaceList = new ArrayList<>();
analyzeImageInterfaceList.add(getQRDecoderAnalyzeImageInterface());
}
addScanQRView();
} else {
error(ERROR_NO_PERMISSION);
}
}


public ImageAnalysis.Analyzer getQRDecoderAnalyzeImageInterface() {
return new QRcodeAnalyzerImpl(new OnWorkFinish<String>() {
@Override
public void finish(String result) {
if (!finish && !isNull(result) && !Objects.equals(oldResult, result)) {
if (!keepScan) finish = true;
if (onWorkFinish != null) {
onWorkFinish.finish(result);
}
oldResult = result;
}
}
});
}

public boolean isFlashOpen() {
return isFlashOpen;
}

public QrDecoderView setFlashOpen(boolean flashOpen) {
isFlashOpen = flashOpen;
if (cameraInternal != null) {
cameraInternal.getCameraControl().enableTorch(isFlashOpen);
}
return this;
}

String oldResult;

private void addScanQRView() {
PreviewView viewFinder = new PreviewView(getContext());
addView(viewFinder, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));

viewFinder.post(new Runnable() {
@Override
public void run() {
cameraExecutor = Executors.newSingleThreadExecutor();

CameraSelector.Builder builder = new CameraSelector.Builder();
builder.requireLensFacing(CameraSelector.LENS_FACING_BACK);
CameraSelector cameraSelector = builder.build();

ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(getContext());
DisplayMetrics metrics = new DisplayMetrics();
viewFinder.getDisplay().getRealMetrics(metrics);
int screenAspectRatio = aspectRatio(metrics.widthPixels, metrics.heightPixels); //屏幕纵横比
int rotation = viewFinder.getDisplay().getRotation();

cameraProviderFuture.addListener(new Runnable() {
@SuppressLint("RestrictedApi")
@Override
public void run() {
try {
ProcessCameraProvider cameraProvider = cameraProviderFuture.get();

Preview.Builder pBuilder = new Preview.Builder();
pBuilder.setTargetAspectRatio(screenAspectRatio);
pBuilder.setTargetRotation(rotation);
Preview preview = pBuilder.build();

preview.setSurfaceProvider(viewFinder.getSurfaceProvider());

ImageAnalysis.Builder iBuilder = new ImageAnalysis.Builder();
iBuilder.setBackpressureStrategy(STRATEGY_KEEP_ONLY_LATEST);
iBuilder.setTargetAspectRatio(screenAspectRatio);
iBuilder.setTargetRotation(rotation);

ImageAnalysis analysis = iBuilder.build();
analysis.setAnalyzer(cameraExecutor, new QRcodeAnalyzerImpl(new OnWorkFinish<String>() {
analysis.setAnalyzer(cameraExecutor, new ImageAnalysis.Analyzer() {
@Override
public void finish(String result) {
if (!finish && !isNull(result) && !Objects.equals(oldResult, result)) {
if (!keepScan) finish = true;
if (onWorkFinish != null) {
onWorkFinish.finish(result);
public void analyze(@NonNull ImageProxy image) {
if (analyzeImageInterfaceList != null && !analyzeImageInterfaceList.isEmpty()) {
for (ImageAnalysis.Analyzer analyzer : analyzeImageInterfaceList) {
analyzer.analyze(image);
}
oldResult = result;
}
}
}));
});

cameraProvider.unbindAll();
cameraProvider.bindToLifecycle((AppCompatActivity) getContext(), cameraSelector, preview, analysis);

cameraInternal = preview.getCamera();
} catch (Exception e) {
if (DEBUGMODE) e.printStackTrace();
Expand All @@ -156,30 +185,38 @@ public void finish(String result) {
}
});
}

double RATIO_4_3_VALUE = 4.0 / 3.0;
double RATIO_16_9_VALUE = 16.0 / 9.0;

private int aspectRatio(int width, int height) {
double previewRatio = Math.max(width, height) / Math.min(width, height);
if (Math.abs(previewRatio - RATIO_4_3_VALUE) <= Math.abs(previewRatio - RATIO_16_9_VALUE)) {
return AspectRatio.RATIO_4_3;
}
return AspectRatio.RATIO_16_9;
}

private void error(String errorNoPermission) {
if (DEBUGMODE) {
Log.e("QrDecoderView", errorNoPermission);
}
}

public boolean isKeepScan() {
return keepScan;
}

public QrDecoderView setKeepScan(boolean keepScan) {
this.keepScan = keepScan;
return this;
}

public QrDecoderView addAnalyzeImageImpl(ImageAnalysis.Analyzer analyzeImageInterface) {
if (analyzeImageInterfaceList == null) {
analyzeImageInterfaceList = new ArrayList<ImageAnalysis.Analyzer>();
}
analyzeImageInterfaceList.add(analyzeImageInterface);
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,4 @@ private byte[] toYBuffer(ImageProxy image) {
}
return mYBuffer;
}



}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.kongzue.cameraxqrdecoder.util;

import android.annotation.SuppressLint;
import android.media.Image;

import androidx.annotation.NonNull;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.ImageProxy;

import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.mlkit.vision.common.InputImage;
import com.google.mlkit.vision.text.Text;
import com.google.mlkit.vision.text.TextRecognition;
import com.google.mlkit.vision.text.TextRecognizer;
import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions;
import com.kongzue.cameraxqrdecoder.interfaces.OnWorkFinish;

public class TextRecognitionImpl implements ImageAnalysis.Analyzer {

OnWorkFinish<String> onWorkFinish;

public TextRecognitionImpl(OnWorkFinish<String> onWorkFinish) {
this.onWorkFinish = onWorkFinish;
}

@Override
@SuppressLint("UnsafeOptInUsageError")
public void analyze(@NonNull ImageProxy imageProxy) {
Image image = imageProxy.getImage();
if (image == null) return;
InputImage inputImage = InputImage.fromMediaImage(image, imageProxy.getImageInfo().getRotationDegrees());
TextRecognizer recognizer = TextRecognition.getClient(new ChineseTextRecognizerOptions.Builder().build());
recognizer.process(inputImage)
.addOnSuccessListener(new OnSuccessListener<Text>() {
@Override
public void onSuccess(Text text) {
if (onWorkFinish != null) {
onWorkFinish.finish(text.getText());
}
}
})
.addOnCompleteListener(new OnCompleteListener<Text>() {
@Override
public void onComplete(@NonNull Task<Text> task) {
imageProxy.close();
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
imageProxy.close();
}
});
}

}
44 changes: 43 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# CameraXQRDecoder

此库是基于 CameraX 的二维码扫描封装。
此库是基于 CameraX 的二维码扫描封装(支持扩展)

此库仅含基础功能(扫码、闪光灯),不包含扫码框动画、相册选择识别等,如有需要请自行实现。

Expand Down Expand Up @@ -95,6 +95,48 @@ new BitmapQRDecoder(bitmap, new OnWorkFinish<String>() {

另外,BitmapQRDecoder 参数也可以是 filePath 图片文件路径,请确保自己的 app 已经获取文件读写相关权限。

### 文本识别

文本识别依赖 MLKit 实现,利用 QrDecoderView 扩展接口可实现一键接入,也可以实现同时进行二维码扫码和文字识别。

引入 MLKit:

```gradle
implementation 'com.google.mlkit:text-recognition-chinese:16.0.0'
```

使用:
```java
//文字识别
qrCodeView.setKeepScan(true)
.addAnalyzeImageImpl(new TextRecognitionImpl(new OnWorkFinish<String>() {
@Override
public void finish(String s) {
PopTip.show(s);
}
}))
.start();
```

要和二维码组合识别:
```java
//文字识别
qrCodeView.setKeepScan(true)
.addAnalyzeImageImpl(qrCodeView.getQRDecoderAnalyzeImageInterface())
.addAnalyzeImageImpl(new TextRecognitionImpl(new OnWorkFinish<String>() {
@Override
public void finish(String s) {
PopTip.show(s);
}
}))
.start(new OnWorkFinish<String>() {
@Override
public void finish(String s) {
//s 返回二维码扫描结果
}
});
```

### 开源协议

DialogX 遵循 Apache License 2.0 开源协议。
Expand Down
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ android {
applicationId "com.kongzue.cameraxqrdecoderdemo"
minSdk 21
targetSdk 30
versionCode 2
versionName "1.0.1"
versionCode 3
versionName "1.0.3"
}

buildTypes {
Expand Down
Loading

0 comments on commit aca1ef0

Please sign in to comment.