public class LegacyCamera2Scanner extends Scanner ...

Creado el: 18 de marzo de 2025

Respondido usando O1 por Chat01

Pregunta

public class LegacyCamera2Scanner extends Scanner
implements BarcodeReader.DecodeCallback,
BarcodeReader.DecoderListener,
OverlayPreviewWindow.Listener {

text
private static final String TAG = "LegacyCamera2Scanner"; private CaptureHandler mUiHandler; //进入休眠模式时延Sleep mode delay private static long InactivityTimer_DURATION_MS = 15 * 1000L; private static final long InactivityTimer__DURATION_CONTINUOUS_MODE_MS = 60 * 1000L; //预览开始当前实际时间 private long workPreviewTime = 0; //是否进入自动休眠预览模式 private boolean workDecodeTimeOutSleep = true; private int CONTINUOUS_MODE = 8; private int saveMode = SaveMode.NOTSAVE.ordinal(); private int openCameraId = 0; private int delayMillis = 0; private long decodeEndTime, decodeStartTime, decodeTime; //skip_first_frame private BarcodeReader mBarcodeReader; private String decoderVersion = ""; /** * 连扫模式下,固定时间内过滤相同条码输出,初始时间 */ private long waitRepeatStartTime = 0; /** * 是否重复解码 * {@link android.device.scanner.configuration.PropertyID#DEC_Multiple_Decode_MODE} */ private int ignoreRepeatMode = 0; /** * 间隔时间内,相同条码不重复解码 * {@link android.device.scanner.configuration.PropertyID#DEC_Multiple_Decode_TIMEOUT} */ private int waitRepeatTimeOut = 10 * 1000; /** * 连扫模式读取多个条码后输出.实现固定时间内不重复解码,配置同张图片最多解码个数 * {@link android.device.scanner.configuration.PropertyID#DEC_MaxMultiRead_COUNT} */ private int cotinuousMultiReadCount = 2; //连扫模式去重缓存条码数据 private byte[] preDecodeData = null; /** * 一次解多码模式。(注:连续扫描模式下不应该存在一次解多码) * {@link android.device.scanner.configuration.PropertyID#MULTI_DECODE_MODE} */ private int multiDecodeMode = 0;// flag for single or multiple decode /** * 一次解多码模式下,解出固定个数后才输出 * {@link android.device.scanner.configuration.PropertyID#FULL_READ_MODE} */ private int fullReadMode = 1; //当前解码的条码个数 private int g_nMultiReadResultCount = 0; // For tracking # of multiread results /** * 一次解多码模式下,最多要解码的个数 * {@link android.device.scanner.configuration.PropertyID#BAR_CODES_TO_READ} */ private int g_nMaxMultiReadCount = 1; // Maximum multiread count /** * {@link android.device.scanner.configuration.PropertyID#TRANSMIT_CODE_ID} */ private int transmitCodeID = 0; /** * {@link android.device.scanner.configuration.PropertyID#LABEL_SEPARATOR_ENABLE} * 应用标识符标示字符,默认() */ private int enableGroupSeperator = 0; /** * {@link android.device.scanner.configuration.PropertyID#LABEL_FORMAT_SEPARATOR_CHAR} * 应用标识符标示字符,默认() */ private String groupSeperatorCharacters = "()"; private boolean activateDecoder = false; private int activateDecoderTimes = 5; private boolean hasLicense = true; private boolean syncLicenseStrore = false; private byte[] headOfByte = null; private byte[] bitmapByte = null; private int[] bounds = new int[10]; private static int MAX_FRAME_WIDTH = 1280; private static int MAX_FRAME_HEIGHT = 720; private int aimerIllumMode = 3; private int intervalTime = 0; private OverlayPreviewWindow mWindow; /** * An additional thread for running tasks that shouldn't block the UI. * 本质继承Thread类&封装Handler类 */ private LegacyCamera2Scanner.DecodeHandlerThread mDecodeHandlerThread; private LegacyCamera2Scanner.DecodeHandler mDecodeHandler; private com.ubx.scanner.camera.CameraManager mCameraManager; private SurfaceHolder surfaceHolder; private static final int BCRDR_MSG_DECODE_COMPLETE = 1; private static final int BCRDR_MSG_DECODE_TIMEOUT = 2; private static final int BCRDR_MSG_DECODE_CANCELED = 3; private static final int BCRDR_MSG_DECODE = 4; private static final int BCRDR_MSG_DECODE_PREVIEW = 5; private static final int BCRDR_MSG_DECODE_REPREVIEW = 6; private static final int BCRDR_MSG_FRAME_ERROR = 7; private static final int BCRDR_MSG_INIT = 8; private static final int BCRDR_MSG_RELEASE = 9; private static final int BCRDR_MSG_INIT_DECODE = 10; private static final int BCRDR_MSG_TIMER_PREVIEW = 11; public static final int MESSAGE_CAPTURE_IMAGE = 12; public static final int MESSAGE_MULTI_DECODE_COMPLETE = 13; public static final int MESSAGE_CONTINUOUS_MULTI_DECODE_COMPLETE = 14; public static final int MESSAGE_RELEASE_QUIT = 15; private int orientationDegress = 90; BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (CAPTURE_IMAGE_ACTION.equals(intent.getAction())) { if (mUiHandler != null) { Message m = Message.obtain(mUiHandler, MESSAGE_CAPTURE_IMAGE); Bundle bundle = intent.getExtras(); m.obj = bundle; mUiHandler.sendMessage(m); } } else if (ENABLE_BROADCAST_IMAGE_ACTION.equals(intent.getAction())) { Bundle bundle = intent.getExtras(); BROADCAST_IMAGE_ACTION = bundle.getString("imageAction", BROADCAST_IMAGE_ACTION); BROADCAST_IMAGE_ACTION_EXTRAS = bundle.getString("imageExtras", BROADCAST_IMAGE_ACTION_EXTRAS); rotateImage = bundle.getInt("rotate", 0); enableSendBarcodeAndBitmap = bundle.getBoolean("enableSendImage", false); } } }; // 单例对象 private static LegacyCamera2Scanner sInstance; // 上下文 private Context mContext; private WindowManager mWindowManager; // 条码相关

// private BarcodeReader mBarcodeReader;
private BarcodeReader.DecodeCallback mDecodeCallback;
private DecoderListener mDecoderListener;

text
// 状态标记 private boolean mIsOpened = false; // Scanner 是否打开 private boolean mIsDecoding = false; // 是否处于解码中 private boolean mIsCameraInitialized = false; // 相机是否初始化 // 相机2相关 private CameraDevice mCameraDevice; private CameraCaptureSession mCaptureSession; private CaptureRequest.Builder mPreviewRequestBuilder; private Size mPreviewSize; private ImageReader mImageReader; private HandlerThread mBackgroundThread; private Handler mBackgroundHandler; private String mCameraId; // 预览相关 private View mCameraPreviewLayout; private TextureView mPreviewView; private View mBlackView; private SurfaceTexture mSurfaceTexture; private SurfaceHolder mSurfaceHolder; private int mSurfaceWidth; private int mSurfaceHeight; // 浮动按钮相关 private View mFloatingButtonView; private WindowManager.LayoutParams mFloatingButtonParams; private boolean mIsDragging = false; private int mInitialX, mInitialY; private float mInitialTouchX, mInitialTouchY; // 解码结果 private HSMDecodeResult mLastDecodeResult; private byte[] mLastDecodeImage; // 多码解码参数(示例保留) private int mMultiDecodeMode = 0; private int mMaxMultiReadCount = 2; private final Runnable mShowRunnable = new Runnable() { @Override public void run() { OverlayPreviewWindow window = new OverlayPreviewWindow(mContextWrapper,MAX_FRAME_WIDTH, MAX_FRAME_HEIGHT, 0,orientationDegress, LegacyCamera2Scanner.this); window.show(aimerIllumMode); synchronized (mDecodeLock) { mWindow = window; } } }; // Runs on the UI thread. private final Runnable mDismissRunnable = new Runnable() { @Override public void run() { OverlayPreviewWindow window; synchronized (mDecodeLock) { window = mWindow; mWindow = null; } //mCameraManager.setTorch(false); surfaceHolder = null; if (window != null) { window.dismiss(); } } }; private final class DecodeHandlerThread extends HandlerThread { private Looper myLooper; public DecodeHandlerThread(String name) { super(name); start(); myLooper = this.getLooper(); } public void startThread() { mDecodeHandler = new DecodeHandler(myLooper); } } final class DecodeHandler extends Handler { private boolean running = true; DecodeHandler(Looper loop) { super(loop); } @Override public void handleMessage(Message message) { if (!running) { return; } if (message.what == BCRDR_MSG_DECODE) { decode((byte[]) message.obj, message.arg1, message.arg2); } else if (message.what == MESSAGE_RELEASE_QUIT) { running = false; Looper.myLooper().quit(); } } /** * Decode the data within the viewfinder rectangle, and time how long it took. For efficiency, * reuse the same reader objects from one decode to the next. * * @param data The YUV preview frame. * @param width The width of the preview frame. * @param height The height of the preview frame. */ private void decode(byte[] data, int width, int height) { Log.d(TAG, "decode width=" + width + " height = " + height); workPreviewTime = System.currentTimeMillis(); int ret = mBarcodeReader.decodeImage(data, width, height); if (mUiHandler != null) { HSMDecodeResult firstResult = null; if(ret > 0) { firstResult = mBarcodeReader.getDecodeData(); } Log.d(TAG, "decode: ret: " + ret + " firstResult == null: " + (firstResult == null)); long end = System.currentTimeMillis(); Log.i(TAG, " startDecode data dec="+ (end - workPreviewTime) + " ms"); if (ret > 0 && firstResult != null) { if(CONTINUOUS_MODE == 4) { if (ignoreRepeatMode == 1) { if (preDecodeData != null && System.currentTimeMillis() - waitRepeatStartTime < waitRepeatTimeOut && compareBytes(firstResult.getBarcodeDataBytes(), firstResult.getBarcodeDataLength())) { decodeStartTime = System.currentTimeMillis(); return; } } g_nMultiReadResultCount++; preDecodeData = firstResult.getBarcodeDataBytes(); waitRepeatStartTime = System.currentTimeMillis(); Message message = mUiHandler.obtainMessage(BCRDR_MSG_DECODE_COMPLETE); DecodeData decodeData = new DecodeData(); decodeData.setBarcodeDataBytes(firstResult.getBarcodeDataBytes()); decodeData.setBarcodeDataLength(firstResult.getBarcodeDataLength()); decodeData.setDecodeTime(end - workPreviewTime); decodeData.setSymbologyId(firstResult.getSymbologyId()); decodeData.setCodeId(firstResult.getCodeId()); decodeData.setAIMCodeLetter(firstResult.getAIMCodeLetter()); decodeData.setAIMModifier(firstResult.getAIMModifier()); message.obj = decodeData; message.sendToTarget(); } else { Message message = mUiHandler.obtainMessage(BCRDR_MSG_DECODE_COMPLETE); DecodeData decodeData = new DecodeData(); decodeData.setBarcodeDataBytes(firstResult.getBarcodeDataBytes()); decodeData.setBarcodeDataLength(firstResult.getBarcodeDataLength()); decodeData.setDecodeTime(workPreviewTime - decodeStartTime); decodeData.setSymbologyId(firstResult.getSymbologyId()); decodeData.setCodeId(firstResult.getCodeId()); decodeData.setAIMCodeLetter(firstResult.getAIMCodeLetter()); decodeData.setAIMModifier(firstResult.getAIMModifier()); message.obj = decodeData; message.sendToTarget(); if (enableSendBarcodeAndBitmap) { String barcode = new String(firstResult.getBarcodeDataBytes(), 0, firstResult.getBarcodeDataLength()); byte[] m_frameData = mLastDecodeImage;

// byte[] m_frameData = mBarcodeReader.getLastDecImage();
int[] bounds = firstResult.getBarcodeBounds();
sendBarcodeAndBitmapBroadcast(barcode, m_frameData, bounds);
}
}
/if (saveMode == SaveMode.SAVEDECODESUCCESSALLBMP.getVal()) {
Utils.saveYUVImage(mContext, data, width, height, end);
} else if (saveMode == SaveMode.SAVEDECODESUCCESSLASTBMP.getVal()) {
Utils.saveYUVImage(mContext, data, width, height, 0);
}
/
}/* else {
Message message = Message.obtain(mUiHandler, BCRDR_MSG_FRAME_ERROR);
message.sendToTarget();
}*/
}
/if (saveMode == SaveMode.SAVEPREVIEWALLBMP.getVal()) {
Utils.saveYUVImage(mContext, data, width, height, start);
} else if (saveMode == SaveMode.SAVEPREVIEWLASTBMP.getVal()) {
Utils.saveYUVImage(mContext, data, width, height, 0);
}
/
}

