重构v2.0

This commit is contained in:
Jenly
2020-12-23 19:03:44 +08:00
parent 7c87efa216
commit 5eb7511ed9
61 changed files with 2032 additions and 4150 deletions

View File

@@ -46,6 +46,10 @@ ZXingLite for Android 是ZXing的精简版基于ZXing库优化扫码和生成
| frameLineWidth | dimension | 1dp | 边框线宽度 |
| scannerAnimationDelay | integer | 20 | 扫描动画延迟间隔时间,单位:毫秒 |
| frameRatio | float | 0.625f | 扫码框与屏幕占比 |
| framePaddingLeft | dimension | 0 | 扫码框左边的内间距 |
| framePaddingTop | dimension | 0 | 扫码框上边的内间距 |
| framePaddingRight | dimension | 0 | 扫码框右边的内间距 |
| framePaddingBottom | dimension | 0 | 扫码框下边的内间距 |
## 引入

View File

@@ -1,4 +1,6 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion build_versions.compileSdk
@@ -22,10 +24,15 @@ android {
abortOnError false
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
@@ -33,11 +40,14 @@ dependencies {
testImplementation deps.test.junit
androidTestImplementation deps.test.runner
androidTestImplementation deps.test.espresso
//support
implementation deps.support.design
implementation deps.support.appcompat
implementation deps.support.constraintlayout
implementation deps.kotlin
implementation deps.corektx
implementation deps.easypermissions
implementation project(':lib')

View File

@@ -46,9 +46,16 @@
android:screenOrientation="portrait"
android:theme="@style/CaptureTheme"/>
<activity
android:name=".QRCodeActivity"
android:screenOrientation="portrait"
android:theme="@style/CaptureTheme"/>
<activity
android:name=".CodeActivity"
android:screenOrientation="portrait"/>
</application>
</manifest>

View File

@@ -9,31 +9,33 @@ import android.widget.TextView;
import android.widget.Toast;
import com.google.zxing.Result;
import com.king.zxing.CaptureHelper;
import com.king.zxing.OnCaptureCallback;
import com.king.zxing.CameraScan;
import com.king.zxing.DefaultCameraScan;
import com.king.zxing.ICameraScan;
import com.king.zxing.ViewfinderView;
import com.king.zxing.app.util.StatusBarUtils;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.camera.view.PreviewView;
import androidx.fragment.app.Fragment;
/**
* 自定义扫码当直接使用CaptureActivity
* 自定义扫码,切记自定义扫码需在{@link Activity}或者{@link Fragment}相对应的生命周期里面调用{@link #mCaptureHelper}对应的生命周期
* 自定义扫码,切记自定义扫码需在{@link Activity}或者{@link Fragment}相对应的生命周期里面调用{@link #mCameraScan}对应的生命周期
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class CustomActivity extends AppCompatActivity implements OnCaptureCallback {
public class CustomActivity extends AppCompatActivity implements CameraScan.OnScanResultCallback {
private boolean isContinuousScan;
private CaptureHelper mCaptureHelper;
private CameraScan mCameraScan;
private SurfaceView surfaceView;
private PreviewView previewView;
private ViewfinderView viewfinderView;
private View ivTorch;
private View ivFlash;
private Toast toast;
@@ -53,60 +55,38 @@ public class CustomActivity extends AppCompatActivity implements OnCaptureCallba
tvTitle.setText(getIntent().getStringExtra(MainActivity.KEY_TITLE));
surfaceView = findViewById(R.id.surfaceView);
previewView = findViewById(R.id.previewView);
viewfinderView = findViewById(R.id.viewfinderView);
ivTorch = findViewById(R.id.ivFlash);
ivTorch.setVisibility(View.INVISIBLE);
ivFlash = findViewById(R.id.ivFlash);
ivFlash.setVisibility(View.INVISIBLE);
isContinuousScan = getIntent().getBooleanExtra(MainActivity.KEY_IS_CONTINUOUS,false);
mCaptureHelper = new CaptureHelper(this,surfaceView,viewfinderView,ivTorch);
mCaptureHelper.setOnCaptureCallback(this);
mCaptureHelper.onCreate();
mCaptureHelper.vibrate(true)
.fullScreenScan(true)//全屏扫码
.supportVerticalCode(true)//支持扫垂直条码,建议有此需求时才使用。
.supportLuminanceInvert(true)//是否支持识别反色码(黑白反色的码),增加识别率
.continuousScan(isContinuousScan);
mCameraScan = new DefaultCameraScan(this,previewView);
mCameraScan.setOnScanResultCallback(this);
}
mCameraScan.startCamera();
@Override
protected void onResume() {
super.onResume();
mCaptureHelper.onResume();
}
@Override
protected void onPause() {
super.onPause();
mCaptureHelper.onPause();
}
@Override
protected void onDestroy() {
mCameraScan.release();
super.onDestroy();
mCaptureHelper.onDestroy();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mCaptureHelper.onTouchEvent(event);
return super.onTouchEvent(event);
}
/**
* 扫码结果回调
* @param result 扫码结果
* @return
*/
@Override
public boolean onResultCallback(Result result) {
public boolean onScanResultCallback(Result result) {
if(isContinuousScan){
showToast(result.getText());
}
return false;
//如果需支持连扫返回true即可
return isContinuousScan;
}
private void showToast(String text){

View File

@@ -52,21 +52,16 @@ public class CustomCaptureActivity extends CaptureActivity {
tvTitle.setText(getIntent().getStringExtra(MainActivity.KEY_TITLE));
isContinuousScan = getIntent().getBooleanExtra(MainActivity.KEY_IS_CONTINUOUS,false);
//获取CaptureHelper里面有扫码相关的配置设置
getCaptureHelper().playBeep(false)//播放音效
.vibrate(true)//震动
.fullScreenScan(true)
.supportVerticalCode(true)//支持扫垂直条码,建议有此需求时才使用。
// .decodeFormats(DecodeFormatManager.QR_CODE_FORMATS)//设置只识别二维码会提升速度
// .framingRectRatio(0.9f)//设置识别区域比例范围建议在0.625 ~ 1.0之间。非全屏识别时才有效
// .framingRectVerticalOffset(0)//设置识别区域垂直方向偏移量,非全屏识别时才有效
// .framingRectHorizontalOffset(0)//设置识别区域水平方向偏移量,非全屏识别时才有效
.tooDarkLux(45f)//设置光线太暗时,自动触发开启闪光灯的照度值
.brightEnoughLux(100f)//设置光线足够明亮时,自动触发关闭闪光灯的照度值
.continuousScan(isContinuousScan)//是否连扫
.supportLuminanceInvert(true);//是否支持识别反色码(黑白反色的码),增加识别率
}
@Override
public void initCameraScan() {
super.initCameraScan();
//获取CaptureHelper里面有扫码相关的配置设置
getCameraScan().setPlayBeep(false)//播放音效
.setVibrate(true);//震动
}
/**
* 扫码结果回调
@@ -74,12 +69,12 @@ public class CustomCaptureActivity extends CaptureActivity {
* @return
*/
@Override
public boolean onResultCallback(Result result) {
if(isContinuousScan){//连续扫码时,直接弹出结果
public boolean onScanResultCallback(Result result) {
if(isContinuousScan){
showToast(result.getText());
}
return super.onResultCallback(result);
//如果支持连扫返回true即可
return isContinuousScan;
}
private void showToast(String text){

View File

@@ -42,10 +42,15 @@ public class EasyCaptureActivity extends CaptureActivity {
StatusBarUtils.immersiveStatusBar(this,toolbar,0.2f);
TextView tvTitle = findViewById(R.id.tvTitle);
tvTitle.setText(getIntent().getStringExtra(MainActivity.KEY_TITLE));
getCaptureHelper()
// .decodeFormats(DecodeFormatManager.QR_CODE_FORMATS)//设置只识别二维码会提升速度
.playBeep(true)
.vibrate(true);
}
@Override
public void initCameraScan() {
super.initCameraScan();
getCameraScan()
.setPlayBeep(true)
.setVibrate(true);
}
public void onClick(View v){

View File

@@ -26,12 +26,12 @@ import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.king.zxing.CameraScan;
import com.king.zxing.CaptureActivity;
import com.king.zxing.Intents;
import com.king.zxing.app.util.UriUtils;
import com.king.zxing.util.CodeUtils;
import com.king.zxing.util.LogUtils;
import org.w3c.dom.Text;
import java.util.List;
@@ -78,6 +78,7 @@ public class MainActivity extends AppCompatActivity implements EasyPermissions.P
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
@@ -86,7 +87,7 @@ public class MainActivity extends AppCompatActivity implements EasyPermissions.P
if(resultCode == RESULT_OK && data!=null){
switch (requestCode){
case REQUEST_CODE_SCAN:
String result = data.getStringExtra(Intents.Scan.RESULT);
String result = CameraScan.parseScanResult(data);
showToast(result);
break;
case REQUEST_CODE_PHOTO:
@@ -109,7 +110,7 @@ public class MainActivity extends AppCompatActivity implements EasyPermissions.P
private void parsePhoto(Intent data){
final String path = UriUtils.getImagePath(this,data);
Log.d("Jenly","path:" + path);
LogUtils.d("path:" + path);
if(TextUtils.isEmpty(path)){
return;
}
@@ -254,6 +255,11 @@ public class MainActivity extends AppCompatActivity implements EasyPermissions.P
case R.id.btn7:
checkExternalStoragePermissions();
break;
case R.id.btn8:
this.cls = QRCodeActivity.class;
this.title = ((Button)v).getText().toString();
checkCameraPermissions();
break;
}
}

View File

@@ -0,0 +1,49 @@
package com.king.zxing.app;
import android.os.Bundle;
import android.widget.TextView;
import com.google.zxing.Result;
import com.king.zxing.CaptureActivity;
import com.king.zxing.DecodeFormatManager;
import com.king.zxing.analyze.MultiFormatAnalyzer;
import com.king.zxing.app.util.StatusBarUtils;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class QRCodeActivity extends CaptureActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Toolbar toolbar = findViewById(R.id.toolbar);
StatusBarUtils.immersiveStatusBar(this,toolbar,0.2f);
TextView tvTitle = findViewById(R.id.tvTitle);
tvTitle.setText(getIntent().getStringExtra(MainActivity.KEY_TITLE));
}
@Override
public int getLayoutId() {
return R.layout.qr_code_activity;
}
@Override
public void initCameraScan() {
super.initCameraScan();
//在启动预览之前,设置分析器,只识别二维码,如果只有识别二维码的需求,这样效率更多高
getCameraScan()
.setVibrate(true)
.setAnalyzer(new MultiFormatAnalyzer(DecodeFormatManager.QR_CODE_HINTS));
}
@Override
public boolean onScanResultCallback(Result result) {
return super.onScanResultCallback(result);
}
}

View File

@@ -131,4 +131,17 @@
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn6"
style="@style/OnClick"/>
<Button
android:id="@+id/btn8"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="6dp"
android:layout_marginBottom="6dp"
android:text="只识别二维码"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn7"
style="@style/OnClick"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -5,8 +5,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<SurfaceView
android:id="@+id/surfaceView"
<androidx.camera.view.PreviewView
android:id="@+id/previewView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.king.zxing.ViewfinderView

View File

@@ -5,8 +5,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<SurfaceView
android:id="@+id/surfaceView"
<androidx.camera.view.PreviewView
android:id="@+id/previewView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.king.zxing.ViewfinderView
@@ -23,7 +23,7 @@
app:laserStyle="grid"
app:showResultPoint="true"/>
<ImageView
android:id="@+id/ivTorch"
android:id="@+id/ivFlashlight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/flash_selected_selector"

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.camera.view.PreviewView
android:id="@+id/previewView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.king.zxing.ViewfinderView
android:id="@+id/viewfinderView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:labelText="@string/tips_scan_code"
app:labelTextSize="@dimen/size_14sp"
app:laserColor="@color/colorAccent"
app:frameColor="@color/colorPrimary"
app:cornerColor="@color/colorPrimary"
app:resultPointColor="@color/colorAccent"
app:labelTextLocation="bottom"
app:laserStyle="grid"
app:showResultPoint="true"/>
<ImageView
android:id="@+id/ivFlashlight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/flash_selected_selector"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginTop="160dp" />
<include layout="@layout/toolbar_capture"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -6,9 +6,9 @@ buildscript {
addRepos(repositories)
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
classpath 'com.novoda:bintray-release:0.9'
classpath "com.android.tools.build:gradle:$versions.gralde"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlin"
classpath "com.novoda:bintray-release:$versions.bintray_release"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}

View File

@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip

View File

@@ -44,4 +44,8 @@ dependencies {
api deps.support.appcompat
api deps.zxing
api deps.camera_core
api deps.camera_camera2
api deps.camera_lifecycle
api deps.camera_view
}

View File

@@ -6,6 +6,7 @@
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.FLASHLIGHT"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application>

View File

@@ -23,82 +23,88 @@ import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import com.king.zxing.camera.CameraManager;
/**
* Detects ambient light and switches on the front light when very dark, and off again when sufficiently light.
*
* @author Sean Owen
* @author Nikolaus Huber
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
final class AmbientLightManager implements SensorEventListener {
public class AmbientLightManager implements SensorEventListener {
private static final int INTERVAL_TIME = 200;
protected static final float TOO_DARK_LUX = 45.0f;
protected static final float BRIGHT_ENOUGH_LUX = 100.0f;
protected static final float DARK_LUX = 45.0f;
protected static final float BRIGHT_LUX = 100.0f;
/**
* 光线太暗时默认照度45 lux
*/
private float tooDarkLux = TOO_DARK_LUX;
private float darkLightLux = DARK_LUX;
/**
* 光线足够亮时默认照度450 lux
*/
private float brightEnoughLux = BRIGHT_ENOUGH_LUX;
private float brightLightLux = BRIGHT_LUX;
private final Context context;
private CameraManager cameraManager;
private SensorManager sensorManager;
private Sensor lightSensor;
private long lastTime;
private boolean isLightSensorEnabled;
private OnLightSensorEventListener mOnLightSensorEventListener;
AmbientLightManager(Context context) {
this.context = context;
sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
isLightSensorEnabled = true;
}
void start(CameraManager cameraManager) {
this.cameraManager = cameraManager;
SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
if (lightSensor != null) {
public void register() {
if (sensorManager != null && lightSensor != null) {
sensorManager.registerListener(this, lightSensor, SensorManager.SENSOR_DELAY_NORMAL);
}
}
void stop() {
if (lightSensor != null) {
SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
public void unregister() {
if (sensorManager != null && lightSensor != null) {
sensorManager.unregisterListener(this);
cameraManager = null;
lightSensor = null;
}
}
@Override
public void onSensorChanged(SensorEvent sensorEvent) {
long currentTime = System.currentTimeMillis();
if(currentTime - lastTime < INTERVAL_TIME){//降低频率
return;
}
lastTime = currentTime;
if(isLightSensorEnabled){
long currentTime = System.currentTimeMillis();
if(currentTime - lastTime < INTERVAL_TIME){//降低频率
return;
}
lastTime = currentTime;
float ambientLightLux = sensorEvent.values[0];
if (cameraManager != null) {
if (ambientLightLux <= tooDarkLux) {
cameraManager.sensorChanged(true,ambientLightLux);
} else if (ambientLightLux >= brightEnoughLux) {
cameraManager.sensorChanged(false,ambientLightLux);
if (mOnLightSensorEventListener != null) {
float lightLux = sensorEvent.values[0];
mOnLightSensorEventListener.onSensorChanged(lightLux);
if (lightLux <= darkLightLux) {
mOnLightSensorEventListener.onSensorChanged(true,darkLightLux);
} else if (lightLux >= brightLightLux) {
mOnLightSensorEventListener.onSensorChanged(false,darkLightLux);
}
}
}
}
public void setTooDarkLux(float tooDarkLux){
this.tooDarkLux = tooDarkLux;
/**
* 设置光线足够暗的阈值单位lux
* @param lightLux
*/
public void setDarkLightLux(float lightLux){
this.darkLightLux = lightLux;
}
public void setBrightEnoughLux(float brightEnoughLux){
this.brightEnoughLux = brightEnoughLux;
/**
* 设置光线足够明亮的阈值单位lux
* @param lightLux
*/
public void setBrightLightLux(float lightLux){
this.darkLightLux = lightLux;
}
@Override
@@ -106,4 +112,40 @@ final class AmbientLightManager implements SensorEventListener {
// do nothing
}
public boolean isLightSensorEnabled() {
return isLightSensorEnabled;
}
/**
* 设置是否启用光线亮度传感器
* @param lightSensorEnabled
*/
public void setLightSensorEnabled(boolean lightSensorEnabled) {
isLightSensorEnabled = lightSensorEnabled;
}
/**
* 设置光线亮度传感器监听器,只有在 {@link #isLightSensorEnabled} 为{@code true} 才有效
* @param listener
*/
public void setOnLightSensorEventListener(OnLightSensorEventListener listener){
mOnLightSensorEventListener = listener;
}
public interface OnLightSensorEventListener{
/**
*
* @param lightLux 当前检测到的光线照度值
*/
default void onSensorChanged(float lightLux){
}
/**
* 传感器改变事件
* @param dark 是否太暗了,当检测到的光线照度值小于{@link #darkLightLux}时,为{@code true}
* @param lightLux 当前检测到的光线照度值
*/
void onSensorChanged(boolean dark,float lightLux);
}
}

View File

@@ -16,9 +16,7 @@ package com.king.zxing;
*/
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.AssetFileDescriptor;
import android.media.AudioManager;
import android.media.MediaPlayer;
@@ -27,23 +25,22 @@ import android.os.Vibrator;
import com.king.zxing.util.LogUtils;
import java.io.Closeable;
import java.io.IOException;
/**
* Manages beeps and vibrations for {@link Activity}.
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public final class BeepManager implements MediaPlayer.OnErrorListener, Closeable {
// private static final float BEEP_VOLUME = 0.10f;
private static final long VIBRATE_DURATION = 200L;
private final Activity activity;
private final Context context;
private MediaPlayer mediaPlayer;
private Vibrator vibrator;
private boolean playBeep;
private boolean vibrate;
BeepManager(Activity activity) {
this.activity = activity;
BeepManager(Context context) {
this.context = context;
this.mediaPlayer = null;
updatePrefs();
}
@@ -56,53 +53,36 @@ public final class BeepManager implements MediaPlayer.OnErrorListener, Closeable
this.playBeep = playBeep;
}
synchronized void updatePrefs() {
// SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
// shouldBeep(prefs, activity);
// vibrate = prefs.getBoolean(Preferences.KEY_VIBRATE, false);
if (playBeep && mediaPlayer == null) {
// The volume on STREAM_SYSTEM is not adjustable, and users found it too loud,
// so we now play on the music stream.
activity.setVolumeControlStream(AudioManager.STREAM_MUSIC);
mediaPlayer = buildMediaPlayer(activity);
private synchronized void updatePrefs() {
if (mediaPlayer == null) {
mediaPlayer = buildMediaPlayer(context);
}
if(vibrator == null){
vibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
}
}
synchronized void playBeepSoundAndVibrate() {
public synchronized void playBeepSoundAndVibrate() {
if (playBeep && mediaPlayer != null) {
mediaPlayer.start();
}
LogUtils.d("vibrate:" + vibrate);
if (vibrate) {
Vibrator vibrator = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(VIBRATE_DURATION);
}
}
private static boolean shouldBeep(SharedPreferences prefs, Context activity) {
boolean shouldPlayBeep = prefs.getBoolean(Preferences.KEY_PLAY_BEEP, false);
if (shouldPlayBeep) {
// See if sound settings overrides this
AudioManager audioService = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE);
if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
shouldPlayBeep = false;
}
}
return shouldPlayBeep;
}
private MediaPlayer buildMediaPlayer(Context activity) {
private MediaPlayer buildMediaPlayer(Context context) {
MediaPlayer mediaPlayer = new MediaPlayer();
try (AssetFileDescriptor file = activity.getResources().openRawResourceFd(R.raw.zxl_beep)) {
try {
AssetFileDescriptor file = context.getResources().openRawResourceFd(R.raw.zxl_beep);
mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength());
mediaPlayer.setOnErrorListener(this);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setLooping(false);
// mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);
mediaPlayer.prepare();
return mediaPlayer;
} catch (IOException ioe) {
LogUtils.w(ioe);
} catch (Exception e) {
LogUtils.w(e);
mediaPlayer.release();
return null;
}
@@ -110,22 +90,20 @@ public final class BeepManager implements MediaPlayer.OnErrorListener, Closeable
@Override
public synchronized boolean onError(MediaPlayer mp, int what, int extra) {
if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
// we are finished, so put up an appropriate error toast if required and finish
activity.finish();
} else {
// possibly media player error, so release and recreate
close();
updatePrefs();
}
close();
updatePrefs();
return true;
}
@Override
public synchronized void close() {
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
try{
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
}catch (Exception e){
LogUtils.e(e);
}
}

View File

@@ -0,0 +1,32 @@
package com.king.zxing;
import androidx.annotation.NonNull;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.Preview;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class CameraConfig {
public CameraConfig(){
}
@NonNull
public Preview options(@NonNull Preview.Builder builder){
return builder.build();
}
@NonNull
public CameraSelector options(@NonNull CameraSelector.Builder builder){
return builder.build();
}
@NonNull
public ImageAnalysis options(@NonNull ImageAnalysis.Builder builder){
return builder.build();
}
}

View File

@@ -0,0 +1,189 @@
package com.king.zxing;
import android.content.Intent;
import android.view.MotionEvent;
import android.view.View;
import com.google.zxing.Result;
import com.king.zxing.analyze.Analyzer;
import com.king.zxing.util.LogUtils;
import androidx.annotation.Nullable;
import androidx.camera.core.CameraSelector;
import static com.king.zxing.CameraScan.*;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public abstract class CameraScan implements ICameraScan {
/**
* 默认触控误差值
*/
private static final int DEVIATION = 6;
public static String SCAN_RESULT = "SCAN_RESULT";
/** A camera on the device facing the same direction as the device's screen. */
public static int LENS_FACING_FRONT = CameraSelector.LENS_FACING_FRONT;
/** A camera on the device facing the opposite direction as the device's screen. */
public static int LENS_FACING_BACK = CameraSelector.LENS_FACING_BACK;
/**
* 是否需要支持自动缩放
*/
private boolean isNeedAutoZoom = true;
/**
* 是否需要支持触摸缩放
*/
private boolean isNeedTouchZoom = true;
private float mOldDistance;
/**
* 设置是否需要支持触摸缩放
* @param needTouchZoom
* @return
*/
public CameraScan setNeedTouchZoom(boolean needTouchZoom) {
isNeedTouchZoom = needTouchZoom;
return this;
}
/**
* 是否需要支持自动缩放
* @return
*/
protected boolean isNeedAutoZoom() {
return isNeedAutoZoom;
}
/**
* 设置是否需要支持自动缩放
* @param needAutoZoom
* @return
*/
public CameraScan setNeedAutoZoom(boolean needAutoZoom) {
isNeedAutoZoom = needAutoZoom;
return this;
}
protected boolean onTouchEvent(MotionEvent event) {
if(getCamera() != null && isNeedTouchZoom){
LogUtils.d("action:" + (event.getAction() & MotionEvent.ACTION_MASK));
if(event.getPointerCount() > 1) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {//多点触控
case MotionEvent.ACTION_POINTER_DOWN:
mOldDistance = calcFingerSpacing(event);
break;
case MotionEvent.ACTION_MOVE:
float newDistance = calcFingerSpacing(event);
if (newDistance > mOldDistance + DEVIATION) {
zoomIn();
} else if (newDistance < mOldDistance - DEVIATION) {
zoomOut();
}
mOldDistance = newDistance;
break;
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_UP:
return false;
}
}
return true;
}
return false;
}
/**
* 计算两指间距离
* @param event
* @return
*/
private float calcFingerSpacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return (float) Math.sqrt(x * x + y * y);
}
/**
* 设置相机配置,请在{@link #startCamera()}之前调用
* @param cameraConfig
*/
public abstract CameraScan setCameraConfig(CameraConfig cameraConfig);
/**
* 设置是否分析图像
* @param analyze
*/
public abstract CameraScan setAnalyzeImage(boolean analyze);
/**
* 设置分析器
* @param analyzer
*/
public abstract CameraScan setAnalyzer(Analyzer analyzer);
/**
* 设置手电筒是否开启
* @param torch
*/
public abstract CameraScan enableTorch(boolean torch);
/**
* 手电筒是否开启
* @return
*/
public abstract boolean isTorchEnabled();
/**
* 是否支持闪光灯
* @return
*/
public abstract boolean hasFlashUnit();
/**
* 设置是否震动
* @param vibrate
*/
public abstract CameraScan setVibrate(boolean vibrate);
/**
* 设置是否播放提示音
* @param playBeep
*/
public abstract CameraScan setPlayBeep(boolean playBeep);
/**
* 设置扫码结果回调
* @param callback
*/
public abstract CameraScan setOnScanResultCallback(OnScanResultCallback callback);
/**
* 绑定手电筒,绑定后可根据光线传感器,动态显示或隐藏手电筒
* @param v
*/
public abstract CameraScan bindFlashlightView(@Nullable View v);
public interface OnScanResultCallback{
boolean onScanResultCallback(Result result);
}
@Nullable
public static String parseScanResult(Intent data){
if(data != null){
return data.getStringExtra(SCAN_RESULT);
}
return null;
}
}

View File

@@ -17,28 +17,27 @@ package com.king.zxing;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.SurfaceView;
import android.view.View;
import com.google.zxing.Result;
import com.king.zxing.camera.CameraManager;
import androidx.annotation.LayoutRes;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.view.PreviewView;
import androidx.core.app.ActivityCompat;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class CaptureActivity extends AppCompatActivity implements OnCaptureCallback{
public class CaptureActivity extends AppCompatActivity implements CameraScan.OnScanResultCallback{
public static final String KEY_RESULT = Intents.Scan.RESULT;
private SurfaceView surfaceView;
private ViewfinderView viewfinderView;
private View ivTorch;
protected PreviewView previewView;
protected ViewfinderView viewfinderView;
protected View ivFlashlight;
private CaptureHelper mCaptureHelper;
private CameraScan mCameraScan;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -48,28 +47,60 @@ public class CaptureActivity extends AppCompatActivity implements OnCaptureCallb
setContentView(layoutId);
}
initUI();
mCaptureHelper.onCreate();
}
/**
* 初始化
*/
public void initUI(){
surfaceView = findViewById(getSurfaceViewId());
previewView = findViewById(getPreviewViewId());
int viewfinderViewId = getViewfinderViewId();
if(viewfinderViewId != 0){
viewfinderView = findViewById(viewfinderViewId);
}
int ivTorchId = getIvTorchId();
if(ivTorchId != 0){
ivTorch = findViewById(ivTorchId);
int ivFlashlightId = getFlashlightId();
if(ivFlashlightId != 0){
ivFlashlight = findViewById(ivFlashlightId);
if(ivFlashlight != null){
ivFlashlight.setOnClickListener(v -> toggleTorch());
}
}
initCaptureHelper();
initCameraScan();
startCamera();
}
public void initCaptureHelper(){
mCaptureHelper = new CaptureHelper(this,surfaceView,viewfinderView,ivTorch);
mCaptureHelper.setOnCaptureCallback(this);
public void initCameraScan(){
mCameraScan = new DefaultCameraScan(this,previewView);
mCameraScan.setOnScanResultCallback(this);
}
public void startCamera(){
if(mCameraScan != null){
mCameraScan.startCamera();
}
}
private void releaseCamera(){
if(mCameraScan != null){
mCameraScan.release();
}
}
protected void toggleTorch(){
if(mCameraScan != null){
boolean isTorch = mCameraScan.isTorchEnabled();
mCameraScan.enableTorch(!isTorch);
if(ivFlashlight != null){
ivFlashlight.setSelected(!isTorch);
}
}
}
@Override
protected void onDestroy() {
releaseCamera();
super.onDestroy();
}
/**
@@ -99,60 +130,27 @@ public class CaptureActivity extends AppCompatActivity implements OnCaptureCallb
/**
* 预览界面{@link #surfaceView} 的ID
* 预览界面{@link #previewView} 的ID
* @return
*/
public int getSurfaceViewId(){
return R.id.surfaceView;
public int getPreviewViewId(){
return R.id.previewView;
}
/**
* 获取 {@link #ivTorch} 的ID
* @return 默认返回{@code R.id.ivTorch}, 如果不需要手电筒按钮可以返回0
* 获取 {@link #ivFlashlight} 的ID
* @return 默认返回{@code R.id.ivFlashlight}, 如果不需要手电筒按钮可以返回0
*/
public int getIvTorchId(){
return R.id.ivTorch;
public int getFlashlightId(){
return R.id.ivFlashlight;
}
/**
* Get {@link CaptureHelper}
* @return {@link #mCaptureHelper}
* Get {@link CameraScan}
* @return {@link #mCameraScan}
*/
public CaptureHelper getCaptureHelper(){
return mCaptureHelper;
}
/**
* Get {@link CameraManager} use {@link #getCaptureHelper()#getCameraManager()}
* @return {@link #mCaptureHelper#getCameraManager()}
*/
@Deprecated
public CameraManager getCameraManager(){
return mCaptureHelper.getCameraManager();
}
@Override
public void onResume() {
super.onResume();
mCaptureHelper.onResume();
}
@Override
public void onPause() {
super.onPause();
mCaptureHelper.onPause();
}
@Override
public void onDestroy() {
super.onDestroy();
mCaptureHelper.onDestroy();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mCaptureHelper.onTouchEvent(event);
return super.onTouchEvent(event);
public CameraScan getCameraScan(){
return mCameraScan;
}
/**
@@ -161,7 +159,7 @@ public class CaptureActivity extends AppCompatActivity implements OnCaptureCallb
* @return 返回true表示拦截将不自动执行后续逻辑为false表示不拦截默认不拦截
*/
@Override
public boolean onResultCallback(Result result) {
public boolean onScanResultCallback(Result result) {
return false;
}
}

View File

@@ -17,31 +17,28 @@ package com.king.zxing;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import com.google.zxing.Result;
import com.king.zxing.camera.CameraManager;
import androidx.annotation.LayoutRes;
import androidx.annotation.Nullable;
import androidx.annotation.NonNull;
import androidx.camera.view.PreviewView;
import androidx.fragment.app.Fragment;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class CaptureFragment extends Fragment implements OnCaptureCallback {
public static final String KEY_RESULT = Intents.Scan.RESULT;
public class CaptureFragment extends Fragment implements CameraScan.OnScanResultCallback {
private View mRootView;
private SurfaceView surfaceView;
private ViewfinderView viewfinderView;
private View ivTorch;
protected PreviewView previewView;
protected ViewfinderView viewfinderView;
protected View ivFlashlight;
private CaptureHelper mCaptureHelper;
private CameraScan mCameraScan;
public static CaptureFragment newInstance() {
@@ -57,7 +54,7 @@ public class CaptureFragment extends Fragment implements OnCaptureCallback {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
int layoutId = getLayoutId();
if(isContentView(layoutId)){
mRootView = inflater.inflate(getLayoutId(),container,false);
mRootView = createRootView(inflater,container);
}
initUI();
return mRootView;
@@ -67,25 +64,57 @@ public class CaptureFragment extends Fragment implements OnCaptureCallback {
* 初始化
*/
public void initUI(){
surfaceView = mRootView.findViewById(getSurfaceViewId());
previewView = mRootView.findViewById(getPreviewViewId());
int viewfinderViewId = getViewfinderViewId();
if(viewfinderViewId != 0){
viewfinderView = mRootView.findViewById(viewfinderViewId);
}
int ivTorchId = getIvTorchId();
if(ivTorchId != 0){
ivTorch = mRootView.findViewById(ivTorchId);
int ivFlashlightId = getFlashlightId();
if(ivFlashlightId != 0){
ivFlashlight = mRootView.findViewById(ivFlashlightId);
if(ivFlashlight != null){
ivFlashlight.setOnClickListener(v -> toggleTorch());
}
}
initCaptureHelper();
initCameraScan();
startCamera();
}
public void initCaptureHelper(){
mCaptureHelper = new CaptureHelper(this,surfaceView,viewfinderView,ivTorch);
mCaptureHelper.setOnCaptureCallback(this);
public void initCameraScan(){
mCameraScan = new DefaultCameraScan(this,previewView);
mCameraScan.setOnScanResultCallback(this);
}
public void startCamera(){
if(mCameraScan != null){
mCameraScan.startCamera();
}
}
private void releaseCamera(){
if(mCameraScan != null){
mCameraScan.release();
}
}
protected void toggleTorch(){
if(mCameraScan != null){
boolean isTorch = mCameraScan.isTorchEnabled();
mCameraScan.enableTorch(!isTorch);
if(ivFlashlight != null){
ivFlashlight.setSelected(!isTorch);
}
}
}
@Override
public void onDestroy() {
releaseCamera();
super.onDestroy();
}
/**
* 返回true时会自动初始化{@link #mRootView}返回为false需自己去通过{@link #setRootView(View)}初始化{@link #mRootView}
* 返回true时会自动初始化{@link #createRootView(LayoutInflater, ViewGroup)}返回为false需自己去初始化{@link #createRootView(LayoutInflater, ViewGroup)}
* @param layoutId
* @return 默认返回true
*/
@@ -93,6 +122,17 @@ public class CaptureFragment extends Fragment implements OnCaptureCallback {
return true;
}
/**
* 创建{@link #mRootView}
* @param inflater
* @param container
* @return
*/
@NonNull
public View createRootView(LayoutInflater inflater, ViewGroup container){
return inflater.inflate(getLayoutId(),container,false);
}
/**
* 布局id
* @return
@@ -102,81 +142,36 @@ public class CaptureFragment extends Fragment implements OnCaptureCallback {
}
/**
* {@link ViewfinderView} 的 id
* {@link #viewfinderView} 的 ID
* @return 默认返回{@code R.id.viewfinderView}, 如果不需要扫码框可以返回0
*/
public int getViewfinderViewId(){
return R.id.viewfinderView;
}
/**
* 预览界面{@link #surfaceView} 的id
* 预览界面{@link #previewView} 的ID
* @return
*/
public int getSurfaceViewId(){
return R.id.surfaceView;
public int getPreviewViewId(){
return R.id.previewView;
}
/**
* 获取 {@link #ivTorch} 的ID
* @return 默认返回{@code R.id.ivTorch}, 如果不需要手电筒按钮可以返回0
* 获取 {@link #ivFlashlight} 的ID
* @return 默认返回{@code R.id.ivFlashlight}, 如果不需要手电筒按钮可以返回0
*/
public int getIvTorchId(){
return R.id.ivTorch;
public int getFlashlightId(){
return R.id.ivFlashlight;
}
/**
* Get {@link CaptureHelper}
* @return {@link #mCaptureHelper}
* Get {@link CameraScan}
* @return {@link #mCameraScan}
*/
public CaptureHelper getCaptureHelper(){
return mCaptureHelper;
}
/**
* Get {@link CameraManager} use {@link #getCaptureHelper()#getCameraManager()}
* @return {@link #mCaptureHelper#getCameraManager()}
*/
@Deprecated
public CameraManager getCameraManager(){
return mCaptureHelper.getCameraManager();
}
//--------------------------------------------
public View getRootView() {
return mRootView;
}
public void setRootView(View rootView) {
this.mRootView = rootView;
}
//--------------------------------------------
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mCaptureHelper.onCreate();
}
@Override
public void onResume() {
super.onResume();
mCaptureHelper.onResume();
}
@Override
public void onPause() {
super.onPause();
mCaptureHelper.onPause();
}
@Override
public void onDestroy() {
super.onDestroy();
mCaptureHelper.onDestroy();
public CameraScan getCameraScan(){
return mCameraScan;
}
/**
@@ -185,8 +180,15 @@ public class CaptureFragment extends Fragment implements OnCaptureCallback {
* @return 返回true表示拦截将不自动执行后续逻辑为false表示不拦截默认不拦截
*/
@Override
public boolean onResultCallback(Result result) {
public boolean onScanResultCallback(Result result) {
return false;
}
//--------------------------------------------
public View getRootView() {
return mRootView;
}
}

View File

@@ -1,199 +0,0 @@
package com.king.zxing;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Display;
import android.view.WindowManager;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.DecodeHintType;
import com.google.zxing.Result;
import com.google.zxing.ResultPoint;
import com.google.zxing.ResultPointCallback;
import com.king.zxing.camera.CameraManager;
import java.util.Collection;
import java.util.Map;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class CaptureHandler extends Handler implements ResultPointCallback {
private final OnCaptureListener onCaptureListener;
private final DecodeThread decodeThread;
private State state;
private final CameraManager cameraManager;
private final ViewfinderView viewfinderView;
/**
* 是否支持垂直的条形码
*/
private boolean isSupportVerticalCode;
/**
* 是否返回扫码原图
*/
private boolean isReturnBitmap;
/**
* 是否支持自动缩放
*/
private boolean isSupportAutoZoom;
/**
*
*/
private boolean isSupportLuminanceInvert;
private enum State {
PREVIEW,
SUCCESS,
DONE
}
CaptureHandler(Activity activity,ViewfinderView viewfinderView,OnCaptureListener onCaptureListener,
Collection<BarcodeFormat> decodeFormats,
Map<DecodeHintType,Object> baseHints,
String characterSet,
CameraManager cameraManager) {
this.viewfinderView = viewfinderView;
this.onCaptureListener = onCaptureListener;
decodeThread = new DecodeThread(activity,cameraManager,this, decodeFormats, baseHints, characterSet, this);
decodeThread.start();
state = State.SUCCESS;
// Start ourselves capturing previews and decoding.
this.cameraManager = cameraManager;
cameraManager.startPreview();
restartPreviewAndDecode();
}
@Override
public void handleMessage(Message message) {
if (message.what == R.id.restart_preview) {
restartPreviewAndDecode();
} else if (message.what == R.id.decode_succeeded) {
state = State.SUCCESS;
Bundle bundle = message.getData();
Bitmap barcode = null;
float scaleFactor = 1.0f;
if (bundle != null) {
byte[] compressedBitmap = bundle.getByteArray(DecodeThread.BARCODE_BITMAP);
if (compressedBitmap != null) {
barcode = BitmapFactory.decodeByteArray(compressedBitmap, 0, compressedBitmap.length, null);
// Mutable copy:
barcode = barcode.copy(Bitmap.Config.ARGB_8888, true);
}
scaleFactor = bundle.getFloat(DecodeThread.BARCODE_SCALED_FACTOR);
}
onCaptureListener.onHandleDecode((Result) message.obj, barcode, scaleFactor);
} 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.requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
}
}
public void quitSynchronously() {
state = State.DONE;
cameraManager.stopPreview();
Message quit = Message.obtain(decodeThread.getHandler(), R.id.quit);
quit.sendToTarget();
try {
// Wait at most half a second; should be enough time, and onPause() will timeout quickly
decodeThread.join(100L);
} catch (InterruptedException e) {
// continue
}
// Be absolutely sure we don't send any queued up messages
removeMessages(R.id.decode_succeeded);
removeMessages(R.id.decode_failed);
}
public void restartPreviewAndDecode() {
if (state == State.SUCCESS) {
state = State.PREVIEW;
cameraManager.requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
if(viewfinderView!= null){
viewfinderView.drawViewfinder();
}
}
}
@Override
public void foundPossibleResultPoint(ResultPoint point) {
if(viewfinderView!=null){
ResultPoint resultPoint = transform(point);
viewfinderView.addPossibleResultPoint(resultPoint);
}
}
private boolean isScreenPortrait(Context context){
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = manager.getDefaultDisplay();
Point screenResolution = new Point();
display.getSize(screenResolution);
return screenResolution.x < screenResolution.y;
}
/**
*
* @return
*/
private ResultPoint transform(ResultPoint originPoint) {
Point screenPoint = cameraManager.getScreenResolution();
Point previewPoint = cameraManager.getPreviewSizeOnScreen();
float scaleX = 1.0f * screenPoint.x / previewPoint.x;
float scaleY = 1.0f * screenPoint.y / previewPoint.y;
float x = originPoint.getX() * scaleX - Math.min(screenPoint.y,previewPoint.y)/2;
float y = originPoint.getY() * scaleY - Math.max(screenPoint.x,previewPoint.x)/2;
return new ResultPoint(x,y);
}
public boolean isSupportVerticalCode() {
return isSupportVerticalCode;
}
public void setSupportVerticalCode(boolean supportVerticalCode) {
isSupportVerticalCode = supportVerticalCode;
}
public boolean isReturnBitmap() {
return isReturnBitmap;
}
public void setReturnBitmap(boolean returnBitmap) {
isReturnBitmap = returnBitmap;
}
public boolean isSupportAutoZoom() {
return isSupportAutoZoom;
}
public void setSupportAutoZoom(boolean supportAutoZoom) {
isSupportAutoZoom = supportAutoZoom;
}
public boolean isSupportLuminanceInvert() {
return isSupportLuminanceInvert;
}
public void setSupportLuminanceInvert(boolean supportLuminanceInvert) {
isSupportLuminanceInvert = supportLuminanceInvert;
}
}

View File

@@ -1,849 +0,0 @@
/*
* Copyright (C) 2019 Jenly Yu
*
* 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.king.zxing;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.RectF;
import android.hardware.Camera;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.DecodeHintType;
import com.google.zxing.Result;
import com.king.zxing.camera.CameraManager;
import com.king.zxing.util.LogUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import androidx.annotation.FloatRange;
import androidx.fragment.app.Fragment;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class CaptureHelper implements CaptureLifecycle,CaptureTouchEvent,CaptureManager, SurfaceHolder.Callback {
private Activity activity;
private CaptureHandler captureHandler;
private OnCaptureListener onCaptureListener;
private CameraManager cameraManager;
private InactivityTimer inactivityTimer;
private BeepManager beepManager;
private AmbientLightManager ambientLightManager;
private SurfaceView surfaceView;
private ViewfinderView viewfinderView;
private SurfaceHolder surfaceHolder;
private View ivTorch;
private Collection<BarcodeFormat> decodeFormats;
private Map<DecodeHintType,Object> decodeHints;
private String characterSet;
private boolean hasSurface;
/**
* 默认触控误差值
*/
private static final int DEVIATION = 6;
/**
* 是否支持缩放(变焦),默认支持
*/
private boolean isSupportZoom = true;
private float oldDistance;
/**
* 是否支持自动缩放(变焦),默认支持
*/
private boolean isSupportAutoZoom = true;
/**
* 是否支持识别颜色反转色的码,黑白颜色反转,默认不支持
*/
private boolean isSupportLuminanceInvert = false;
/**
* 是否支持连扫,默认不支持
*/
private boolean isContinuousScan = false;
/**
* 连扫时,是否自动重置预览和解码器,默认自动重置
*/
private boolean isAutoRestartPreviewAndDecode = true;
/**
* 是否播放音效
*/
private boolean isPlayBeep;
/**
* 是否震动
*/
private boolean isVibrate;
/**
* 是否支持垂直的条形码
*/
private boolean isSupportVerticalCode;
/**
* 是否返回扫码原图
*/
private boolean isReturnBitmap;
/**
* 是否支持全屏扫码识别
*/
private boolean isFullScreenScan;
/**
* 识别区域比例范围建议在0.625 ~ 1.0之间默认0.9
*/
private float framingRectRatio = 0.9f;
/**
* 识别区域垂直方向偏移量
*/
private int framingRectVerticalOffset;
/**
* 识别区域水平方向偏移量
*/
private int framingRectHorizontalOffset;
/**
* 光线太暗,当光线亮度太暗,亮度低于此值时,显示手电筒按钮
*/
private float tooDarkLux = AmbientLightManager.TOO_DARK_LUX;
/**
* 光线足够明亮,当光线亮度足够明亮,亮度高于此值时,隐藏手电筒按钮
*/
private float brightEnoughLux = AmbientLightManager.BRIGHT_ENOUGH_LUX;
/**
* 扫码回调
*/
private OnCaptureCallback onCaptureCallback;
private boolean hasCameraFlash;
/**
* 是否启用光线传感器
*/
private boolean isAmbientLightEnabled = true;
/**
* use {@link #CaptureHelper(Fragment, SurfaceView, ViewfinderView, View)}
* @param fragment
* @param surfaceView
* @param viewfinderView
*/
@Deprecated
public CaptureHelper(Fragment fragment, SurfaceView surfaceView, ViewfinderView viewfinderView){
this(fragment,surfaceView,viewfinderView,null);
}
public CaptureHelper(Fragment fragment, SurfaceView surfaceView, ViewfinderView viewfinderView,View ivTorch){
this(fragment.getActivity(),surfaceView,viewfinderView,ivTorch);
}
/**
* use {@link #CaptureHelper(Activity, SurfaceView, ViewfinderView, View)}
* @param activity
* @param surfaceView
* @param viewfinderView
*/
@Deprecated
public CaptureHelper(Activity activity,SurfaceView surfaceView,ViewfinderView viewfinderView){
this(activity,surfaceView,viewfinderView,null);
}
/**
*
* @param activity
* @param surfaceView
* @param viewfinderView
* @param ivTorch
*/
public CaptureHelper(Activity activity,SurfaceView surfaceView,ViewfinderView viewfinderView,View ivTorch){
this.activity = activity;
this.surfaceView = surfaceView;
this.viewfinderView = viewfinderView;
this.ivTorch = ivTorch;
}
@Override
public void onCreate(){
surfaceHolder = surfaceView.getHolder();
hasSurface = false;
inactivityTimer = new InactivityTimer(activity);
beepManager = new BeepManager(activity);
ambientLightManager = new AmbientLightManager(activity);
hasCameraFlash = activity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);
initCameraManager();
onCaptureListener = (result, barcode, scaleFactor) -> {
inactivityTimer.onActivity();
beepManager.playBeepSoundAndVibrate();
onResult(result,barcode,scaleFactor);
};
//设置是否播放音效和震动
beepManager.setPlayBeep(isPlayBeep);
beepManager.setVibrate(isVibrate);
//设置闪光灯的太暗时和足够亮时的照度值
ambientLightManager.setTooDarkLux(tooDarkLux);
ambientLightManager.setBrightEnoughLux(brightEnoughLux);
}
@Override
public void onResume(){
beepManager.updatePrefs();
inactivityTimer.onResume();
if (hasSurface) {
initCamera(surfaceHolder);
} else {
surfaceHolder.addCallback(this);
}
if(isAmbientLightEnabled){
ambientLightManager.start(cameraManager);
}
}
@Override
public void onPause(){
if (captureHandler != null) {
captureHandler.quitSynchronously();
captureHandler = null;
}
inactivityTimer.onPause();
if(isAmbientLightEnabled) {
ambientLightManager.stop();
}
beepManager.close();
cameraManager.closeDriver();
if (!hasSurface) {
surfaceHolder.removeCallback(this);
}
if(ivTorch != null && ivTorch.getVisibility() == View.VISIBLE){
ivTorch.setSelected(false);
ivTorch.setVisibility(View.INVISIBLE);
}
}
@Override
public void onDestroy(){
inactivityTimer.shutdown();
}
/**
* 支持缩放时,须在{@link Activity#onTouchEvent(MotionEvent)}调用此方法
* @param event
*/
@Override
public boolean onTouchEvent(MotionEvent event){
if(isSupportZoom && cameraManager.isOpen()){
Camera camera = cameraManager.getOpenCamera().getCamera();
if(camera ==null){
return false;
}
if(event.getPointerCount() > 1) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {//多点触控
case MotionEvent.ACTION_POINTER_DOWN:
oldDistance = calcFingerSpacing(event);
break;
case MotionEvent.ACTION_MOVE:
float newDistance = calcFingerSpacing(event);
if (newDistance > oldDistance + DEVIATION) {//
handleZoom(true, camera);
} else if (newDistance < oldDistance - DEVIATION) {
handleZoom(false, camera);
}
oldDistance = newDistance;
break;
}
return true;
}
}
return false;
}
private void initCameraManager(){
cameraManager = new CameraManager(activity);
cameraManager.setFullScreenScan(isFullScreenScan);
cameraManager.setFramingRectRatio(framingRectRatio);
cameraManager.setFramingRectVerticalOffset(framingRectVerticalOffset);
cameraManager.setFramingRectHorizontalOffset(framingRectHorizontalOffset);
if(ivTorch !=null && hasCameraFlash){
ivTorch.setOnClickListener(v -> {
if(cameraManager!=null){
cameraManager.setTorch(!ivTorch.isSelected());
}
});
cameraManager.setOnSensorListener((torch, tooDark, ambientLightLux) -> {
if(tooDark){
if(ivTorch.getVisibility() != View.VISIBLE){
ivTorch.setVisibility(View.VISIBLE);
}
}else if(!torch){
if(ivTorch.getVisibility() == View.VISIBLE){
ivTorch.setVisibility(View.INVISIBLE);
}
}
});
cameraManager.setOnTorchListener(torch -> ivTorch.setSelected(torch));
}
}
/**
* 初始化Camera
* @param surfaceHolder
*/
private void initCamera(SurfaceHolder surfaceHolder) {
if (surfaceHolder == null) {
throw new IllegalStateException("No SurfaceHolder provided");
}
if (cameraManager.isOpen()) {
LogUtils.w("initCamera() while already open -- late SurfaceView callback?");
return;
}
try {
cameraManager.openDriver(surfaceHolder);
// Creating the handler starts the preview, which can also throw a RuntimeException.
if (captureHandler == null) {
captureHandler = new CaptureHandler(activity,viewfinderView,onCaptureListener, decodeFormats, decodeHints, characterSet, cameraManager);
captureHandler.setSupportVerticalCode(isSupportVerticalCode);
captureHandler.setReturnBitmap(isReturnBitmap);
captureHandler.setSupportAutoZoom(isSupportAutoZoom);
captureHandler.setSupportLuminanceInvert(isSupportLuminanceInvert);
}
} catch (IOException ioe) {
LogUtils.w(ioe);
} catch (RuntimeException e) {
// Barcode Scanner has seen crashes in the wild of this variety:
// java.?lang.?RuntimeException: Fail to connect to camera service
LogUtils.w( "Unexpected error initializing camera", e);
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (holder == null) {
LogUtils.w( "*** WARNING *** surfaceCreated() gave us a null surface!");
}
if (!hasSurface) {
hasSurface = true;
initCamera(holder);
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
hasSurface = false;
}
/**
* 处理变焦缩放
* @param isZoomIn
* @param camera
*/
private void handleZoom(boolean isZoomIn, Camera camera) {
Camera.Parameters params = camera.getParameters();
if (params.isZoomSupported()) {
int maxZoom = params.getMaxZoom();
int zoom = params.getZoom();
if (isZoomIn && zoom < maxZoom) {
zoom++;
} else if (zoom > 0) {
zoom--;
}
params.setZoom(zoom);
camera.setParameters(params);
} else {
LogUtils.i( "zoom not supported");
}
}
/**
* 聚焦
* @param event
* @param camera
*/
@Deprecated
private void focusOnTouch(MotionEvent event,Camera camera) {
Camera.Parameters params = camera.getParameters();
Camera.Size previewSize = params.getPreviewSize();
Rect focusRect = calcTapArea(event.getRawX(), event.getRawY(), 1f,previewSize);
Rect meteringRect = calcTapArea(event.getRawX(), event.getRawY(), 1.5f,previewSize);
Camera.Parameters parameters = camera.getParameters();
if (parameters.getMaxNumFocusAreas() > 0) {
List<Camera.Area> focusAreas = new ArrayList<>();
focusAreas.add(new Camera.Area(focusRect, 600));
parameters.setFocusAreas(focusAreas);
}
if (parameters.getMaxNumMeteringAreas() > 0) {
List<Camera.Area> meteringAreas = new ArrayList<>();
meteringAreas.add(new Camera.Area(meteringRect, 600));
parameters.setMeteringAreas(meteringAreas);
}
final String currentFocusMode = params.getFocusMode();
params.setFocusMode(Camera.Parameters.FOCUS_MODE_MACRO);
camera.setParameters(params);
camera.autoFocus((success, camera1) -> {
Camera.Parameters params1 = camera1.getParameters();
params1.setFocusMode(currentFocusMode);
camera1.setParameters(params1);
});
}
/**
* 计算两指间距离
* @param event
* @return
*/
private float calcFingerSpacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return (float) Math.sqrt(x * x + y * y);
}
/**
* 计算对焦区域
* @param x
* @param y
* @param coefficient
* @param previewSize
* @return
*/
private Rect calcTapArea(float x, float y, float coefficient, Camera.Size previewSize) {
float focusAreaSize = 200;
int areaSize = Float.valueOf(focusAreaSize * coefficient).intValue();
int centerX = (int) ((x / previewSize.width) * 2000 - 1000);
int centerY = (int) ((y / previewSize.height) * 2000 - 1000);
int left = clamp(centerX - (areaSize / 2), -1000, 1000);
int top = clamp(centerY - (areaSize / 2), -1000, 1000);
RectF rectF = new RectF(left, top, left + areaSize, top + areaSize);
return new Rect(Math.round(rectF.left), Math.round(rectF.top),
Math.round(rectF.right), Math.round(rectF.bottom));
}
/**
* 根据范围限定值
* @param x
* @param min 范围最小值
* @param max 范围最大值
* @return
*/
private int clamp(int x, int min, int max) {
if (x > max) {
return max;
}
if (x < min) {
return min;
}
return x;
}
/**
* 重新启动扫码和解码器
*/
public void restartPreviewAndDecode(){
if(captureHandler != null){
captureHandler.restartPreviewAndDecode();
}
}
/**
* 接收扫码结果
* @param result
* @param barcode
* @param scaleFactor
*/
public void onResult(Result result, Bitmap barcode, float scaleFactor){
onResult(result);
}
/**';, mnb
*
* 接收扫码结果,想支持连扫时,可将{@link #continuousScan(boolean)}设置为{@code true}
* 如果{@link #isContinuousScan}支持连扫,则默认重启扫码和解码器;当连扫逻辑太复杂时,
* 请将{@link #autoRestartPreviewAndDecode(boolean)}设置为{@code false},并手动调用{@link #restartPreviewAndDecode()}
* @param result 扫码结果
*/
public void onResult(Result result){
final String text = result.getText();
if(isContinuousScan){
if(onCaptureCallback!=null){
onCaptureCallback.onResultCallback(result);
}
if(isAutoRestartPreviewAndDecode){
restartPreviewAndDecode();
}
return;
}
if(isPlayBeep && captureHandler != null){//如果播放音效,则稍微延迟一点,给予播放音效时间
captureHandler.postDelayed(() -> {
//如果设置了回调并且onCallback返回为true则表示拦截
if(onCaptureCallback!=null && onCaptureCallback.onResultCallback(result)){
return;
}
Intent intent = new Intent();
intent.putExtra(Intents.Scan.RESULT,text);
activity.setResult(Activity.RESULT_OK,intent);
activity.finish();
},100);
return;
}
//如果设置了回调并且onCallback返回为true则表示拦截
if(onCaptureCallback!=null && onCaptureCallback.onResultCallback(result)){
return;
}
Intent intent = new Intent();
intent.putExtra(Intents.Scan.RESULT,text);
activity.setResult(Activity.RESULT_OK,intent);
activity.finish();
}
/**
* 设置是否连续扫码,如果想支持连续扫码,则将此方法返回{@code true}并重写{@link #onResult(Result)}
*/
public CaptureHelper continuousScan(boolean isContinuousScan){
this.isContinuousScan = isContinuousScan;
return this;
}
/**
* 设置是否自动重启扫码和解码器,当支持连扫时才起作用。
* @return 默认返回 true
*/
public CaptureHelper autoRestartPreviewAndDecode(boolean isAutoRestartPreviewAndDecode){
this.isAutoRestartPreviewAndDecode = isAutoRestartPreviewAndDecode;
return this;
}
/**
* 设置是否播放音效
* @return
*/
public CaptureHelper playBeep(boolean playBeep){
this.isPlayBeep = playBeep;
if(beepManager!=null){
beepManager.setPlayBeep(playBeep);
}
return this;
}
/**
* 设置是否震动
* @return
*/
public CaptureHelper vibrate(boolean vibrate){
this.isVibrate = vibrate;
if(beepManager!=null){
beepManager.setVibrate(vibrate);
}
return this;
}
/**
* 设置是否支持缩放
* @param supportZoom
* @return
*/
public CaptureHelper supportZoom(boolean supportZoom) {
isSupportZoom = supportZoom;
return this;
}
/**
* 设置支持的解码一/二维码格式,默认常规的码都支持
* @param decodeFormats 可参见{@link DecodeFormatManager}
* @return
*/
public CaptureHelper decodeFormats(Collection<BarcodeFormat> decodeFormats) {
this.decodeFormats = decodeFormats;
return this;
}
/**
* {@link DecodeHintType}
* @param decodeHints
* @return
*/
public CaptureHelper decodeHints(Map<DecodeHintType,Object> decodeHints) {
this.decodeHints = decodeHints;
return this;
}
/**
* {@link DecodeHintType}
* @param key {@link DecodeHintType}
* @param value {@link }
* @return
*/
public CaptureHelper decodeHint(DecodeHintType key,Object value){
if(decodeHints == null){
decodeHints = new EnumMap<>(DecodeHintType.class);
}
decodeHints.put(key,value);
return this;
}
/**
* 设置解码时编码字符集
* @param characterSet
* @return
*/
public CaptureHelper characterSet(String characterSet) {
this.characterSet = characterSet;
return this;
}
/**
* 设置是否支持扫垂直的条码
* @param supportVerticalCode 默认为false想要增强扫条码识别度时可使用相应的会增加性能消耗。
* @return
*/
public CaptureHelper supportVerticalCode(boolean supportVerticalCode) {
this.isSupportVerticalCode = supportVerticalCode;
if(captureHandler!=null){
captureHandler.setSupportVerticalCode(isSupportVerticalCode);
}
return this;
}
/**
* 设置光线太暗时,自动显示手电筒按钮
* @param tooDarkLux 默认:{@link AmbientLightManager#TOO_DARK_LUX}
* @return
*/
public CaptureHelper tooDarkLux(float tooDarkLux) {
this.tooDarkLux = tooDarkLux;
if(ambientLightManager != null){
ambientLightManager.setTooDarkLux(tooDarkLux);
}
return this;
}
/**
* 设置光线足够明亮时,自动隐藏手电筒按钮
* @param brightEnoughLux 默认:{@link AmbientLightManager#BRIGHT_ENOUGH_LUX}
* @return
*/
public CaptureHelper brightEnoughLux(float brightEnoughLux) {
this.brightEnoughLux = brightEnoughLux;
if(ambientLightManager != null){
ambientLightManager.setTooDarkLux(tooDarkLux);
}
return this;
}
/**
* 设置返回扫码原图
* @param returnBitmap 默认为false当返回true表示扫码就结果会返回扫码原图相应的会增加性能消耗。
* @return
*/
public CaptureHelper returnBitmap(boolean returnBitmap) {
isReturnBitmap = returnBitmap;
if(captureHandler!=null){
captureHandler.setReturnBitmap(isReturnBitmap);
}
return this;
}
/**
* 设置是否支持自动缩放
* @param supportAutoZoom
* @return
*/
public CaptureHelper supportAutoZoom(boolean supportAutoZoom) {
isSupportAutoZoom = supportAutoZoom;
if(captureHandler!=null){
captureHandler.setSupportAutoZoom(isSupportAutoZoom);
}
return this;
}
/**
* 是否支持识别反色码,黑白颜色反转
* @param supportLuminanceInvert 默认为false当返回true时表示支持会增加识别率但相应的也会增加性能消耗。
* @return
*/
public CaptureHelper supportLuminanceInvert(boolean supportLuminanceInvert) {
isSupportLuminanceInvert = supportLuminanceInvert;
if(captureHandler!=null){
captureHandler.setSupportLuminanceInvert(isSupportLuminanceInvert);
}
return this;
}
/**
* 设置是否支持全屏扫码识别
* @param fullScreenScan 默认为false
* @return
*/
public CaptureHelper fullScreenScan(boolean fullScreenScan) {
isFullScreenScan = fullScreenScan;
if(cameraManager!=null){
cameraManager.setFullScreenScan(isFullScreenScan);
}
return this;
}
/**
* 设置识别区域比例范围建议在0.625 ~ 1.0之间。非全屏识别时才有效
* 0.625 即与默认推荐显示区域一致1.0表示与宽度一致
* @param framingRectRatio 默认0.9
* @return
*/
public CaptureHelper framingRectRatio(@FloatRange(from = 0.0f ,to = 1.0f) float framingRectRatio) {
this.framingRectRatio = framingRectRatio;
if(cameraManager!=null){
cameraManager.setFramingRectRatio(framingRectRatio);
}
return this;
}
/**
* 设置识别区域垂直方向偏移量,非全屏识别时才有效
* @param framingRectVerticalOffset 默认0表示不偏移
* @return
*/
public CaptureHelper framingRectVerticalOffset(int framingRectVerticalOffset) {
this.framingRectVerticalOffset = framingRectVerticalOffset;
if(cameraManager!=null){
cameraManager.setFramingRectVerticalOffset(framingRectVerticalOffset);
}
return this;
}
/**
* 设置识别区域水平方向偏移量,非全屏识别时才有效
* @param framingRectHorizontalOffset 默认0表示不偏移
* @return
*/
public CaptureHelper framingRectHorizontalOffset(int framingRectHorizontalOffset) {
this.framingRectHorizontalOffset = framingRectHorizontalOffset;
if(cameraManager!=null){
cameraManager.setFramingRectHorizontalOffset(framingRectHorizontalOffset);
}
return this;
}
/**
* 是否启用光线传感器
* @param isAmbientLightEnabled
* @return
*/
public CaptureHelper ambientLightEnabled(boolean isAmbientLightEnabled){
this.isAmbientLightEnabled = isAmbientLightEnabled;
return this;
}
/**
* 设置扫码回调
* @param callback
* @return
*/
public CaptureHelper setOnCaptureCallback(OnCaptureCallback callback) {
this.onCaptureCallback = callback;
return this;
}
/**
* {@link CameraManager}
* @return {@link #cameraManager}
*/
@Override
public CameraManager getCameraManager() {
return cameraManager;
}
/**
* {@link BeepManager}
* @return {@link #beepManager}
*/
@Override
public BeepManager getBeepManager() {
return beepManager;
}
/**
* {@link AmbientLightManager}
* @return {@link #ambientLightManager}
*/
@Override
public AmbientLightManager getAmbientLightManager() {
return ambientLightManager;
}
/**
* {@link InactivityTimer}
* @return {@link #inactivityTimer}
*/
@Override
public InactivityTimer getInactivityTimer() {
return inactivityTimer;
}
}

View File

@@ -1,45 +0,0 @@
/*
* Copyright (C) 2019 Jenly Yu
*
* 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.king.zxing;
import android.app.Activity;
import android.os.Bundle;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public interface CaptureLifecycle {
/**
* {@link Activity#onCreate(Bundle)}
*/
void onCreate();
/**
* {@link Activity#onResume()}
*/
void onResume();
/**
* {@link Activity#onPause()}
*/
void onPause();
/**
* {@link Activity#onDestroy()}
*/
void onDestroy();
}

View File

@@ -1,48 +0,0 @@
/*
* Copyright (C) 2019 Jenly Yu
*
* 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.king.zxing;
import com.king.zxing.camera.CameraManager;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public interface CaptureManager {
/**
* Get {@link CameraManager}
* @return {@link CameraManager}
*/
CameraManager getCameraManager();
/**
* Get {@link BeepManager}
* @return {@link BeepManager}
*/
BeepManager getBeepManager();
/**
* Get {@link AmbientLightManager}
* @return {@link AmbientLightManager}
*/
AmbientLightManager getAmbientLightManager();
/**
* Get {@link InactivityTimer}
* @return {@link InactivityTimer}
*/
InactivityTimer getInactivityTimer();
}

View File

@@ -1,29 +0,0 @@
/*
* Copyright (C) 2019 Jenly Yu
*
* 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.king.zxing;
import android.view.MotionEvent;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public interface CaptureTouchEvent {
/**
* {@link android.app.Activity#onTouchEvent(MotionEvent)}
*/
boolean onTouchEvent(MotionEvent event);
}

View File

@@ -0,0 +1,284 @@
package com.king.zxing;
import android.graphics.Rect;
import com.google.zxing.DecodeHintType;
import com.google.zxing.common.GlobalHistogramBinarizer;
import com.google.zxing.common.HybridBinarizer;
import java.util.Map;
import androidx.annotation.FloatRange;
/**
*
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class DecodeConfig {
private Map<DecodeHintType,Object> hints = DecodeFormatManager.DEFAULT_HINTS;
/**
* 是否支持使用多解码
*/
private boolean isMultiDecode = true;
/**
* 是否支持识别反色码(条码黑白颜色反转的码)
*/
private boolean isSupportLuminanceInvert;
/**
* 是否支持识别反色码(条码黑白颜色反转的码)使用多解码
*/
private boolean isSupportLuminanceInvertMultiDecode;
/**
* 是否支持垂直的条码
*/
private boolean isSupportVerticalCode;
/**
* 是否支持垂直的条码,使用多解码
*/
private boolean isSupportVerticalCodeMultiDecode;
/**
* 需要分析识别区域
*/
private Rect analyzeAreaRect;
/**
* 是否支持全区域扫码识别
*/
private boolean isFullAreaScan = true;
/**
* 识别区域比例默认0.9
*/
private float areaRectRatio = 0.9f;
/**
* 识别区域垂直方向偏移量
*/
private int areaRectVerticalOffset;
/**
* 识别区域水平方向偏移量
*/
private int areaRectHorizontalOffset;
public DecodeConfig(){
}
public Map<DecodeHintType, Object> getHints() {
return hints;
}
/**
* 设置解码
* @param hints {@link DecodeFormatManager}
* @return
*/
public DecodeConfig setHints(Map<DecodeHintType, Object> hints) {
this.hints = hints;
return this;
}
/**
* 是否支持识别反色码,黑白颜色反转
* @return
*/
public boolean isSupportLuminanceInvert() {
return isSupportLuminanceInvert;
}
/**
* 设置是否支持识别反色码,黑白颜色反转
* @param supportLuminanceInvert 默认为{@code false},想要增强支持扫码识别反色码时可使用,相应的也会增加性能消耗。
* @return
*/
public DecodeConfig setSupportLuminanceInvert(boolean supportLuminanceInvert) {
isSupportLuminanceInvert = supportLuminanceInvert;
return this;
}
/**
* 是否支持扫垂直的条码
* @return
*/
public boolean isSupportVerticalCode() {
return isSupportVerticalCode;
}
/**
* 设置是否支持扫垂直的条码
* @param supportVerticalCode 默认为{@code false},想要增强支持扫码识别垂直的条码时可使用,相应的也会增加性能消耗。
* @return
*/
public DecodeConfig setSupportVerticalCode(boolean supportVerticalCode) {
isSupportVerticalCode = supportVerticalCode;
return this;
}
/**
* 是否支持使用多解码
* @return
*/
public boolean isMultiDecode() {
return isMultiDecode;
}
/**
* 是否支持使用多解码
* @see {@link HybridBinarizer} , {@link GlobalHistogramBinarizer}
* @param multiDecode 默认为{@code true}
* @return
*/
public DecodeConfig setMultiDecode(boolean multiDecode) {
isMultiDecode = multiDecode;
return this;
}
/**
* 是否支持识别反色码(条码黑白颜色反转的码)使用多解码
* @return
*/
public boolean isSupportLuminanceInvertMultiDecode() {
return isSupportLuminanceInvertMultiDecode;
}
/**
* 设置是否支持识别反色码(条码黑白颜色反转的码)使用多解码
* @see {@link HybridBinarizer} , {@link GlobalHistogramBinarizer}
* @param supportLuminanceInvertMultiDecode 默认为{@code false},想要增强支持扫码识别反色码时可使用,相应的也会增加性能消耗。
* @return
*/
public DecodeConfig setSupportLuminanceInvertMultiDecode(boolean supportLuminanceInvertMultiDecode) {
isSupportLuminanceInvertMultiDecode = supportLuminanceInvertMultiDecode;
return this;
}
/**
* 是否支持垂直的条码,使用多解码
* @return
*/
public boolean isSupportVerticalCodeMultiDecode() {
return isSupportVerticalCodeMultiDecode;
}
/**
* 设置是否支持垂直的条码,使用多解码
* @see {@link HybridBinarizer} , {@link GlobalHistogramBinarizer}
* @param supportVerticalCodeMultiDecode 默认为{@code false},想要增强支持扫码识别垂直的条码时可使用,相应的也会增加性能消耗。
* @return
*/
public DecodeConfig setSupportVerticalCodeMultiDecode(boolean supportVerticalCodeMultiDecode) {
isSupportVerticalCodeMultiDecode = supportVerticalCodeMultiDecode;
return this;
}
/**
* 需要分析识别区域
* @return
*/
public Rect getAnalyzeAreaRect() {
return analyzeAreaRect;
}
/**
* 设置需要分析识别区域,当设置了指定的分析区域时,识别区域比例和识别区域相关参数都将无效
* @param analyzeAreaRect
* @return
*/
public DecodeConfig setAnalyzeAreaRect(Rect analyzeAreaRect) {
this.analyzeAreaRect = analyzeAreaRect;
return this;
}
/**
* 是否支持全区域扫码识别
* @return
*/
public boolean isFullAreaScan() {
return isFullAreaScan;
}
/**
* 设置是否支持全区域扫码识别,优先级比识别区域比例高
* @param fullAreaScan 默认为{@code true}
* @return
*/
public DecodeConfig setFullAreaScan(boolean fullAreaScan) {
isFullAreaScan = fullAreaScan;
return this;
}
/**
* 识别区域比例默认0.9,设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别
* @return
*/
public float getAreaRectRatio() {
return areaRectRatio;
}
/**
* 设置识别区域比例默认0.9,设置的比例最终会在预览区域裁剪基于此比例的一个矩形进行扫码识别
* @param areaRectRatio
* @return
*/
public DecodeConfig setAreaRectRatio(@FloatRange(from = 0.5,to = 1.0) float areaRectRatio) {
this.areaRectRatio = areaRectRatio;
return this;
}
/**
* 识别区域垂直方向偏移量
* @return
*/
public int getAreaRectVerticalOffset() {
return areaRectVerticalOffset;
}
/**
* 设置识别区域垂直方向偏移量
* @param areaRectVerticalOffset
* @return
*/
public DecodeConfig setAreaRectVerticalOffset(int areaRectVerticalOffset) {
this.areaRectVerticalOffset = areaRectVerticalOffset;
return this;
}
/**
* 识别区域水平方向偏移量
* @return
*/
public int getAreaRectHorizontalOffset() {
return areaRectHorizontalOffset;
}
/**
* 设置识别区域水平方向偏移量
* @param areaRectHorizontalOffset
* @return
*/
public DecodeConfig setAreaRectHorizontalOffset(int areaRectHorizontalOffset) {
this.areaRectHorizontalOffset = areaRectHorizontalOffset;
return this;
}
@Override
public String toString() {
return "DecodeConfig{" +
"hints=" + hints +
", isMultiDecode=" + isMultiDecode +
", isSupportLuminanceInvert=" + isSupportLuminanceInvert +
", isSupportLuminanceInvertMultiDecode=" + isSupportLuminanceInvertMultiDecode +
", isSupportVerticalCode=" + isSupportVerticalCode +
", isSupportVerticalCodeMultiDecode=" + isSupportVerticalCodeMultiDecode +
", analyzeAreaRect=" + analyzeAreaRect +
", isFullAreaScan=" + isFullAreaScan +
", areaRectRatio=" + areaRectRatio +
", areaRectVerticalOffset=" + areaRectVerticalOffset +
", areaRectHorizontalOffset=" + areaRectHorizontalOffset +
'}';
}
}

View File

@@ -1,105 +1,195 @@
package com.king.zxing;
/*
* 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.
*/
import android.content.Intent;
import android.net.Uri;
import com.google.zxing.BarcodeFormat;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
public final class DecodeFormatManager {
private static final Pattern COMMA_PATTERN = Pattern.compile(",");
public static final Set<BarcodeFormat> PRODUCT_FORMATS;
public static final Set<BarcodeFormat> INDUSTRIAL_FORMATS;
public static final Set<BarcodeFormat> ONE_D_FORMATS;
public static final Set<BarcodeFormat> QR_CODE_FORMATS = EnumSet.of(BarcodeFormat.QR_CODE);
public static final Set<BarcodeFormat> DATA_MATRIX_FORMATS = EnumSet.of(BarcodeFormat.DATA_MATRIX);
public static final Set<BarcodeFormat> AZTEC_FORMATS = EnumSet.of(BarcodeFormat.AZTEC);
public static final Set<BarcodeFormat> PDF417_FORMATS = EnumSet.of(BarcodeFormat.PDF_417);
static {
PRODUCT_FORMATS = EnumSet.of(BarcodeFormat.UPC_A,
BarcodeFormat.UPC_E,
BarcodeFormat.EAN_13,
BarcodeFormat.EAN_8,
BarcodeFormat.RSS_14,
BarcodeFormat.RSS_EXPANDED);
INDUSTRIAL_FORMATS = EnumSet.of(BarcodeFormat.CODE_39,
BarcodeFormat.CODE_93,
BarcodeFormat.CODE_128,
BarcodeFormat.ITF,
BarcodeFormat.CODABAR);
ONE_D_FORMATS = EnumSet.copyOf(PRODUCT_FORMATS);
ONE_D_FORMATS.addAll(INDUSTRIAL_FORMATS);
}
private static final Map<String,Set<BarcodeFormat>> FORMATS_FOR_MODE;
static {
FORMATS_FOR_MODE = new HashMap<>();
FORMATS_FOR_MODE.put(Intents.Scan.ONE_D_MODE, ONE_D_FORMATS);
FORMATS_FOR_MODE.put(Intents.Scan.PRODUCT_MODE, PRODUCT_FORMATS);
FORMATS_FOR_MODE.put(Intents.Scan.QR_CODE_MODE, QR_CODE_FORMATS);
FORMATS_FOR_MODE.put(Intents.Scan.DATA_MATRIX_MODE, DATA_MATRIX_FORMATS);
FORMATS_FOR_MODE.put(Intents.Scan.AZTEC_MODE, AZTEC_FORMATS);
FORMATS_FOR_MODE.put(Intents.Scan.PDF417_MODE, PDF417_FORMATS);
}
private DecodeFormatManager() {}
static Set<BarcodeFormat> parseDecodeFormats(Intent intent) {
Iterable<String> scanFormats = null;
CharSequence scanFormatsString = intent.getStringExtra(Intents.Scan.FORMATS);
if (scanFormatsString != null) {
scanFormats = Arrays.asList(COMMA_PATTERN.split(scanFormatsString));
}
return parseDecodeFormats(scanFormats, intent.getStringExtra(Intents.Scan.MODE));
}
static Set<BarcodeFormat> parseDecodeFormats(Uri inputUri) {
List<String> formats = inputUri.getQueryParameters(Intents.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 Set<BarcodeFormat> parseDecodeFormats(Iterable<String> scanFormats, String decodeMode) {
if (scanFormats != null) {
Set<BarcodeFormat> formats = EnumSet.noneOf(BarcodeFormat.class);
try {
for (String format : scanFormats) {
formats.add(BarcodeFormat.valueOf(format));
}
return formats;
} catch (IllegalArgumentException iae) {
// ignore it then
}
}
if (decodeMode != null) {
return FORMATS_FOR_MODE.get(decodeMode);
}
return null;
}
}
package com.king.zxing;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.DecodeHintType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import androidx.annotation.NonNull;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public final class DecodeFormatManager {
/**
* 所有的
*/
public static final Map<DecodeHintType,Object> ALL_HINTS = new EnumMap<>(DecodeHintType.class);
/**
* CODE_128 (最常用的一维码)
*/
public static final Map<DecodeHintType,Object> CODE_128_HINTS = createDecodeHint(BarcodeFormat.CODE_128);
/**
* QR_CODE (最常用的二维码)
*/
public static final Map<DecodeHintType,Object> QR_CODE_HINTS = createDecodeHint(BarcodeFormat.QR_CODE);
/**
* 一维码
*/
public static final Map<DecodeHintType,Object> ONE_DIMENSIONAL_HINTS = new EnumMap<>(DecodeHintType.class);
/**
* 二维码
*/
public static final Map<DecodeHintType,Object> TWO_DIMENSIONAL_HINTS = new EnumMap<>(DecodeHintType.class);
/**
* 默认
*/
public static final Map<DecodeHintType,Object> DEFAULT_HINTS = new EnumMap<>(DecodeHintType.class);
static {
//all hints
addDecodeHintTypes(ALL_HINTS,getAllFormats());
//one dimension
addDecodeHintTypes(ONE_DIMENSIONAL_HINTS,getOneDimensionalFormats());
//Two dimension
addDecodeHintTypes(TWO_DIMENSIONAL_HINTS,getTwoDimensionalFormats());
//default hints
addDecodeHintTypes(DEFAULT_HINTS,getDefaultFormats());
}
/**
* 所有支持的{@link BarcodeFormat}
* @return
*/
private static List<BarcodeFormat> getAllFormats(){
List<BarcodeFormat> list = new ArrayList<>();
list.add(BarcodeFormat.AZTEC);
list.add(BarcodeFormat.CODABAR);
list.add(BarcodeFormat.CODE_39);
list.add(BarcodeFormat.CODE_93);
list.add(BarcodeFormat.CODE_128);
list.add(BarcodeFormat.DATA_MATRIX);
list.add(BarcodeFormat.EAN_8);
list.add(BarcodeFormat.EAN_13);
list.add(BarcodeFormat.ITF);
list.add(BarcodeFormat.MAXICODE);
list.add(BarcodeFormat.PDF_417);
list.add(BarcodeFormat.QR_CODE);
list.add(BarcodeFormat.RSS_14);
list.add(BarcodeFormat.RSS_EXPANDED);
list.add(BarcodeFormat.UPC_A);
list.add(BarcodeFormat.UPC_E);
list.add(BarcodeFormat.UPC_EAN_EXTENSION);
return list;
}
/**
* 二维码
* 包括如下几种格式:
* {@link BarcodeFormat#CODABAR}
* {@link BarcodeFormat#CODE_39}
* {@link BarcodeFormat#CODE_93}
* {@link BarcodeFormat#CODE_128}
* {@link BarcodeFormat#EAN_8}
* {@link BarcodeFormat#EAN_13}
* {@link BarcodeFormat#ITF}
* {@link BarcodeFormat#RSS_14}
* {@link BarcodeFormat#RSS_EXPANDED}
* {@link BarcodeFormat#UPC_A}
* {@link BarcodeFormat#UPC_E}
* {@link BarcodeFormat#UPC_EAN_EXTENSION}
* @return
*/
private static List<BarcodeFormat> getOneDimensionalFormats(){
List<BarcodeFormat> list = new ArrayList<>();
list.add(BarcodeFormat.CODABAR);
list.add(BarcodeFormat.CODE_39);
list.add(BarcodeFormat.CODE_93);
list.add(BarcodeFormat.CODE_128);
list.add(BarcodeFormat.EAN_8);
list.add(BarcodeFormat.EAN_13);
list.add(BarcodeFormat.ITF);
list.add(BarcodeFormat.RSS_14);
list.add(BarcodeFormat.RSS_EXPANDED);
list.add(BarcodeFormat.UPC_A);
list.add(BarcodeFormat.UPC_E);
list.add(BarcodeFormat.UPC_EAN_EXTENSION);
return list;
}
/**
* 二维码
* 包括如下几种格式:
* {@link BarcodeFormat#AZTEC}
* {@link BarcodeFormat#DATA_MATRIX}
* {@link BarcodeFormat#MAXICODE}
* {@link BarcodeFormat#PDF_417}
* {@link BarcodeFormat#QR_CODE}
* @return
*/
private static List<BarcodeFormat> getTwoDimensionalFormats(){
List<BarcodeFormat> list = new ArrayList<>();
list.add(BarcodeFormat.AZTEC);
list.add(BarcodeFormat.DATA_MATRIX);
list.add(BarcodeFormat.MAXICODE);
list.add(BarcodeFormat.PDF_417);
list.add(BarcodeFormat.QR_CODE);
return list;
}
/**
* 默认支持的格式
* 包括如下几种格式:
* {@link BarcodeFormat#QR_CODE}
* {@link BarcodeFormat#UPC_A}
* {@link BarcodeFormat#EAN_13}
* {@link BarcodeFormat#CODE_128}
* @return
*/
private static List<BarcodeFormat> getDefaultFormats(){
List<BarcodeFormat> list = new ArrayList<>();
list.add(BarcodeFormat.QR_CODE);
list.add(BarcodeFormat.UPC_A);
list.add(BarcodeFormat.EAN_13);
list.add(BarcodeFormat.CODE_128);
return list;
}
private static <T> List<T> singletonList(T o){
return Collections.singletonList(o);
}
/**
* 支持解码的格式
* @param barcodeFormats {@link BarcodeFormat}
* @return
*/
public static Map<DecodeHintType,Object> createDecodeHints(@NonNull BarcodeFormat... barcodeFormats){
Map<DecodeHintType,Object> hints = new EnumMap<>(DecodeHintType.class);
addDecodeHintTypes(hints, Arrays.asList(barcodeFormats));
return hints;
}
/**
* 支持解码的格式
* @param barcodeFormat {@link BarcodeFormat}
* @return
*/
public static Map<DecodeHintType,Object> createDecodeHint(@NonNull BarcodeFormat barcodeFormat){
Map<DecodeHintType,Object> hints = new EnumMap<>(DecodeHintType.class);
addDecodeHintTypes(hints,singletonList(barcodeFormat));
return hints;
}
/**
*
* @param hints
* @param formats
*/
private static void addDecodeHintTypes(Map<DecodeHintType,Object> hints,List<BarcodeFormat> formats){
// Image is known to be of one of a few possible formats.
hints.put(DecodeHintType.POSSIBLE_FORMATS, formats);
// Spend more time to try to find a barcode; optimize for accuracy, not speed.
hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
// Specifies what character encoding to use when decoding, where applicable (type String)
hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
}
}

View File

@@ -1,250 +0,0 @@
package com.king.zxing;
/*
* 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.
*/
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.hardware.Camera;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.Display;
import android.view.WindowManager;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.Result;
import com.google.zxing.ResultPoint;
import com.google.zxing.common.GlobalHistogramBinarizer;
import com.google.zxing.common.HybridBinarizer;
import com.king.zxing.camera.CameraManager;
import com.king.zxing.util.LogUtils;
import java.io.ByteArrayOutputStream;
import java.util.Map;
final class DecodeHandler extends Handler {
private final Context context;
private final CameraManager cameraManager;
private final CaptureHandler handler;
private final MultiFormatReader multiFormatReader;
private boolean running = true;
private long lastZoomTime;
DecodeHandler(Context context, CameraManager cameraManager,CaptureHandler handler, Map<DecodeHintType,Object> hints) {
multiFormatReader = new MultiFormatReader();
multiFormatReader.setHints(hints);
this.context = context;
this.cameraManager = cameraManager;
this.handler = handler;
}
@Override
public void handleMessage(Message message) {
if (message == null || !running) {
return;
}
if (message.what == R.id.decode) {
decode((byte[]) message.obj, message.arg1, message.arg2,isScreenPortrait(),handler.isSupportVerticalCode());
} else if (message.what == R.id.quit) {
running = false;
Looper.myLooper().quit();
}
}
private boolean isScreenPortrait(){
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = manager.getDefaultDisplay();
Point screenResolution = new Point();
display.getSize(screenResolution);
return screenResolution.x < screenResolution.y;
}
/**
* 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,boolean isScreenPortrait,boolean isSupportVerticalCode) {
long start = System.currentTimeMillis();
Result rawResult = null;
PlanarYUVLuminanceSource source = buildPlanarYUVLuminanceSource(data,width,height,isScreenPortrait);
if (source != null) {
boolean isReDecode;
try {
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
rawResult = multiFormatReader.decodeWithState(bitmap);
isReDecode = false;
} catch (Exception e) {
isReDecode = true;
}
if(isReDecode && handler.isSupportLuminanceInvert()){
try {
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source.invert()));
rawResult = multiFormatReader.decodeWithState(bitmap);
isReDecode = false;
} catch (Exception e) {
isReDecode = true;
}
}
if(isReDecode){
try{
BinaryBitmap bitmap = new BinaryBitmap(new GlobalHistogramBinarizer(source));
rawResult = multiFormatReader.decodeWithState(bitmap);
isReDecode = false;
}catch (Exception e){
isReDecode = true;
}
}
if(isReDecode && isSupportVerticalCode){
source = buildPlanarYUVLuminanceSource(data,width,height,!isScreenPortrait);
if(source!=null){
try{
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
rawResult = multiFormatReader.decodeWithState(bitmap);
}catch (Exception e){
}
}
}
multiFormatReader.reset();
}
if (rawResult != null) {
// Don't log the barcode contents for security.
long end = System.currentTimeMillis();
LogUtils.d("Found barcode in " + (end - start) + " ms");
if(handler.isSupportAutoZoom()){//是否支持自动放大
ResultPoint[] resultPoints = rawResult.getResultPoints();
LogUtils.d("resultPoints:" +resultPoints.length);
final int length = resultPoints.length;
if(length >= 2){//超过两个点则计算距离
int ratio = 6;
int maxDistance = (int)ResultPoint.distance(resultPoints[0],resultPoints[1]);
if(length >= 3){
float distance2 = ResultPoint.distance(resultPoints[1],resultPoints[2]);
float distance3 = ResultPoint.distance(resultPoints[0],resultPoints[2]);
maxDistance = (int)Math.max(Math.max(maxDistance,distance2),distance3);
ratio = 5;
}
if(handleAutoZoom(maxDistance,width,ratio)){//根据点之间的最大距离
Message message = Message.obtain();
message.what = R.id.decode_succeeded;
message.obj = rawResult;
if(handler.isReturnBitmap()){
Bundle bundle = new Bundle();
bundleThumbnail(source, bundle);
message.setData(bundle);
}
handler.sendMessageDelayed(message,300);
return;
}
}
}
Message message = Message.obtain(handler, R.id.decode_succeeded, rawResult);
if(handler.isReturnBitmap()){
Bundle bundle = new Bundle();
bundleThumbnail(source, bundle);
message.setData(bundle);
}
message.sendToTarget();
} else {
Message message = Message.obtain(handler, R.id.decode_failed);
message.sendToTarget();
}
}
private PlanarYUVLuminanceSource buildPlanarYUVLuminanceSource(byte[] data, int width, int height,boolean isRotate){
PlanarYUVLuminanceSource source;
if(isRotate){
byte[] rotatedData = new byte[data.length];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++)
rotatedData[x * height + height - y - 1] = data[x + y * width];
}
int tmp = width;
width = height;
height = tmp;
source = cameraManager.buildLuminanceSource(rotatedData, width, height);
}else{
source = cameraManager.buildLuminanceSource(data, width, height);
}
return source;
}
private static void bundleThumbnail(PlanarYUVLuminanceSource source, Bundle bundle) {
int[] pixels = source.renderThumbnail();
int width = source.getThumbnailWidth();
int height = source.getThumbnailHeight();
Bitmap bitmap = Bitmap.createBitmap(pixels, 0, width, width, height, Bitmap.Config.ARGB_8888);
ByteArrayOutputStream out = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, out);
bundle.putByteArray(DecodeThread.BARCODE_BITMAP, out.toByteArray());
bundle.putFloat(DecodeThread.BARCODE_SCALED_FACTOR, (float) width / source.getWidth());
}
private boolean handleAutoZoom(int length,int width,int ratio){
if(lastZoomTime > System.currentTimeMillis() - 1000){
return false;
}
if(length < width / ratio){
Camera camera = cameraManager.getOpenCamera().getCamera();
if(camera!=null){
Camera.Parameters params = camera.getParameters();
if (params.isZoomSupported()) {
int maxZoom = params.getMaxZoom();
int zoom = params.getZoom();
params.setZoom(Math.min(zoom + maxZoom/5,maxZoom));
camera.setParameters(params);
lastZoomTime = System.currentTimeMillis();
return true;
} else {
LogUtils.d("Zoom not supported");
}
}
}
return false;
}
}

View File

@@ -1,121 +0,0 @@
package com.king.zxing;
/*
* 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.
*/
import com.google.zxing.BarcodeFormat;
import com.google.zxing.DecodeHintType;
import com.google.zxing.ResultPointCallback;
import com.king.zxing.camera.CameraManager;
import com.king.zxing.util.LogUtils;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.Looper;
import android.preference.PreferenceManager;
import java.util.Collection;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Map;
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";
public static final String BARCODE_SCALED_FACTOR = "barcode_scaled_factor";
private final Context context;
private final CameraManager cameraManager;
private final Map<DecodeHintType,Object> hints;
private Handler handler;
private CaptureHandler captureHandler;
private final CountDownLatch handlerInitLatch;
DecodeThread(Context context,CameraManager cameraManager,
CaptureHandler captureHandler,
Collection<BarcodeFormat> decodeFormats,
Map<DecodeHintType,Object> baseHints,
String characterSet,
ResultPointCallback resultPointCallback) {
this.context = context;
this.cameraManager = cameraManager;
this.captureHandler = captureHandler;
handlerInitLatch = new CountDownLatch(1);
hints = new EnumMap<>(DecodeHintType.class);
if (baseHints != null) {
hints.putAll(baseHints);
}
// 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(context);
decodeFormats = EnumSet.noneOf(BarcodeFormat.class);
if (prefs.getBoolean(Preferences.KEY_DECODE_1D_PRODUCT, true)) {
decodeFormats.addAll(DecodeFormatManager.PRODUCT_FORMATS);
}
if (prefs.getBoolean(Preferences.KEY_DECODE_1D_INDUSTRIAL, true)) {
decodeFormats.addAll(DecodeFormatManager.INDUSTRIAL_FORMATS);
}
if (prefs.getBoolean(Preferences.KEY_DECODE_QR, true)) {
decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
}
if (prefs.getBoolean(Preferences.KEY_DECODE_DATA_MATRIX, true)) {
decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);
}
if (prefs.getBoolean(Preferences.KEY_DECODE_AZTEC, false)) {
decodeFormats.addAll(DecodeFormatManager.AZTEC_FORMATS);
}
if (prefs.getBoolean(Preferences.KEY_DECODE_PDF417, false)) {
decodeFormats.addAll(DecodeFormatManager.PDF417_FORMATS);
}
}
hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
if (characterSet != null) {
hints.put(DecodeHintType.CHARACTER_SET, characterSet);
}
hints.put(DecodeHintType.NEED_RESULT_POINT_CALLBACK, resultPointCallback);
LogUtils.i("Hints: " + hints);
}
Handler getHandler() {
try {
handlerInitLatch.await();
} catch (InterruptedException ie) {
// continue?
}
return handler;
}
@Override
public void run() {
Looper.prepare();
handler = new DecodeHandler(context,cameraManager,captureHandler, hints);
handlerInitLatch.countDown();
Looper.loop();
}
}

View File

@@ -0,0 +1,404 @@
package com.king.zxing;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.Result;
import com.google.zxing.ResultPoint;
import com.king.zxing.analyze.Analyzer;
import com.king.zxing.analyze.MultiFormatAnalyzer;
import com.king.zxing.util.LogUtils;
import java.util.concurrent.Executors;
import androidx.annotation.FloatRange;
import androidx.annotation.Nullable;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.Preview;
import androidx.camera.core.TorchState;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.view.PreviewView;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.MutableLiveData;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class DefaultCameraScan extends CameraScan {
private FragmentActivity mFragmentActivity;
private Context mContext;
private LifecycleOwner mLifecycleOwner;
private PreviewView mPreviewView;
private ListenableFuture<ProcessCameraProvider> mCameraProviderFuture;
private Camera mCamera;
private CameraConfig mCameraConfig;
private Analyzer mAnalyzer;
/**
* 是否分析
*/
private volatile boolean isAnalyze = true;
/**
* 是否已经分析出结果
*/
private volatile boolean isAnalyzeResult;
private View flashlightView;
private MutableLiveData<Result> mResultLiveData;
private OnScanResultCallback mOnScanResultCallback;
private BeepManager mBeepManager;
private AmbientLightManager mAmbientLightManager;
private int mScreenWidth;
private int mScreenHeight;
private long mLastAutoZoomTime;
public DefaultCameraScan(FragmentActivity activity, PreviewView previewView){
this.mFragmentActivity = activity;
this.mLifecycleOwner = activity;
this.mContext = activity;
this.mPreviewView = previewView;
initData();
}
public DefaultCameraScan(Fragment fragment, PreviewView previewView){
this.mFragmentActivity = fragment.getActivity();
this.mLifecycleOwner = fragment;
this.mContext = fragment.getContext();
this.mPreviewView = previewView;
initData();
}
private void initData(){
mResultLiveData = new MutableLiveData<>();
mResultLiveData.observe(mLifecycleOwner, result -> {
handleAnalyzeResult(result);
});
mPreviewView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LogUtils.d("click");
}
});
mPreviewView.setOnTouchListener((v, event) -> onTouchEvent(event));
DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
mScreenWidth = displayMetrics.widthPixels;
mScreenHeight = displayMetrics.heightPixels;
mBeepManager = new BeepManager(mContext);
mAmbientLightManager = new AmbientLightManager(mContext);
if(mAmbientLightManager != null){
mAmbientLightManager.register();
mAmbientLightManager.setOnLightSensorEventListener((dark, lightLux) -> {
if(flashlightView != null){
flashlightView.setSelected(!dark);
if(dark){
if(flashlightView.getVisibility() != View.VISIBLE){
flashlightView.setVisibility(View.VISIBLE);
}
}else if(flashlightView.getVisibility() == View.VISIBLE){
flashlightView.setVisibility(View.INVISIBLE);
}
}
});
}
}
private void initConfig(){
if(mCameraConfig == null){
mCameraConfig = new CameraConfig();
}
if(mAnalyzer == null){
mAnalyzer = new MultiFormatAnalyzer();
}
}
@Override
public CameraScan setCameraConfig(CameraConfig cameraConfig) {
if(cameraConfig != null){
this.mCameraConfig = cameraConfig;
}
return this;
}
@Override
public void startCamera(){
initConfig();
mCameraProviderFuture = ProcessCameraProvider.getInstance(mContext);
mCameraProviderFuture.addListener(() -> {
try{
Preview preview = mCameraConfig.options(new Preview.Builder());
//相机选择器
CameraSelector cameraSelector = mCameraConfig.options(new CameraSelector.Builder()
.requireLensFacing(LENS_FACING_BACK));
//设置SurfaceProvider
preview.setSurfaceProvider(mPreviewView.getSurfaceProvider());
//图像分析
ImageAnalysis imageAnalysis = mCameraConfig.options(new ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST));
imageAnalysis.setAnalyzer(Executors.newSingleThreadExecutor(), image -> {
if(isAnalyze && !isAnalyzeResult && mAnalyzer != null){
Result result = mAnalyzer.analyze(image);
if(result != null){
mResultLiveData.postValue(result);
}
}
image.close();
});
if(mCamera != null){
mCameraProviderFuture.get().unbindAll();
}
//绑定到生命周期
mCamera = mCameraProviderFuture.get().bindToLifecycle(mLifecycleOwner, cameraSelector, preview, imageAnalysis);
}catch (Exception e){
LogUtils.e(e);
}
},ContextCompat.getMainExecutor(mContext));
}
/**
* 处理分析结果
* @param result
*/
private synchronized void handleAnalyzeResult(Result result){
if(isAnalyzeResult || !isAnalyze){
return;
}
isAnalyzeResult = true;
if(mBeepManager != null){
mBeepManager.playBeepSoundAndVibrate();
}
if(result.getBarcodeFormat() == BarcodeFormat.QR_CODE && isNeedAutoZoom() && mLastAutoZoomTime + 100 < System.currentTimeMillis()){
ResultPoint[] points = result.getResultPoints();
if(points != null && points.length >= 2){
float distance1 = ResultPoint.distance(points[0],points[1]);
float maxDistance = distance1;
if(points.length >= 3){
float distance2 = ResultPoint.distance(points[1],points[2]);
float distance3 = ResultPoint.distance(points[0],points[2]);
maxDistance = Math.max(Math.max(distance1,distance2),distance3);
}
if(handleAutoZoom((int)maxDistance,result)){
return;
}
}
}
scanResultCallback(result);
}
private boolean handleAutoZoom(int distance,Result result){
int size = Math.min(mScreenWidth,mScreenHeight);
if(distance * 4 < size){
mLastAutoZoomTime = System.currentTimeMillis();
zoomIn();
scanResultCallback(result);
return true;
}
return false;
}
private void scanResultCallback(Result result){
if(mOnScanResultCallback != null && mOnScanResultCallback.onScanResultCallback(result)){
//如果拦截了结果,则重置分析结果状态,直接可以连扫
isAnalyzeResult = false;
return;
}
if(mFragmentActivity != null){
Intent intent = new Intent();
intent.putExtra(SCAN_RESULT,result.getText());
mFragmentActivity.setResult(Activity.RESULT_OK,intent);
mFragmentActivity.finish();
}
}
@Override
public void stopCamera(){
if(mCameraProviderFuture != null){
try {
mCameraProviderFuture.get().unbindAll();
}catch (Exception e){
LogUtils.e(e);
}
}
}
@Override
public CameraScan setAnalyzeImage(boolean analyze) {
isAnalyze = analyze;
return this;
}
/**
* 设置分析器,如果内置的一些分析器不满足您的需求,你也可以自定义{@link Analyzer}
* 自定义时,切记需在{@link #startCamera()}之前调用才有效
* @param analyzer
*/
@Override
public CameraScan setAnalyzer(Analyzer analyzer) {
mAnalyzer = analyzer;
return this;
}
@Override
public void zoomIn(){
if(mCamera != null){
float ratio = mCamera.getCameraInfo().getZoomState().getValue().getZoomRatio() + 0.1f;
float maxRatio = mCamera.getCameraInfo().getZoomState().getValue().getMaxZoomRatio();
if(ratio <= maxRatio){
mCamera.getCameraControl().setZoomRatio(ratio);
}
}
}
@Override
public void zoomOut(){
if(mCamera != null){
float ratio = mCamera.getCameraInfo().getZoomState().getValue().getZoomRatio() - 0.1f;
float minRatio = mCamera.getCameraInfo().getZoomState().getValue().getMinZoomRatio();
if(ratio >= minRatio){
mCamera.getCameraControl().setZoomRatio(ratio);
}
}
}
@Override
public void zoomTo(float ratio) {
if(mCamera != null){
mCamera.getCameraControl().setZoomRatio(ratio);
}
}
@Override
public void lineZoomIn() {
if(mCamera != null){
float zoom = mCamera.getCameraInfo().getZoomState().getValue().getLinearZoom() + 0.1f;
if(zoom <= 1f){
mCamera.getCameraControl().setLinearZoom(zoom);
}
}
}
@Override
public void lineZoomOut() {
if(mCamera != null){
float zoom = mCamera.getCameraInfo().getZoomState().getValue().getLinearZoom() - 0.1f;
if(zoom >= 0f){
mCamera.getCameraControl().setLinearZoom(zoom);
}
}
}
@Override
public void lineZoomTo(@FloatRange(from = 0.0,to = 1.0) float linearZoom) {
if(mCamera != null){
mCamera.getCameraControl().setLinearZoom(linearZoom);
}
}
@Override
public CameraScan enableTorch(boolean torch) {
if(mCamera != null && hasFlashUnit()){
mCamera.getCameraControl().enableTorch(torch);
}
return this;
}
@Override
public boolean isTorchEnabled() {
if(mCamera != null){
return mCamera.getCameraInfo().getTorchState().getValue() == TorchState.ON;
}
return false;
}
/**
* 是否支持闪光灯
* @return
*/
@Override
public boolean hasFlashUnit(){
if(mCamera != null){
return mCamera.getCameraInfo().hasFlashUnit();
}
return false;
}
@Override
public CameraScan setVibrate(boolean vibrate) {
if(mBeepManager != null){
mBeepManager.setVibrate(vibrate);
}
return this;
}
@Override
public CameraScan setPlayBeep(boolean playBeep) {
if(mBeepManager != null){
mBeepManager.setPlayBeep(playBeep);
}
return this;
}
@Override
public CameraScan setOnScanResultCallback(OnScanResultCallback callback) {
this.mOnScanResultCallback = callback;
return this;
}
@Nullable
@Override
public Camera getCamera(){
return mCamera;
}
@Override
public void release() {
isAnalyze = false;
flashlightView = null;
if(mAmbientLightManager != null){
mAmbientLightManager.unregister();
}
if(mBeepManager != null){
mBeepManager.close();
}
stopCamera();
}
@Override
public CameraScan bindFlashlightView(@Nullable View v) {
flashlightView = v;
if(mAmbientLightManager != null){
mAmbientLightManager.setLightSensorEnabled(v != null);
}
return this;
}
}

View File

@@ -0,0 +1,69 @@
package com.king.zxing;
import android.view.MotionEvent;
import com.google.zxing.Result;
import androidx.annotation.FloatRange;
import androidx.annotation.Nullable;
import androidx.camera.core.Camera;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public interface ICameraScan {
/**
* 启动相机预览
*/
void startCamera();
/**
* 停止相机预览
*/
void stopCamera();
/**
* 放大
*/
void zoomIn();
/**
* 缩小
*/
void zoomOut();
/**
* 缩放到指定比例
* @param ratio
*/
void zoomTo(float ratio);
/**
* 放大
*/
void lineZoomIn();
/**
* 缩小
*/
void lineZoomOut();
/**
* 线性缩放到指定比例
* @param linearZoom
*/
void lineZoomTo(@FloatRange(from = 0.0,to = 1.0) float linearZoom);
/**
* 获取{@link Camera}
* @return
*/
@Nullable Camera getCamera();
void release();
}

View File

@@ -1,144 +0,0 @@
package com.king.zxing;
/*
* 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.
*/
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncTask;
import android.os.BatteryManager;
import com.king.zxing.util.LogUtils;
import java.lang.ref.WeakReference;
import java.util.concurrent.RejectedExecutionException;
/**
* Finishes an activity after a period of inactivity if the device is on battery power.
*/
final class InactivityTimer {
private static final long INACTIVITY_DELAY_MS = 5 * 60 * 1000L;
private final Activity activity;
private final BroadcastReceiver powerStatusReceiver;
private boolean registered;
private AsyncTask<Object,Object,Object> inactivityTask;
InactivityTimer(Activity activity) {
this.activity = activity;
powerStatusReceiver = new PowerStatusReceiver(this);
registered = false;
onActivity();
}
void onActivity() {
cancel();
inactivityTask = new InactivityAsyncTask(activity);
try {
inactivityTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} catch (RejectedExecutionException ree) {
LogUtils.w( "Couldn't schedule inactivity task; ignoring");
}
}
void onPause() {
cancel();
if (registered) {
activity.unregisterReceiver(powerStatusReceiver);
registered = false;
} else {
LogUtils.w( "PowerStatusReceiver was never registered?");
}
}
void onResume() {
if (registered) {
LogUtils.w( "PowerStatusReceiver was already registered?");
} else {
activity.registerReceiver(powerStatusReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
registered = true;
}
onActivity();
}
private void cancel() {
AsyncTask<?,?,?> task = inactivityTask;
if (task != null) {
task.cancel(true);
inactivityTask = null;
}
}
void shutdown() {
cancel();
}
private static class PowerStatusReceiver extends BroadcastReceiver {
private WeakReference<InactivityTimer> weakReference;
public PowerStatusReceiver(InactivityTimer inactivityTimer){
weakReference = new WeakReference<>(inactivityTimer);
}
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
// 0 indicates that we're on battery
InactivityTimer inactivityTimer = weakReference.get();
if(inactivityTimer != null){
boolean onBatteryNow = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) <= 0;
if (onBatteryNow) {
inactivityTimer.onActivity();
} else {
inactivityTimer.cancel();
}
}
}
}
}
private static class InactivityAsyncTask extends AsyncTask<Object,Object,Object> {
private WeakReference<Activity> weakReference;
public InactivityAsyncTask(Activity activity){
weakReference = new WeakReference<>(activity);
}
@Override
protected Object doInBackground(Object... objects) {
try {
Thread.sleep(INACTIVITY_DELAY_MS);
LogUtils.i("Finishing activity due to inactivity");
Activity activity = weakReference.get();
if(activity!=null){
activity.finish();
}
} catch (InterruptedException e) {
// continue without killing
}
return null;
}
}
}

View File

@@ -1,303 +0,0 @@
package com.king.zxing;
/*
* 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.
*/
import android.content.Intent;
/**
* 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() {
}
/**
* Constants related to the {@link Scan#ACTION} Intent.
*/
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 this will decode all barcodes that we understand. However it
* may be useful to limit scanning to certain formats. Use
* {@link android.content.Intent#putExtra(String, String)} with one of the values below.
*
* Setting this is effectively shorthand for setting explicit formats with {@link #FORMATS}.
* It is overridden by that setting.
*/
public static final String MODE = "SCAN_MODE";
/**
* 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.
*/
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";
/**
* Decode only Aztec.
*/
public static final String AZTEC_MODE = "AZTEC_MODE";
/**
* Decode only PDF417.
*/
public static final String PDF417_MODE = "PDF417_MODE";
/**
* Comma-separated list of formats to scan for. The values must match the names of
* {@link com.google.zxing.BarcodeFormat}s, e.g. {@link com.google.zxing.BarcodeFormat#EAN_13}.
* Example: "EAN_13,EAN_8,QR_CODE". This overrides {@link #MODE}.
*/
public static final String FORMATS = "SCAN_FORMATS";
/**
* Optional parameter to specify the id of the camera from which to recognize barcodes.
* Overrides the default camera that would otherwise would have been selected.
* If provided, should be an int.
*/
public static final String CAMERA_ID = "SCAN_CAMERA_ID";
/**
* @see com.google.zxing.DecodeHintType#CHARACTER_SET
*/
public static final String CHARACTER_SET = "CHARACTER_SET";
/**
* Optional parameters to specify the width and height of the scanning rectangle in pixels.
* The app will try to honor these, but will clamp them to the size of the preview frame.
* You should specify both or neither, and pass the size as an int.
*/
public static final String WIDTH = "SCAN_WIDTH";
public static final String HEIGHT = "SCAN_HEIGHT";
/**
* Desired duration in milliseconds for which to pause after a successful scan before
* returning to the calling intent. Specified as a long, not an integer!
* For example: 1000L, not 1000.
*/
public static final String RESULT_DISPLAY_DURATION_MS = "RESULT_DISPLAY_DURATION_MS";
/**
* Prompt to show on-screen when scanning by intent. Specified as a {@link String}.
*/
public static final String PROMPT_MESSAGE = "PROMPT_MESSAGE";
/**
* If a barcode is found, Barcodes returns {@link android.app.Activity#RESULT_OK} to
* {@link android.app.Activity#onActivityResult(int, int, android.content.Intent)}
* of the app which requested the scan via
* {@link android.app.Activity#startActivityForResult(android.content.Intent, int)}
* The barcodes contents can be retrieved with
* {@link android.content.Intent#getStringExtra(String)}.
* If the user presses Back, the result code will be {@link android.app.Activity#RESULT_CANCELED}.
*/
public static final String RESULT = "SCAN_RESULT";
/**
* Call {@link android.content.Intent#getStringExtra(String)} with {@code RESULT_FORMAT}
* to determine which barcode format was found.
* See {@link com.google.zxing.BarcodeFormat} for possible values.
*/
public static final String RESULT_FORMAT = "SCAN_RESULT_FORMAT";
/**
* Call {@link android.content.Intent#getStringExtra(String)} with {@code RESULT_UPC_EAN_EXTENSION}
* to return the content of any UPC extension barcode that was also found. Only applicable
* to {@link com.google.zxing.BarcodeFormat#UPC_A} and {@link com.google.zxing.BarcodeFormat#EAN_13}
* formats.
*/
public static final String RESULT_UPC_EAN_EXTENSION = "SCAN_RESULT_UPC_EAN_EXTENSION";
/**
* Call {@link android.content.Intent#getByteArrayExtra(String)} with {@code RESULT_BYTES}
* to get a {@code byte[]} of raw bytes in the barcode, if available.
*/
public static final String RESULT_BYTES = "SCAN_RESULT_BYTES";
/**
* Key for the value of {@link com.google.zxing.ResultMetadataType#ORIENTATION}, if available.
* Call {@link android.content.Intent#getIntArrayExtra(String)} with {@code RESULT_ORIENTATION}.
*/
public static final String RESULT_ORIENTATION = "SCAN_RESULT_ORIENTATION";
/**
* Key for the value of {@link com.google.zxing.ResultMetadataType#ERROR_CORRECTION_LEVEL}, if available.
* Call {@link android.content.Intent#getStringExtra(String)} with {@code RESULT_ERROR_CORRECTION_LEVEL}.
*/
public static final String RESULT_ERROR_CORRECTION_LEVEL = "SCAN_RESULT_ERROR_CORRECTION_LEVEL";
/**
* Prefix for keys that map to the values of {@link com.google.zxing.ResultMetadataType#BYTE_SEGMENTS},
* if available. The actual values will be set under a series of keys formed by adding 0, 1, 2, ...
* to this prefix. So the first byte segment is under key "SCAN_RESULT_BYTE_SEGMENTS_0" for example.
* Call {@link android.content.Intent#getByteArrayExtra(String)} with these keys.
*/
public static final String RESULT_BYTE_SEGMENTS_PREFIX = "SCAN_RESULT_BYTE_SEGMENTS_";
/**
* Setting this to false will not save scanned codes in the history. Specified as a {@code boolean}.
*/
public static final String SAVE_HISTORY = "SAVE_HISTORY";
private Scan() {
}
}
/**
* Constants related to the scan history and retrieving history items.
*/
public static final class History {
public static final String ITEM_NUMBER = "ITEM_NUMBER";
private History() {
}
}
/**
* Constants related to the {@link Encode#ACTION} Intent.
*/
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 {@link android.content.Intent#putExtra(String, String)} or
* {@link android.content.Intent#putExtra(String, android.os.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
* {@link android.content.Intent#putExtra(String, String)} 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 {@link android.content.Intent#putExtra(String, String)}, where
* format is one of {@link com.google.zxing.BarcodeFormat}.
*/
public static final String FORMAT = "ENCODE_FORMAT";
/**
* Normally the contents of the barcode are displayed to the user in a TextView. Setting this
* boolean to false will hide that TextView, showing only the encode barcode.
*/
public static final String SHOW_CONTENTS = "ENCODE_SHOW_CONTENTS";
private Encode() {
}
}
/**
* Constants related to the {@link SearchBookContents#ACTION} Intent.
*/
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() {
}
}
/**
* Constants related to the {@link WifiConnect#ACTION} Intent.
*/
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() {
}
}
/**
* Constants related to the {@link Share#ACTION} Intent.
*/
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() {
}
}
// Not the best place for this, but, better than a new class
// Should be FLAG_ACTIVITY_NEW_DOCUMENT in API 21+.
// Defined once here because the current value is deprecated, so generates just one warning
public static final int FLAG_NEW_DOC = Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET;
}

View File

@@ -1,17 +0,0 @@
package com.king.zxing;
import com.google.zxing.Result;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public interface OnCaptureCallback {
/**
* 接收扫码结果回调
* @param result 扫码结果
* @return 返回true表示拦截将不自动执行后续逻辑为false表示不拦截
*/
boolean onResultCallback(Result result);
}

View File

@@ -1,22 +0,0 @@
package com.king.zxing;
import android.graphics.Bitmap;
import com.google.zxing.Result;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public interface OnCaptureListener {
/**
* 接收解码后的扫码结果
* @param result
* @param barcode
* @param scaleFactor
*/
void onHandleDecode(Result result, Bitmap barcode, float scaleFactor);
}

View File

@@ -1,50 +0,0 @@
package com.king.zxing;
/*
* 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.
*/
public final class Preferences {
public static final String KEY_DECODE_1D_PRODUCT = "preferences_decode_1D_product";
public static final String KEY_DECODE_1D_INDUSTRIAL = "preferences_decode_1D_industrial";
public static final String KEY_DECODE_QR = "preferences_decode_QR";
public static final String KEY_DECODE_DATA_MATRIX = "preferences_decode_Data_Matrix";
public static final String KEY_DECODE_AZTEC = "preferences_decode_Aztec";
public static final String KEY_DECODE_PDF417 = "preferences_decode_PDF417";
public static final String KEY_CUSTOM_PRODUCT_SEARCH = "preferences_custom_product_search";
public static final String KEY_PLAY_BEEP = "preferences_play_beep";
public static final String KEY_VIBRATE = "preferences_vibrate";
public static final String KEY_COPY_TO_CLIPBOARD = "preferences_copy_to_clipboard";
public static final String KEY_FRONT_LIGHT_MODE = "preferences_front_light_mode";
public static final String KEY_BULK_MODE = "preferences_bulk_mode";
public static final String KEY_REMEMBER_DUPLICATES = "preferences_remember_duplicates";
public static final String KEY_ENABLE_HISTORY = "preferences_history";
public static final String KEY_SUPPLEMENTAL = "preferences_supplemental";
public static final String KEY_AUTO_FOCUS = "preferences_auto_focus";
public static final String KEY_INVERT_SCAN = "preferences_invert_scan";
public static final String KEY_SEARCH_COUNTRY = "preferences_search_country";
public static final String KEY_DISABLE_AUTO_ORIENTATION = "preferences_orientation";
public static final String KEY_DISABLE_CONTINUOUS_FOCUS = "preferences_disable_continuous_focus";
public static final String KEY_DISABLE_EXPOSURE = "preferences_disable_exposure";
public static final String KEY_DISABLE_METERING = "preferences_disable_metering";
public static final String KEY_DISABLE_BARCODE_SCENE_MODE = "preferences_disable_barcode_scene_mode";
public static final String KEY_AUTO_OPEN_WEB = "preferences_auto_open_web";
}

View File

@@ -48,7 +48,7 @@ import androidx.core.content.ContextCompat;
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public final class ViewfinderView extends View {
public class ViewfinderView extends View {
private static final int CURRENT_POINT_OPACITY = 0xA0;
private static final int MAX_RESULT_POINTS = 20;
@@ -185,6 +185,10 @@ public final class ViewfinderView extends View {
*/
private float frameRatio;
private float framePaddingLeft;
private float framePaddingTop;
private float framePaddingRight;
private float framePaddingBottom;
private List<ResultPoint> possibleResultPoints;
private List<ResultPoint> lastPossibleResultPoints;
@@ -276,6 +280,10 @@ public final class ViewfinderView extends View {
frameLineWidth = (int)array.getDimension(R.styleable.ViewfinderView_frameLineWidth,TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,1,getResources().getDisplayMetrics()));
scannerAnimationDelay = array.getInteger(R.styleable.ViewfinderView_scannerAnimationDelay,20);
frameRatio = array.getFloat(R.styleable.ViewfinderView_frameRatio,0.625f);
framePaddingLeft = array.getDimension(R.styleable.ViewfinderView_framePaddingLeft,0);
framePaddingTop = array.getDimension(R.styleable.ViewfinderView_framePaddingTop,0);
framePaddingRight = array.getDimension(R.styleable.ViewfinderView_framePaddingRight,0);
framePaddingBottom = array.getDimension(R.styleable.ViewfinderView_framePaddingBottom,0);
array.recycle();
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -323,8 +331,8 @@ public final class ViewfinderView extends View {
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//扫码框默认居中,支持利用内距偏移扫码框
int leftOffset = (screenWidth - frameWidth) / 2 + getPaddingLeft() - getPaddingRight();
int topOffset = (screenHeight - frameHeight) / 2 + getPaddingTop() - getPaddingBottom();
int leftOffset = (int)((screenWidth - frameWidth) / 2 + framePaddingLeft - framePaddingRight);
int topOffset = (int)((screenHeight - frameHeight) / 2 + framePaddingTop - framePaddingBottom);
frame = new Rect(leftOffset, topOffset, leftOffset + frameWidth, topOffset + frameHeight);
}

View File

@@ -0,0 +1,17 @@
package com.king.zxing.analyze;
import com.google.zxing.Result;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.camera.core.ImageProxy;
/**
* 分析器
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public interface Analyzer {
@Nullable
Result analyze(@NonNull ImageProxy image);
}

View File

@@ -0,0 +1,57 @@
package com.king.zxing.analyze;
import android.graphics.Rect;
import com.google.zxing.DecodeHintType;
import com.google.zxing.Result;
import com.king.zxing.DecodeFormatManager;
import com.king.zxing.DecodeConfig;
import java.util.Map;
import androidx.annotation.Nullable;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public abstract class AreaRectAnalyzer extends ImageAnalyzer {
DecodeConfig mDecodeConfig;
Map<DecodeHintType,?> mHints;
boolean isMultiDecode = true;
public AreaRectAnalyzer(@Nullable DecodeConfig config){
this.mDecodeConfig = config;
if(config != null){
mHints = config.getHints();
isMultiDecode = config.isMultiDecode();
}else{
mHints = DecodeFormatManager.DEFAULT_HINTS;
}
}
@Nullable
@Override
public Result analyze(byte[] data, int width, int height) {
if(mDecodeConfig == null || mDecodeConfig.isFullAreaScan()){
//mDecodeConfig为空或者支持全区域扫码识别时直接使用全区域进行扫码识别
return analyze(data,width,height,0,0,width,height);
}
Rect rect = mDecodeConfig.getAnalyzeAreaRect();
if(rect != null){//如果分析区域不为空,则使用指定的区域进行扫码识别
return analyze(data,width,height,rect.left,rect.top,rect.width(),rect.height());
}
//如果分析区域为空,则通过识别区域比例和相关的偏移量计算出最终的区域进行扫码识别
int size = (int)(Math.min(width,height) * mDecodeConfig.getAreaRectRatio());
int left = (width-size)/2 + mDecodeConfig.getAreaRectHorizontalOffset();
int top = (height-size)/2 + mDecodeConfig.getAreaRectVerticalOffset();
return analyze(data,width,height,left,top,size,size);
}
abstract Result analyze(byte[] data, int dataWidth, int dataHeight,int left,int top,int width,int height);
}

View File

@@ -0,0 +1,96 @@
package com.king.zxing.analyze;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.LuminanceSource;
import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.Reader;
import com.google.zxing.Result;
import com.google.zxing.common.GlobalHistogramBinarizer;
import com.google.zxing.common.HybridBinarizer;
import com.king.zxing.DecodeConfig;
import com.king.zxing.util.LogUtils;
import java.util.Map;
import androidx.annotation.Nullable;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public abstract class BarcodeFormatAnalyzer extends AreaRectAnalyzer {
private Reader mReader;
public BarcodeFormatAnalyzer(@Nullable Map<DecodeHintType,Object> hints){
this(new DecodeConfig().setHints(hints));
}
public BarcodeFormatAnalyzer(@Nullable DecodeConfig config) {
super(config);
initReader();
}
private void initReader(){
mReader = createReader();
}
@Nullable
@Override
public Result analyze(byte[] data, int dataWidth, int dataHeight,int left,int top,int width,int height) {
Result rawResult = null;
if(mReader != null){
try {
long start = System.currentTimeMillis();
PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(data,dataWidth,dataHeight,left,top,width,height,false);
rawResult = decodeInternal(source,isMultiDecode);
if(rawResult == null && mDecodeConfig != null){
if(rawResult == null && mDecodeConfig.isSupportVerticalCode()){
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];
}
rawResult = decodeInternal(new PlanarYUVLuminanceSource(rotatedData,dataHeight,dataWidth,top,left,height,width,false),mDecodeConfig.isSupportVerticalCodeMultiDecode());
}
if(mDecodeConfig.isSupportLuminanceInvert()){
rawResult = decodeInternal(source.invert(),mDecodeConfig.isSupportLuminanceInvertMultiDecode());
}
}
if(rawResult != null){
long end = System.currentTimeMillis();
LogUtils.d("Found barcode in " + (end - start) + " ms");
}
} catch (Exception e) {
}finally {
mReader.reset();
}
}
return rawResult;
}
private Result decodeInternal(LuminanceSource source,boolean isMultiDecode){
Result result = null;
try{
try{
//采用HybridBinarizer解析
result = mReader.decode(new BinaryBitmap(new HybridBinarizer(source)),mHints);
}catch (Exception e){
}
if(isMultiDecode && result == null){
//如果没有解析成功再采用GlobalHistogramBinarizer解析一次
result = mReader.decode(new BinaryBitmap(new GlobalHistogramBinarizer(source)),mHints);
}
}catch (Exception e){
}
return result;
}
public abstract Reader createReader();
}

View File

@@ -0,0 +1,41 @@
package com.king.zxing.analyze;
import android.annotation.SuppressLint;
import android.graphics.ImageFormat;
import com.google.zxing.Result;
import java.nio.ByteBuffer;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.camera.core.ImageProxy;
/**
* 图像分析器
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public abstract class ImageAnalyzer implements Analyzer {
/**
* 分析图像数据
* @param data
* @param width
* @param height
*/
@Nullable
public abstract Result analyze(byte[] data, int width, int height);
@Override
public Result analyze(@NonNull ImageProxy image) {
if(image.getFormat() == ImageFormat.YUV_420_888){
@SuppressLint("UnsafeExperimentalUsageError")
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
return analyze(data,image.getWidth(),image.getHeight());
}
return null;
}
}

View File

@@ -0,0 +1,98 @@
package com.king.zxing.analyze;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.Result;
import com.google.zxing.common.GlobalHistogramBinarizer;
import com.google.zxing.common.HybridBinarizer;
import com.king.zxing.DecodeConfig;
import com.king.zxing.util.LogUtils;
import java.util.Map;
import androidx.annotation.Nullable;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class MultiFormatAnalyzer extends AreaRectAnalyzer {
MultiFormatReader mReader;
public MultiFormatAnalyzer(){
this((DecodeConfig)null);
}
public MultiFormatAnalyzer(@Nullable Map<DecodeHintType,Object> hints){
this(new DecodeConfig().setHints(hints));
}
public MultiFormatAnalyzer(@Nullable DecodeConfig config) {
super(config);
initReader();
}
private void initReader(){
mReader = new MultiFormatReader();
}
@Nullable
@Override
public Result analyze(byte[] data, int dataWidth, int dataHeight,int left,int top,int width,int height) {
Result rawResult = null;
try {
long start = System.currentTimeMillis();
mReader.setHints(mHints);
PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(data,dataWidth,dataHeight,left,top,width,height,false);
rawResult = decodeInternal(source,isMultiDecode);
if(rawResult == null && mDecodeConfig != null){
if(rawResult == null && mDecodeConfig.isSupportVerticalCode()){
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];
}
rawResult = decodeInternal(new PlanarYUVLuminanceSource(rotatedData,dataHeight,dataWidth,top,left,height,width,false),mDecodeConfig.isSupportVerticalCodeMultiDecode());
}
if(mDecodeConfig.isSupportLuminanceInvert()){
rawResult = decodeInternal(source.invert(),mDecodeConfig.isSupportLuminanceInvertMultiDecode());
}
}
if(rawResult != null){
long end = System.currentTimeMillis();
LogUtils.d("Found barcode in " + (end - start) + " ms");
}
} catch (Exception e) {
}finally {
mReader.reset();
}
return rawResult;
}
private Result decodeInternal(LuminanceSource source,boolean isMultiDecode){
Result result = null;
try{
try{
//采用HybridBinarizer解析
result = mReader.decodeWithState(new BinaryBitmap(new HybridBinarizer(source)));
}catch (Exception e){
}
if(isMultiDecode && result == null){
//如果没有解析成功再采用GlobalHistogramBinarizer解析一次
result = mReader.decodeWithState(new BinaryBitmap(new GlobalHistogramBinarizer(source)));
}
}catch (Exception e){
}
return result;
}
}

View File

@@ -0,0 +1,35 @@
package com.king.zxing.analyze;
import com.google.zxing.DecodeHintType;
import com.google.zxing.Reader;
import com.google.zxing.qrcode.QRCodeReader;
import com.king.zxing.DecodeConfig;
import java.util.Map;
import androidx.annotation.Nullable;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class QRCodeAnalyzer extends BarcodeFormatAnalyzer {
public QRCodeAnalyzer() {
this((DecodeConfig)null);
}
public QRCodeAnalyzer(@Nullable Map<DecodeHintType,Object> hints){
this(new DecodeConfig().setHints(hints));
}
public QRCodeAnalyzer(@Nullable DecodeConfig config) {
super(config);
}
@Override
public Reader createReader() {
return new QRCodeReader();
}
}

View File

@@ -1,140 +0,0 @@
package com.king.zxing.camera;
/*
* Copyright (C) 2012 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.
*/
import android.content.Context;
import android.content.SharedPreferences;
import android.hardware.Camera;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.RejectedExecutionException;
import com.king.zxing.Preferences;
import com.king.zxing.util.LogUtils;
final class AutoFocusManager implements Camera.AutoFocusCallback {
private static final long AUTO_FOCUS_INTERVAL_MS = 1200L;
private static final Collection<String> FOCUS_MODES_CALLING_AF;
static {
FOCUS_MODES_CALLING_AF = new ArrayList<>(2);
FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_AUTO);
FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_MACRO);
}
private boolean stopped;
private boolean focusing;
private final boolean useAutoFocus;
private final Camera camera;
private AsyncTask<?,?,?> outstandingTask;
AutoFocusManager(Context context, Camera camera) {
this.camera = camera;
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
String currentFocusMode = camera.getParameters().getFocusMode();
useAutoFocus =
sharedPrefs.getBoolean(Preferences.KEY_AUTO_FOCUS, true) &&
FOCUS_MODES_CALLING_AF.contains(currentFocusMode);
LogUtils.i("Current focus mode '" + currentFocusMode + "'; use auto focus? " + useAutoFocus);
start();
}
@Override
public synchronized void onAutoFocus(boolean success, Camera theCamera) {
focusing = false;
autoFocusAgainLater();
}
private synchronized void autoFocusAgainLater() {
if (!stopped && outstandingTask == null) {
AutoFocusTask newTask = new AutoFocusTask(this);
try {
newTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
outstandingTask = newTask;
} catch (RejectedExecutionException ree) {
LogUtils.w("Could not request auto focus", ree);
}
}
}
synchronized void start() {
if (useAutoFocus) {
outstandingTask = null;
if (!stopped && !focusing) {
try {
camera.autoFocus(this);
focusing = true;
} catch (RuntimeException re) {
// Have heard RuntimeException reported in Android 4.0.x+; continue?
LogUtils.w("Unexpected exception while focusing", re);
// Try again later to keep cycle going
autoFocusAgainLater();
}
}
}
}
private synchronized void cancelOutstandingTask() {
if (outstandingTask != null) {
if (outstandingTask.getStatus() != AsyncTask.Status.FINISHED) {
outstandingTask.cancel(true);
}
outstandingTask = null;
}
}
synchronized void stop() {
stopped = true;
if (useAutoFocus) {
cancelOutstandingTask();
// Doesn't hurt to call this even if not focusing
try {
camera.cancelAutoFocus();
} catch (RuntimeException re) {
// Have heard RuntimeException reported in Android 4.0.x+; continue?
LogUtils.w("Unexpected exception while cancelling focusing", re);
}
}
}
private static class AutoFocusTask extends AsyncTask<Object,Object,Object> {
private WeakReference<AutoFocusManager> weakReference;
public AutoFocusTask(AutoFocusManager manager){
weakReference = new WeakReference<>(manager);
}
@Override
protected Object doInBackground(Object... voids) {
try {
Thread.sleep(AUTO_FOCUS_INTERVAL_MS);
} catch (InterruptedException e) {
// continue
}
AutoFocusManager manager = weakReference.get();
if(manager!=null){
manager.start();
}
return null;
}
}
}

View File

@@ -1,236 +0,0 @@
package com.king.zxing.camera;
/*
* 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.
*/
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Point;
import android.hardware.Camera;
import android.preference.PreferenceManager;
import android.view.Display;
import android.view.Surface;
import android.view.WindowManager;
import com.king.zxing.Preferences;
import com.king.zxing.camera.open.CameraFacing;
import com.king.zxing.camera.open.OpenCamera;
import com.king.zxing.util.LogUtils;
/**
* A class which deals with reading, parsing, and setting the camera parameters which are used to
* configure the camera hardware.
*/
@SuppressWarnings("deprecation") // camera APIs
final class CameraConfigurationManager {
private final Context context;
private int cwNeededRotation;
private int cwRotationFromDisplayToCamera;
private Point screenResolution;
private Point bestPreviewSize;
private Point previewSizeOnScreen;
CameraConfigurationManager(Context context) {
this.context = context;
}
/**
* Reads, one time, values from the camera that are needed by the app.
*/
void initFromCameraParameters(OpenCamera camera) {
Camera.Parameters parameters = camera.getCamera().getParameters();
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = manager.getDefaultDisplay();
int displayRotation = display.getRotation();
int cwRotationFromNaturalToDisplay;
switch (displayRotation) {
case Surface.ROTATION_0:
cwRotationFromNaturalToDisplay = 0;
break;
case Surface.ROTATION_90:
cwRotationFromNaturalToDisplay = 90;
break;
case Surface.ROTATION_180:
cwRotationFromNaturalToDisplay = 180;
break;
case Surface.ROTATION_270:
cwRotationFromNaturalToDisplay = 270;
break;
default:
// Have seen this return incorrect values like -90
if (displayRotation % 90 == 0) {
cwRotationFromNaturalToDisplay = (360 + displayRotation) % 360;
} else {
throw new IllegalArgumentException("Bad rotation: " + displayRotation);
}
}
LogUtils.i("Display at: " + cwRotationFromNaturalToDisplay);
int cwRotationFromNaturalToCamera = camera.getOrientation();
LogUtils.i("Camera at: " + cwRotationFromNaturalToCamera);
// Still not 100% sure about this. But acts like we need to flip this:
if (camera.getFacing() == CameraFacing.FRONT) {
cwRotationFromNaturalToCamera = (360 - cwRotationFromNaturalToCamera) % 360;
LogUtils.i("Front camera overriden to: " + cwRotationFromNaturalToCamera);
}
cwRotationFromDisplayToCamera = (360 + cwRotationFromNaturalToCamera - cwRotationFromNaturalToDisplay) % 360;
LogUtils.i("Final display orientation: " + cwRotationFromDisplayToCamera);
if (camera.getFacing() == CameraFacing.FRONT) {
LogUtils.i("Compensating rotation for front camera");
cwNeededRotation = (360 - cwRotationFromDisplayToCamera) % 360;
} else {
cwNeededRotation = cwRotationFromDisplayToCamera;
}
LogUtils.i("Clockwise rotation from display to camera: " + cwNeededRotation);
screenResolution = new Point();
display.getSize(screenResolution);
LogUtils.i("Screen resolution in current orientation: " + screenResolution);
bestPreviewSize = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, screenResolution);
LogUtils.i("Best available preview size: " + bestPreviewSize);
boolean isScreenPortrait = screenResolution.x < screenResolution.y;
if (isScreenPortrait) {
previewSizeOnScreen = new Point(bestPreviewSize.y, bestPreviewSize.x);
} else {
previewSizeOnScreen = bestPreviewSize;
}
LogUtils.i("Preview size on screen: " + previewSizeOnScreen);
}
void setDesiredCameraParameters(OpenCamera camera, boolean safeMode) {
Camera theCamera = camera.getCamera();
Camera.Parameters parameters = theCamera.getParameters();
if (parameters == null) {
LogUtils.w("Device error: no camera parameters are available. Proceeding without configuration.");
return;
}
LogUtils.i("Initial camera parameters: " + parameters.flatten());
if (safeMode) {
LogUtils.w("In camera config safe mode -- most settings will not be honored");
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
if(parameters.isZoomSupported()){
parameters.setZoom(parameters.getMaxZoom() / 10);
}
// initializeTorch(parameters, prefs, safeMode);
CameraConfigurationUtils.setFocus(
parameters,
prefs.getBoolean(Preferences.KEY_AUTO_FOCUS, true),
prefs.getBoolean(Preferences.KEY_DISABLE_CONTINUOUS_FOCUS, true),
safeMode);
if (!safeMode) {
if (prefs.getBoolean(Preferences.KEY_INVERT_SCAN, false)) {
CameraConfigurationUtils.setInvertColor(parameters);
}
if (!prefs.getBoolean(Preferences.KEY_DISABLE_BARCODE_SCENE_MODE, true)) {
CameraConfigurationUtils.setBarcodeSceneMode(parameters);
}
if (!prefs.getBoolean(Preferences.KEY_DISABLE_METERING, true)) {
CameraConfigurationUtils.setVideoStabilization(parameters);
CameraConfigurationUtils.setFocusArea(parameters);
CameraConfigurationUtils.setMetering(parameters);
}
//SetRecordingHint to true also a workaround for low framerate on Nexus 4
//https://stackoverflow.com/questions/14131900/extreme-camera-lag-on-nexus-4
parameters.setRecordingHint(true);
}
parameters.setPreviewSize(bestPreviewSize.x, bestPreviewSize.y);
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
theCamera.setParameters(parameters);
theCamera.cancelAutoFocus();
theCamera.setDisplayOrientation(cwRotationFromDisplayToCamera);
Camera.Parameters afterParameters = theCamera.getParameters();
Camera.Size afterSize = afterParameters.getPreviewSize();
if (afterSize != null && (bestPreviewSize.x != afterSize.width || bestPreviewSize.y != afterSize.height)) {
LogUtils.w("Camera said it supported preview size " + bestPreviewSize.x + 'x' + bestPreviewSize.y +
", but after setting it, preview size is " + afterSize.width + 'x' + afterSize.height);
bestPreviewSize.x = afterSize.width;
bestPreviewSize.y = afterSize.height;
}
}
Point getBestPreviewSize() {
return bestPreviewSize;
}
Point getPreviewSizeOnScreen() {
return previewSizeOnScreen;
}
Point getScreenResolution() {
return screenResolution;
}
int getCWNeededRotation() {
return cwNeededRotation;
}
boolean getTorchState(Camera camera) {
if (camera != null) {
Camera.Parameters parameters = camera.getParameters();
if (parameters != null) {
String flashMode = parameters.getFlashMode();
return
Camera.Parameters.FLASH_MODE_ON.equals(flashMode) ||
Camera.Parameters.FLASH_MODE_TORCH.equals(flashMode);
}
}
return false;
}
void setTorch(Camera camera, boolean newSetting) {
Camera.Parameters parameters = camera.getParameters();
doSetTorch(parameters, newSetting, false);
camera.setParameters(parameters);
}
private void initializeTorch(Camera.Parameters parameters, boolean safeMode) {
doSetTorch(parameters, false, safeMode);
}
private void doSetTorch(Camera.Parameters parameters, boolean newSetting, boolean safeMode) {
CameraConfigurationUtils.setTorch(parameters, newSetting);
// SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
// if (!safeMode && !prefs.getBoolean(Preferences.KEY_DISABLE_EXPOSURE, true)) {
// CameraConfigurationUtils.setBestExposure(parameters, newSetting);
// }
}
}

View File

@@ -1,408 +0,0 @@
package com.king.zxing.camera;
/*
* Copyright (C) 2014 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.
*/
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.Camera;
import android.os.Build;
import com.king.zxing.util.LogUtils;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
/**
* Utility methods for configuring the Android camera.
*
* @author Sean Owen
*/
@SuppressWarnings("deprecation") // camera APIs
public final class CameraConfigurationUtils {
private static final Pattern SEMICOLON = Pattern.compile(";");
private static final int MIN_PREVIEW_PIXELS = 480 * 320; // normal screen
private static final float MAX_EXPOSURE_COMPENSATION = 1.5f;
private static final float MIN_EXPOSURE_COMPENSATION = 0.0f;
private static final double MAX_ASPECT_DISTORTION = 0.05;
private static final int MIN_FPS = 10;
private static final int MAX_FPS = 20;
private static final int AREA_PER_1000 = 400;
private CameraConfigurationUtils() {
}
public static void setFocus(Camera.Parameters parameters,
boolean autoFocus,
boolean disableContinuous,
boolean safeMode) {
List<String> supportedFocusModes = parameters.getSupportedFocusModes();
String focusMode = null;
if (autoFocus) {
if (safeMode || disableContinuous) {
focusMode = findSettableValue("focus mode",
supportedFocusModes,
Camera.Parameters.FOCUS_MODE_AUTO);
} else {
focusMode = findSettableValue("focus mode",
supportedFocusModes,
Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE,
Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO,
Camera.Parameters.FOCUS_MODE_AUTO);
}
}
// Maybe selected auto-focus but not available, so fall through here:
if (!safeMode && focusMode == null) {
focusMode = findSettableValue("focus mode",
supportedFocusModes,
Camera.Parameters.FOCUS_MODE_MACRO,
Camera.Parameters.FOCUS_MODE_EDOF);
}
if (focusMode != null) {
if (focusMode.equals(parameters.getFocusMode())) {
LogUtils.d( "Focus mode already set to " + focusMode);
} else {
parameters.setFocusMode(focusMode);
}
}
}
public static void setTorch(Camera.Parameters parameters, boolean on) {
List<String> supportedFlashModes = parameters.getSupportedFlashModes();
String flashMode;
if (on) {
flashMode = findSettableValue("flash mode",
supportedFlashModes,
Camera.Parameters.FLASH_MODE_TORCH,
Camera.Parameters.FLASH_MODE_ON);
} else {
flashMode = findSettableValue("flash mode",
supportedFlashModes,
Camera.Parameters.FLASH_MODE_OFF);
}
if (flashMode != null) {
if (flashMode.equals(parameters.getFlashMode())) {
LogUtils.d( "Flash mode already set to " + flashMode);
} else {
LogUtils.d( "Setting flash mode to " + flashMode);
parameters.setFlashMode(flashMode);
}
}
}
public static void setBestExposure(Camera.Parameters parameters, boolean lightOn) {
int minExposure = parameters.getMinExposureCompensation();
int maxExposure = parameters.getMaxExposureCompensation();
float step = parameters.getExposureCompensationStep();
if ((minExposure != 0 || maxExposure != 0) && step > 0.0f) {
// Set low when light is on
float targetCompensation = lightOn ? MIN_EXPOSURE_COMPENSATION : MAX_EXPOSURE_COMPENSATION;
int compensationSteps = Math.round(targetCompensation / step);
float actualCompensation = step * compensationSteps;
// Clamp value:
compensationSteps = Math.max(Math.min(compensationSteps, maxExposure), minExposure);
if (parameters.getExposureCompensation() == compensationSteps) {
LogUtils.d( "Exposure compensation already set to " + compensationSteps + " / " + actualCompensation);
} else {
LogUtils.d( "Setting exposure compensation to " + compensationSteps + " / " + actualCompensation);
parameters.setExposureCompensation(compensationSteps);
}
} else {
LogUtils.d( "Camera does not support exposure compensation");
}
}
public static void setBestPreviewFPS(Camera.Parameters parameters) {
setBestPreviewFPS(parameters, MIN_FPS, MAX_FPS);
}
public static void setBestPreviewFPS(Camera.Parameters parameters, int minFPS, int maxFPS) {
List<int[]> supportedPreviewFpsRanges = parameters.getSupportedPreviewFpsRange();
LogUtils.d( "Supported FPS ranges: " + toString(supportedPreviewFpsRanges));
if (supportedPreviewFpsRanges != null && !supportedPreviewFpsRanges.isEmpty()) {
int[] suitableFPSRange = null;
for (int[] fpsRange : supportedPreviewFpsRanges) {
int thisMin = fpsRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX];
int thisMax = fpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
if (thisMin >= minFPS * 1000 && thisMax <= maxFPS * 1000) {
suitableFPSRange = fpsRange;
break;
}
}
if (suitableFPSRange == null) {
LogUtils.d( "No suitable FPS range?");
} else {
int[] currentFpsRange = new int[2];
parameters.getPreviewFpsRange(currentFpsRange);
if (Arrays.equals(currentFpsRange, suitableFPSRange)) {
LogUtils.d( "FPS range already set to " + Arrays.toString(suitableFPSRange));
} else {
LogUtils.d( "Setting FPS range to " + Arrays.toString(suitableFPSRange));
parameters.setPreviewFpsRange(suitableFPSRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
suitableFPSRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
}
}
}
}
public static void setFocusArea(Camera.Parameters parameters) {
if (parameters.getMaxNumFocusAreas() > 0) {
LogUtils.d( "Old focus areas: " + toString(parameters.getFocusAreas()));
List<Camera.Area> middleArea = buildMiddleArea(AREA_PER_1000);
LogUtils.d( "Setting focus area to : " + toString(middleArea));
parameters.setFocusAreas(middleArea);
} else {
LogUtils.d( "Device does not support focus areas");
}
}
public static void setMetering(Camera.Parameters parameters) {
if (parameters.getMaxNumMeteringAreas() > 0) {
LogUtils.d( "Old metering areas: " + parameters.getMeteringAreas());
List<Camera.Area> middleArea = buildMiddleArea(AREA_PER_1000);
LogUtils.d( "Setting metering area to : " + toString(middleArea));
parameters.setMeteringAreas(middleArea);
} else {
LogUtils.d( "Device does not support metering areas");
}
}
private static List<Camera.Area> buildMiddleArea(int areaPer1000) {
return Collections.singletonList(
new Camera.Area(new Rect(-areaPer1000, -areaPer1000, areaPer1000, areaPer1000), 1));
}
public static void setVideoStabilization(Camera.Parameters parameters) {
if (parameters.isVideoStabilizationSupported()) {
if (parameters.getVideoStabilization()) {
LogUtils.d( "Video stabilization already enabled");
} else {
LogUtils.d( "Enabling video stabilization...");
parameters.setVideoStabilization(true);
}
} else {
LogUtils.d( "This device does not support video stabilization");
}
}
public static void setBarcodeSceneMode(Camera.Parameters parameters) {
if (Camera.Parameters.SCENE_MODE_BARCODE.equals(parameters.getSceneMode())) {
LogUtils.d( "Barcode scene mode already set");
return;
}
String sceneMode = findSettableValue("scene mode",
parameters.getSupportedSceneModes(),
Camera.Parameters.SCENE_MODE_BARCODE);
if (sceneMode != null) {
parameters.setSceneMode(sceneMode);
}
}
public static void setZoom(Camera.Parameters parameters, double targetZoomRatio) {
if (parameters.isZoomSupported()) {
Integer zoom = indexOfClosestZoom(parameters, targetZoomRatio);
if (zoom == null) {
return;
}
if (parameters.getZoom() == zoom) {
LogUtils.d( "Zoom is already set to " + zoom);
} else {
LogUtils.d( "Setting zoom to " + zoom);
parameters.setZoom(zoom);
}
} else {
LogUtils.d( "Zoom is not supported");
}
}
private static Integer indexOfClosestZoom(Camera.Parameters parameters, double targetZoomRatio) {
List<Integer> ratios = parameters.getZoomRatios();
LogUtils.d( "Zoom ratios: " + ratios);
int maxZoom = parameters.getMaxZoom();
if (ratios == null || ratios.isEmpty() || ratios.size() != maxZoom + 1) {
LogUtils.w( "Invalid zoom ratios!");
return null;
}
double target100 = 100.0 * targetZoomRatio;
double smallestDiff = Double.POSITIVE_INFINITY;
int closestIndex = 0;
for (int i = 0; i < ratios.size(); i++) {
double diff = Math.abs(ratios.get(i) - target100);
if (diff < smallestDiff) {
smallestDiff = diff;
closestIndex = i;
}
}
LogUtils.d( "Chose zoom ratio of " + (ratios.get(closestIndex) / 100.0));
return closestIndex;
}
public static void setInvertColor(Camera.Parameters parameters) {
if (Camera.Parameters.EFFECT_NEGATIVE.equals(parameters.getColorEffect())) {
LogUtils.d( "Negative effect already set");
return;
}
String colorMode = findSettableValue("color effect",
parameters.getSupportedColorEffects(),
Camera.Parameters.EFFECT_NEGATIVE);
if (colorMode != null) {
parameters.setColorEffect(colorMode);
}
}
/**
* 预览尺寸与给定的宽高尺寸比较器。首先比较宽高的比例,在宽高比相同的情况下,根据宽和高的最小差进行比较。
*/
private static class SizeComparator implements Comparator<Camera.Size> {
private final int width;
private final int height;
private final float ratio;
SizeComparator(int width, int height) {
//不管横屏还是竖屏parameters.getSupportedPreviewSizes()的size.width 始终大于或等于 size.height
if (width < height) {
this.width = height;
this.height = width;
} else {
this.width = width;
this.height = height;
}
this.ratio = (float) this.height / this.width;
}
@Override
public int compare(Camera.Size size1, Camera.Size size2) {
int width1 = size1.width;
int height1 = size1.height;
int width2 = size2.width;
int height2 = size2.height;
float ratio1 = Math.abs((float) height1 / width1 - ratio);
float ratio2 = Math.abs((float) height2 / width2 - ratio);
int result = Float.compare(ratio1, ratio2);
if (result != 0) {
return result;
} else {
int minGap1 = Math.abs(width - width1) + Math.abs(height - height1);
int minGap2 = Math.abs(width - width2) + Math.abs(height - height2);
return minGap1 - minGap2;
}
}
}
/**
* 在Camera支持的预览尺寸中找到最佳的预览尺寸
* @param parameters
* @param screenResolution
* @return
*/
public static Point findBestPreviewSizeValue(Camera.Parameters parameters,final Point screenResolution) {
List<Camera.Size> preList = parameters.getSupportedPreviewSizes();
Collections.sort(preList, new SizeComparator(screenResolution.x, screenResolution.y));
Camera.Size size = preList.get(0);
return new Point(size.width,size.height);
}
private static String findSettableValue(String name,
Collection<String> supportedValues,
String... desiredValues) {
LogUtils.d( "Requesting " + name + " value from among: " + Arrays.toString(desiredValues));
LogUtils.d( "Supported " + name + " values: " + supportedValues);
if (supportedValues != null) {
for (String desiredValue : desiredValues) {
if (supportedValues.contains(desiredValue)) {
LogUtils.d( "Can set " + name + " to: " + desiredValue);
return desiredValue;
}
}
}
LogUtils.d( "No supported values match");
return null;
}
private static String toString(Collection<int[]> arrays) {
if (arrays == null || arrays.isEmpty()) {
return "[]";
}
StringBuilder buffer = new StringBuilder();
buffer.append('[');
Iterator<int[]> it = arrays.iterator();
while (it.hasNext()) {
buffer.append(Arrays.toString(it.next()));
if (it.hasNext()) {
buffer.append(", ");
}
}
buffer.append(']');
return buffer.toString();
}
private static String toString(Iterable<Camera.Area> areas) {
if (areas == null) {
return null;
}
StringBuilder result = new StringBuilder();
for (Camera.Area area : areas) {
result.append(area.rect).append(':').append(area.weight).append(' ');
}
return result.toString();
}
public static String collectStats(Camera.Parameters parameters) {
return collectStats(parameters.flatten());
}
public static String collectStats(CharSequence flattenedParams) {
StringBuilder result = new StringBuilder(1000);
result.append("BOARD=").append(Build.BOARD).append('\n');
result.append("BRAND=").append(Build.BRAND).append('\n');
result.append("CPU_ABI=").append(Build.CPU_ABI).append('\n');
result.append("DEVICE=").append(Build.DEVICE).append('\n');
result.append("DISPLAY=").append(Build.DISPLAY).append('\n');
result.append("FINGERPRINT=").append(Build.FINGERPRINT).append('\n');
result.append("HOST=").append(Build.HOST).append('\n');
result.append("ID=").append(Build.ID).append('\n');
result.append("MANUFACTURER=").append(Build.MANUFACTURER).append('\n');
result.append("MODEL=").append(Build.MODEL).append('\n');
result.append("PRODUCT=").append(Build.PRODUCT).append('\n');
result.append("TAGS=").append(Build.TAGS).append('\n');
result.append("TIME=").append(Build.TIME).append('\n');
result.append("TYPE=").append(Build.TYPE).append('\n');
result.append("USER=").append(Build.USER).append('\n');
result.append("VERSION.CODENAME=").append(Build.VERSION.CODENAME).append('\n');
result.append("VERSION.INCREMENTAL=").append(Build.VERSION.INCREMENTAL).append('\n');
result.append("VERSION.RELEASE=").append(Build.VERSION.RELEASE).append('\n');
result.append("VERSION.SDK_INT=").append(Build.VERSION.SDK_INT).append('\n');
if (flattenedParams != null) {
String[] params = SEMICOLON.split(flattenedParams);
Arrays.sort(params);
for (String param : params) {
result.append(param).append('\n');
}
}
return result.toString();
}
}

View File

@@ -1,440 +0,0 @@
package com.king.zxing.camera;
/*
* 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.
*/
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.Camera;
import android.os.Handler;
import android.view.SurfaceHolder;
import com.google.zxing.PlanarYUVLuminanceSource;
import com.king.zxing.camera.open.OpenCamera;
import com.king.zxing.camera.open.OpenCameraInterface;
import com.king.zxing.util.LogUtils;
import java.io.IOException;
import androidx.annotation.FloatRange;
/**
* 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)
*/
@SuppressWarnings("deprecation") // camera APIs
public final class CameraManager {
private static final int MIN_FRAME_WIDTH = 240;
private static final int MIN_FRAME_HEIGHT = 240;
private static final int MAX_FRAME_WIDTH = 1200; // = 5/8 * 1920
private static final int MAX_FRAME_HEIGHT = 675; // = 5/8 * 1080
private final Context context;
private final CameraConfigurationManager configManager;
private OpenCamera camera;
private AutoFocusManager autoFocusManager;
private Rect framingRect;
private Rect framingRectInPreview;
private boolean initialized;
private boolean previewing;
private int requestedCameraId = OpenCameraInterface.NO_REQUESTED_CAMERA;
private int requestedFramingRectWidth;
private int requestedFramingRectHeight;
private boolean isFullScreenScan;
private float framingRectRatio;
private int framingRectVerticalOffset;
private int framingRectHorizontalOffset;
/**
* Preview frames are delivered here, which we pass on to the registered handler. Make sure to
* clear the handler so it will only receive one message.
*/
private final PreviewCallback previewCallback;
private OnTorchListener onTorchListener;
private OnSensorListener onSensorListener;
private boolean isTorch;
public CameraManager(Context context) {
this.context = context.getApplicationContext();
this.configManager = new CameraConfigurationManager(context);
previewCallback = new PreviewCallback(configManager);
}
/**
* 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 {
OpenCamera theCamera = camera;
if (theCamera == null) {
theCamera = OpenCameraInterface.open(requestedCameraId);
if (theCamera == null) {
throw new IOException("Camera.open() failed to return object from driver");
}
camera = theCamera;
}
if (!initialized) {
initialized = true;
configManager.initFromCameraParameters(theCamera);
if (requestedFramingRectWidth > 0 && requestedFramingRectHeight > 0) {
setManualFramingRect(requestedFramingRectWidth, requestedFramingRectHeight);
requestedFramingRectWidth = 0;
requestedFramingRectHeight = 0;
}
}
Camera cameraObject = theCamera.getCamera();
Camera.Parameters parameters = cameraObject.getParameters();
String parametersFlattened = parameters == null ? null : parameters.flatten(); // Save these, temporarily
try {
configManager.setDesiredCameraParameters(theCamera, false);
} catch (RuntimeException re) {
// Driver failed
LogUtils.w("Camera rejected parameters. Setting only minimal safe-mode parameters");
LogUtils.i( "Resetting to saved camera params: " + parametersFlattened);
// Reset:
if (parametersFlattened != null) {
parameters = cameraObject.getParameters();
parameters.unflatten(parametersFlattened);
try {
cameraObject.setParameters(parameters);
configManager.setDesiredCameraParameters(theCamera, true);
} catch (RuntimeException re2) {
// Well, darn. Give up
LogUtils.w("Camera rejected even safe-mode parameters! No configuration");
}
}
}
cameraObject.setPreviewDisplay(holder);
}
public synchronized boolean isOpen() {
return camera != null;
}
public OpenCamera getOpenCamera() {
return camera;
}
/**
* Closes the camera driver if still in use.
*/
public void closeDriver() {
if (camera != null) {
camera.getCamera().release();
camera = null;
// Make sure to clear these each time we close the camera, so that any scanning rect
// requested by intent is forgotten.
framingRect = null;
framingRectInPreview = null;
}
isTorch = false;
if(onTorchListener!=null){
onTorchListener.onTorchChanged(false);
}
}
/**
* Asks the camera hardware to begin drawing preview frames to the screen.
*/
public void startPreview() {
OpenCamera theCamera = camera;
if (theCamera != null && !previewing) {
theCamera.getCamera().startPreview();
previewing = true;
// autoFocusManager = new AutoFocusManager(context, theCamera.getCamera());
}
}
/**
* Tells the camera to stop drawing preview frames.
*/
public void stopPreview() {
if (autoFocusManager != null) {
autoFocusManager.stop();
autoFocusManager = null;
}
if (camera != null && previewing) {
camera.getCamera().stopPreview();
previewCallback.setHandler(null, 0);
previewing = false;
}
}
/**
* Convenience method for {@link com.king.zxing.CaptureActivity}
*
* @param newSetting if {@code true}, light should be turned on if currently off. And vice versa.
*/
public synchronized void setTorch(boolean newSetting) {
OpenCamera theCamera = camera;
if (theCamera != null && newSetting != configManager.getTorchState(theCamera.getCamera())) {
boolean wasAutoFocusManager = autoFocusManager != null;
if (wasAutoFocusManager) {
autoFocusManager.stop();
autoFocusManager = null;
}
this.isTorch = newSetting;
configManager.setTorch(theCamera.getCamera(), newSetting);
if (wasAutoFocusManager) {
autoFocusManager = new AutoFocusManager(context, theCamera.getCamera());
// autoFocusManager.start();
}
if(onTorchListener!=null){
onTorchListener.onTorchChanged(newSetting);
}
}
}
/**
* 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 synchronized void requestPreviewFrame(Handler handler, int message) {
LogUtils.d("requestPreviewFrame");
OpenCamera theCamera = camera;
if (theCamera != null && previewing) {
previewCallback.setHandler(handler, message);
theCamera.getCamera().setOneShotPreviewCallback(previewCallback);
}
}
/**
* 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 synchronized Rect getFramingRect() {
if (framingRect == null) {
if (camera == null) {
return null;
}
Point point = configManager.getBestPreviewSize();
if (point == null) {
// Called early, before init even finished
return null;
}
int width = point.x;
int height = point.y;
if(isFullScreenScan){
framingRect = new Rect(0,0,width,height);
}else{
int size = (int)(Math.min(width,height) * framingRectRatio);
int leftOffset = (width - size) / 2 + framingRectHorizontalOffset;
int topOffset = (height - size) / 2 + framingRectVerticalOffset;
framingRect = new Rect(leftOffset, topOffset, leftOffset + size, topOffset + size);
}
}
return framingRect;
}
/**
* Like {@link #getFramingRect} but coordinates are in terms of the preview frame,
* not UI / screen.
*
* @return {@link Rect} expressing barcode scan area in terms of the preview size
*/
public synchronized Rect getFramingRectInPreview() {
if (framingRectInPreview == null) {
Rect framingRect = getFramingRect();
if (framingRect == null) {
return null;
}
Rect rect = new Rect(framingRect);
Point preViewResolution = configManager.getPreviewSizeOnScreen();
Point screenResolution = configManager.getScreenResolution();
if (preViewResolution == null || screenResolution == null) {
// Called early, before init even finished
return null;
}
rect.left = rect.left * preViewResolution.x / screenResolution.x;
rect.right = rect.right * preViewResolution.x / screenResolution.x;
rect.top = rect.top * preViewResolution.y / screenResolution.y;
rect.bottom = rect.bottom * preViewResolution.y / screenResolution.y;
framingRectInPreview = rect;
}
return framingRectInPreview;
}
public void setFullScreenScan(boolean fullScreenScan) {
isFullScreenScan = fullScreenScan;
}
public void setFramingRectRatio(@FloatRange(from = 0.0f ,to = 1.0f) float framingRectRatio) {
this.framingRectRatio = framingRectRatio;
}
public void setFramingRectVerticalOffset(int framingRectVerticalOffset) {
this.framingRectVerticalOffset = framingRectVerticalOffset;
}
public void setFramingRectHorizontalOffset(int framingRectHorizontalOffset) {
this.framingRectHorizontalOffset = framingRectHorizontalOffset;
}
public Point getBestPreviewSize(){
return configManager.getBestPreviewSize();
}
public Point getPreviewSizeOnScreen() {
return configManager.getPreviewSizeOnScreen();
}
public Point getScreenResolution() {
return configManager.getScreenResolution();
}
/**
* Allows third party apps to specify the camera ID, rather than determine
* it automatically based on available cameras and their orientation.
*
* @param cameraId camera ID of the camera to use. A negative value means "no preference".
*/
public synchronized void setManualCameraId(int cameraId) {
requestedCameraId = cameraId;
}
/**
* Allows third party apps to specify the scanning rectangle dimensions, rather than determine
* them automatically based on screen resolution.
*
* @param width The width in pixels to scan.
* @param height The height in pixels to scan.
*/
public synchronized void setManualFramingRect(int width, int height) {
if (initialized) {
Point screenResolution = configManager.getScreenResolution();
if (width > screenResolution.x) {
width = screenResolution.x;
}
if (height > screenResolution.y) {
height = screenResolution.y;
}
int leftOffset = (screenResolution.x - width) / 2;
int topOffset = (screenResolution.y - height) / 2;
framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);
LogUtils.d( "Calculated manual framing rect: " + framingRect);
framingRectInPreview = null;
} else {
requestedFramingRectWidth = width;
requestedFramingRectHeight = height;
}
}
/**
* 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();
if (rect == null) {
return null;
}
if(isFullScreenScan){
return new PlanarYUVLuminanceSource(data,width,height,0,0,width,height,false);
}
int size = (int)(Math.min(width,height) * framingRectRatio);
int left = (width-size)/2 + framingRectHorizontalOffset;
int top = (height-size)/2 + framingRectVerticalOffset;
// Go ahead and assume it's YUV rather than die.
return new PlanarYUVLuminanceSource(data, width, height, left, top,
size, size, false);
}
/**
* 提供闪光灯监听
* @param listener
*/
public void setOnTorchListener(OnTorchListener listener){
this.onTorchListener = listener;
}
/**
* 传感器光线照度监听
* @param listener
*/
public void setOnSensorListener(OnSensorListener listener){
this.onSensorListener = listener;
}
public void sensorChanged(boolean tooDark,float ambientLightLux){
if(onSensorListener!=null){
onSensorListener.onSensorChanged(isTorch,tooDark,ambientLightLux);
}
}
public interface OnTorchListener{
/**
* 当闪光灯状态改变时触发
* @param torch true表示开启、false表示关闭
*/
void onTorchChanged(boolean torch);
}
/**
* 传感器灯光亮度监听
*/
public interface OnSensorListener{
/**
*
* @param torch 闪光灯是否开启
* @param tooDark 传感器检测到的光线亮度,是否太暗
* @param ambientLightLux 光线照度
*/
void onSensorChanged(boolean torch,boolean tooDark,float ambientLightLux);
}
}

View File

@@ -1,50 +0,0 @@
package com.king.zxing.camera;
/*
* Copyright (C) 2012 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.
*/
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import com.king.zxing.Preferences;
/**
* Enumerates settings of the preference controlling the front light.
*/
public enum FrontLightMode {
/** Always on. */
ON,
/** On only when ambient light is low. */
AUTO,
/** Always off. */
OFF;
private static FrontLightMode parse(String modeString) {
return modeString == null ? AUTO : valueOf(modeString);
}
public static FrontLightMode readPref(SharedPreferences sharedPrefs) {
return parse(sharedPrefs.getString(Preferences.KEY_FRONT_LIGHT_MODE, AUTO.toString()));
}
public static void put(Context context, FrontLightMode mode) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
prefs.edit().putString(Preferences.KEY_FRONT_LIGHT_MODE, mode.toString()).commit();
}
}

View File

@@ -1,59 +0,0 @@
package com.king.zxing.camera;
/*
* 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.
*/
import android.graphics.Point;
import android.hardware.Camera;
import android.os.Handler;
import android.os.Message;
import com.king.zxing.util.LogUtils;
@SuppressWarnings("deprecation") // camera APIs
final class PreviewCallback implements Camera.PreviewCallback {
private static final String TAG = PreviewCallback.class.getSimpleName();
private final CameraConfigurationManager configManager;
private Handler previewHandler;
private int previewMessage;
PreviewCallback(CameraConfigurationManager configManager) {
this.configManager = configManager;
}
void setHandler(Handler previewHandler, int previewMessage) {
this.previewHandler = previewHandler;
this.previewMessage = previewMessage;
}
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
Point cameraResolution = configManager.getBestPreviewSize();
Handler thePreviewHandler = previewHandler;
if (cameraResolution != null && thePreviewHandler != null) {
Message message = thePreviewHandler.obtainMessage(previewMessage, cameraResolution.x,
cameraResolution.y, data);
message.sendToTarget();
previewHandler = null;
} else {
LogUtils.d("Got preview callback, but no handler or resolution available");
}
}
}

View File

@@ -1,22 +0,0 @@
package com.king.zxing.camera.open;
/*
* Copyright (C) 2015 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.
*/
public enum CameraFacing {
BACK, // must be value 0!
FRONT, // must be value 1!
}

View File

@@ -1,57 +0,0 @@
package com.king.zxing.camera.open;
/*
* Copyright (C) 2015 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.
*/
import android.hardware.Camera;
/**
* Represents an open {@link Camera} and its metadata, like facing direction and orientation.
*/
@SuppressWarnings("deprecation") // camera APIs
public final class OpenCamera {
private final int index;
private final Camera camera;
private final CameraFacing facing;
private final int orientation;
public OpenCamera(int index, Camera camera, CameraFacing facing, int orientation) {
this.index = index;
this.camera = camera;
this.facing = facing;
this.orientation = orientation;
}
public Camera getCamera() {
return camera;
}
public CameraFacing getFacing() {
return facing;
}
public int getOrientation() {
return orientation;
}
@Override
public String toString() {
return "Camera #" + index + " : " + facing + ',' + orientation;
}
}

View File

@@ -1,88 +0,0 @@
package com.king.zxing.camera.open;
/*
* Copyright (C) 2012 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.
*/
import android.hardware.Camera;
import com.king.zxing.util.LogUtils;
/**
* Abstraction over the {@link Camera} API that helps open them and return their metadata.
*/
@SuppressWarnings("deprecation") // camera APIs
public final class OpenCameraInterface {
/**
* For {@link #open(int)}, means no preference for which camera to open.
*/
public static final int NO_REQUESTED_CAMERA = -1;
private OpenCameraInterface() {
}
/**
* Opens the requested camera with {@link Camera#open(int)}, if one exists.
*
* @param cameraId camera ID of the camera to use. A negative value
* or {@link #NO_REQUESTED_CAMERA} means "no preference", in which case a rear-facing
* camera is returned if possible or else any camera
* @return handle to {@link OpenCamera} that was opened
*/
public static OpenCamera open(int cameraId) {
int numCameras = Camera.getNumberOfCameras();
if (numCameras == 0) {
LogUtils.w("No cameras!");
return null;
}
if (cameraId >= numCameras) {
LogUtils.w("Requested camera does not exist: " + cameraId);
return null;
}
if (cameraId <= NO_REQUESTED_CAMERA) {
cameraId = 0;
while (cameraId < numCameras) {
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, cameraInfo);
if (CameraFacing.values()[cameraInfo.facing] == CameraFacing.BACK) {
break;
}
cameraId++;
}
if (cameraId == numCameras) {
LogUtils.i("No camera facing " + CameraFacing.BACK + "; returning camera #0");
cameraId = 0;
}
}
LogUtils.i("Opening camera #" + cameraId);
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, cameraInfo);
Camera camera = Camera.open(cameraId);
if (camera == null) {
return null;
}
return new OpenCamera(cameraId,
camera,
CameraFacing.values()[cameraInfo.facing],
cameraInfo.orientation);
}
}

View File

@@ -32,6 +32,7 @@ import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.EncodeHintType;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.RGBLuminanceSource;
@@ -40,14 +41,12 @@ import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.GlobalHistogramBinarizer;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.king.zxing.DecodeFormatManager;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
/**
@@ -55,8 +54,8 @@ import java.util.Vector;
*/
public final class CodeUtils {
public static final int DEFAULT_REQ_WIDTH = 450;
public static final int DEFAULT_REQ_HEIGHT = 800;
public static final int DEFAULT_REQ_WIDTH = 480;
public static final int DEFAULT_REQ_HEIGHT = 640;
private CodeUtils(){
throw new AssertionError();
@@ -246,21 +245,8 @@ public final class CodeUtils {
* @param bitmapPath
* @return
*/
public static String parseQRCode(String bitmapPath) {
Map<DecodeHintType, Object> hints = new HashMap<>();
hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
return parseQRCode(bitmapPath,hints);
}
/**
* 解析二维码图片
* @param bitmapPath
* @param hints
* @return
*/
public static String parseQRCode(String bitmapPath, Map<DecodeHintType,?> hints){
Result result = parseQRCodeResult(bitmapPath,hints);
public static String parseQRCode(String bitmapPath){
Result result = parseQRCodeResult(bitmapPath);
if(result != null){
return result.getText();
}
@@ -270,11 +256,10 @@ public final class CodeUtils {
/**
* 解析二维码图片
* @param bitmapPath
* @param hints
* @return
*/
public static Result parseQRCodeResult(String bitmapPath, Map<DecodeHintType,?> hints){
return parseQRCodeResult(bitmapPath,DEFAULT_REQ_WIDTH,DEFAULT_REQ_HEIGHT,hints);
public static Result parseQRCodeResult(String bitmapPath){
return parseQRCodeResult(bitmapPath,DEFAULT_REQ_WIDTH,DEFAULT_REQ_HEIGHT);
}
/**
@@ -282,63 +267,10 @@ public final class CodeUtils {
* @param bitmapPath
* @param reqWidth
* @param reqHeight
* @param hints
* @return
*/
public static Result parseQRCodeResult(String bitmapPath,int reqWidth,int reqHeight,Map<DecodeHintType,?> hints){
Result result = null;
try{
QRCodeReader reader = new QRCodeReader();
RGBLuminanceSource source = getRGBLuminanceSource(compressBitmap(bitmapPath,reqWidth,reqHeight));
if (source != null) {
boolean isReDecode;
try {
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
result = reader.decode(bitmap,hints);
isReDecode = false;
} catch (Exception e) {
isReDecode = true;
}
if(isReDecode){
try {
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source.invert()));
result = reader.decode(bitmap,hints);
isReDecode = false;
} catch (Exception e) {
isReDecode = true;
}
}
if(isReDecode){
try{
BinaryBitmap bitmap = new BinaryBitmap(new GlobalHistogramBinarizer(source));
result = reader.decode(bitmap,hints);
isReDecode = false;
}catch (Exception e){
isReDecode = true;
}
}
if(isReDecode && source.isRotateSupported()){
try{
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source.rotateCounterClockwise()));
result = reader.decode(bitmap,hints);
}catch (Exception e){
}
}
reader.reset();
}
}catch (Exception e){
LogUtils.w(e.getMessage());
}
return result;
public static Result parseQRCodeResult(String bitmapPath,int reqWidth,int reqHeight){
return parseCodeResult(bitmapPath,reqWidth,reqHeight, DecodeFormatManager.QR_CODE_HINTS);
}
/**
@@ -347,19 +279,7 @@ public final class CodeUtils {
* @return
*/
public static String parseCode(String bitmapPath){
Map<DecodeHintType,Object> hints = new HashMap<>();
//添加可以解析的编码类型
Vector<BarcodeFormat> decodeFormats = new Vector<>();
decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS);
decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);
decodeFormats.addAll(DecodeFormatManager.AZTEC_FORMATS);
decodeFormats.addAll(DecodeFormatManager.PDF417_FORMATS);
hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
hints.put(DecodeHintType.TRY_HARDER,Boolean.TRUE);
hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
return parseCode(bitmapPath,hints);
return parseCode(bitmapPath, DecodeFormatManager.ALL_HINTS);
}
/**
@@ -396,61 +316,48 @@ public final class CodeUtils {
*/
public static Result parseCodeResult(String bitmapPath,int reqWidth,int reqHeight, Map<DecodeHintType,Object> hints){
Result result = null;
MultiFormatReader reader = new MultiFormatReader();
try{
MultiFormatReader reader = new MultiFormatReader();
reader.setHints(hints);
RGBLuminanceSource source = getRGBLuminanceSource(compressBitmap(bitmapPath,reqWidth,reqHeight));
if (source != null) {
boolean isReDecode;
try {
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
result = reader.decodeWithState(bitmap);
isReDecode = false;
} catch (Exception e) {
isReDecode = true;
result = decodeInternal(reader,source);
if(result == null){
result = decodeInternal(reader,source.invert());
}
if(isReDecode){
try {
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source.invert()));
result = reader.decodeWithState(bitmap);
isReDecode = false;
} catch (Exception e) {
isReDecode = true;
}
if(result == null && source.isRotateSupported()){
result = decodeInternal(reader,source.rotateCounterClockwise());
}
if(isReDecode){
try{
BinaryBitmap bitmap = new BinaryBitmap(new GlobalHistogramBinarizer(source));
result = reader.decodeWithState(bitmap);
isReDecode = false;
}catch (Exception e){
isReDecode = true;
}
}
if(isReDecode && source.isRotateSupported()){
try{
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source.rotateCounterClockwise()));
result = reader.decodeWithState(bitmap);
}catch (Exception e){
}
}
reader.reset();
}
}catch (Exception e){
LogUtils.w(e.getMessage());
}finally {
reader.reset();
}
return result;
}
private static Result decodeInternal(MultiFormatReader reader,LuminanceSource source){
Result result = null;
try{
try{
//采用HybridBinarizer解析
result = reader.decodeWithState(new BinaryBitmap(new HybridBinarizer(source)));
}catch (Exception e){
}
if(result == null){
//如果没有解析成功再采用GlobalHistogramBinarizer解析一次
result = reader.decodeWithState(new BinaryBitmap(new GlobalHistogramBinarizer(source)));
}
}catch (Exception e){
}
return result;
}
/**
@@ -686,4 +593,6 @@ public final class CodeUtils {
return bitmap;
}
}

View File

@@ -15,8 +15,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<SurfaceView
android:id="@+id/surfaceView"
<androidx.camera.view.PreviewView
android:id="@+id/previewView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.king.zxing.ViewfinderView
@@ -24,7 +24,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ImageView
android:id="@+id/ivTorch"
android:id="@+id/ivFlashlight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"

View File

@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="CaptureTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="windowNoTitle">true</item>
<item name="windowActionBar">false</item>
<item name="colorPrimary">@android:color/black</item>
<item name="colorPrimaryDark">@android:color/black</item>
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
</style>
</resources>

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="CaptureTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="windowNoTitle">true</item>
<item name="windowActionBar">false</item>
<item name="colorPrimary">@android:color/black</item>
<item name="colorPrimaryDark">@android:color/black</item>
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
<item name="android:statusBarColor">@color/capture_status_bar_color</item>
<item name="android:navigationBarColor">@color/capture_navigation_bar_color</item>
</style>
</resources>

View File

@@ -30,6 +30,10 @@
<attr name="frameLineWidth" format="dimension"/>
<attr name="scannerAnimationDelay" format="integer"/>
<attr name="frameRatio" format="float"/>
<attr name="framePaddingLeft" format="dimension"/>
<attr name="framePaddingTop" format="dimension"/>
<attr name="framePaddingRight" format="dimension"/>
<attr name="framePaddingBottom" format="dimension"/>
</declare-styleable>

View File

@@ -6,5 +6,10 @@
<item name="windowActionBar">false</item>
<item name="colorPrimary">@android:color/black</item>
<item name="colorPrimaryDark">@android:color/black</item>
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
<item name="android:statusBarColor">@color/capture_status_bar_color</item>
<item name="android:navigationBarColor">@color/capture_navigation_bar_color</item>
</style>
</resources>

View File

@@ -6,7 +6,7 @@ ext.app_version = app_version
//build version
def build_versions = [:]
build_versions.minSdk = 16
build_versions.minSdk = 21
build_versions.targetSdk = 28
build_versions.compileSdk = 28
build_versions.buildTools = "28.0.3"
@@ -27,8 +27,15 @@ versions.test = "1.2.0"
versions.runner = "1.2.0"
versions.espresso = "3.2.0"
versions.bintray_release = "0.9.2"
versions.gralde = "3.6.3"
versions.kotlin = "1.4.10"
versions.coreKtx = "1.3.2"
//zxing
versions.zxing = "3.4.0"
versions.zxing = "3.3.0"
versions.camerax = "1.0.0-beta12"
versions.easypermissions = "3.0.0"
@@ -49,9 +56,21 @@ test.runner = "androidx.test:runner:$versions.runner"
test.espresso = "androidx.test.espresso:espresso-core:$versions.espresso"
deps.test = test
deps.kotlin = "org.jetbrains.kotlin:kotlin-stdlib:$versions.kotlin"
deps.corektx = "androidx.core:core-ktx:$versions.coreKtx"
//zxing
deps.zxing = "com.google.zxing:core:$versions.zxing"
//CameraX
deps.camera_core = "androidx.camera:camera-core:$versions.camerax"
deps.camera_camera2 = "androidx.camera:camera-camera2:$versions.camerax"
deps.camera_lifecycle = "androidx.camera:camera-lifecycle:$versions.camerax"
deps.camera_view = "androidx.camera:camera-view:1.0.0-alpha19"
//permission
deps.easypermissions = "pub.devrel:easypermissions:$versions.easypermissions"