1、新增CaptureFragment

2、将扫码相关逻辑与界面分离,ZXingLite使用更容易扩展。
This commit is contained in:
jenly1314
2019-04-19 18:45:13 +08:00
parent 4c7757fcb0
commit 36ccf48b7f
33 changed files with 1483 additions and 1186 deletions

View File

@@ -32,7 +32,7 @@ import java.io.Closeable;
import java.io.IOException;
/**
* Manages beeps and vibrations for {@link CaptureActivity}.
* Manages beeps and vibrations for {@link Activity}.
*/
public final class BeepManager implements MediaPlayer.OnErrorListener, Closeable {
@@ -62,7 +62,7 @@ public final class BeepManager implements MediaPlayer.OnErrorListener, Closeable
synchronized void updatePrefs() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
playBeep = shouldBeep(prefs, 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,
@@ -83,7 +83,7 @@ public final class BeepManager implements MediaPlayer.OnErrorListener, Closeable
}
private static boolean shouldBeep(SharedPreferences prefs, Context activity) {
boolean shouldPlayBeep = prefs.getBoolean(Preferences.KEY_PLAY_BEEP, true);
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);
@@ -97,7 +97,7 @@ public final class BeepManager implements MediaPlayer.OnErrorListener, Closeable
@TargetApi(Build.VERSION_CODES.KITKAT)
private MediaPlayer buildMediaPlayer(Context activity) {
MediaPlayer mediaPlayer = new MediaPlayer();
try (AssetFileDescriptor file = activity.getResources().openRawResourceFd(R.raw.beep)) {
try (AssetFileDescriptor file = activity.getResources().openRawResourceFd(R.raw.zxl_beep)) {
mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength());
mediaPlayer.setOnErrorListener(this);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,170 @@
/*
* 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.os.Bundle;
import android.support.annotation.LayoutRes;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import com.king.zxing.camera.CameraManager;
/**
* @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;
private View mRootView;
private SurfaceView surfaceView;
private ViewfinderView viewfinderView;
private CaptureHelper mCaptureHelper;
public static CaptureFragment newInstance() {
Bundle args = new Bundle();
CaptureFragment fragment = new CaptureFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
int layoutId = getLayoutId();
if(isContentView(layoutId)){
mRootView = inflater.inflate(getLayoutId(),container,false);
}
initUI();
return mRootView;
}
/**
* 初始化
*/
public void initUI(){
surfaceView = mRootView.findViewById(getSurfaceViewId());
viewfinderView = mRootView.findViewById(getViewfinderViewId());
mCaptureHelper = new CaptureHelper(this,surfaceView,viewfinderView);
mCaptureHelper.setOnCaptureCallback(this);
}
/**
* 返回true时会自动初始化{@link #mRootView}返回为false时需自己去通过{@link #setRootView(View)}初始化{@link #mRootView}
* @param layoutId
* @return 默认返回true
*/
public boolean isContentView(@LayoutRes int layoutId){
return true;
}
/**
* 布局id
* @return
*/
public int getLayoutId(){
return R.layout.zxl_capture;
}
/**
* {@link ViewfinderView} 的 id
* @return
*/
public int getViewfinderViewId(){
return R.id.viewfinderView;
}
/**
* 预览界面{@link #surfaceView} 的id
* @return
*/
public int getSurfaceViewId(){
return R.id.surfaceView;
}
/**
* Get {@link CaptureHelper}
* @return {@link #mCaptureHelper}
*/
public CaptureHelper getCaptureHelper(){
return mCaptureHelper;
}
/**
* Get {@link CameraManager}
* @return {@link #mCaptureHelper#getCameraManager()}
*/
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();
}
/**
* 接收扫码结果回调
* @param result 扫码结果
* @return 返回true表示拦截将不自动执行后续逻辑为false表示不拦截默认不拦截
*/
@Override
public boolean onResultCallback(String result) {
return false;
}
}

View File

@@ -1,169 +1,154 @@
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.ActivityNotFoundException;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.BitmapFactory;
import android.provider.Browser;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.DecodeHintType;
import com.google.zxing.Result;
import com.king.zxing.camera.CameraManager;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import java.util.Collection;
import java.util.Map;
/**
* This class handles all the messaging which comprises the state machine for capture.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public final class CaptureActivityHandler extends Handler {
private static final String TAG = CaptureActivityHandler.class.getSimpleName();
private final CaptureActivity activity;
private final DecodeThread decodeThread;
private State state;
private final CameraManager cameraManager;
private enum State {
PREVIEW,
SUCCESS,
DONE
}
CaptureActivityHandler(CaptureActivity activity,
Collection<BarcodeFormat> decodeFormats,
Map<DecodeHintType,?> baseHints,
String characterSet,
CameraManager cameraManager) {
this.activity = activity;
decodeThread = new DecodeThread(activity, decodeFormats, baseHints, characterSet,
new ViewfinderResultPointCallback(activity.getViewfinderView()));
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);
}
activity.handleDecode((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);
} else if (message.what == R.id.return_scan_result) {
activity.setResult(Activity.RESULT_OK, (Intent) message.obj);
activity.finish();
} else if (message.what == R.id.launch_product_query) {
String url = (String) message.obj;
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intents.FLAG_NEW_DOC);
intent.setData(Uri.parse(url));
ResolveInfo resolveInfo =
activity.getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
String browserPackageName = null;
if (resolveInfo != null && resolveInfo.activityInfo != null) {
browserPackageName = resolveInfo.activityInfo.packageName;
Log.d(TAG, "Using browser in package " + browserPackageName);
}
// Needed for default Android browser / Chrome only apparently
if (browserPackageName != null) {
switch (browserPackageName) {
case "com.android.browser":
case "com.android.chrome":
intent.setPackage(browserPackageName);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Browser.EXTRA_APPLICATION_ID, browserPackageName);
break;
}
}
try {
activity.startActivity(intent);
} catch (ActivityNotFoundException ignored) {
Log.w(TAG, "Can't find anything to handle VIEW of URI " + url);
}
}
}
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(500L);
} 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);
activity.drawViewfinder();
}
}
}
package com.king.zxing;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.Browser;
import android.util.Log;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.DecodeHintType;
import com.google.zxing.Result;
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 {
private static final String TAG = CaptureHandler.class.getSimpleName();
private final OnCaptureListener onCaptureListener;
private final DecodeThread decodeThread;
private State state;
private final CameraManager cameraManager;
private final Activity activity;
private final ViewfinderView viewfinderView;
private enum State {
PREVIEW,
SUCCESS,
DONE
}
CaptureHandler(Activity activity,ViewfinderView viewfinderView,OnCaptureListener onCaptureListener,
Collection<BarcodeFormat> decodeFormats,
Map<DecodeHintType,?> baseHints,
String characterSet,
CameraManager cameraManager) {
this.activity = activity;
this.viewfinderView = viewfinderView;
this.onCaptureListener = onCaptureListener;
decodeThread = new DecodeThread(activity,cameraManager,this, decodeFormats, baseHints, characterSet,
new ViewfinderResultPointCallback(viewfinderView));
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);
} else if (message.what == R.id.return_scan_result) {
activity.setResult(Activity.RESULT_OK, (Intent) message.obj);
activity.finish();
} else if (message.what == R.id.launch_product_query) {
String url = (String) message.obj;
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intents.FLAG_NEW_DOC);
intent.setData(Uri.parse(url));
ResolveInfo resolveInfo =
activity.getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
String browserPackageName = null;
if (resolveInfo != null && resolveInfo.activityInfo != null) {
browserPackageName = resolveInfo.activityInfo.packageName;
Log.d(TAG, "Using browser in package " + browserPackageName);
}
// Needed for default Android browser / Chrome only apparently
if (browserPackageName != null) {
switch (browserPackageName) {
case "com.android.browser":
case "com.android.chrome":
intent.setPackage(browserPackageName);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Browser.EXTRA_APPLICATION_ID, browserPackageName);
break;
}
}
try {
activity.startActivity(intent);
} catch (ActivityNotFoundException ignored) {
Log.w(TAG, "Can't find anything to handle VIEW of URI " + url);
}
}
}
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(500L);
} 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);
viewfinderView.drawViewfinder();
}
}
}

View File

@@ -0,0 +1,522 @@
/*
* 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.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.RectF;
import android.hardware.Camera;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.DecodeHintType;
import com.google.zxing.Result;
import com.king.zxing.camera.CameraManager;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class CaptureHelper implements CaptureLifecycle,CaptureTouchEvent,CaptureManager {
public static final String TAG = CaptureHelper.class.getSimpleName();
private Activity activity;
private CaptureHandler captureHandler;
private OnCaptureListener onCaptureListener;
private CameraManager cameraManager;
private InactivityTimer inactivityTimer;
private BeepManager beepManager;
private AmbientLightManager ambientLightManager;
private ViewfinderView viewfinderView;
private SurfaceHolder surfaceHolder;
private SurfaceHolder.Callback callback;
private Collection<BarcodeFormat> decodeFormats;
private Map<DecodeHintType,?> decodeHints;
private String characterSet;
private boolean hasSurface;
/**
* 默认触控误差值
*/
private static final int DEVIATION = 6;
/**
* 是否支持缩放(变焦),默认支持
*/
private boolean isSupportZoom = true;
private float oldDistance;
/**
* 是否支持连扫,默认不支持
*/
private boolean isContinuousScan = false;
/**
* 连扫时,是否自动重置预览和解码器,默认自动重置
*/
private boolean isAutoRestartPreviewAndDecode = true;
/**
* 是否播放音效
*/
private boolean isPlayBeep;
/**
* 是否震动
*/
private boolean isVibrate;
private OnCaptureCallback onCaptureCallback;
public CaptureHelper(Fragment fragment, SurfaceView surfaceView, ViewfinderView viewfinderView){
this(fragment.getActivity(),surfaceView,viewfinderView);
}
public CaptureHelper(Activity activity,SurfaceView surfaceView,ViewfinderView viewfinderView){
this.activity = activity;
this.viewfinderView = viewfinderView;
surfaceHolder = surfaceView.getHolder();
hasSurface = false;
}
@Override
public void onCreate(){
inactivityTimer = new InactivityTimer(activity);
beepManager = new BeepManager(activity);
ambientLightManager = new AmbientLightManager(activity);
cameraManager = new CameraManager(activity);
callback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (holder == null) {
Log.e(TAG, "*** 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;
}
};
onCaptureListener = new OnCaptureListener() {
@Override
public void onHandleDecode(Result result, Bitmap barcode, float scaleFactor) {
inactivityTimer.onActivity();
beepManager.playBeepSoundAndVibrate();
onResult(result);
}
};
//设置是否播放音效和震动
beepManager.setPlayBeep(isPlayBeep);
beepManager.setVibrate(isVibrate);
}
/**
* {@link Activity#onResume()}
*/
@Override
public void onResume(){
beepManager.updatePrefs();
ambientLightManager.start(cameraManager);
inactivityTimer.onResume();
surfaceHolder.addCallback(callback);
if (hasSurface) {
initCamera(surfaceHolder);
} else {
surfaceHolder.addCallback(callback);
}
}
/**
* {@link Activity#onPause()}
*/
@Override
public void onPause(){
if (captureHandler != null) {
captureHandler.quitSynchronously();
captureHandler = null;
}
inactivityTimer.onPause();
ambientLightManager.stop();
beepManager.close();
cameraManager.closeDriver();
if (!hasSurface) {
surfaceHolder.removeCallback(callback);
}
}
/**
* {@link Activity#onDestroy()}
*/
@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 initCamera(SurfaceHolder surfaceHolder) {
if (surfaceHolder == null) {
throw new IllegalStateException("No SurfaceHolder provided");
}
if (cameraManager.isOpen()) {
Log.w(TAG, "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);
}
} catch (IOException ioe) {
Log.w(TAG, ioe);
} catch (RuntimeException e) {
// Barcode Scanner has seen crashes in the wild of this variety:
// java.?lang.?RuntimeException: Fail to connect to camera service
Log.w(TAG, "Unexpected error initializing camera", e);
}
}
/**
* 处理变焦缩放
* @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 {
Log.i(TAG, "zoom not supported");
}
}
/**
* 聚焦
* @param event
* @param camera
*/
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(new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
Camera.Parameters params = camera.getParameters();
params.setFocusMode(currentFocusMode);
camera.setParameters(params);
}
});
}
/**
* 计算两指间距离
* @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();
}
}
/**
* 接收扫码结果,想支持连扫时,可将{@link #continuousScan(boolean)}设置为{@code true}
* 如果{@link #isContinuousScan}支持连扫,则默认重启扫码和解码器;当连扫逻辑太复杂时,
* 请将{@link #autoRestartPreviewAndDecode(boolean)}设置为{@code false},并手动调用{@link #restartPreviewAndDecode()}
* @param result 扫码结果
*/
public void onResult(Result result){
String text = result.getText();
if(isContinuousScan){
if(isAutoRestartPreviewAndDecode){
if(onCaptureCallback!=null){
onCaptureCallback.onResultCallback(text);
}
restartPreviewAndDecode();
}
}else{
//如果设置了回调并且onCallback返回为true则表示拦截
if(onCaptureCallback!=null && onCaptureCallback.onResultCallback(text)){
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
* @return
*/
public CaptureHelper decodeFormats(Collection<BarcodeFormat> decodeFormats) {
this.decodeFormats = decodeFormats;
return this;
}
/**
*
* @param decodeHints
* @return
*/
public CaptureHelper decodeHints(Map<DecodeHintType,?> decodeHints) {
this.decodeHints = decodeHints;
return this;
}
/**
*
* @param characterSet
* @return
*/
public CaptureHelper characterSet(String characterSet) {
this.characterSet = characterSet;
return this;
}
/**
* 设置扫码回调
* @param callback
* @return
*/
public CaptureHelper setOnCaptureCallback(OnCaptureCallback callback) {
this.onCaptureCallback = callback;
return this;
}
@Override
public CameraManager getCameraManager() {
return cameraManager;
}
@Override
public BeepManager getBeepManager() {
return beepManager;
}
@Override
public AmbientLightManager getAmbientLightManager() {
return ambientLightManager;
}
@Override
public InactivityTimer getInactivityTimer() {
return inactivityTimer;
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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 android.app.Activity#onCreate(Bundle)}
*/
void onCreate();
/**
* {@link Activity#onResume()}
*/
void onResume();
/**
* {@link Activity#onPause()}
*/
void onPause();
/**
* {@link Activity#onDestroy()}
*/
void onDestroy();
}

View File

@@ -0,0 +1,48 @@
/*
* 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

@@ -0,0 +1,29 @@
/*
* 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

@@ -23,10 +23,10 @@ import com.google.zxing.DecodeHintType;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.NotFoundException;
import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.ReaderException;
import com.google.zxing.Result;
import com.google.zxing.common.GlobalHistogramBinarizer;
import com.google.zxing.common.HybridBinarizer;
import com.king.zxing.camera.CameraManager;
import android.graphics.Point;
import android.os.Bundle;
@@ -44,14 +44,18 @@ final class DecodeHandler extends Handler {
private static final String TAG = DecodeHandler.class.getSimpleName();
private final CaptureActivity activity;
private final Context context;
private final CameraManager cameraManager;
private final Handler handler;
private final MultiFormatReader multiFormatReader;
private boolean running = true;
DecodeHandler(CaptureActivity activity, Map<DecodeHintType,Object> hints) {
DecodeHandler(Context context, CameraManager cameraManager,Handler handler, Map<DecodeHintType,Object> hints) {
multiFormatReader = new MultiFormatReader();
multiFormatReader.setHints(hints);
this.activity = activity;
this.context = context;
this.cameraManager = cameraManager;
this.handler = handler;
}
@Override
@@ -70,7 +74,7 @@ final class DecodeHandler extends Handler {
}
private boolean isScreenPortrait(){
WindowManager manager = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE);
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = manager.getDefaultDisplay();
Point screenResolution = new Point();
display.getSize(screenResolution);
@@ -98,9 +102,9 @@ final class DecodeHandler extends Handler {
int tmp = width;
width = height;
height = tmp;
source = activity.getCameraManager().buildLuminanceSource(rotatedData, width, height);
source = cameraManager.buildLuminanceSource(rotatedData, width, height);
}else{
source = activity.getCameraManager().buildLuminanceSource(data, width, height);
source = cameraManager.buildLuminanceSource(data, width, height);
}
if (source != null) {
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
@@ -118,7 +122,6 @@ final class DecodeHandler extends Handler {
}
}
Handler handler = activity.getHandler();
if (rawResult != null) {
// Don't log the barcode contents for security.
long end = System.currentTimeMillis();

View File

@@ -20,7 +20,9 @@ package com.king.zxing;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.DecodeHintType;
import com.google.zxing.ResultPointCallback;
import com.king.zxing.camera.CameraManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.Looper;
@@ -43,18 +45,23 @@ 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 CaptureActivity activity;
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(CaptureActivity activity,
DecodeThread(Context context,CameraManager cameraManager,
CaptureHandler captureHandler,
Collection<BarcodeFormat> decodeFormats,
Map<DecodeHintType,?> baseHints,
String characterSet,
ResultPointCallback resultPointCallback) {
this.activity = activity;
this.context = context;
this.cameraManager = cameraManager;
this.captureHandler = captureHandler;
handlerInitLatch = new CountDownLatch(1);
hints = new EnumMap<>(DecodeHintType.class);
@@ -64,7 +71,7 @@ final class DecodeThread extends Thread {
// The prefs can't change while the thread is running, so pick them up once here.
if (decodeFormats == null || decodeFormats.isEmpty()) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
decodeFormats = EnumSet.noneOf(BarcodeFormat.class);
if (prefs.getBoolean(Preferences.KEY_DECODE_1D_PRODUCT, true)) {
decodeFormats.addAll(DecodeFormatManager.PRODUCT_FORMATS);
@@ -106,7 +113,7 @@ final class DecodeThread extends Thread {
@Override
public void run() {
Looper.prepare();
handler = new DecodeHandler(activity, hints);
handler = new DecodeHandler(context,cameraManager,captureHandler, hints);
handlerInitLatch.countDown();
Looper.loop();
}

View File

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

View File

@@ -0,0 +1,22 @@
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

@@ -66,7 +66,7 @@ public final class CameraManager {
private final PreviewCallback previewCallback;
public CameraManager(Context context) {
this.context = context;
this.context = context.getApplicationContext();
this.configManager = new CameraConfigurationManager(context);
previewCallback = new PreviewCallback(configManager);
}