text
} class CaptureHandler extends Handler { public CaptureHandler(Looper looper) { super(looper); state = State.DONE; } @Override public void handleMessage(Message message) { switch (message.what) { case BCRDR_MSG_DECODE_COMPLETE: workPreviewTime = System.currentTimeMillis(); if(CONTINUOUS_MODE == 4) { if(intervalTime > 0) { this.sendEmptyMessageDelayed(BCRDR_MSG_DECODE_PREVIEW, intervalTime); } else { /*if (mCameraManager != null) { mCameraManager.setDecodeHandler(mDecodeHandler); }*/ } } else { removeMessages(BCRDR_MSG_DECODE_TIMEOUT); state = State.DONE; mIsDecoding = false; //sendBroadcast((byte[])message.obj, message.arg2, message.arg1); /*if (mCameraManager != null) { mCameraManager.stopDecoding(); }*/

// stopDecode();
/if (mImageReader != null) {
mImageReader.close();
}
/
stopDecode();
/stopPreview();
closeCamera();
new Handler(Looper.getMainLooper()).post(() -> {
mCameraPreviewLayout.setVisibility(View.INVISIBLE);
});
if (mBarcodeReader != null) {
Log.d(TAG, "handleMessage: BCR.stopDecode(), start: " + state);
mBarcodeReader.stopDecode();
state = State.PREVIEW;
}
/
// mUiHandler.post(mDismissRunnable);
}
DecodeData decodeData = (DecodeData)message.obj;
int codeId = decodeData.getCodeId();
int SymbologyId = OEMSymbologyId.getHSMSymbologyId(codeId);
String symName = OEMSymbologyId.stringFromHSMSymbology(codeId);
aimCodeId[0] = ']';
aimCodeId[1] = decodeData.getAIMCodeLetter();
aimCodeId[2] = decodeData.getAIMModifier();
byte[] byteArraryData = decodeData.getBarcodeDataBytes();
String barcode = new String(decodeData.getBarcodeDataBytes(), 0, decodeData.getBarcodeDataLength());
Log.d(TAG, "onDecodeComplete: barcode: " + barcode);
Log.d(TAG, "handleMessage: transmitCodeID: " + transmitCodeID + " enableGroupSeperator: " + enableGroupSeperator);
if (transmitCodeID > 0 || enableGroupSeperator == 1) {
byte[] barcodeDataBytes = composeAISeparator(aimCodeId[1], aimCodeId[2], codeId, byteArraryData, decodeData.getBarcodeDataLength());
if (barcodeDataBytes != null) {
if (mOnScannedListener != null) {
mOnScannedListener.receivedDecodedData(barcodeDataBytes, barcodeDataBytes.length, aimCodeId, SymbologyId, symName, decodeTime);
}
} else {
if (mOnScannedListener != null) {
mOnScannedListener.receivedDecodedData(byteArraryData, byteArraryData.length, aimCodeId, SymbologyId, symName, decodeTime);
}
}
} else {
if (mOnScannedListener != null) {
mOnScannedListener.receivedDecodedData(byteArraryData, byteArraryData.length, aimCodeId, SymbologyId, symName, decodeTime);
}
}
break;
case BCRDR_MSG_DECODE_PREVIEW:
workPreviewTime = System.currentTimeMillis();
if (mCameraManager != null && state == State.DECODING) {
mCameraManager.setDecodeHandler(mDecodeHandler);
}
break;
case BCRDR_MSG_FRAME_ERROR:
if(state == State.DECODING) {
mCameraManager.setDecodeHandler(mDecodeHandler);
}
break;
case BCRDR_MSG_DECODE_TIMEOUT:
state = State.DONE;
stopDecode();
break;
case BCRDR_MSG_TIMER_PREVIEW:
long currentTime = System.currentTimeMillis();
Log.d(TAG, "timer_preview " + (currentTime - workPreviewTime));
if (currentTime - workPreviewTime >= (CONTINUOUS_MODE == 4 ? InactivityTimer__DURATION_CONTINUOUS_MODE_MS : InactivityTimer_DURATION_MS)) {
if (mBarcodeReader != null) {
mBarcodeReader.stopDecode();
}
if(mCameraManager != null) {
mCameraManager.closeDriver();
}
workPreviewTime = 0;
state = State.PREVIEW;
} else {
workPreviewTime = System.currentTimeMillis();
sendEmptyMessageDelayed(BCRDR_MSG_TIMER_PREVIEW, CONTINUOUS_MODE == 4 ? InactivityTimer__DURATION_CONTINUOUS_MODE_MS : InactivityTimer_DURATION_MS);
}
break;
case BCRDR_MSG_INIT:
connectDecoderLibrary();
break;
case BCRDR_MSG_RELEASE:
disconnectDecoderLibrary();
break;
case MESSAGE_CAPTURE_IMAGE:
Bundle bundle = (Bundle) message.obj;
int w = 0;
int h = 0;
int rotate = 0;
if (bundle != null) {
w = bundle.getInt("ImageWidth", 0);
h = bundle.getInt("ImageHeight", 0);
rotate = bundle.getInt("rotate", 0);
}
captureImage(w, h, rotate);
break;
case MESSAGE_MULTI_DECODE_COMPLETE:
receivedMultipleData();
break;
case MESSAGE_CONTINUOUS_MULTI_DECODE_COMPLETE:
if (mBarcodeReader != null && state == State.DECODING) {
mBarcodeReader.stopDecode();
}
state = State.PREVIEW;
receivedMultipleData();
break;
}
}

text
} private void captureImage(int width, int height, int rotate) { if (mBarcodeReader != null) { try { Intent intent = new Intent(BROADCAST_IMAGE_ACTION); byte[] m_frameData = mBarcodeReader.getLastDecImage(); if (m_frameData != null && m_frameData.length > 0/* && bitmapByte != null*/) { Bitmap modBm = createBitmap(m_frameData, rotate); if (modBm != null) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); modBm.compress(Bitmap.CompressFormat.JPEG, 50, baos); modBm.recycle(); modBm = null; intent.putExtra(BROADCAST_IMAGE_ACTION_EXTRAS, baos.toByteArray()); } else { intent.putExtra(BROADCAST_IMAGE_ACTION_EXTRAS, new byte[1]); } if (bounds != null) { for (int i = 0; i < bounds.length; i++) { if (i % 2 == 0) { bounds[i] = MAX_FRAME_WIDTH - bounds[i]; } else { bounds[i] = MAX_FRAME_HEIGHT - bounds[i]; } } intent.putExtra("bounds", bounds); } } else { intent.putExtra(BROADCAST_IMAGE_ACTION_EXTRAS, new byte[1]); } mContextWrapper.sendBroadcastAsUser(intent, UserHandleEx.ALL); } catch (Exception e) { e.printStackTrace(); } } } private int connectDecoderLibrary() { /*final boolean enable = LicenseHelper.setWifiWnable(mContextWrapper, true); if (enable == false) { File wifiAddress = new File("sys/class/net/wlan0/address"); for (int i = 0; i < 8; i++) { try { Thread.sleep(100); } catch (Exception e) { } if (wifiAddress.exists()) { Log.d(TAG, " getwifiAddress sleep " + i); break; } } } try { new Thread(new Runnable() { @Override public void run() { //接口有访问远程服务器动作,避免服务器或网络异常需超时才退出,造成ANR //if(BarcodeReader.activateLicense(ActivationManager.Activation_DIR) != 0){ if (ActivationManager.activateAPIWithLocalFile(mContextWrapper, false) == false) { activateDecoder = false; } else { activateDecoder = true; if (!syncLicenseStrore) { syncLicenseStrore(true); syncLicenseStrore = true; } } if (enable == false && activateDecoder) LicenseHelper.setWifiWnable(mContextWrapper, enable); } }).start(); for (int i = 0; i < 2; i++) { Thread.sleep(70); if (activateDecoder) { Log.d(TAG, " activateDecoder sleep " + i); break; } Thread.sleep(30); } } catch (Exception e) { e.printStackTrace(); }*/ try{ mBarcodeReader = BarcodeReader.open(-1, mContextWrapper); } catch (ReaderException e) { e.printStackTrace(); mBarcodeReader = null; } if (mBarcodeReader != null) { byte[] version = new byte[256]; mBarcodeReader.reportDecoderVersion(version); decoderVersion = (new String(version)).trim(); Log.d(TAG, "version " + decoderVersion); mBarcodeReader.setDecodeCallback(LegacyCamera2Scanner.this); mBarcodeReader.setDecoderListeners(LegacyCamera2Scanner.this); new Thread(new Runnable() { @Override public void run() { openCamera(openCameraId); } }).start(); state = State.PREVIEW; preDecodeData = null; g_nMultiReadResultCount = 0; } else { return ERR_GENERAL_ERROR; } if (mUiHandler != null && workDecodeTimeOutSleep) { mUiHandler.removeMessages(BCRDR_MSG_TIMER_PREVIEW); mUiHandler.sendEmptyMessageDelayed(BCRDR_MSG_TIMER_PREVIEW, CONTINUOUS_MODE == 4 ? InactivityTimer__DURATION_CONTINUOUS_MODE_MS : InactivityTimer_DURATION_MS); workPreviewTime = System.currentTimeMillis(); } if (mBarcodeReader != null) { SparseArray<Property> mCacheProperties = mParametersHelper.getALLDecoderProperty(); for (int i = 0; i < mCacheProperties.size(); i++) { int keyForIndex = mCacheProperties.keyAt(i); setDecodeParameter(keyForIndex); } SparseArray<Property> mCacheStringProperties = mParametersHelper.getALLStringProperty(); for (int i = 0; i < mCacheStringProperties.size(); i++) { int keyForIndex = mCacheStringProperties.keyAt(i); setDecodeParameter(keyForIndex); } CONTINUOUS_MODE = mParametersHelper.getIntProperty(PropertyID.TRIGGERING_MODES); } return SUCCESS; } /** * Method used to disconnect from the camera engine and deinitialize the API. */ private void disconnectDecoderLibrary() { if (/*state != State.DONE && */mBarcodeReader != null) { mBarcodeReader.stopDecode(); mBarcodeReader.release(); } state = State.DONE; preDecodeData = null; g_nMultiReadResultCount = 0; mBarcodeReader = null; workPreviewTime = 0; } private void syncLicenseStrore(final boolean activate) { try { String buildDate = SystemPropertiesEx.get("ro.build.date.utc"); long defaultBuildTime = Long.parseLong(buildDate) * 1000; long currentTime = System.currentTimeMillis(); Log.d(TAG, " currentTime" + currentTime + " defaultBuildTime " + defaultBuildTime); if (currentTime < defaultBuildTime) { android.os.SystemClock.setCurrentTimeMillis(defaultBuildTime); } if (activate) { new Thread(new Runnable() { @Override public void run() { ActivationManager.syncLicenseStrore(activate); } }).start(); } } catch (Exception e) { e.printStackTrace(); } } public synchronized void openCamera(int cameraId) { state = State.DONE; if (mCameraManager != null && mCameraManager.isOpen()) { return; } try { Log.d(TAG, "openDriver = " + state); mCameraManager.openDriver(cameraId, new com.ubx.scanner.camera.CameraManager.StateCallback() { @Override public void onOpened(Camera camera) { synchronized (mDecodeLock) { if(camera !=null) { Point previewSize = mCameraManager.getPreviewResolution(); orientationDegress = mCameraManager.getDisplayOrientation(); MAX_FRAME_WIDTH = previewSize.x; MAX_FRAME_HEIGHT = previewSize.y; if(surfaceHolder != null) { try { mCameraManager.initializeCamera(surfaceHolder); mCameraManager.startPreview(); if(aimerIllumMode == 2 || aimerIllumMode == 3) { mCameraManager.setTorch(true); } } catch (IOException e) { e.printStackTrace(); } } } } } }); Log.d(TAG, "openDriver end ==================="); } catch (Exception e) { e.printStackTrace(); } } private void setDecodeParameter(int property) { int SDSProperty = -1; int val = 0; switch (property) { case PropertyID.TRIGGERING_MODES: CONTINUOUS_MODE = mParametersHelper.getIntProperty(PropertyID.TRIGGERING_MODES); break; case PropertyID.LOW_POWER_SLEEP_MODE: { int mode = mParametersHelper.getIntProperty(PropertyID.LOW_POWER_SLEEP_MODE); workDecodeTimeOutSleep = mode == 0 ? false : true; } break; case PropertyID.LOW_POWER_SLEEP_TIMEOUT: InactivityTimer_DURATION_MS = mParametersHelper.getIntProperty(PropertyID.LOW_POWER_SLEEP_TIMEOUT); if (InactivityTimer_DURATION_MS < 15 * 1000 || InactivityTimer_DURATION_MS > 60 * 1000) { InactivityTimer_DURATION_MS = 15 * 1000; } break; case PropertyID.TRANSMIT_CODE_ID: transmitCodeID = mParametersHelper.getIntProperty(PropertyID.TRANSMIT_CODE_ID); break; case PropertyID.LABEL_SEPARATOR_ENABLE: enableGroupSeperator = mParametersHelper.getIntProperty(PropertyID.LABEL_SEPARATOR_ENABLE); break; case PropertyID.LABEL_FORMAT_SEPARATOR_CHAR: groupSeperatorCharacters = mParametersHelper.getStringProperty(PropertyID.LABEL_FORMAT_SEPARATOR_CHAR); break; case PropertyID.LINEAR_CODE_TYPE_SECURITY_LEVEL: //delayDecode = property.get(keyForIndex); int seclevel = mParametersHelper.getIntProperty(PropertyID.LINEAR_CODE_TYPE_SECURITY_LEVEL); Log.d(TAG, "SECURITY_LEVEL= " + seclevel); if (mBarcodeReader != null) { if (seclevel > 1) { mBarcodeReader.setParameter(SDSProperties.SD_PROP_C128_SECURITY, 1); mBarcodeReader.setParameter(SDSProperties.SD_PROP_UPC_SECURITY, 1); } else { mBarcodeReader.setParameter(SDSProperties.SD_PROP_C128_SECURITY, 0); mBarcodeReader.setParameter(SDSProperties.SD_PROP_UPC_SECURITY, 0); } } break; case PropertyID.MULTI_DECODE_MODE: case PropertyID.BAR_CODES_TO_READ: val = mParametersHelper.getIntProperty(PropertyID.MULTI_DECODE_MODE); Log.d(TAG, "setProperties property multiDecodeMode= " + val); multiDecodeMode = val; if (mBarcodeReader != null) { mBarcodeReader.setParameter(PropertyID.MULTI_DECODE_MODE, val); g_nMaxMultiReadCount = mParametersHelper.getIntProperty(PropertyID.BAR_CODES_TO_READ); if (g_nMaxMultiReadCount <= 0) { g_nMaxMultiReadCount = 1; } if (multiDecodeMode > 0) { if (g_nMaxMultiReadCount > 5) { //单张图片解码数量,增加搜索条码时间 mBarcodeReader.setParameter(PropertyID.LASER_ON_TIME, 1500); } mBarcodeReader.setMultiReadCount(g_nMaxMultiReadCount); } else { mBarcodeReader.setMultiReadCount(1); } } break; case PropertyID.DEC_PICKLIST_AIM_MODE: val = mParametersHelper.getIntProperty(PropertyID.DEC_PICKLIST_AIM_MODE); if (mBarcodeReader != null) { mBarcodeReader.setParameter(PropertyID.DEC_PICKLIST_AIM_MODE, val); } break; case PropertyID.DEC_PICKLIST_AIM_DELAY: val = mParametersHelper.getIntProperty(PropertyID.DEC_PICKLIST_AIM_DELAY); if (mBarcodeReader != null) { if (val >= 0 && val <= 4000) { val = val; } mBarcodeReader.setParameter(PropertyID.DEC_PICKLIST_AIM_DELAY, val); } break; case PropertyID.FULL_READ_MODE: val = mParametersHelper.getIntProperty(PropertyID.FULL_READ_MODE); Log.d(TAG, "setProperties property fullReadMode= " + val); fullReadMode = val; if (mBarcodeReader != null) mBarcodeReader.setParameter(PropertyID.FULL_READ_MODE, val); break; case PropertyID.DEC_2D_LIGHTS_MODE: aimerIllumMode = mParametersHelper.getIntProperty(PropertyID.DEC_2D_LIGHTS_MODE); if (mBarcodeReader != null) mBarcodeReader.setParameter(PropertyID.DEC_2D_LIGHTS_MODE, aimerIllumMode); break; case PropertyID.DEC_ILLUM_POWER_LEVEL: int level = mParametersHelper.getIntProperty(PropertyID.DEC_ILLUM_POWER_LEVEL); Log.d(TAG, "setProperties property level= " + level); //updateLightGrade(level); if (mBarcodeReader != null) mBarcodeReader.setParameter(PropertyID.DEC_ILLUM_POWER_LEVEL, level); break; case PropertyID.DEC_DECODE_DELAY: delayMillis = mParametersHelper.getIntProperty(PropertyID.DEC_DECODE_DELAY); Log.d(TAG, "setProperties property delay= " + delayMillis); if (mBarcodeReader != null) mBarcodeReader.setParameter(PropertyID.DEC_DECODE_DELAY, delayMillis); break; case PropertyID.DEC_DECODE_DEBUG_MODE: saveMode = mParametersHelper.getIntProperty(PropertyID.DEC_DECODE_DEBUG_MODE); Log.d(TAG, "setProperties property saveMode= " + saveMode); if (mBarcodeReader != null) { if (saveMode >= 0 && saveMode <= 4) { mBarcodeReader.setParameter(PropertyID.DEC_DECODE_DEBUG_MODE, saveMode); } else { mBarcodeReader.setParameter(PropertyID.DEC_DECODE_DEBUG_MODE, 0); } } break; case PropertyID.DEC_Multiple_Decode_INTERVAL: intervalTime = mParametersHelper.getIntProperty(PropertyID.DEC_Multiple_Decode_INTERVAL); if (mBarcodeReader != null) { mBarcodeReader.setParameter(PropertyID.DEC_Multiple_Decode_INTERVAL, intervalTime); } break; case PropertyID.DEC_MaxMultiRead_COUNT: cotinuousMultiReadCount = mParametersHelper.getIntProperty(PropertyID.DEC_MaxMultiRead_COUNT); if (cotinuousMultiReadCount <= 2 || cotinuousMultiReadCount > 10) { cotinuousMultiReadCount = 2; } Log.d(TAG, "setProperties property cotinuousMultiReadCount= " + cotinuousMultiReadCount); break; case PropertyID.DEC_Multiple_Decode_MODE: ignoreRepeatMode = mParametersHelper.getIntProperty(PropertyID.DEC_Multiple_Decode_MODE); break; case PropertyID.DEC_Multiple_Decode_TIMEOUT: try { waitRepeatTimeOut = mParametersHelper.getIntProperty(PropertyID.DEC_Multiple_Decode_TIMEOUT); if (waitRepeatTimeOut < 50) { waitRepeatTimeOut = 10 * 1000; } Log.d(TAG, "setProperties property g_waitRepeatTimeOut= " + waitRepeatTimeOut); } catch (Exception e) { // TODO Auto-generated catch block } break; case PropertyID.DEC_EachImageAttempt_TIME: try { int attemptLimitTime = mParametersHelper.getIntProperty(PropertyID.DEC_EachImageAttempt_TIME); if (attemptLimitTime <= 30) { attemptLimitTime = 125; } mBarcodeReader.setDecodeAttemptLimit(attemptLimitTime); } catch (Exception e) { // TODO Auto-generated catch block } break; case PropertyID.LOW_CONTRAST_IMPROVED_ALGORITHM: try { int ALGMode = mParametersHelper.getIntProperty(PropertyID.LOW_CONTRAST_IMPROVED_ALGORITHM); ALGMode = ALGMode != 1 ? 0 : ALGMode; mBarcodeReader.setParameter(PropertyID.LOW_CONTRAST_IMPROVED_ALGORITHM, ALGMode); } catch (Exception e) { // TODO Auto-generated catch block } break; case PropertyID.LOW_CONTRAST_IMPROVED: SDSProperty = SDSProperties.SD_PROP_MISC_LOW_CONTRAST_IMPROVEMENTS; val = mParametersHelper.getIntProperty(PropertyID.LOW_CONTRAST_IMPROVED); Log.d(TAG, "lowCONTRASTMode= " + val); if (val < 0 || val > 5) { val = 0; } if (val == 3) { val = 4; } else if (val == 4) { val = 3; } else if (val == 5) { val = 7; } break; case PropertyID.AZTEC_ENABLE: case PropertyID.AZTEC_INVERSE: /*The property value is a bit field defined as follows: b0: Enable normal video Aztec decoding //1 b1: Enable inverse video Aztec decoding //2 b2: Enable Compact Aztec Code decoding ////4 0010 b3: Enable Full-Size Aztec Code decoding*///8 SDSProperty = SDSProperties.SD_PROP_AZ_ENABLED; int aztec = mParametersHelper.getIntProperty(PropertyID.AZTEC_ENABLE);//1 int inverse = mParametersHelper.getIntProperty(PropertyID.AZTEC_INVERSE);//2 if (aztec == 1) val += 9; if (aztec == 1 && inverse == 1) { val = 2; } else if (aztec == 1 && inverse == 2) { val += 2; } if (mBarcodeReader != null) { mBarcodeReader.setParameter(SDSProperties.SD_PROP_AZ_ENABLED, val); if (val != General.CONST_DISABLE) { val = mParametersHelper.getIntProperty(PropertyID.AZTEC_SYMBOL_SIZE); if(val < 0 || val > 1) { val = 1; } mBarcodeReader.setParameter(SDSProperties.SD_PROP_AZ_SYMBOL_SIZE, val); } SDSProperty = -1; } break; case PropertyID.AZTEC_SYMBOL_SIZE: SDSProperty = SDSProperties.SD_PROP_AZ_SYMBOL_SIZE; val = mParametersHelper.getIntProperty(PropertyID.AZTEC_SYMBOL_SIZE); if(val < 0 || val > 1) { val = 1; } break; case PropertyID.UPCA_ENABLE: SDSProperty = SDSProperties.UPCA_ENABLE; val = mParametersHelper.getIntProperty(PropertyID.UPCA_ENABLE); break; case PropertyID.UPCA_SEND_CHECK: SDSProperty = SDSProperties.UPCA_SEND_CHECK; val = mParametersHelper.getIntProperty(PropertyID.UPCA_SEND_CHECK); break; case PropertyID.UPCA_SEND_SYS: SDSProperty = SDSProperties.UPCA_SEND_SYS; val = mParametersHelper.getIntProperty(PropertyID.UPCA_SEND_SYS); break; case PropertyID.UPCA_TO_EAN13: SDSProperty = SDSProperties.UPCA_TO_EAN13; val = mParametersHelper.getIntProperty(PropertyID.UPCA_TO_EAN13); break; case PropertyID.UPCE_ENABLE: SDSProperty = SDSProperties.UPCE_ENABLE; val = mParametersHelper.getIntProperty(PropertyID.UPCE_ENABLE); break; case PropertyID.UPCE_TO_UPCA: SDSProperty = SDSProperties.UPCE_TO_UPCA; val = mParametersHelper.getIntProperty(PropertyID.UPCE_TO_UPCA); break; case PropertyID.UPCE_SEND_CHECK: SDSProperty = SDSProperties.UPCE_SEND_CHECK; val = mParametersHelper.getIntProperty(PropertyID.UPCE_SEND_CHECK); break; case PropertyID.UPCE_SEND_SYS: SDSProperty = SDSProperties.UPCE_SEND_SYS; val = mParametersHelper.getIntProperty(PropertyID.UPCE_SEND_SYS); break; case PropertyID.UPCE1_TO_UPCA: SDSProperty = SDSProperties.UPCE1_TO_UPCA; val = mParametersHelper.getIntProperty(PropertyID.UPCE1_TO_UPCA); break; case PropertyID.UPCE1_SEND_CHECK: SDSProperty = SDSProperties.UPCE1_SEND_CHECK; val = mParametersHelper.getIntProperty(PropertyID.UPCE1_SEND_CHECK); break; case PropertyID.UPCE1_SEND_SYS: SDSProperty = SDSProperties.UPCE1_SEND_SYS; val = mParametersHelper.getIntProperty(PropertyID.UPCE1_SEND_SYS); break; case PropertyID.UPCE1_ENABLE: SDSProperty = SDSProperties.UPCE1_ENABLE; val = mParametersHelper.getIntProperty(PropertyID.UPCE1_ENABLE); break; case PropertyID.EAN13_BOOKLANDEAN: SDSProperty = SDSProperties.EAN13_BOOKLANDEAN; val = mParametersHelper.getIntProperty(PropertyID.EAN13_BOOKLANDEAN); break; case PropertyID.EAN13_BOOKLAND_FORMAT: SDSProperty = SDSProperties.EAN13_BOOKLAND_FORMAT; val = mParametersHelper.getIntProperty(PropertyID.EAN13_BOOKLAND_FORMAT); break; /*case PropertyID.EAN13_SEND_CHECK: SDSProperty = SDSProperties.EAN13_SEND_CHECK; val = mParametersHelper.getIntProperty(PropertyID.EAN13_SEND_CHECK); break;*/ case PropertyID.EAN13_ENABLE: SDSProperty = SDSProperties.EAN13_ENABLE; val = mParametersHelper.getIntProperty(PropertyID.EAN13_ENABLE); break; case PropertyID.EAN8_ENABLE: SDSProperty = SDSProperties.EAN8_ENABLE; val = mParametersHelper.getIntProperty(PropertyID.EAN8_ENABLE); break; case PropertyID.EAN8_TO_EAN13: SDSProperty = SDSProperties.EAN8_TO_EAN13; val = mParametersHelper.getIntProperty(PropertyID.EAN8_TO_EAN13); break; case PropertyID.EAN_EXT_ENABLE_2_5_DIGIT: SDSProperty = SDSProperties.SD_PROP_UPC_SUPPLEMENTALS; val = mParametersHelper.getIntProperty(PropertyID.EAN_EXT_ENABLE_2_5_DIGIT); break; case PropertyID.CODE39_ENABLE: SDSProperty = SDSProperties.SD_PROP_C39_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.CODE39_ENABLE); break; case PropertyID.CODE39_FULL_ASCII: //会默认发送check char SDSProperty = SDSProperties.SD_PROP_C39_FULL_ASCII; val = mParametersHelper.getIntProperty(PropertyID.CODE39_FULL_ASCII); break; case PropertyID.CODE39_ENABLE_CHECK: case PropertyID.CODE39_SEND_CHECK: { SDSProperty = SDSProperties.SD_PROP_C39_CHECKSUM; int enableCk = mParametersHelper.getIntProperty(PropertyID.CODE39_ENABLE_CHECK); int sendCk = mParametersHelper.getIntProperty(PropertyID.CODE39_SEND_CHECK); if (enableCk == 1 && sendCk == 1) { val = 1;//文档描述有误 } else if (enableCk == 1) { val = 2; } else { val = 0; } } break; case PropertyID.TRIOPTIC_ENABLE: SDSProperty = SDSProperties.SD_PROP_TRIOPTIC_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.TRIOPTIC_ENABLE); break; case PropertyID.CODE128_ENABLE: SDSProperty = SDSProperties.SD_PROP_C128_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.CODE128_ENABLE); break; case PropertyID.CODE_ISBT_128: SDSProperty = SDSProperties.ISBT128_ENABLE; val = mParametersHelper.getIntProperty(PropertyID.CODE_ISBT_128); break; case PropertyID.CODE128_GS1_ENABLE: //gs1-128 SDSProperty = SDSProperties.GS1_128_ENABLE; val = mParametersHelper.getIntProperty(PropertyID.CODE128_GS1_ENABLE); break; case PropertyID.I25_ENABLE: SDSProperty = SDSProperties.SD_PROP_I25_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.I25_ENABLE); break; case PropertyID.I25_ENABLE_CHECK: case PropertyID.I25_SEND_CHECK: { //0: Disable checksum checking. 1 //1: Enable checksum checking. 2 //2: Enable checksum checking and strip the checksum from the result string. 4 SDSProperty = SDSProperties.SD_PROP_I25_CHECKSUM; int enableCk = mParametersHelper.getIntProperty(PropertyID.I25_ENABLE_CHECK); int sendCk = mParametersHelper.getIntProperty(PropertyID.I25_SEND_CHECK); if (enableCk == 1 && sendCk == 1) { val = 1;//文档描述有误 } else if (enableCk == 1) { val = 2; } else { val = 0; } } break; case PropertyID.I25_TO_EAN13: SDSProperty = SDSProperties.I25_TO_EAN13; val = mParametersHelper.getIntProperty(PropertyID.I25_TO_EAN13); break; case PropertyID.CODABAR_ENABLE: SDSProperty = SDSProperties.SD_PROP_CB_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.CODABAR_ENABLE); break; case PropertyID.CODABAR_NOTIS: SDSProperty = SDSProperties.CODABAR_NOTIS_Editing; val = mParametersHelper.getIntProperty(PropertyID.CODABAR_NOTIS); break; case PropertyID.CODABAR_CLSI: SDSProperty = SDSProperties.CODABAR_CLSI_Editing; val = mParametersHelper.getIntProperty(PropertyID.CODABAR_CLSI); break; /*case PropertyID.CODABAR_ENABLE_CHECK: case PropertyID.CODABAR_SEND_CHECK: SDSProperty = SDSProperties.SD_PROP_CB_CHECKSUM; int enableCk = mParametersHelper.getIntProperty(PropertyID.CODABAR_ENABLE_CHECK); int sendCk = mParametersHelper.getIntProperty(PropertyID.CODABAR_SEND_CHECK); if(sendCk == 1) { val = 2; } else if(enableCk == 1) { val = 1; } else { val = 0; } break;*/ case PropertyID.GS1_14_ENABLE: case PropertyID.GS1_EXP_ENABLE: case PropertyID.GS1_LIMIT_ENABLE: SDSProperty = SDSProperties.SD_PROP_RSS_ENABLED; /*The property value is a bit field defined as follows: b0: Enable GS1 Databar Expanded decoding. 1 b1: Enable GS1 Databar Expanded Stacked decoding. 2 b2: Enable GS1 Databar Limited decoding. 4 b3: Enable GS1 Databar Omnidirectional and GS1 Databar truncated decoding. 8 b4: Enable GS1 Databar Stacked Omnidirectional and GS1 Databar Stacked decoding. 16*/ int gs1_trun = mParametersHelper.getIntProperty(PropertyID.GS1_14_ENABLE);//8//10 int exp_val = mParametersHelper.getIntProperty(PropertyID.GS1_EXP_ENABLE);//1 int limit_val = mParametersHelper.getIntProperty(PropertyID.GS1_LIMIT_ENABLE);//4 0010 if (gs1_trun == 1 && exp_val == 1 && limit_val == 1) { val = 0x1F;//31 } else { if (gs1_trun == 1) val += 24; if (exp_val == 1) val += 3; if (limit_val == 1) val += 4; } break; case PropertyID.GS1_14_TO_UPC_EAN: SDSProperty = SDSProperties.GS1_DATABAR14_TO_UPC; val = mParametersHelper.getIntProperty(PropertyID.GS1_14_TO_UPC_EAN); break; case PropertyID.MSI_ENABLE: case PropertyID.MSI_CHECK_2_MOD_11: case PropertyID.MSI_REQUIRE_2_CHECK: case PropertyID.MSI_SEND_CHECK: { /* default 0 文档有误 0: Disable checksum checking. 1: Enable a single mod 10 checksum check. 2: Enable a mod 11 and a mod 10 checksum check. 3: Enable two mod 10 checksum checks. 5: Enable a single mod 10 checksum check and strip the checksum 6: Enable a mod 11 and a mod 10 checksum check and strip the checksums 7: Enable two mod 10 checksum checks and strip the checksums */ if (mBarcodeReader != null) { int msiEnable = mParametersHelper.getIntProperty(PropertyID.MSI_ENABLE); mBarcodeReader.setParameter(SDSProperties.SD_PROP_MSIP_ENABLED, msiEnable); if (msiEnable == 1) { SDSProperty = SDSProperties.SD_PROP_MSIP_CHECKSUM; int ckDigitAlg = mParametersHelper.getIntProperty(PropertyID.MSI_CHECK_2_MOD_11); int ckDigitMode = mParametersHelper.getIntProperty(PropertyID.MSI_REQUIRE_2_CHECK); int sendCK = mParametersHelper.getIntProperty(PropertyID.MSI_SEND_CHECK); if (sendCK == 1) { if (ckDigitMode == 0) { //one check; val = 1; } else { //two check; if (ckDigitAlg == 0) { //MOD 10/MOD 11 val = 2; } else { //MOD 10/MOD 10 val = 3; } } } else { if (ckDigitMode == 0) { //one check; val = 5; } else { //two check; if (ckDigitAlg == 0) { //MOD 10/MOD 11 val = 6; } else { //MOD 10/MOD 10 val = 7; } } } //value is ignored if MSI Plessey is disable mBarcodeReader.setParameter(SDSProperties.SD_PROP_MSIP_CHECKSUM, val); } SDSProperty = -1; } } break; case PropertyID.PDF417_ENABLE: SDSProperty = SDSProperties.SD_PROP_PDF_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.PDF417_ENABLE); break; case PropertyID.MICROPDF417_ENABLE: SDSProperty = SDSProperties.SD_PROP_MICROPDF_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.MICROPDF417_ENABLE); break; case PropertyID.MAXICODE_ENABLE: SDSProperty = SDSProperties.SD_PROP_MC_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.MAXICODE_ENABLE); if (val == 1) { val = 0x3f; } if (mBarcodeReader != null) { mBarcodeReader.setParameter(SDSProperties.SD_PROP_MC_ENABLED, val); if (val != General.CONST_DISABLE) { val = mParametersHelper.getIntProperty(PropertyID.MAXICODE_SYMBOL_SIZE); if(val < 0 || val > 1) { val = 1; } mBarcodeReader.setParameter(SDSProperties.SD_PROP_MC_SYMBOL_SIZE, val); } SDSProperty = -1; } break; case PropertyID.MAXICODE_SYMBOL_SIZE: SDSProperty = SDSProperties.SD_PROP_MC_SYMBOL_SIZE; val = mParametersHelper.getIntProperty(PropertyID.MAXICODE_SYMBOL_SIZE); if(val < 0 || val > 1) { val = 1; } break; case PropertyID.QRCODE_ENABLE: case PropertyID.QRCODE_INVERSE: case PropertyID.MICROQRCODE_ENABLE: SDSProperty = SDSProperties.SD_PROP_QR_ENABLED; int qr = mParametersHelper.getIntProperty(PropertyID.QRCODE_ENABLE);//1 int qrInverse = mParametersHelper.getIntProperty(PropertyID.QRCODE_INVERSE);//2/3 int micqr = mParametersHelper.getIntProperty(PropertyID.MICROQRCODE_ENABLE);//2/3 val = General.CONST_DISABLE; /*b0: Enable normal video QR Code decoding. qr + 1 b1: Enable inverse video QR Code decoding. qr +2 b2: Enable normal video Micro QR Code decoding. qr + 4 b3: Enable inverse video Micro QR Code decoding. qr + 8*/ if (qr == 1) { val = General.CONST_ENABLE; } if (qrInverse == 1 && qr == 1) { val = 2; } else if (qrInverse == 2 && qr == 1) { val += 2; } if (micqr == 1) { val += 4; if (qrInverse > 0) { val += 8; } } if (mBarcodeReader != null) { mBarcodeReader.setParameter(SDSProperties.SD_PROP_QR_ENABLED, val); if (val != General.CONST_DISABLE) { val = mParametersHelper.getIntProperty(PropertyID.QRCODE_SYMBOL_SIZE); if(val < 0 || val > 1) { val = 1; } mBarcodeReader.setParameter(SDSProperties.SD_PROP_QR_SYMBOL_SIZE, val); } SDSProperty = -1; } break; case PropertyID.QRCODE_SYMBOL_SIZE: SDSProperty = SDSProperties.SD_PROP_QR_SYMBOL_SIZE; val = mParametersHelper.getIntProperty(PropertyID.QRCODE_SYMBOL_SIZE); if(val < 0 || val > 1) { val = 1; } break; case PropertyID.DATAMATRIX_SYMBOL_SIZE: SDSProperty = SDSProperties.SD_PROP_DM_SYMBOL_SIZE; val = mParametersHelper.getIntProperty(PropertyID.DATAMATRIX_SYMBOL_SIZE); if(val < 0 || val > 2) { val = 1; } break; case PropertyID.DATAMATRIX_ENABLE: case PropertyID.DATAMATRIX_INVERSE: SDSProperty = SDSProperties.SD_PROP_DM_ENABLED; int dm = mParametersHelper.getIntProperty(PropertyID.DATAMATRIX_ENABLE);//1 int dmInverse = mParametersHelper.getIntProperty(PropertyID.DATAMATRIX_INVERSE);//2/3 //b0: Enable normal video Data Matrix decoding //b1: Enable inverse video Data Matrix decoding if (dm == 1) { val = 1; } if (dm == 1 && dmInverse == 1) { val = 2; } else if (dm == 1 && dmInverse == 2) { val += 2; } if (mBarcodeReader != null) { mBarcodeReader.setParameter(SDSProperties.SD_PROP_DM_ENABLED, val); if (val != General.CONST_DISABLE) { val = mParametersHelper.getIntProperty(PropertyID.DATAMATRIX_SYMBOL_SIZE); if(val < 0 || val > 2) { val = 1; } mBarcodeReader.setParameter(SDSProperties.SD_PROP_DM_SYMBOL_SIZE, val); } SDSProperty = -1; } break; case PropertyID.DPM_DECODE_MODE: SDSProperty = SDSProperties.SD_PROP_DPM_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.DPM_DECODE_MODE); break; case PropertyID.HANXIN_ENABLE: SDSProperty = SDSProperties.SD_PROP_HX_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.HANXIN_ENABLE); break; case PropertyID.M25_ENABLE: SDSProperty = SDSProperties.SD_PROP_M25_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.M25_ENABLE); break; case PropertyID.D25_ENABLE: //Straight 2 of 5 (with 2 bar start/stop codes) is also known as: Standard 2 of 5, IATA 2 of 5, and Airline 2 of 5 SDSProperty = SDSProperties.SD_PROP_S25_2SS_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.D25_ENABLE); try { if (mBarcodeReader != null) { mBarcodeReader.setParameter(SDSProperty, val); } } catch (Exception e) { // TODO Auto-generated catch block } //Straight 2 of 5 (with 3 bar start/stop codes) is also known as: Industrial 2 of 5, Code 2 of 5, and Discrete 2 of 5 SDSProperty = SDSProperties.SD_PROP_S25_3SS_ENABLED; break; case PropertyID.CODE11_ENABLE: SDSProperty = SDSProperties.SD_PROP_C11_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.CODE11_ENABLE); break; case PropertyID.CODE11_ENABLE_CHECK: case PropertyID.CODE11_SEND_CHECK: SDSProperty = SDSProperties.SD_PROP_C11_CHECKSUM; //0 two check digits;1 one check; 2 two check and stripped form result data; 3 one check and stripped form result data /*boolean enableCK = mParametersHelper.getIntProperty(PropertyID.CODE11_ENABLE_CHECK) == 1; if(enableCK) { val = mParametersHelper.getIntProperty(PropertyID.CODE11_SEND_CHECK); } else { val = 4; }*/ val = mParametersHelper.getIntProperty(PropertyID.CODE11_SEND_CHECK); break; case PropertyID.CODE93_ENABLE: SDSProperty = SDSProperties.SD_PROP_C93_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.CODE93_ENABLE); break; case PropertyID.DOTCODE_ENABLE: SDSProperty = SDSProperties.SD_PROP_DOTCODE_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.DOTCODE_ENABLE); break; case PropertyID.DEC_OCR_MODE: case PropertyID.DEC_OCR_TEMPLATE: case PropertyID.DEC_OCR_USER_TEMPLATE: { setOcrSettings(); break; } case PropertyID.C25_ENABLE: SDSProperty = SDSProperties.SD_PROP_HK25_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.C25_ENABLE); break; case PropertyID.US_POSTNET_ENABLE: case PropertyID.US_PLANET_ENABLE: case PropertyID.US_POSTAL_SEND_CHECK: case PropertyID.USPS_4STATE_ENABLE: case PropertyID.UPU_FICS_ENABLE: case PropertyID.ROYAL_MAIL_ENABLE: case PropertyID.ROYAL_MAIL_SEND_CHECK: case PropertyID.AUSTRALIAN_POST_ENABLE: case PropertyID.KIX_CODE_ENABLE: case PropertyID.JAPANESE_POST_ENABLE: SDSProperty = -1; disablePostalSymbologies(); break; case PropertyID.KOREA_POST_ENABLE: SDSProperty = SDSProperties.SD_PROP_KP_ENABLED; val = mParametersHelper.getIntProperty(PropertyID.KOREA_POST_ENABLE); break; case PropertyID.CODABAR_LENGTH1: SDSProperty = SDSProperties.CODABAR_LENGTH1; val = mParametersHelper.getIntProperty(PropertyID.CODABAR_LENGTH1); break; case PropertyID.CODABAR_LENGTH2: SDSProperty = SDSProperties.CODABAR_LENGTH2; val = mParametersHelper.getIntProperty(PropertyID.CODABAR_LENGTH2); break; case PropertyID.CODE11_LENGTH1: SDSProperty = SDSProperties.CODE11_LENGTH1; val = mParametersHelper.getIntProperty(PropertyID.CODE11_LENGTH1); break; case PropertyID.CODE11_LENGTH2: SDSProperty = SDSProperties.CODE11_LENGTH2; val = mParametersHelper.getIntProperty(PropertyID.CODE11_LENGTH2); break; case PropertyID.CODE39_LENGTH1: SDSProperty = SDSProperties.CODE39_LENGTH1; val = mParametersHelper.getIntProperty(PropertyID.CODE39_LENGTH1); break; case PropertyID.CODE39_LENGTH2: SDSProperty = SDSProperties.CODE39_LENGTH2; val = mParametersHelper.getIntProperty(PropertyID.CODE39_LENGTH2); break; case PropertyID.CODE93_LENGTH1: SDSProperty = SDSProperties.CODE93_LENGTH1; val = mParametersHelper.getIntProperty(PropertyID.CODE93_LENGTH1); break; case PropertyID.CODE93_LENGTH2: SDSProperty = SDSProperties.CODE93_LENGTH2; val = mParametersHelper.getIntProperty(PropertyID.CODE93_LENGTH2); break; case PropertyID.I25_LENGTH1: SDSProperty = SDSProperties.I25_LENGTH1; val = mParametersHelper.getIntProperty(PropertyID.I25_LENGTH1); break; case PropertyID.I25_LENGTH2: SDSProperty = SDSProperties.I25_LENGTH2; val = mParametersHelper.getIntProperty(PropertyID.I25_LENGTH2); break; case PropertyID.CODE128_LENGTH1: SDSProperty = SDSProperties.CODE128_LENGTH1; val = mParametersHelper.getIntProperty(PropertyID.CODE128_LENGTH1); break; case PropertyID.CODE128_LENGTH2: SDSProperty = SDSProperties.CODE128_LENGTH2; val = mParametersHelper.getIntProperty(PropertyID.CODE128_LENGTH2); break; case PropertyID.D25_LENGTH1: SDSProperty = SDSProperties.D25_LENGTH1; val = mParametersHelper.getIntProperty(PropertyID.D25_LENGTH1); break; case PropertyID.D25_LENGTH2: SDSProperty = SDSProperties.D25_LENGTH2; val = mParametersHelper.getIntProperty(PropertyID.D25_LENGTH2); break; case PropertyID.MSI_LENGTH1: SDSProperty = SDSProperties.MSI_LENGTH1; val = mParametersHelper.getIntProperty(PropertyID.MSI_LENGTH1); break; case PropertyID.MSI_LENGTH2: SDSProperty = SDSProperties.MSI_LENGTH2; val = mParametersHelper.getIntProperty(PropertyID.MSI_LENGTH2); break; case PropertyID.GS1_EXP_LENGTH1: SDSProperty = SDSProperties.GS1_EXP_LENGTH1; val = mParametersHelper.getIntProperty(PropertyID.GS1_EXP_LENGTH1); break; case PropertyID.GS1_EXP_LENGTH2: SDSProperty = SDSProperties.GS1_EXP_LENGTH2; val = mParametersHelper.getIntProperty(PropertyID.GS1_EXP_LENGTH2); break; case PropertyID.DEC_2D_CENTERING_ENABLE: case PropertyID.DEC_2D_CENTERING_MODE: case PropertyID.DEC_2D_WINDOW_UPPER_LX: case PropertyID.DEC_2D_WINDOW_UPPER_LY: case PropertyID.DEC_2D_WINDOW_LOWER_RX: case PropertyID.DEC_2D_WINDOW_LOWER_RY: setDecodeWindowSettings(); break; default: SDSProperty = -1; break; } if (SDSProperty != -1 && mBarcodeReader != null) { mBarcodeReader.setParameter(SDSProperty, val); SDSProperty = -1; } } /** * Sets the Decoder settings based on user preferences * (412, 316), (420,324),Field of View,(一定要选择这个) */ private void setDecodeWindowSettings() { if (mBarcodeReader != null) { // Windowing boolean enable_windowing = mParametersHelper.getIntProperty(PropertyID.DEC_2D_CENTERING_ENABLE) == 1; try { if (enable_windowing) { int mode = mParametersHelper.getIntProperty(PropertyID.DEC_2D_CENTERING_MODE); mode = mode >= 0 && mode <= 2 ? mode : 0; mBarcodeReader.setParameter(PropertyID.DEC_2D_CENTERING_MODE, mode); if (mode == DecodeWindowMode.DECODE_WINDOW_MODE_FIELD_OF_VIEW.toInt()) { int UpperLeftX = mParametersHelper.getIntProperty(PropertyID.DEC_2D_WINDOW_UPPER_LX); int UpperLeftY = mParametersHelper.getIntProperty(PropertyID.DEC_2D_WINDOW_UPPER_LY); int LowerRightX = mParametersHelper.getIntProperty(PropertyID.DEC_2D_WINDOW_LOWER_RX); int LowerRightY = mParametersHelper.getIntProperty(PropertyID.DEC_2D_WINDOW_LOWER_RY); if (UpperLeftX < 0) UpperLeftX = 0; if (UpperLeftY < 0) UpperLeftY = 0; if (LowerRightX < 20) LowerRightX = 1280; if (LowerRightY < 20) LowerRightY = 720; Log.d(TAG, "nMode = " + mode + " upX " + UpperLeftX + " upY " + UpperLeftY + " lwX " + LowerRightX + " lwY " + LowerRightY); // enable the mode //镜像处理坐标转换 int X = MAX_FRAME_WIDTH - UpperLeftX; int Y = MAX_FRAME_HEIGHT - UpperLeftY; UpperLeftX = MAX_FRAME_WIDTH - LowerRightX; UpperLeftY = MAX_FRAME_HEIGHT - LowerRightY; LowerRightX = X; LowerRightY = Y; mBarcodeReader.setParameter(SDSProperties.UpperLeftX, UpperLeftX); mBarcodeReader.setParameter(SDSProperties.UpperLeftY, UpperLeftY); mBarcodeReader.setParameter(SDSProperties.LowerRightX, LowerRightX); mBarcodeReader.setParameter(SDSProperties.LowerRightY, LowerRightY); } } else { // disable windowing mBarcodeReader.setParameter(PropertyID.DEC_2D_CENTERING_MODE, DecodeWindowMode.DECODE_WINDOW_MODE_OFF.toInt()); } } catch (Exception e) { } } } /** * Sets the OCR settings based on user preferences * The property value is a bit field defined as follows: * b0: User * b1: Passport * b2: ISBN * b3: Price Field * b4: MICR E-13B * enable theUser template along with ISBN * SD_Set(Handle, SD_PROP_OCR_ACTIVE_TEMPLATES, (void *)(1+4)); */ private void setOcrSettings() { Log.d(TAG, "SetOcrSettings++"); if (mBarcodeReader != null) { int ocr_mode = 0; int ocr_template = 0; byte[] ocr_user_defined_template; ocr_mode = mParametersHelper.getIntProperty(PropertyID.DEC_OCR_MODE); ocr_template = mParametersHelper.getIntProperty(PropertyID.DEC_OCR_TEMPLATE); String userDefinedTemplate = mParametersHelper.getStringProperty(PropertyID.DEC_OCR_USER_TEMPLATE); Log.d(TAG, "SetOcrSettings++ ocr_mode " + ocr_mode); if (ocr_mode > 0) { mBarcodeReader.setOCRMode(ocr_mode); if (ocr_template == 0) ocr_template = OCRActiveTemplate.USER; else if (ocr_template == 1) ocr_template = OCRActiveTemplate.PASSPORT; else if (ocr_template == 2) ocr_template = OCRActiveTemplate.ISBN; else if (ocr_template == 3) ocr_template = OCRActiveTemplate.PRICE_FIELD; else if (ocr_template == 4) ocr_template = OCRActiveTemplate.MICR;//MICRE13B Log.d(TAG, "SetOcrSettings++ ocr_template " + ocr_template); mBarcodeReader.setOCRTemplates(ocr_template); //default Template "13777777770" to bytearrary 01 03 07 07 07 07 00 //1,2,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,8,8,0 if (ocr_template == OCRActiveTemplate.USER) { try { //userDefinedTemplate = "1.3777778E10"; if (userDefinedTemplate != null && !userDefinedTemplate.equals("")) { ocr_user_defined_template = userDefinedTemplate.getBytes(); //char[] templateChar = userDefinedTemplate.toCharArray(); for (int i = 0; i < ocr_user_defined_template.length; i++) { if (ocr_user_defined_template[i] >= 48) ocr_user_defined_template[i] = (byte) (ocr_user_defined_template[i] - 48); } mBarcodeReader.setOCRUserTemplate(ocr_user_defined_template); } } catch (Exception e) { } } } else { mBarcodeReader.setParameter(SDSProperties.SD_PROP_OCR_ENABLED, 0); } Log.d(TAG, "SetOcrSettings--"); } } private void disablePostalSymbologies() { if (mBarcodeReader != null) { int SDSProperty = SDSProperties.SD_PROP_POSTAL_ENABLED; int PLANET = mParametersHelper.getIntProperty(PropertyID.US_PLANET_ENABLE); int POSTNET = mParametersHelper.getIntProperty(PropertyID.US_POSTNET_ENABLE); int UPS4 = mParametersHelper.getIntProperty(PropertyID.USPS_4STATE_ENABLE); int UPU_FICS = mParametersHelper.getIntProperty(PropertyID.UPU_FICS_ENABLE); int ROYAL = mParametersHelper.getIntProperty(PropertyID.ROYAL_MAIL_ENABLE); int AUSTRALIAN = mParametersHelper.getIntProperty(PropertyID.AUSTRALIAN_POST_ENABLE); int KIX = mParametersHelper.getIntProperty(PropertyID.AUSTRALIAN_POST_ENABLE); int JAPANESE = mParametersHelper.getIntProperty(PropertyID.AUSTRALIAN_POST_ENABLE); if (PLANET == 0 && POSTNET == 0 && UPS4 == 0 && UPU_FICS == 0 && AUSTRALIAN == 0 && KIX == 0 && ROYAL == 0 && JAPANESE == 0) { mBarcodeReader.setParameter(SDSProperty, 0); } else { if (PLANET == 1 && POSTNET == 1) { mBarcodeReader.setParameter(SDSProperty, 0x2081); } else if (PLANET == 1) { mBarcodeReader.setParameter(SDSProperty, General.SD_CONST_PL); } else if (POSTNET == 1) { mBarcodeReader.setParameter(SDSProperty, General.SD_CONST_PN); } if (UPS4 == 1) { mBarcodeReader.setParameter(SDSProperty, General.SD_CONST_UPU); } if (UPU_FICS == 1) { mBarcodeReader.setParameter(SDSProperty, General.SD_CONST_USPS4CB); } if (ROYAL == 1) { mBarcodeReader.setParameter(SDSProperty, General.SD_CONST_RM); } if (AUSTRALIAN == 1) { mBarcodeReader.setParameter(SDSProperty, General.SD_CONST_AP); } if (KIX == 1) { mBarcodeReader.setParameter(SDSProperty, General.SD_CONST_RM + 1); } if (JAPANESE == 1) { mBarcodeReader.setParameter(SDSProperty, General.SD_CONST_JP); } //暂时开启这几种postal mBarcodeReader.setParameter(SDSProperty, General.SD_CONST_PL + General.SD_CONST_PN + General.SD_CONST_USPS4CB + General.SD_CONST_UPU + 1); } } } boolean compareBytes(byte[] data, int length) { int i = 0; if (length != preDecodeData.length) return false; for (i = 0; i < length && i < preDecodeData.length; i++) { if (preDecodeData[i] != data[i]) { return false; } } return true; } boolean compareBytes(byte[] preData, byte[] data, int length) { int i = 0; if (length != preData.length) return false; for (i = 0; i < length && i < preData.length; i++) { if (preData[i] != data[i]) { return false; } } return true; } private void sendBarcodeAndBitmapBroadcast(String barcode, byte[] m_frameData, int[] bounds) { try { Intent intent = new Intent(BROADCAST_IMAGE_ACTION); if (barcode != null) { //Log.d(TAG , "onDecodeComplete , --------- barcode:" + barcode); intent.putExtra("barcode", barcode); } if (bounds != null) { //镜像坐标转换 for (int i = 0; i < bounds.length; i++) { if (i % 2 == 0) { bounds[i] = MAX_FRAME_WIDTH - bounds[i]; } else { bounds[i] = MAX_FRAME_HEIGHT - bounds[i]; } } if (rotateImage == 90) { /*旋转-90*/ for (int i = 0; i < bounds.length; ) { int temp = bounds[i]; bounds[i] = MAX_FRAME_HEIGHT - bounds[i + 1]; bounds[i + 1] = temp; i = i + 2; } } intent.putExtra("bounds", bounds); intent.putExtra("x1", bounds[0]); intent.putExtra("y1", bounds[1]); intent.putExtra("x2", bounds[2]); intent.putExtra("y2", bounds[3]); intent.putExtra("x3", bounds[4]); intent.putExtra("y3", bounds[5]); intent.putExtra("x4", bounds[6]); intent.putExtra("y4", bounds[7]); intent.putExtra("cx", bounds[8]); intent.putExtra("cy", bounds[9]); } if (m_frameData != null) { if (m_frameData != null && m_frameData.length > 0) { Bitmap modBm = createBitmap(m_frameData, rotateImage); if (modBm != null) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); modBm.compress(Bitmap.CompressFormat.JPEG, 50, baos); modBm.recycle(); modBm = null; intent.putExtra(BROADCAST_IMAGE_ACTION_EXTRAS, baos.toByteArray()); } else { intent.putExtra(BROADCAST_IMAGE_ACTION_EXTRAS, new byte[1]); } } else { intent.putExtra(BROADCAST_IMAGE_ACTION_EXTRAS, new byte[1]); } } mContextWrapper.sendBroadcastAsUser(intent, UserHandleEx.ALL); } catch (Exception e) { e.printStackTrace(); } } private Bitmap createBitmap(byte[] m_frameData, int rotate) { try { if (headOfByte == null) { InputStream localInputStream = new FileInputStream(new File("etc/bh_1280_720.bin")); ; //InputStream localInputStream =mContextWrapper.getResources().openRawResource(R.raw.bh_1280_720); headOfByte = Utils.readFileHead(localInputStream); if (headOfByte != null) { bitmapByte = new byte[headOfByte.length + MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT]; } } if (headOfByte != null) { if (m_frameData != null && m_frameData.length > 0 && bitmapByte != null) { System.arraycopy(headOfByte, 0, bitmapByte, 0, headOfByte.length); System.arraycopy(m_frameData, 0, bitmapByte, headOfByte.length, MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT); Bitmap yuvbitmap = BitmapFactory.decodeStream(new ByteArrayInputStream(bitmapByte)).copy(Bitmap.Config.RGB_565, true); if (yuvbitmap != null) { Log.d(TAG, "createBitmap "); Bitmap modBm = Bitmap.createBitmap(yuvbitmap.getWidth(), yuvbitmap.getHeight(), yuvbitmap.getConfig()); Matrix matrix = new Matrix(); Canvas canvas = new Canvas(modBm); Paint paint = new Paint(); //绘制矩阵 Matrix主要用于对平面进行平移(Translate),缩放(Scale),旋转(Rotate)以及斜切(Skew)操作。 //matrix.setRotate(90,modBm.getWidth()/2,modBm.getHeight()/2); // matrix.setTranslate(20,20); //镜子效果: matrix.setScale(-1, 1);//翻转 matrix.postTranslate(yuvbitmap.getWidth(), 0); canvas.drawBitmap(yuvbitmap, matrix, paint); yuvbitmap.recycle(); yuvbitmap = null; /*旋转-90*/ if (rotate > 0 && rotate <= 270) { matrix.reset(); matrix.setRotate(rotate); // 重新绘制Bitmap Bitmap rotateBm = Bitmap.createBitmap(modBm, 0, 0, modBm.getWidth(), modBm.getHeight(), matrix, true); modBm.recycle(); modBm = null; return rotateBm; } else { return modBm; } } } } } catch (Exception e) { e.printStackTrace(); } return null; } private void receivedMultipleData() { if (transmitCodeID > 0 || enableGroupSeperator == 1) { ArrayList<DecodeData> composeDecodeDataArray = new ArrayList<DecodeData>(); for (DecodeData firstResult : decodeDataArray) { byte[] byteArraryData = firstResult.getBarcodeDataBytes(); byte[] barcodeDataBytes = composeAISeparator(firstResult.getAIMCodeLetter(), firstResult.getAIMModifier(), firstResult.getCodeId(), byteArraryData, firstResult.getBarcodeDataLength()); if (barcodeDataBytes != null) { firstResult.setBarcodeDataBytes(barcodeDataBytes); firstResult.setBarcodeDataLength(barcodeDataBytes.length); } composeDecodeDataArray.add(firstResult); } decodeDataArray.clear(); if (mOnScannedListener != null) { mOnScannedListener.receivedMultipleDecodedData(composeDecodeDataArray); } } else { if (mOnScannedListener != null) { mOnScannedListener.receivedMultipleDecodedData(decodeDataArray); } } } private byte[] composeAISeparator(byte aimID, byte aimModifier, int codeID, byte[] barcodeByteData, int dataLength) { try { byte[] aiSeparatorData = null; if (enableGroupSeperator == 1) { byte[] sepBytes = null; if (!TextUtils.isEmpty(groupSeperatorCharacters)) { sepBytes = groupSeperatorCharacters.getBytes(); } aiSeparatorData = GS1DataParser.addAISeparator(aimID, aimModifier, barcodeByteData, sepBytes); } if (transmitCodeID > 0) { try { byte[] byteArraryData; if (transmitCodeID == 1) { if (aiSeparatorData != null && aiSeparatorData.length > 0) { byteArraryData = new byte[3 + aiSeparatorData.length]; System.arraycopy(aiSeparatorData, 0, byteArraryData, 3, aiSeparatorData.length); } else { byteArraryData = new byte[3 + barcodeByteData.length]; System.arraycopy(barcodeByteData, 0, byteArraryData, 3, barcodeByteData.length); } byteArraryData[0] = ']'; byteArraryData[1] = aimID; byteArraryData[2] = aimModifier; } else { if (aiSeparatorData != null && aiSeparatorData.length > 0) { byteArraryData = new byte[1 + aiSeparatorData.length]; System.arraycopy(aiSeparatorData, 0, byteArraryData, 1, aiSeparatorData.length); } else { byteArraryData = new byte[1 + barcodeByteData.length]; System.arraycopy(barcodeByteData, 0, byteArraryData, 1, barcodeByteData.length); } byteArraryData[0] = (byte)codeID; } return byteArraryData; } catch (Exception e) { e.printStackTrace(); } } else { return aiSeparatorData; } } catch (Exception e) { e.printStackTrace(); } return null; } private void quitHandlerThreadLocked() { try { if (mDecodeHandlerThread != null) { if (mDecodeHandler != null) { Message m = Message.obtain(mDecodeHandler,MESSAGE_RELEASE_QUIT); mDecodeHandler.sendMessage(m); } mDecodeHandlerThread.getLooper().quitSafely(); try { // Wait for the thread to quit. An ANR waiting to happen. mDecodeHandlerThread.join(); } catch (InterruptedException ex) { ex.printStackTrace(); } mDecodeHandler = null; mDecodeHandlerThread.interrupt(); mDecodeHandlerThread = null; } } catch (Exception e) { Log.e(TAG, "quitHandlerThread " + e.getMessage()); e.printStackTrace(); } mDecodeHandlerThread = null; mDecodeHandler = null; } /** * 私有构造函数,禁止外部直接实例化。 */ private LegacyCamera2Scanner(Context context) throws ReaderException { super(context); mContext = context.getApplicationContext(); // 建议使用应用级Context mContextWrapper = context; mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); mUiHandler = new LegacyCamera2Scanner.CaptureHandler(mContextWrapper.getMainLooper()); // 这里不主动 open(),由外部决定什么时候打开/初始化 // 仅做一些基础的初始化(如后台线程) startBackgroundThread(); mDecodeHandlerThread = new DecodeHandlerThread("Overlay-scan"); mDecodeHandlerThread.startThread(); open(-1); // 初始化UI布局:预览布局 & 悬浮按钮 createCameraPreviewLayout(); createFloatingButton(); } public LegacyCamera2Scanner(Context context, int type) { super(context); mScannerType = type; mContextWrapper = context; mContext = context; mUiHandler = new CaptureHandler(mContextWrapper.getMainLooper()); syncLicenseStrore(false); IntentFilter filter = new IntentFilter(); filter.addAction(CAPTURE_IMAGE_ACTION); filter.addAction(ENABLE_BROADCAST_IMAGE_ACTION); mContextWrapper.registerReceiver(mReceiver, filter); mDecodeHandlerThread = new DecodeHandlerThread("Overlay-scan"); mDecodeHandlerThread.startThread(); mCameraManager = com.ubx.scanner.camera.CameraManager.sharedObject(mContextWrapper); mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); startBackgroundThread(); mDecodeHandlerThread = new DecodeHandlerThread("Overlay-scan"); mDecodeHandlerThread.startThread(); open(-1); // 初始化UI布局:预览布局 & 悬浮按钮 createCameraPreviewLayout(); createFloatingButton(); } /** * 单例获取方法,需线程安全。 */ public static synchronized LegacyCamera2Scanner getInstance(Context context) throws ReaderException { if (sInstance == null) { sInstance = new LegacyCamera2Scanner(context); } return sInstance; } /** * 创建相机预览布局(包含 TextureView)。 */ private void createCameraPreviewLayout() { mCameraPreviewLayout = LayoutInflater.from(mContext).inflate(R.layout.scanner_layout, null); mPreviewView = mCameraPreviewLayout.findViewById(R.id.previewView); mBlackView = mCameraPreviewLayout.findViewById(R.id.blackMaskView); // 若布局中不是TextureView,动态替换 if (!(mPreviewView instanceof TextureView)) { mPreviewView = new TextureView(mContext); ViewGroup viewGroup = (ViewGroup) mCameraPreviewLayout; viewGroup.removeAllViews(); viewGroup.addView(mPreviewView); } // 设置布局参数 WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT ); // 默认先设置为不可见(根据业务需求可自行调整) new Handler(Looper.getMainLooper()).post(() -> { mBlackView.setVisibility(View.VISIBLE); mCameraPreviewLayout.setVisibility(View.INVISIBLE); }); mWindowManager.addView(mCameraPreviewLayout, params); // 添加TextureView监听 mPreviewView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { mSurfaceTexture = surface; mSurfaceWidth = width; mSurfaceHeight = height; Log.d(TAG, "onSurfaceTextureAvailable: " + width + "x" + height); setupCamera(width, height); openCamera(); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { Log.d(TAG, "onSurfaceTextureSizeChanged: " + width + "x" + height); configureTransform(width, height); } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { return true; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { } }); // 若TextureView此时已经可用 if (mPreviewView.isAvailable()) { mSurfaceTexture = mPreviewView.getSurfaceTexture(); mSurfaceWidth = mPreviewView.getWidth(); mSurfaceHeight = mPreviewView.getHeight(); if (mIsOpened && !mIsCameraInitialized) { initCamera(); } } } /** * 创建悬浮按钮示例。 */ private void createFloatingButton() { mFloatingButtonView = LayoutInflater.from(mContext).inflate(R.layout.floating_scan_button, null); // 配置布局参数 mFloatingButtonParams = new WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT ); // 设置初始位置(示例) mFloatingButtonParams.gravity = Gravity.RIGHT | Gravity.TOP; mFloatingButtonParams.x = 0; mFloatingButtonParams.y = 200; // 触摸事件,用于拖动和触发解码 mFloatingButtonView.setOnTouchListener((v, event) -> { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 记录初始位置 mInitialX = mFloatingButtonParams.x; mInitialY = mFloatingButtonParams.y; mInitialTouchX = event.getRawX(); mInitialTouchY = event.getRawY(); // 短按触发开始解码 startDecode(500); // 延迟判断是否拖动 v.postDelayed(() -> { mIsDragging = true; // 放大按钮 mFloatingButtonView.setScaleX(1.2f); mFloatingButtonView.setScaleY(1.2f); }, 300); return true; case MotionEvent.ACTION_MOVE: if (mIsDragging) { float deltaX = event.getRawX() - mInitialTouchX; float deltaY = event.getRawY() - mInitialTouchY; // RIGHT|TOP 方向的修正 int newX = mInitialX - (int) deltaX; int newY = mInitialY + (int) deltaY; mFloatingButtonParams.x = newX; mFloatingButtonParams.y = newY; mWindowManager.updateViewLayout(mFloatingButtonView, mFloatingButtonParams); } return true; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: v.removeCallbacks(null); mIsDragging = false; // 恢复按钮缩放 mFloatingButtonView.setScaleX(1.0f); mFloatingButtonView.setScaleY(1.0f); // 停止解码 stopDecode(); return true; } return false; }); mWindowManager.addView(mFloatingButtonView, mFloatingButtonParams); } /** * 打开扫描器接口 */ @Override public int open(int id) { Log.d(TAG, "open: 打开扫描器 ID=" + id); synchronized (mUiHandler) { if (mBarcodeReader != null && state != State.DONE) { Log.d(TAG, "open State=" + state); return BUSY; } /*mUiHandler.removeMessages(BCRDR_MSG_INIT); Message m = Message.obtain(mUiHandler, BCRDR_MSG_INIT); mUiHandler.sendMessage(m);*/ return connectDecoderLibrary(); } } @Override public boolean isOpened() { return (mBarcodeReader != null && state != State.DONE); } @Override public int close() { Log.d(TAG, "close: 关闭扫描器"); if (!mIsOpened) { return 0; } // 停止解码 stopDecode(); // 关闭相机 closeCamera(); // 停止后台线程(可视情况决定是否要常驻) stopBackgroundThread(); synchronized (mUiHandler) { mUiHandler.post(mDismissRunnable); //mUiHandler.removeMessages(BCRDR_MSG_RELEASE); mUiHandler.removeMessages(BCRDR_MSG_TIMER_PREVIEW); //Message m = Message.obtain(mUiHandler, BCRDR_MSG_RELEASE); //mUiHandler.sendMessage(m); disconnectDecoderLibrary(); } mUiHandler.removeMessages(BCRDR_MSG_FRAME_ERROR); mUiHandler.removeMessages(BCRDR_MSG_RELEASE); if (mCameraManager != null) { mCameraManager.stopDecoding(); mCameraManager.closeDriver(); } state = State.DONE; surfaceHolder = null; g_nMultiReadResultCount = 0; decodeDataArray.clear(); return SUCCESS; } // ----------------- 解码控制 ------------------- @Override public int startDecode(int timeout) { Log.d(TAG, "startDecode: 开始解码,超时=" + timeout); synchronized (mUiHandler) { // 标记处于解码中 mIsDecoding = true; // 显示预览布局 if (mIsCameraInitialized) { // 如果相机已初始化,只需开始预览 new Handler(Looper.getMainLooper()).post(() -> { mCameraPreviewLayout.setVisibility(View.VISIBLE); }); startPreview(); Log.d(TAG, "Decoding started (preview only)"); } else { // 如果相机未初始化,需要初始化相机 startCamera(); Log.d(TAG, "Decoding started (full camera initialization)"); } if (mBarcodeReader != null) { if (state != State.DECODING) { //开机后存在无法激活 /*if (activateDecoder == false && activateDecoderTimes > 0) { activateDecoderTimes = activateDecoderTimes - 1; if (ActivationManager.checkActivateStroreFile()) { try { disconnectDecoderLibrary(); Thread.sleep(50); connectDecoderLibrary(openCameraId); } catch (Exception e) { e.printStackTrace(); } } }*/ decodeDataArray.clear(); g_nMultiReadResultCount = 0; preDecodeData = null; decodeStartTime = System.currentTimeMillis(); /*if (CONTINUOUS_MODE == Triggering.CONTINUOUS.toInt()) { mBarcodeReader.startHandsFreeDecode(1); } else { mBarcodeReader.startDecode(timeout); }*/ /*if(mCameraDevice == null) { openCamera(); }*/

// mUiHandler.post(mShowRunnable);
state = State.DECODING;

// mCameraManager.setDecodeHandler(mDecodeHandler);
if(CONTINUOUS_MODE == 2) {
mUiHandler.removeMessages(BCRDR_MSG_DECODE_TIMEOUT);
mUiHandler.sendEmptyMessageDelayed(BCRDR_MSG_DECODE_TIMEOUT, timeout > 1000 ? timeout: 1500);
}
if (mUiHandler != null && workDecodeTimeOutSleep && workPreviewTime == 0) {
mUiHandler.removeMessages(BCRDR_MSG_TIMER_PREVIEW);
mUiHandler.sendEmptyMessageDelayed(BCRDR_MSG_TIMER_PREVIEW, CONTINUOUS_MODE == 4 ? InactivityTimer__DURATION_CONTINUOUS_MODE_MS : InactivityTimer_DURATION_MS);
workPreviewTime = System.currentTimeMillis();
}
return SUCCESS;
}
return BUSY;
} else {
state = State.DONE;
Log.d(TAG, "startDecode mBarcodeReader");
return NONE;
}
}
}

