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

@@ -5,14 +5,10 @@
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.FLASHLIGHT"/>
<!-- unavailable in API 23 -->
<uses-permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application>
<activity
android:name="com.king.zxing.CaptureActivity"
android:screenOrientation="portrait"/>
</application>
</manifest>

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);
}

View File

@@ -1,25 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<SurfaceView
android:id="@+id/preview_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.king.zxing.ViewfinderView
android:id="@+id/viewfinder_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</merge>
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SurfaceView
android:id="@+id/surfaceView"
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"/>
</FrameLayout>

View File

@@ -4,7 +4,7 @@
<color name="viewfinder_mask">#60000000</color>
<color name="viewfinder_frame">#7F1FB3E2</color>
<color name="viewfinder_corner">#FF1FB3E2</color>
<color name="viewfinder_laser">#FF1FB3E2</color> <!-- Android standard ICS color -->
<color name="viewfinder_laser">#FF1FB3E2</color>
<color name="viewfinder_result_point_color">#C0FFBD21</color>
<color name="viewfinder_text_color">#FFC0C0C0</color>