text
@Override public int stopDecode() { Log.d(TAG, "stopDecode: 停止解码"); mIsDecoding = false; // 停止预览 stopPreview(); // 关闭摄像头 closeCamera(); // 隐藏预览布局 if (mCameraPreviewLayout != null) { new Handler(Looper.getMainLooper()).post(() -> { /*Canvas canvas = mPreviewView.lockCanvas(); if (canvas != null) { try { // 覆盖整个区域 canvas.drawColor(Color.BLACK); } finally { mPreviewView.unlockCanvasAndPost(canvas); } }*/ Log.d(TAG, "stopDecode: state: " + state); mCameraPreviewLayout.setVisibility(View.INVISIBLE); mBlackView.setVisibility(View.VISIBLE); }); } synchronized (mUiHandler) { Log.d(TAG, "stopDecode state=" + state); if (mBarcodeReader != null) { if (state == State.DECODING) { mBarcodeReader.stopDecode(); } /*if(mCameraManager != null) { mCameraManager.setTorch(false); mCameraManager.stopDecoding(); mCameraManager.stopPreview(); } mUiHandler.removeCallbacks(mShowRunnable); mUiHandler.post(mDismissRunnable); mUiHandler.removeMessages(BCRDR_MSG_DECODE_TIMEOUT);*/ state = State.PREVIEW; return SUCCESS; } else { state = State.DONE; } g_nMultiReadResultCount = 0; return NONE; } } // ----------------- 回调 & 属性设置 ------------------- @Override public void onDecodeComplete(int event, HSMDecodeResult result, BarcodeReader reader) { Log.d(TAG, "onDecodeComplete: event=" + event); workPreviewTime = System.currentTimeMillis(); if (event == BarcodeReader.BCR_SUCCESS) { decodeTime =workPreviewTime - decodeStartTime; if (CONTINUOUS_MODE != Triggering.CONTINUOUS.toInt()) { state = State.PREVIEW; if (result != null) { if (result.getBarcodeBounds() != null) { System.arraycopy(result.getBarcodeBounds(), 0, bounds, 0, result.getBarcodeBounds().length >= 10 ? 10 : result.getBarcodeBounds().length); } if (saveMode == 5) { final byte[] m_frameData = mBarcodeReader.getLastDecImage(); new Thread(new Runnable() { @Override public void run() { Bitmap modBm = createBitmap(m_frameData, 0); Utils.saveImageAsPNG(modBm, "sdcard/temp/", "temp"); modBm.recycle(); modBm = null; } }).start(); } byte codeId = result.getCodeId(); int SymbologyId = OEMSymbologyId.getHSMSymbologyId(codeId); String symName = OEMSymbologyId.stringFromHSMSymbology(codeId); aimCodeId[0] = ']'; aimCodeId[1] = result.getAIMCodeLetter(); aimCodeId[2] = result.getAIMModifier(); byte[] byteArraryData = result.getBarcodeDataBytes(); if (transmitCodeID > 0 || enableGroupSeperator == 1) { byte[] barcodeDataBytes = composeAISeparator(aimCodeId[1], aimCodeId[2], codeId, byteArraryData, result.getBarcodeDataLength()); if (barcodeDataBytes != null) { if (mOnScannedListener != null) { mOnScannedListener.receivedDecodedData(barcodeDataBytes, barcodeDataBytes.length, aimCodeId, SymbologyId, symName, decodeTime); } } else { if (mOnScannedListener != null) { mOnScannedListener.receivedDecodedData(byteArraryData, byteArraryData.length, aimCodeId, SymbologyId, symName, decodeTime); } } } else { if (mOnScannedListener != null) { mOnScannedListener.receivedDecodedData(byteArraryData, byteArraryData.length, aimCodeId, SymbologyId, symName, decodeTime); } } if (enableSendBarcodeAndBitmap) { String barcode = new String(result.getBarcodeDataBytes(), 0, result.getBarcodeDataLength()); Log.d(TAG, "onDecodeComplete: barcode: " + barcode); byte[] m_frameData = mBarcodeReader.getLastDecImage(); int[] bounds = result.getBarcodeBounds(); sendBarcodeAndBitmapBroadcast(barcode, m_frameData, bounds); } } } else { if (ignoreRepeatMode == 2) {//解多码,已移除次设置项 if (g_nMultiReadResultCount > 0 && decodeDataArray.size() > 0) { for (DecodeData firstResult : decodeDataArray) { if (result.getBarcodeDataBytes() != null && compareBytes(firstResult.getBarcodeDataBytes(), result.getBarcodeDataBytes(), result.getBarcodeDataLength())) { //decodeDataArray.clear(); return; } } } g_nMultiReadResultCount++; DecodeData data = new DecodeData(); data.setBarcodeDataBytes(result.getBarcodeDataBytes()); data.setBarcodeDataLength(result.getBarcodeDataLength()); data.setDecodeTime(result.getDecodeTime()); data.setSymbologyId(result.getSymbologyId()); data.setCodeId(result.getCodeId()); data.setAIMCodeLetter(result.getAIMCodeLetter()); data.setAIMModifier(result.getAIMModifier()); decodeDataArray.add(data); if (g_nMultiReadResultCount >= g_nMaxMultiReadCount) { Log.d(TAG, "MAX onMultiReadCallback " + g_nMultiReadResultCount); //receivedMultipleDecodedData(decodeDataArray); if (mUiHandler != null) { mUiHandler.sendEmptyMessage(MESSAGE_CONTINUOUS_MULTI_DECODE_COMPLETE); } } } else { if (ignoreRepeatMode == 1) { if (preDecodeData != null && System.currentTimeMillis() - waitRepeatStartTime < waitRepeatTimeOut && compareBytes(result.getBarcodeDataBytes(), result.getBarcodeDataLength())) { decodeStartTime = System.currentTimeMillis(); return; } } //g_nMultiReadResultCount++; preDecodeData = result.getBarcodeDataBytes(); decodeStartTime = waitRepeatStartTime = System.currentTimeMillis(); byte codeId = result.getCodeId(); int SymbologyId = OEMSymbologyId.getHSMSymbologyId(codeId); String symName = OEMSymbologyId.stringFromHSMSymbology(codeId); aimCodeId[0] = ']'; aimCodeId[1] = result.getAIMCodeLetter(); aimCodeId[2] = result.getAIMModifier(); if (transmitCodeID > 0 || enableGroupSeperator == 1) { byte[] barcodeDataBytes = composeAISeparator(aimCodeId[1], aimCodeId[2], codeId, preDecodeData, result.getBarcodeDataLength()); if (barcodeDataBytes != null) { if (mOnScannedListener != null) { mOnScannedListener.receivedDecodedData(barcodeDataBytes, barcodeDataBytes.length, aimCodeId, SymbologyId, symName, decodeTime); } } else { if (mOnScannedListener != null) { mOnScannedListener.receivedDecodedData(preDecodeData, preDecodeData.length, aimCodeId, SymbologyId, symName, decodeTime); } } } else { if (mOnScannedListener != null) { mOnScannedListener.receivedDecodedData(preDecodeData, preDecodeData.length, aimCodeId, SymbologyId, symName, decodeTime); } } } } } else if (BarcodeReader.DECODE_STATUS_MULTI_DEC_COUNT == event) { //解码超时,未读取固定个数,输出已解码数据 Log.i(TAG, "onDecodeComplete multiDecodeMode=" + multiDecodeMode + " fullReadMode=" + fullReadMode + " decodeDataArray=" + decodeDataArray.size()); if (multiDecodeMode == 1 && fullReadMode == 0 && decodeDataArray.size() > 0) { //receivedMultipleDecodedData(decodeDataArray); if (mUiHandler != null) { if (mUiHandler != null) { mUiHandler.sendEmptyMessage(MESSAGE_MULTI_DECODE_COMPLETE); } } } state = State.PREVIEW; } else { state = State.PREVIEW; } } @Override public void onEvent(int event, int info, byte[] data, BarcodeReader reader) { Log.d(TAG, "onEvent: event=" + event + ", info=" + info); if (BarcodeReader.DECODE_STATUS_MULTI_DEC_COUNT == event) { //单张图片解码超时,未清空decodeDataArray if (g_nMultiReadResultCount > 0 && decodeDataArray.size() > 0) { g_nMultiReadResultCount = 0; decodeDataArray.clear(); } } } @Override public boolean onKeepGoingCallback() { return state == State.DECODING; } @Override public boolean onMultiReadCallback() { //Log.i(TAG, "onMultiReadCallback"); //final long startdec = System.currentTimeMillis(); final HSMDecodeResult firstResult = mBarcodeReader.getDecodeData(); if (firstResult != null) { workPreviewTime = System.currentTimeMillis(); if (multiDecodeMode > 0) { //单张图片解码超时,未清空decodeDataArray /*if(g_nMultiReadResultCount > 0 && decodeDataArray.size() > 0) { for(DecodeData result: decodeDataArray) { if(result.getBarcodeDataBytes() != null && comparabytes(firstResult.getBarcodeDataBytes(), result.getBarcodeDataBytes(), result.getBarcodeDataLength())) { //decodeDataArray.clear(); return true; } } }*/ g_nMultiReadResultCount++; DecodeData data = new DecodeData(); data.setBarcodeDataBytes(firstResult.getBarcodeDataBytes()); //if(firstResult.getBarcodeDataBytes() != null) //Log.d(TAG, "onMultiReadCallback " + (new String(firstResult.getBarcodeDataBytes()))); data.setBarcodeDataLength(firstResult.getBarcodeDataLength()); data.setDecodeTime(firstResult.getDecodeTime()); data.setSymbologyId(firstResult.getSymbologyId()); data.setCodeId(firstResult.getCodeId()); data.setAIMCodeLetter(firstResult.getAIMCodeLetter()); data.setAIMModifier(firstResult.getAIMModifier()); decodeDataArray.add(data); if (g_nMultiReadResultCount >= g_nMaxMultiReadCount) { Log.d(TAG, "MAX onMultiReadCallback " + g_nMultiReadResultCount); //receivedMultipleDecodedData(decodeDataArray); if (mUiHandler != null) { mUiHandler.sendEmptyMessage(MESSAGE_MULTI_DECODE_COMPLETE); } return false; } } else { preDecodeData = firstResult.getBarcodeDataBytes(); waitRepeatStartTime = System.currentTimeMillis(); decodeTime = waitRepeatStartTime -decodeStartTime; if (mOnScannedListener != null) { byte codeId = firstResult.getCodeId(); int symId = OEMSymbologyId.getHSMSymbologyId(codeId); aimCodeId[0] = ']'; aimCodeId[1] = firstResult.getAIMCodeLetter(); aimCodeId[2] = firstResult.getAIMModifier(); String symName = OEMSymbologyId.stringFromHSMSymbology(codeId); mOnScannedListener.receivedDecodedData(preDecodeData, preDecodeData.length, aimCodeId, symId, symName, decodeTime); } return false; } } return true; } @Override public void setDefaults(boolean enable) { if (mBarcodeReader != null) { if(enable) { mParametersHelper.setDefaultProperty(); } SparseArray<Property> mCacheProperties = mParametersHelper.getALLDecoderProperty(); for (int i = 0; i < mCacheProperties.size(); i++) { int keyForIndex = mCacheProperties.keyAt(i); setDecodeParameter(keyForIndex); } SparseArray<Property> mCacheStringProperties = mParametersHelper.getALLStringProperty(); for (int i = 0; i < mCacheStringProperties.size(); i++) { int keyForIndex = mCacheStringProperties.keyAt(i); setDecodeParameter(keyForIndex); } CONTINUOUS_MODE = mParametersHelper.getIntProperty(PropertyID.TRIGGERING_MODES); } } @Override public int setProperties(SparseArray<Integer> property) { if (mBarcodeReader != null) { if (property != null) { int size = property.size(); Log.d(TAG, "setProperties property size= " + size); for (int i = 0; i < size; i++) { int keyForIndex = property.keyAt(i); int value = property.get(keyForIndex); int ret = mParametersHelper.setIntProperty(keyForIndex, value); if(ret == 0) { setDecodeParameter(keyForIndex); } else { Log.d(TAG, "setProperties error property= " + keyForIndex); } } return SUCCESS; } } return NONE; } @Override public int setStringProperties(SparseArray<String> property) { if (mBarcodeReader != null) { if (property != null) { int size = property.size(); Log.d(TAG, "setStringProperties property size= " + size); for (int i = 0; i < size; i++) { int keyForIndex = property.keyAt(i); String value = property.get(keyForIndex); int ret = mParametersHelper.setStringProperty(keyForIndex, value); if(ret == 0) { setDecodeParameter(keyForIndex); } else { Log.d(TAG, "setStringProperties error property= " + keyForIndex); } } return SUCCESS; } } return NONE; } @Override protected void release() { close(); mContext = null; sInstance = null; // 若希望下次重新创建,则可释放单例 Log.d(TAG, "release"); synchronized (mUiHandler) { mUiHandler.removeMessages(BCRDR_MSG_TIMER_PREVIEW); if (mBarcodeReader != null) { //mBarcodeReader.stopDecode(); mBarcodeReader.release(); mBarcodeReader = null; } state = State.DONE; quitHandlerThreadLocked(); } } // ----------------- 相机操作 ------------------- private void initCamera() { if (mSurfaceTexture == null) { Log.e(TAG, "initCamera: SurfaceTexture 为空,无法初始化相机"); return; } setupCamera(mSurfaceWidth, mSurfaceHeight); openCamera(); } private void setupCamera(int width, int height) { Log.d(TAG, "setupCamera: " + width + "x" + height); CameraManager cameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE); try { String[] cameraIds = cameraManager.getCameraIdList(); String foundCameraId = null; for (String id : cameraIds) { CameraCharacteristics cc = cameraManager.getCameraCharacteristics(id); Integer facing = cc.get(CameraCharacteristics.LENS_FACING); if (facing != null && facing == CameraCharacteristics.LENS_FACING_BACK) { foundCameraId = id; break; } } if (foundCameraId == null) { Log.e(TAG, "未找到后置摄像头"); return; } mCameraId = foundCameraId; CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(mCameraId); StreamConfigurationMap map = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); if (map == null) { throw new RuntimeException("无法获取可用预览尺寸"); } // 选一个合适的预览大小 mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), width, height); Log.d(TAG, "setupCamera: mPreviewSize.getWidth()" + mPreviewSize.getWidth() + "mPreviewSize.getHeight" + mPreviewSize.getHeight()); // 创建ImageReader,用于获取图像帧 mImageReader = ImageReader.newInstance( mPreviewSize.getWidth(), mPreviewSize.getHeight(), ImageFormat.YUV_420_888, 2 ); mImageReader.setOnImageAvailableListener( mOnImageAvailableListener, mBackgroundHandler ); configureTransform(width, height); } catch (CameraAccessException e) { Log.e(TAG, "setupCamera: CameraAccessException", e); } } private Size chooseOptimalSize(Size[] choices, int width, int height) { // 示例:简单找个和3:2最接近的 double targetRatio = 3.0 / 2.0; Size optimalSize = null; double minDiff = Double.MAX_VALUE; int maxWidth = 1080; int maxHeight = 720; for (Size size : choices) { // 筛选出小于等于最大尺寸的选项 if (size.getWidth() <= maxWidth && size.getHeight() <= maxHeight) { double ratio = (double) size.getWidth() / size.getHeight(); double diff = Math.abs(ratio - targetRatio); if (diff < minDiff) { optimalSize = size; minDiff = diff; } } } // 如果没有符合条件的尺寸,可以返回默认尺寸(选择最接近的) return optimalSize == null ? choices[0] : optimalSize; } private void openCamera() { CameraManager cameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE); try { // 权限检查 if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { Log.e(TAG, "openCamera: 没有CAMERA权限"); return; } cameraManager.openCamera(mCameraId, mStateCallback, mBackgroundHandler); } catch (CameraAccessException e) { Log.e(TAG, "openCamera: CameraAccessException", e); } } private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(@NonNull CameraDevice camera) { mCameraDevice = camera; createCameraPreviewSession(); mIsCameraInitialized = true; } @Override public void onDisconnected(@NonNull CameraDevice camera) { camera.close(); mCameraDevice = null; mIsCameraInitialized = false; } @Override public void onError(@NonNull CameraDevice camera, int error) { camera.close(); mCameraDevice = null; mIsCameraInitialized = false; Log.e(TAG, "相机错误: " + error); } }; private void createCameraPreviewSession() { try { if (mCameraDevice == null || mPreviewView == null || mPreviewView.getSurfaceTexture() == null) { Log.e(TAG, "createCameraPreviewSession: 条件不满足"); return; } SurfaceTexture texture = mPreviewView.getSurfaceTexture(); texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); Surface previewSurface = new Surface(texture); Surface readerSurface = mImageReader.getSurface(); mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); mPreviewRequestBuilder.addTarget(previewSurface); mPreviewRequestBuilder.addTarget(readerSurface); mCameraDevice.createCaptureSession( Arrays.asList(previewSurface, readerSurface), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession session) { if (mCameraDevice == null) return; mCaptureSession = session; // 自动对焦 mPreviewRequestBuilder.set( CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE ); // 若此时处于解码状态,开始预览 if (mIsDecoding) { startPreview(); } } @Override public void onConfigureFailed(@NonNull CameraCaptureSession session) { Log.e(TAG, "onConfigureFailed: 配置失败"); } }, mBackgroundHandler ); } catch (CameraAccessException e) { Log.e(TAG, "createCameraPreviewSession: ", e); } } private void startPreview() { if (mCaptureSession == null || mPreviewRequestBuilder == null) { Log.e(TAG, "startPreview: CaptureSession或RequestBuilder为空"); return; } try { mCaptureSession.setRepeatingRequest( mPreviewRequestBuilder.build(), null, mBackgroundHandler ); new Handler(Looper.getMainLooper()).post(() -> { mBlackView.setVisibility(View.INVISIBLE); }); Log.d(TAG, "startPreview: 预览开始"); } catch (CameraAccessException e) { Log.e(TAG, "startPreview: 失败", e); } } private void stopPreview() { if (mCaptureSession == null) return; try { mCaptureSession.stopRepeating(); Log.d(TAG, "stopPreview: 预览已停止"); } catch (CameraAccessException e) { Log.e(TAG, "stopPreview: 失败", e); } } private void closeCamera() { try { if (mCaptureSession != null) { mCaptureSession.close(); mCaptureSession = null; } if (mCameraDevice != null) { mCameraDevice.close(); mCameraDevice = null; } if (mImageReader != null) { mImageReader.close(); mImageReader = null; } } catch (Exception e) { Log.e(TAG, "closeCamera: ", e); } mIsCameraInitialized = false; } private void configureTransform(int viewWidth, int viewHeight) { if (mPreviewSize == null || mPreviewView == null) { return; } int rotation = mWindowManager.getDefaultDisplay().getRotation(); Matrix matrix = new Matrix(); RectF viewRect = new RectF(0, 0, viewWidth, viewHeight); RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth()); float centerX = viewRect.centerX(); float centerY = viewRect.centerY(); // 根据屏幕方向进行必要的变换 if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) { bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY()); matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL); float scale = Math.max( (float) viewHeight / mPreviewSize.getHeight(), (float) viewWidth / mPreviewSize.getWidth() ); matrix.postScale(scale, scale, centerX, centerY); matrix.postRotate(90 * (rotation - 2), centerX, centerY); } else if (rotation == Surface.ROTATION_180) { matrix.postRotate(180, centerX, centerY); } new Handler(Looper.getMainLooper()).post(() -> { mPreviewView.setTransform(matrix); }); } // ----------------- 图像处理与解码示例 ------------------- private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() { @Override public void onImageAvailable(ImageReader reader) { try (Image image = reader.acquireLatestImage()) { if (image == null || !mIsDecoding) { return; } processImageForDecode(image); } /*try { image = reader.acquireLatestImage(); if (image == null || !mIsDecoding) { return; } processImageForDecode(image); } finally { if (image != null) { image.close(); } }*/ } }; private void processImageForDecode(Image image) { // 这里示例使用Zxing解码 // 获取图像数据并解码 byte[] imageData = imageToYUV(image); ByteBuffer buffer = image.getPlanes()[0].getBuffer(); byte[] data = new byte[buffer.remaining()]; buffer.get(data); int width = image.getWidth(); int height = image.getHeight(); Log.d(TAG, "processImageForDecode: data.length(): " + imageData.length + " width: " + width + " height: " + height); if (mIsDecoding) { Log.d(TAG, "processImageForDecode: startDecode"); mDecodeHandler.decode(imageData, width, height); } /*PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource( data, width, height, 0, 0, width, height, false ); BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); String decodeResult = decodeBarCode(bitmap); if (decodeResult != null) { Log.d(TAG, "Decode成功: " + decodeResult); // 解码成功示例:直接stopDecode() stopDecode(); }*/ // 保存最后帧YUV数据 mLastDecodeImage = imageData; } /*private String decodeBarCode(BinaryBitmap bitmap) { try { MultiFormatReader multiFormatReader = new MultiFormatReader(); Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class); Vector<BarcodeFormat> decodeFormats = new Vector<>(); decodeFormats.add(BarcodeFormat.QR_CODE); decodeFormats.add(BarcodeFormat.CODE_128); decodeFormats.add(BarcodeFormat.EAN_13); hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats); hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE); multiFormatReader.setHints(hints); Result result = multiFormatReader.decodeWithState(bitmap); return result.getText(); } catch (NotFoundException e) { // 没有检测到码 return null; } catch (Exception e) { Log.e(TAG, "decodeBarCode error", e); return null; } }*/ private byte[] imageToYUV(Image image) { int width = image.getWidth(); // 1080 int height = image.getHeight(); // 720 Log.d(TAG, "imageToYUV: image.width: " + width + " image.height: " + height); Image.Plane[] planes = image.getPlanes(); ByteBuffer yBuffer = planes[0].getBuffer(); int rowStride = planes[0].getRowStride(); int pixelStride = planes[0].getPixelStride(); byte[] yuvData = new byte[width * height]; // 只提取 Y 分量 // 逐行拷贝 Y 数据,去除填充 for (int row = 0; row < height; row++) { yBuffer.position(row * rowStride); // 移动到该行的起始位置 yBuffer.get(yuvData, row * width, width); // 读取有效数据 } return yuvData; } // ----------------- 其它Scanner抽象方法实现示例 ------------------- @Override public int aimerOn(boolean on) { Log.d(TAG, "aimerOn: " + on + " (本示例不支持)"); return 0; } @Override public int illuminationOn(boolean on) { // 闪光灯开关示例 try { if (mCameraDevice != null && mPreviewRequestBuilder != null && mCaptureSession != null) { mPreviewRequestBuilder.set( CaptureRequest.FLASH_MODE, on ? CaptureRequest.FLASH_MODE_TORCH : CaptureRequest.FLASH_MODE_OFF ); mCaptureSession.setRepeatingRequest( mPreviewRequestBuilder.build(), null, mBackgroundHandler ); return 0; } } catch (CameraAccessException e) { Log.e(TAG, "illuminationOn: 闪光灯设置失败", e); } return -1; } @Override public byte[] getLastDecImage(int rotate) { // 如果需要根据 rotate 进行旋转,这里可以自行实现 if (mBarcodeReader != null) { byte[] m_frameData = mBarcodeReader.getLastDecImage(); Bitmap modBm = createBitmap(m_frameData, rotate >= 0 ? rotate : rotateImage); if (modBm != null) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); modBm.compress(Bitmap.CompressFormat.JPEG, 50, baos); modBm.recycle(); modBm = null; return baos.toByteArray(); } } return null; } @Override public String getScanEngineInfo() { if(decoderVersion != null) { decoderVersion = decoderVersion.trim(); } return decoderVersion; } @Override public int setNumParameter(int paramNum, int paramVal) { if (mBarcodeReader != null) { return mBarcodeReader.setParameter(paramNum, paramVal); } return NONE; } @Override public int setStrParameter(int paramNum, String paramVal) { if (mBarcodeReader != null && !TextUtils.isEmpty(paramVal)) { return mBarcodeReader.setParameter(paramNum, paramVal); } return NONE; } @Override public int getNumParameter(int paramNum) { if (mBarcodeReader != null) { return mBarcodeReader.getNumParameter(paramNum); } return NONE; } @Override public String getStrParameter(int paramNum) { // 示例中如果SDK没有相应的getStr接口,可以自行封装 return null; } @Override public boolean lockHwTriggler(boolean lock) { Log.d(TAG, "lockHwTriggler: " + lock + " (本示例不支持)"); return false; } // OverlayPreviewWindow.Listener 相关(如无实际逻辑可不实现) @Override public void onWindowCreated(SurfaceTexture surfaceTexture, int width, int height) { // 示例中实际由TextureView的Listener处理 } @Override public void onWindowCreated(SurfaceHolder holder) { Log.d(TAG, "onWindowCreated"); if(holder != null) { surfaceHolder = holder; synchronized (mDecodeLock) { if(mCameraManager != null && mCameraManager.isOpen()) { try { mCameraManager.initializeCamera(surfaceHolder); mCameraManager.startPreview(); if(aimerIllumMode == 2 || aimerIllumMode == 3) { mCameraManager.setTorch(true); } } catch (IOException e) { e.printStackTrace(); } } } } } @Override public void onWindowDestroyed() { // 示例中可视情况决定是否closeCamera Log.d(TAG, "onWindowDestroyed"); if(mCameraManager != null) { mCameraManager.setTorch(false); mCameraManager.stopPreview(); } surfaceHolder = null; } @Override public void onStateChanged(int state) { Log.d(TAG, "onStateChanged = " + state); if(mCameraManager != null) { if (state == 1000) { mCameraManager.setTorch(true); } else if (state == -1000) { mCameraManager.setTorch(false); } } } // ----------------- 后台线程控制 ------------------- private void startCamera() { // 启动后台线程 startBackgroundThread(); // 如果 TextureView 已经可用,直接设置相机 if (mPreviewView.isAvailable()) { setupCamera(mPreviewView.getWidth(), mPreviewView.getHeight()); openCamera(); } // 显示预览窗口 new Handler(Looper.getMainLooper()).post(() -> { mCameraPreviewLayout.setVisibility(View.VISIBLE); mBlackView.setVisibility(View.INVISIBLE); }); Log.d(TAG, "Camera started successfully"); } private void startBackgroundThread() { if (mBackgroundThread == null) { mBackgroundThread = new HandlerThread("CameraBackground"); mBackgroundThread.start(); mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); } } private void stopBackgroundThread() { if (mBackgroundThread != null) { mBackgroundThread.quitSafely(); try { mBackgroundThread.join(); } catch (InterruptedException e) { Log.e(TAG, "stopBackgroundThread: ", e); } mBackgroundThread = null; mBackgroundHandler = null; } }

}

这里有个LegacyCamera2Scanner, 我想要在 停止预览 时就「彻底清空或销毁」与 TextureView 相关的所有内容,下次再启动预览时「重新创建一个全新的 TextureView」,请使用中文分析并回答我应该修改哪几处代码,直接使用markdown格式给出代码

Pensamiento

Adjusting the manager

The user is working through removing all toxicity-related references from the code within the stopPreview method, ensuring a fresh layout setup during startPreview.

Managing preview layout

Clearing the camera preview layout in stopPreview() ensures it is entirely removed from the window manager. During startPreview(), createCameraPreviewLayout() is called to recreate a fresh layout.

Piecing together the process

To ensure smooth execution, we avoid double removal or invalid view manager errors. After recreating the layout, evaluating whether to reinitialize the camera or reuse the existing setup is crucial.

Revisiting the process

OK, let me see. In startDecode(), if mCameraPreviewLayout is null, createCameraPreviewLayout() should be executed to ensure a fresh texture view creation.

Finalizing the approach

I’m working through calls and removals to ensure a fresh mCameraPreviewLayout anytime the camera is started. Heartening progress towards an efficient layout reset.

Reasoned for 12 seconds

Respuesta

以下给出一个示例思路,演示如何在「停止预览」时真正销毁与 TextureView 相关的所有布局和引用;然后下次再启动时重新创建一个全新的 TextureView。这个示例做了如下关键修改:

  1. 新增一个销毁预览布局的方法 destroyCameraPreviewLayout(),里面移除并清空与预览相关的所有引用,尤其是从 WindowManager 中 remove 掉 mCameraPreviewLayout
  2. stopPreview()stopDecode() 调用 destroyCameraPreviewLayout(),确保真正销毁 UI;
  3. 在下次需要启动预览(例如 startDecode()startCamera() 里),先检查 mCameraPreviewLayout 是否为 null。如果为空,就重新调用 createCameraPreviewLayout() 来生成一个全新的 TextureView 以及对应的布局;

这样就能保证每次“停止预览”后都彻底清空,下一次启动时再全新创建。下面用 Markdown 形式给出相对精简的修改示例(请根据你项目里实际的方法调用顺序、成员变量及业务逻辑进行适配):


java
public class LegacyCamera2Scanner extends Scanner implements BarcodeReader.DecodeCallback, BarcodeReader.DecoderListener, OverlayPreviewWindow.Listener { private static final String TAG = "LegacyCamera2Scanner"; // ...(其他字段略)... // 预览相关 private View mCameraPreviewLayout; private TextureView mPreviewView; private View mBlackView; private WindowManager mWindowManager; private SurfaceTexture mSurfaceTexture; private int mSurfaceWidth; private int mSurfaceHeight; // ============================== // 1) 新增“销毁”预览布局的方法 // ============================== /** * 真正从 WindowManager 中移除 CameraPreviewLayout, * 并清空所有与 TextureView / 布局相关的引用。 */ private void destroyCameraPreviewLayout() { if (mCameraPreviewLayout != null) { try { mWindowManager.removeView(mCameraPreviewLayout); } catch (Exception e) { Log.w(TAG, "destroyCameraPreviewLayout: removeView failed or already removed.", e); } // 将所有引用置空,确保下次可以重新创建 mCameraPreviewLayout = null; mPreviewView = null; mBlackView = null; mSurfaceTexture = null; mSurfaceWidth = 0; mSurfaceHeight = 0; } } // ============================== // 2) 创建预览布局的方法(保持原逻辑), // 但注意下次需要重新 new // ============================== private void createCameraPreviewLayout() { mCameraPreviewLayout = LayoutInflater.from(mContext).inflate(R.layout.scanner_layout, null); mPreviewView = mCameraPreviewLayout.findViewById(R.id.previewView); mBlackView = mCameraPreviewLayout.findViewById(R.id.blackMaskView); // 如果布局文件里不是 TextureView,动态替换 if (!(mPreviewView instanceof TextureView)) { mPreviewView = new TextureView(mContext); ViewGroup viewGroup = (ViewGroup) mCameraPreviewLayout; viewGroup.removeAllViews(); viewGroup.addView(mPreviewView); } WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT ); // 默认先设置为不可见 new Handler(Looper.getMainLooper()).post(() -> { if (mBlackView != null) { mBlackView.setVisibility(View.VISIBLE); } if (mCameraPreviewLayout != null) { mCameraPreviewLayout.setVisibility(View.INVISIBLE); } }); // 加到 WindowManager 中 mWindowManager.addView(mCameraPreviewLayout, params); // 设置 TextureView 回调 mPreviewView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { mSurfaceTexture = surface; mSurfaceWidth = width; mSurfaceHeight = height; Log.d(TAG, "onSurfaceTextureAvailable: " + width + "x" + height); setupCamera(width, height); openCamera(); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { configureTransform(width, height); } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { return true; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { } }); // 若已可用直接处理 if (mPreviewView.isAvailable()) { mSurfaceTexture = mPreviewView.getSurfaceTexture(); mSurfaceWidth = mPreviewView.getWidth(); mSurfaceHeight = mPreviewView.getHeight(); if (mIsOpened && !mIsCameraInitialized) { initCamera(); } } } // ============================== // 3) 修改停止预览相关的代码 // ============================== private void stopPreview() { if (mCaptureSession == null) return; try { mCaptureSession.stopRepeating(); Log.d(TAG, "stopPreview: 预览已停止"); } catch (CameraAccessException e) { Log.e(TAG, "stopPreview: 失败", e); } } // ============================== // 4) 在 stopDecode() 中,停止预览后, // 调用 destroyCameraPreviewLayout() // ============================== @Override public int stopDecode() { Log.d(TAG, "stopDecode: 停止解码"); mIsDecoding = false; // 先停止硬件层的预览 stopPreview(); // 关闭摄像头 closeCamera(); // ============ 关键:真正销毁布局 ============= // destroyCameraPreviewLayout(); // 下面是原有的逻辑,只是把“隐藏布局”等操作换成了上面的 destroy synchronized (mUiHandler) { if (mBarcodeReader != null) { if (state == State.DECODING) { mBarcodeReader.stopDecode(); } state = State.PREVIEW; return SUCCESS; } else { state = State.DONE; } g_nMultiReadResultCount = 0; return NONE; } } // ============================== // 5) 在 startDecode() 或 startCamera() 里, // 如果布局已被销毁,就重新创建 // ============================== private void startCamera() { // 启动后台线程 startBackgroundThread(); // 如果上次 stopDecode() 已经把布局销毁了,这里要重新创建 if (mCameraPreviewLayout == null) { createCameraPreviewLayout(); } // 如果 TextureView 已可用则走相机初始化 if (mPreviewView.isAvailable()) { setupCamera(mPreviewView.getWidth(), mPreviewView.getHeight()); openCamera(); } // 显示预览窗口 new Handler(Looper.getMainLooper()).post(() -> { if (mCameraPreviewLayout != null) { mCameraPreviewLayout.setVisibility(View.VISIBLE); } if (mBlackView != null) { mBlackView.setVisibility(View.INVISIBLE); } }); Log.d(TAG, "Camera started successfully"); } @Override public int startDecode(int timeout) { Log.d(TAG, "startDecode: 开始解码, 超时=" + timeout); synchronized (mUiHandler) { mIsDecoding = true; // 如果相机没初始化或布局销毁, 需重新创建布局并打开相机 if (!mIsCameraInitialized || mCameraPreviewLayout == null) { startCamera(); } else { // 如果相机已初始化, 直接显示并开预览 if (mCameraPreviewLayout != null) { new Handler(Looper.getMainLooper()).post(() -> { mCameraPreviewLayout.setVisibility(View.VISIBLE); }); } startPreview(); } // ... 以下是原有解码的逻辑 ... if (mBarcodeReader != null) { if (state != State.DECODING) { decodeDataArray.clear(); g_nMultiReadResultCount = 0; preDecodeData = null; decodeStartTime = System.currentTimeMillis(); state = State.DECODING; // ... 设置超时之类 ... return SUCCESS; } return BUSY; } else { state = State.DONE; return NONE; } } } // ...后续其他代码不变... // ========================================================= }

代码修改要点说明

  1. 创建/销毁预览布局

    • 原先 createCameraPreviewLayout() 里只是在 stopDecode() 中把布局设置成不可见 (View.INVISIBLE);这样并不会真正销毁 TextureView
    • 现在增加一个 destroyCameraPreviewLayout(),调用 mWindowManager.removeView(mCameraPreviewLayout) 来把它从窗口中移除,并将引用全部置空。
  2. stopDecode()stopPreview() 里销毁布局

    • 停止解码后,把相机也关掉,然后调用 destroyCameraPreviewLayout(),使上一次的 TextureView 得到彻底清理。
  3. 下次启动预览时再“重新创建”

    • 当再次调用 startDecode() 或专门的 startCamera() 方法时,先判断 mCameraPreviewLayout == null,如果为 null,说明上一次已经销毁,需要重新调用 createCameraPreviewLayout()
    • 这样就会获得一个新的 TextureView,从而实现“下次预览全新创建”的效果。

请根据项目的实际初始化流程(例如是否在 startDecode() 内就创建布局,或者在外部先调用某些方法)进行微调。核心思路就是:真正 removeView + 置空,然后 下一次需要时,再重新 new 一个布局。这样就能在“停止预览”时彻底销毁 TextureView 相关内容,下次启动再拿到一个全新的 TextureView。

Pregunta

// 加到 WindowManager 中
mWindowManager.addView(mCameraPreviewLayout, params);
这里报错了:
java.lang.RuntimeException: Can't create handler inside thread Thread[Binder:27006_3,5,main] that has not called Looper.prepare()

Pensamiento

Handling UI thread constraints

OK, let me see. The user can't create a handler within a thread skipping Looper.prepare(). They need to run code for the WindowManager on the main UI thread with Handler and Looper.getMainLooper().

Reasoned for a few seconds

Respuesta

你遇到的错误:

java.lang.RuntimeException: Can't create handler inside thread Thread[Binder:27006_3,5,main] that has not called Looper.prepare()

说明当前调用 mWindowManager.addView(...) 的线程 并不是 主线程(UI线程),而是一个 Binder 线程或者后台线程。但 调用 WindowManager 去添加 View 必须在主线程 执行,否则就会出现类似错误。


解决思路

  1. 必须在主线程(UI线程)中调用 mWindowManager.addView(...)
  2. 如果你的代码是在某个后台线程 / Binder 回调中执行,可以通过 Handler(Looper.getMainLooper()) 或其他方式切回主线程,然后再调用 UI 操作。

比如,你可以这样写(示例):

java
private void createCameraPreviewLayout() { // 创建你的布局 mCameraPreviewLayout = LayoutInflater.from(mContext).inflate(R.layout.scanner_layout, null); mPreviewView = mCameraPreviewLayout.findViewById(R.id.previewView); mBlackView = mCameraPreviewLayout.findViewById(R.id.blackMaskView); // 如果布局文件里不是 TextureView, 动态替换 if (!(mPreviewView instanceof TextureView)) { mPreviewView = new TextureView(mContext); ViewGroup viewGroup = (ViewGroup) mCameraPreviewLayout; viewGroup.removeAllViews(); viewGroup.addView(mPreviewView); } // 布局参数 WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT ); // 确保在主线程调用 addView Handler mainHandler = new Handler(Looper.getMainLooper()); mainHandler.post(() -> { try { mWindowManager.addView(mCameraPreviewLayout, params); } catch (Exception e) { e.printStackTrace(); } // 这里可以再做可见性等其他UI相关操作 if (mBlackView != null) { mBlackView.setVisibility(View.VISIBLE); } if (mCameraPreviewLayout != null) { mCameraPreviewLayout.setVisibility(View.INVISIBLE); } }); // 之后给 TextureView 设置监听(可在上面的 mainHandler.post() 里也行) mPreviewView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { mSurfaceTexture = surface; mSurfaceWidth = width; mSurfaceHeight = height; // ...后续操作... } // 其余回调省略 ... }); }

这样做的关键点在于:

  • 不要在构造方法、Binder 回调 或 其他后台线程 直接调用 mWindowManager.addView(...);必须把它切回主线程去执行。
  • 你可以在任何地方写 new Handler(Looper.getMainLooper()).post(...),把对 WindowManager 的操作包装进去。

常见场景

  1. 如果你是 Android Service(尤其是非前台进程)里做 WindowManager.addView,同样需要切回主线程(或者直接在 onCreate() 里就已经是主线程);
  2. 如果是某个耗时操作回调,比如解码回调、Binder回调,也需要显式地使用 HandlerrunOnUiThread()(如果有 Activity 上下文的话)把 UI 操作切到主线程。

总之:所有 UI 相关的操作都必须在 UI 线程执行。你的异常正是因为在非UI线程中“创建了一个 Handler”并执行了 UI 操作引起的。

Pregunta

我现在在做条码识别,如何使用OpenCV来优化我的条码识别功能(解码速度,污码,错码识别)或者还有没有其他方式可以优化我的条码识别功能。使用中文回答我的问题

Pensamiento

Assessing barcode strategies

Exploring OpenCV's role in optimizing barcode recognition, including handling damage and decoding speed. Considering custom solutions and hardware options.

Reasoned for a few seconds

Respuesta

在实际做条码识别(含一维码、二维码等)时,为了提升解码速度、提升对模糊、污损、失真条码的识别率,可以从以下几个方面入手进行优化。下面着重介绍 OpenCV 在图像预处理阶段的一些思路,以及其他可行的优化方法。


一、使用 OpenCV 进行图像预处理

很多解码库(如 Zxing、ZBar 等)在高质量、对比度适中的图像上识别速度和准确度都比较好。但是在真实场景中,可能会存在污损、失焦、运动模糊、对比度低等问题。此时可以先用 OpenCV 做一系列预处理,再把处理后的图像交给解码引擎,提高成功率。

常见预处理手段包括:

  1. 去噪/平滑(Denoise/Smoothing)

    • 当图像存在大量随机噪声或斑点时,可先用 高斯模糊 (Gaussian Blur)中值滤波 (Median Filter) 做去噪。
    • 对二维码/条形码这种高反差图像来说,也可以尝试 双边滤波 (Bilateral Filter),在保留边缘的同时去除噪点。
  2. 图像增强 (Image Enhancement)

    • 可以对光照不均或对比度不足的图像进行亮度/对比度的调整,例如 直方图均衡化 (Histogram Equalization)CLAHE (对比度受限自适应直方图均衡化) 等。
    • 如果图像灰度太暗或过亮,均衡化可以让条码区域更清晰。
  3. 阈值化 (Thresholding) 或自适应阈值 (Adaptive Threshold)

    • 对条码区域做二值化,可以让条码结构更突出。
    • 有时全局阈值化效果不佳,需要使用 自适应阈值 (Adaptive Threshold)OTSU 算法来自动寻找合适的阈值。
  4. 形态学操作 (Morphological Operations)

    • 利用 腐蚀 (erode)膨胀 (dilate)开运算 (open)闭运算 (close) 去掉小黑点或小白洞,增强条码整体结构。
    • 如果条码有破损,可适当使用膨胀/闭运算使条码线条连贯;如果条码粘连,则尝试腐蚀/开运算分离干扰区域。
  5. 图像校正 (Deskew / Perspective Transform)

    • 当拍摄角度较大或条码发生透视畸变时,解码库可能难以正确解析。可以在 OpenCV 中通过 边缘检测 + HoughLinesfindContours 等方式检测出条码的轮廓,然后使用 仿射变换 (Affine Transform)透视变换 (Perspective Transform) 将图像矫正到比较正的俯视图。
    • 这样可以显著提高解码稳定性,尤其对于二维码来说,非常有效。
  6. ROI 提取 (Region of Interest)

    • 如果条码只占整个图像的一小部分,可以先定位到条码区域(例如通过 Sobel/Canny 边缘检测 + 投影分析 + 闭运算 等方法快速找到疑似条码的矩形区域),再把该 ROI 剪裁给解码引擎,减少解码库需要处理的像素范围,从而加快速度

小结:以上思路的核心就是“让条码图像变得更纯净、更高对比度、更不失真”再交给解码库。这样能显著提高对污码、破损码以及拍摄质量不佳情况下的识别率和识别速度。


二、其他优化手段

除了借助 OpenCV 的预处理,你还可以从以下方向做进一步优化:

  1. 硬件层面

    • 使用分辨率更高的摄像头或专用的扫描头;
    • 增加补光/闪光灯或利用红外线等方式减少环境光影响;
    • 尝试自动对焦或固定对焦在条码距离上,让图像更清晰。
  2. 多帧合成/智能选帧

    • 如果在视频流中持续采集图像,可以采用多帧叠加或选帧策略:
      • 同时对多帧进行解码,如果有一帧清晰就立刻返回结果;
      • 或者对多帧做图像融合,降低噪声(需要较复杂的图像算法)。
  3. 调整解码库参数

    • 若使用 Zxing、ZBar 或其他商业解码SDK,一般都可以传入一些“尝试更强力的模式 / TryHarder / PureBarcode 模式”等参数;
    • 有些解码器有专门的 高速模式容错模式失真校正模式 等,需要结合实际情况进行配置。
  4. 多算法/多库融合

    • 同时集成多种算法库(例如 Zxing、ZBar、HMS ML Kit、Google ML Kit Barcode 等),并行处理同一帧图像,谁先解出就返回谁的结果;
    • 这种做法在处理“挑剔码”时往往提高成功率,但也需要一定并行编程技巧。
  5. 神经网络/深度学习

    • 如果条码破损非常严重或传统手段无法准确定位条码区域,可以利用基于深度学习的检测模型(如使用 YOLO 训练一个“条码检测”模型),先定位条码再裁剪;
    • 也有学术界或商业公司做“深度学习还原破损二维码”或“条形码修复”的研究,但实现门槛较高,需要标注数据集并做训练。

三、实践建议

  1. 先从简单的 OpenCV 预处理开始

    • 建议先做基础去噪适当增强阈值化,再把结果给解码库试试;
    • 如果效果还不够,可再尝试形态学修复透视校正
  2. 如果是视频流

    • 可以做实时预览,对每帧(或隔帧)执行预处理 + 解码;
    • 一旦解出结果就停止,减少重复运算。
  3. 注意性能平衡

    • 不要过度堆叠复杂的预处理,否则可能降低整体速度;
    • 选择合适分辨率并用 ROI 先裁剪条码区域,往往可以显著提升性能。
  4. 测试环境 & 条码多样性

    • 在不同环境光、不同距离、不同污损情况、不同条码种类上做大量测试;
    • 根据失效率最高的情况去专门优化对应环节。

小结

  • 使用 OpenCV 的核心价值在于:** 利用各种图像处理方法,让条码在像素层面更“干净、对比度更强、畸变更小”**。这是提升解码速度和准确率的最常用手段。
  • 结合硬件、解码库参数、甚至深度学习检测等方式,可进一步增强对严重污损的鲁棒性。

希望这些思路能帮助你更好地利用 OpenCV(或其他图像处理手段)来优化条码识别的速度和准确度。祝开发顺利!

Compartir este Q&A