deviceIterator = deviceList.values().iterator();
60 | UsbDevice usbDevice = null;
61 | while (deviceIterator.hasNext()) {
62 | UsbDevice device = deviceIterator.next();
63 | LogUtils.i("id:" + device.getDeviceId() + ",mName:" + device.getDeviceName() + ",vendorID:" + device.getVendorId() + ",ProductId:" + device.getProductId());
64 | if (device.getVendorId() == usbConfig.getVendorId() && device.getProductId() == usbConfig.getProductId()) {
65 | usbDevice = device; // 获取USBDevice
66 | break;
67 | }
68 | }
69 | return usbDevice;
70 | }
71 |
72 |
73 |
74 | /**
75 | * 分发扫码数据
76 | * @param barcode
77 | */
78 | protected void disposeScanData(final String barcode) {
79 | if (mOnScanListener != null) {
80 | MainThreadExecutor.runMainThread(new Runnable() {
81 | @Override
82 | public void run() {
83 | mOnScanListener.onScanSuccess(barcode);
84 | }
85 | });
86 | }
87 | }
88 |
89 |
90 | /**
91 | * 设置扫码监听
92 | *
93 | * @param onScanListener
94 | * @return
95 | */
96 | public void setOnScanListener(OnScanListener onScanListener) {
97 | mOnScanListener = onScanListener;
98 | }
99 |
100 | @Override
101 | public void closeScan() {
102 | removeScanListener();
103 | }
104 |
105 | /**
106 | * 移除扫码监听
107 | */
108 | public void removeScanListener() {
109 | if (mOnScanListener != null) {
110 | mOnScanListener = null;
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/scanner/src/main/java/com/sjl/scanner/IUsbScan.java:
--------------------------------------------------------------------------------
1 | package com.sjl.scanner;
2 |
3 | /**
4 | * TODO
5 | *
6 | * @author Kelly
7 | * @version 1.0.0
8 | * @filename IUsbScan
9 | * @time 2020/7/3 18:01
10 | * @copyright(C) 2020 song
11 | */
12 | public interface IUsbScan {
13 | /**
14 | * 打开指定usb扫码
15 | *
16 | * @param usbConfig
17 | * @return
18 | */
19 | int openScan(UsbConfig usbConfig);
20 |
21 |
22 | /**
23 | * 发送数据
24 | *
25 | * @param buffer 字节数组
26 | * @return
27 | */
28 | int sendData(byte[] buffer);
29 |
30 |
31 | /**
32 | * 开线程读取数据,必须在正常打开usb的情况下读取数据
33 | */
34 | void startReading();
35 |
36 |
37 | /**
38 | * 停止读扫码数据
39 | */
40 | void stopReading();
41 |
42 | /**
43 | * 重连
44 | */
45 | void reconnect();
46 |
47 | /**
48 | * 关闭扫描
49 | */
50 | void closeScan();
51 |
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/scanner/src/main/java/com/sjl/scanner/MainThreadExecutor.java:
--------------------------------------------------------------------------------
1 | package com.sjl.scanner;
2 |
3 | import android.os.Handler;
4 | import android.os.Looper;
5 |
6 | /**
7 | * MainThreadExecutor
8 | *
9 | * @author Kelly
10 | * @version 1.0.0
11 | * @filename MainThreadExecutor.java
12 | * @time 2019/5/22 10:10
13 | * @copyright(C) 2019 song
14 | */
15 | public final class MainThreadExecutor {
16 | private MainThreadExecutor() {
17 | }
18 |
19 | private static final Handler HANDLER = new Handler(Looper.getMainLooper());
20 |
21 | public static void runMainThread(Runnable runnable) {
22 | if (isMainThread()) {
23 | runnable.run();
24 | } else {
25 | HANDLER.post(runnable);
26 | }
27 | }
28 |
29 | public static boolean isMainThread() {
30 | return Looper.myLooper() == Looper.getMainLooper();
31 | }
32 |
33 | public static Handler getHandler() {
34 | return HANDLER;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/scanner/src/main/java/com/sjl/scanner/UsbCmdScan.java:
--------------------------------------------------------------------------------
1 | package com.sjl.scanner;
2 |
3 | import android.content.Context;
4 | import android.hardware.usb.UsbConstants;
5 | import android.hardware.usb.UsbDevice;
6 | import android.hardware.usb.UsbDeviceConnection;
7 | import android.hardware.usb.UsbEndpoint;
8 | import android.hardware.usb.UsbInterface;
9 | import android.os.SystemClock;
10 |
11 |
12 | import com.sjl.scanner.util.LogUtils;
13 |
14 | import java.util.Arrays;
15 |
16 | /**
17 | * usb连接扫码封装,自己控制命令
18 | *
19 | * @author Kelly
20 | * @version 1.0.0
21 | * @filename UsbCmdScan
22 | * @time 2020/7/1 11:01
23 | * @copyright(C) 2020 song
24 | */
25 | public class UsbCmdScan extends BaseUsbScan {
26 |
27 | /**
28 | * 块输出端点
29 | */
30 | private UsbEndpoint epBulkOut;
31 | private UsbEndpoint epBulkIn;
32 | /**
33 | * 控制端点
34 | */
35 | private UsbEndpoint epControl;
36 | /**
37 | * 中断端点
38 | */
39 | private UsbEndpoint epIntEndpointOut;
40 | private UsbEndpoint epIntEndpointIn;
41 |
42 | private UsbDeviceConnection conn = null;
43 |
44 |
45 |
46 |
47 |
48 | public UsbCmdScan(Context context) {
49 | super(context);
50 | }
51 |
52 |
53 | @Override
54 | public int openScan(UsbConfig usbConfig) {
55 | if (connected) {
56 | LogUtils.i("已经打开");
57 | return 0;
58 | }
59 | UsbDevice usbDevice = findUsbDevice(usbConfig);
60 | if (usbDevice == null) {
61 | return UsbErrorCode.USB_FIND_THIS_FAIL;
62 | }
63 | //3.获取usb接口
64 | UsbInterface usbInterface = null;
65 | for (int i = 0; i < usbDevice.getInterfaceCount(); i++) {
66 | //一个设备上面一般只有一个接口,有两个端点,分别接受和发送数据
67 | usbInterface = usbDevice.getInterface(i);
68 | break;
69 | }
70 |
71 | //4.获取usb设备的通信通道endpoint
72 | int endpointCount = usbInterface.getEndpointCount();
73 | LogUtils.i("usb设备的通信通道数:" + endpointCount);
74 | for (int i = 0; i < endpointCount; i++) {
75 | UsbEndpoint ep = usbInterface.getEndpoint(i);
76 | switch (ep.getType()) {
77 | case UsbConstants.USB_ENDPOINT_XFER_BULK://USB端口传输
78 | if (UsbConstants.USB_DIR_OUT == ep.getDirection()) {//输出
79 | epBulkOut = ep;
80 | } else {
81 | epBulkIn = ep;
82 | }
83 | break;
84 | case UsbConstants.USB_ENDPOINT_XFER_CONTROL://控制
85 | epControl = ep;
86 | break;
87 | case UsbConstants.USB_ENDPOINT_XFER_INT://中断
88 | if (ep.getDirection() == UsbConstants.USB_DIR_OUT) {//输出
89 | epIntEndpointOut = ep;
90 | }
91 | if (ep.getDirection() == UsbConstants.USB_DIR_IN) {
92 | epIntEndpointIn = ep;
93 | }
94 | break;
95 | case UsbConstants.USB_ENDPOINT_XFER_ISOC://中断
96 | break;
97 | default:
98 | break;
99 | }
100 | }
101 | boolean b = checkUsbEndpoint();
102 | if (!b) {
103 | return UsbErrorCode.USB_CHANNEL_FAIL;
104 | }
105 |
106 |
107 | //5.打开conn连接通道
108 | if (UsbScanHelper.getInstance().hasPermission(usbDevice)) {
109 | //有权限,那么打开
110 | conn = mUsbManager.openDevice(usbDevice);
111 | } else {
112 | //一般需要外部申请授权
113 | UsbScanHelper.getInstance().requestPermission(usbDevice);
114 | if (!UsbScanHelper.getInstance().hasPermission(usbDevice)){
115 | LogUtils.e("申请usb权限失败");
116 | return UsbErrorCode.USB_PERMISSION_FAIL;
117 | }else {
118 | conn = mUsbManager.openDevice(usbDevice);
119 | }
120 | }
121 | if (conn == null) {
122 | LogUtils.w("不能连接到设备");
123 | return UsbErrorCode.USB_OPEN_FAIL;
124 | }
125 | if (conn.claimInterface(usbInterface, true)) {
126 | LogUtils.w("usb扫码连接成功");
127 | connected = true;
128 | return UsbErrorCode.USB_OK;
129 | } else {
130 | LogUtils.w("无法打开连接通道。");
131 | conn.close();
132 | }
133 | return UsbErrorCode.USB_UNKNOWN_FAIL;
134 | }
135 |
136 |
137 |
138 | private boolean checkUsbEndpoint() {
139 | boolean ret = false;
140 | if (epBulkOut != null) {
141 | ret = true;
142 | }
143 | if (epBulkIn != null) {
144 | ret = true;
145 | }
146 | if (epControl != null) {
147 | ret = true;
148 | }
149 | if (epIntEndpointOut != null) {
150 | ret = true;
151 | }
152 | if (epIntEndpointIn != null) {
153 | ret = true;
154 | }
155 | return ret;
156 | }
157 |
158 | @Override
159 | public int sendData(byte[] buffer) {
160 | if (conn == null || epBulkOut == null) return UsbErrorCode.USB_UNKNOWN_FAIL;
161 | if (conn.bulkTransfer(epBulkOut, buffer, buffer.length, usbConfig.getWriteTimeout()) >= 0) {
162 | //0 或者正数表示成功
163 | return UsbErrorCode.USB_OK;
164 | } else {
165 | return UsbErrorCode.USB_SEND_DATA_FAIL;
166 | }
167 | }
168 |
169 |
170 |
171 | @Override
172 | public void startReading() {
173 | if (!connected) {
174 | LogUtils.e("not connected");
175 | return;
176 | }
177 | readFlag = true;
178 | executorService.execute(new Runnable() {
179 | @Override
180 | public void run() {
181 | //防止读取多次
182 | try {
183 | byte[] buffer = new byte[512];
184 | Arrays.fill(buffer, 0, buffer.length, (byte) 0);
185 | UsbConfig.ScanCmd scanCmd = usbConfig.getScanCmd();
186 | while (readFlag) {
187 | synchronized (this) {
188 | int i = sendData(scanCmd.getScanOpen());//写
189 | if (i == UsbErrorCode.USB_SEND_DATA_FAIL) {
190 | continue;
191 | }
192 | if (conn == null) {
193 | break;
194 | }
195 | int ret = conn.bulkTransfer(epBulkIn, buffer, buffer.length, usbConfig.getReadTimeout());//获取数据流
196 | if (ret > 0) {
197 | sendData(scanCmd.getScanClose());
198 | try {
199 | byte[] bs = new byte[ret];
200 | System.arraycopy(buffer, 0, bs, 0, ret);
201 | String barcode = new String(bs).trim();
202 | disposeScanData(barcode);
203 | } catch (Exception e) {
204 | LogUtils.e("扫码拷贝异常", e);
205 | }
206 | SystemClock.sleep(2000);
207 | resetBuffer(buffer, ret);
208 | } else {
209 | SystemClock.sleep(200);
210 | }
211 | sendData(scanCmd.getScanClose());
212 | }
213 | }
214 | } catch (Exception e) {
215 | LogUtils.e("扫码异常", e);
216 | if (conn != null) {
217 | reconnect();
218 | }
219 |
220 | }
221 | LogUtils.i("退出扫码读取线程");
222 | }
223 |
224 | /**
225 | * 折中处理缓存数据,无法解决扫码连续两次声音
226 | * @param buffer
227 | * @param toIndex
228 | */
229 | private void resetBuffer(byte[] buffer, int toIndex) {
230 | Arrays.fill(buffer, 0, toIndex, (byte) 0);
231 | int i = conn.bulkTransfer(epBulkIn, buffer, buffer.length, usbConfig.getReadTimeout());
232 | if (i > 0) {
233 | LogUtils.w("=========释放重复读数据,length:" + i);
234 | Arrays.fill(buffer, 0, i, (byte) 0);
235 | }
236 | }
237 | });
238 | }
239 |
240 |
241 |
242 | @Override
243 | public void stopReading() {
244 | readFlag = false;
245 | if (usbConfig != null){
246 | UsbConfig.ScanCmd scanCmd = usbConfig.getScanCmd();
247 | sendData(scanCmd.getScanClose());
248 | }
249 |
250 | }
251 |
252 | @Override
253 | public void reconnect() {
254 | if (reconnectCount > MAX_RECONNECT_COUNT) {
255 | return;
256 | }
257 | reconnectCount++;
258 | LogUtils.i("开始第" + reconnectCount + "重连");
259 | closeScan();
260 | int i = openScan(usbConfig);
261 | if (i == UsbErrorCode.USB_OK) {
262 | LogUtils.i("=====重连成功=====");
263 | startReading();
264 | reconnectCount = 0;
265 | } else {
266 | reconnect();
267 | }
268 | }
269 |
270 | @Override
271 | public void closeScan() {
272 | super.closeScan();
273 | connected = false;
274 | stopReading();
275 | if (mContext != null && broadcastReceiver != null) {
276 | mContext.unregisterReceiver(broadcastReceiver);
277 | }
278 | if (conn != null) {
279 | conn.close();
280 | conn = null;
281 | }
282 | }
283 |
284 | }
285 |
--------------------------------------------------------------------------------
/scanner/src/main/java/com/sjl/scanner/UsbComAutoScan.java:
--------------------------------------------------------------------------------
1 | package com.sjl.scanner;
2 |
3 |
4 | import android.content.Context;
5 | import android.hardware.usb.UsbDevice;
6 | import android.hardware.usb.UsbDeviceConnection;
7 |
8 | import com.hoho.android.usbserial.driver.CommonUsbSerialPort;
9 | import com.hoho.android.usbserial.driver.UsbSerialDriver;
10 | import com.hoho.android.usbserial.driver.UsbSerialProber;
11 | import com.hoho.android.usbserial.util.SerialInputOutputManager;
12 | import com.sjl.scanner.util.LogUtils;
13 |
14 | /**
15 | * usb传虚拟串口通讯,自感模式
16 | *
17 | * @author Kelly
18 | * @version 1.0.0
19 | * @filename UsbComAutoScan
20 | * @time 2020/7/3 18:01
21 | * @copyright(C) 2020 song
22 | */
23 | public class UsbComAutoScan extends BaseUsbScan {
24 | private SerialInputOutputManager usbIoManager;
25 | private CommonUsbSerialPort usbSerialPort;
26 |
27 | public UsbComAutoScan(Context context) {
28 | super(context);
29 | }
30 |
31 | @Override
32 | public int openScan(UsbConfig usbConfig) {
33 | if (connected) {
34 | LogUtils.i("扫码器已经打开");
35 | return 0;
36 | }
37 | UsbConfig.SerialPortConfig serialPortConfig = usbConfig.getSerialPortConfig();
38 | if (serialPortConfig == null){
39 | throw new NullPointerException("serialPortConfig is null.");
40 | }
41 | UsbDevice usbDevice = findUsbDevice(usbConfig);
42 | if (usbDevice == null) {
43 | return UsbErrorCode.USB_FIND_THIS_FAIL;
44 | }
45 |
46 | try {
47 | UsbSerialDriver usbSerialDriver = UsbSerialProber.getDefaultProber().probeDevice(usbDevice);
48 | if (usbSerialDriver == null) {
49 | LogUtils.e("connection failed: no driver for device");
50 | return UsbErrorCode.USB_FIND_THIS_FAIL;
51 | }
52 | //获取已插入的串口驱动
53 | UsbDeviceConnection connection;
54 | if (mUsbManager.hasPermission(usbDevice)) {
55 | //有权限,那么打开
56 | connection = mUsbManager.openDevice(usbSerialDriver.getDevice());
57 | } else {
58 | //一般需要外部申请授权
59 | UsbScanHelper.getInstance().requestPermission(usbDevice);
60 | if (!UsbScanHelper.getInstance().hasPermission(usbDevice)){
61 | LogUtils.e("申请usb权限失败");
62 | return UsbErrorCode.USB_PERMISSION_FAIL;
63 | }else {
64 | connection = mUsbManager.openDevice(usbDevice);
65 | }
66 | }
67 | if (connection == null) {
68 | LogUtils.w("不能连接到设备");
69 | return UsbErrorCode.USB_OPEN_FAIL;
70 | }
71 |
72 | usbSerialPort = (CommonUsbSerialPort) usbSerialDriver.getPorts().get(0);
73 | usbSerialPort.open(connection);
74 | //此条码默认配置为:设置串口的波特率、数据位,停止位,校验位
75 | //115200 波特率,8 位数据位,无校验位,1 位停止位)
76 | usbSerialPort.setParameters(serialPortConfig.getBaudRate(), serialPortConfig.getDataBits(), serialPortConfig.getStopBits(),serialPortConfig.getParity());
77 |
78 | //添加监听
79 | usbIoManager = new SerialInputOutputManager(usbSerialPort, new SerialInputOutputManager.Listener() {
80 |
81 | /*
82 | * Serial
83 | */
84 | @Override
85 | public void onNewData(byte[] data) {
86 | long nowTime = System.currentTimeMillis();
87 | if (nowTime - mLastSendTime > TIME_INTERVAL) {
88 | // do something
89 | mLastSendTime = nowTime;
90 | } else {
91 | LogUtils.i("过滤重复数据");
92 | return;
93 | }
94 |
95 | LogUtils.i("connected:" + connected + ",readFlag:" + readFlag);
96 | if (!connected) {
97 | LogUtils.e("not connected");
98 | reconnect();
99 | return;
100 | } else {
101 | if (!readFlag) {
102 | LogUtils.w("Have stopped reading.");
103 | return;
104 | }
105 | try {
106 | String barcode = new String(data).trim();
107 | disposeScanData(barcode);
108 | } catch (Exception e) {
109 | e.printStackTrace();
110 | }
111 |
112 | }
113 | }
114 |
115 | @Override
116 | public void onRunError(Exception e) {
117 | if (!connected) {
118 | LogUtils.e("not connected", e);
119 | return;
120 | }
121 | if (!readFlag) {
122 | LogUtils.w("Have stopped reading.");
123 | return;
124 | }
125 | LogUtils.e("扫码运行错误", e);
126 | reconnect();
127 | }
128 | });
129 | usbIoManager.setReadTimeout(usbConfig.getReadTimeout());
130 | usbIoManager.setWriteTimeout(usbConfig.getWriteTimeout());
131 | //在新的线程中监听串口的数据变化
132 | executorService.submit(usbIoManager);
133 | connected = true;
134 | return UsbErrorCode.USB_OK;
135 | } catch (Exception e) {
136 | LogUtils.e("connection failed: ", e);
137 | return UsbErrorCode.USB_UNKNOWN_FAIL;
138 | }
139 | }
140 |
141 |
142 | @Override
143 | public int sendData(byte[] buffer) {
144 | try {
145 | if (usbConfig != null){
146 | usbSerialPort.write(buffer, usbConfig.getWriteTimeout());
147 | return UsbErrorCode.USB_OK;
148 | }
149 |
150 | } catch (Exception e) {
151 | LogUtils.e("发送数据异常", e);
152 | }
153 | return UsbErrorCode.USB_SEND_DATA_FAIL;
154 | }
155 |
156 | private long mLastSendTime = 0;
157 | public static final long TIME_INTERVAL = 1200L;
158 |
159 | @Override
160 | public void startReading() {
161 | if (!connected) {
162 | LogUtils.e("not connected");
163 | return;
164 | }
165 | readFlag = true;
166 | //不能关闭读的通道,不然下次打开会有历史数据
167 | }
168 |
169 |
170 | @Override
171 | public void stopReading() {
172 | readFlag = false;
173 | }
174 |
175 | @Override
176 | public void reconnect() {
177 | if (reconnectCount > MAX_RECONNECT_COUNT) {
178 | return;
179 | }
180 | reconnectCount++;
181 | try {
182 | LogUtils.i("扫码重连开始第" + reconnectCount + "重连");
183 | closeScan();
184 | int i = openScan(usbConfig);
185 | if (i == UsbErrorCode.USB_OK) {
186 | LogUtils.i("=====扫码重连成功=====");
187 | startReading();
188 | reconnectCount = 0;
189 | } else {
190 | reconnect();
191 | }
192 | } catch (Exception e) {
193 | LogUtils.e("扫码重连异常",e);
194 | }
195 | }
196 |
197 |
198 | @Override
199 | public void closeScan() {
200 | super.closeScan();
201 | connected = false;
202 | try {
203 | if (usbIoManager != null) {
204 | usbIoManager.stop();
205 | }
206 | usbIoManager = null;
207 | if (usbSerialPort != null) {
208 | usbSerialPort.close();
209 | }
210 | } catch (Exception ignored) {
211 |
212 | }
213 | usbSerialPort = null;
214 |
215 | }
216 |
217 | }
218 |
--------------------------------------------------------------------------------
/scanner/src/main/java/com/sjl/scanner/UsbConfig.java:
--------------------------------------------------------------------------------
1 | package com.sjl.scanner;
2 |
3 | import com.hoho.android.usbserial.driver.UsbSerialPort;
4 | import com.sjl.scanner.util.ByteUtils;
5 |
6 | import java.util.Arrays;
7 |
8 | /**
9 | * usb连接配置类,默认有缺省值
10 | *
11 | * @author Kelly
12 | * @version 1.0.0
13 | * @filename UsbConfig
14 | * @time 2020/7/1 16:55
15 | * @copyright(C) 2020 song
16 | */
17 | public class UsbConfig {
18 |
19 | //usb连公共参数,
20 | // USB Serial(RS232的常规COM 端口) 6790, 29987
21 | // USB hID 1550,2407
22 | /**
23 | * 厂商 id
24 | */
25 | private int vendorId;
26 | /**
27 | * 产品 id
28 | */
29 | private int productId;
30 | /**
31 | * 写超时,毫秒
32 | */
33 | private int writeTimeout = 800;
34 | /**
35 | * 读超时,毫秒
36 | */
37 | private int readTimeout = 800;
38 |
39 | /**
40 | * usb com 需要
41 | */
42 | private SerialPortConfig serialPortConfig = new SerialPortConfig();
43 |
44 | public final static class SerialPortConfig {
45 | /**
46 | * 波特率
47 | */
48 | private int baudRate = 115200;
49 | /**
50 | * 数据位
51 | *
52 | * @param dataBits 默认8,可选值为5~8
53 | */
54 | private int dataBits = UsbSerialPort.DATABITS_8;
55 | /**
56 | * 校验位
57 | *
58 | * @param parity 0:无校验位(NONE,默认);1:奇校验位(ODD);2:偶校验位(EVEN);3:高位(MARK);4:低位(SPACE)
59 | */
60 | private int parity = UsbSerialPort.PARITY_NONE;
61 | /**
62 | * 停止位
63 | *
64 | * @param stopBits 默认1;1:1位停止位;2:2位停止位,3:1.5位停止位
65 | */
66 | private int stopBits = UsbSerialPort.STOPBITS_1;
67 | /**
68 | * 标志
69 | *
70 | * @param flags 默认0
71 | */
72 | private int flags = 0;
73 |
74 | public SerialPortConfig() {
75 | }
76 |
77 | /**
78 | * 波特率
79 | *
80 | * @param baudRate
81 | */
82 | public SerialPortConfig(int baudRate) {
83 | this.baudRate = baudRate;
84 | }
85 |
86 | public int getBaudRate() {
87 | return baudRate;
88 | }
89 |
90 | public void setBaudRate(int baudRate) {
91 | this.baudRate = baudRate;
92 | }
93 |
94 | public int getDataBits() {
95 | return dataBits;
96 | }
97 |
98 | public void setDataBits(int dataBits) {
99 | this.dataBits = dataBits;
100 | }
101 |
102 | public int getParity() {
103 | return parity;
104 | }
105 |
106 | public void setParity(int parity) {
107 | this.parity = parity;
108 | }
109 |
110 | public int getStopBits() {
111 | return stopBits;
112 | }
113 |
114 | public void setStopBits(int stopBits) {
115 | this.stopBits = stopBits;
116 | }
117 |
118 | public int getFlags() {
119 | return flags;
120 | }
121 |
122 | public void setFlags(int flags) {
123 | this.flags = flags;
124 | }
125 |
126 | @Override
127 | public String toString() {
128 | return "{" +
129 | "baudRate=" + baudRate +
130 | ", dataBits=" + dataBits +
131 | ", parity=" + parity +
132 | ", stopBits=" + stopBits +
133 | ", flags=" + flags +
134 | '}';
135 | }
136 | }
137 |
138 | /**
139 | * usb cmd需要
140 | */
141 | private ScanCmd scanCmd = new ScanCmd();
142 |
143 | public static final class ScanCmd {
144 | /**
145 | * 扫码触发指令,打开
146 | */
147 | private byte[] scanOpen = ByteUtils.hexStringToByteArr("FF540D");
148 | /**
149 | * 扫码触发指令,关闭
150 | */
151 | private byte[] scanClose = ByteUtils.hexStringToByteArr("FF550D");
152 |
153 | public byte[] getScanOpen() {
154 | return scanOpen;
155 | }
156 |
157 | public void setScanOpen(byte[] scanOpen) {
158 | this.scanOpen = scanOpen;
159 | }
160 |
161 | public byte[] getScanClose() {
162 | return scanClose;
163 | }
164 |
165 | public void setScanClose(byte[] scanClose) {
166 | this.scanClose = scanClose;
167 | }
168 |
169 | @Override
170 | public String toString() {
171 | return "{" +
172 | "scanOpen=" + ByteUtils.byteArrToHexString(scanOpen) +
173 | ", scanClose=" + ByteUtils.byteArrToHexString(scanClose) +
174 | '}';
175 | }
176 | }
177 |
178 | public int getVendorId() {
179 | return vendorId;
180 | }
181 |
182 | public void setVendorId(int vendorId) {
183 | this.vendorId = vendorId;
184 | }
185 |
186 | public int getProductId() {
187 | return productId;
188 | }
189 |
190 | public void setProductId(int productId) {
191 | this.productId = productId;
192 | }
193 |
194 | public SerialPortConfig getSerialPortConfig() {
195 | return serialPortConfig;
196 | }
197 |
198 | public void setSerialPortConfig(SerialPortConfig serialPortConfig) {
199 | this.serialPortConfig = serialPortConfig;
200 | }
201 |
202 | public ScanCmd getScanCmd() {
203 | return scanCmd;
204 | }
205 |
206 | public void setScanCmd(ScanCmd scanCmd) {
207 | this.scanCmd = scanCmd;
208 | }
209 |
210 | public int getWriteTimeout() {
211 | return writeTimeout;
212 | }
213 |
214 | public void setWriteTimeout(int writeTimeout) {
215 | this.writeTimeout = writeTimeout;
216 | }
217 |
218 | public int getReadTimeout() {
219 | return readTimeout;
220 | }
221 |
222 | public void setReadTimeout(int readTimeout) {
223 | this.readTimeout = readTimeout;
224 | }
225 |
226 | @Override
227 | public String toString() {
228 | return "UsbConfig{" +
229 | "vendorId=" + vendorId +
230 | ", productId=" + productId +
231 | ", writeTimeout=" + writeTimeout +
232 | ", readTimeout=" + readTimeout +
233 | ", serialPortConfig=" + serialPortConfig +
234 | ", scanCmd=" + scanCmd +
235 | '}';
236 | }
237 | }
238 |
239 |
--------------------------------------------------------------------------------
/scanner/src/main/java/com/sjl/scanner/UsbErrorCode.java:
--------------------------------------------------------------------------------
1 | package com.sjl.scanner;
2 |
3 | /**
4 | * TODO
5 | *
6 | * @author Kelly
7 | * @version 1.0.0
8 | * @filename UsbErrorCode
9 | * @time 2020/7/1 13:43
10 | * @copyright(C) 2020 song
11 | */
12 | public interface UsbErrorCode {
13 | /**
14 | * usb正常状态
15 | */
16 | int USB_OK = 0;
17 | /**
18 | * USB授权失败
19 | */
20 | int USB_PERMISSION_FAIL = -1;
21 | /**
22 | * 没有找到指定设备
23 | */
24 | int USB_FIND_THIS_FAIL = -2;
25 | /**
26 | * 没有找到任何设备
27 | */
28 | int USB_FIND_ALL_FAIL = -3;
29 | /**
30 | * USB设备打开失败
31 | */
32 | int USB_OPEN_FAIL = -4;
33 | /**
34 | * USB通道打开失败
35 | */
36 | int USB_CHANNEL_FAIL = -5;
37 | /**
38 | * USB发送数据失败
39 | */
40 | int USB_SEND_DATA_FAIL = -6;
41 | /**
42 | * USB读取数据失败
43 | */
44 | int USB_READ_DATA_FAIL = -7;
45 |
46 | /**
47 | * USB未知错误
48 | */
49 | int USB_UNKNOWN_FAIL = -99;//USB未知错误
50 | }
51 |
--------------------------------------------------------------------------------
/scanner/src/main/java/com/sjl/scanner/UsbKeyboardAutoScan.java:
--------------------------------------------------------------------------------
1 | package com.sjl.scanner;
2 |
3 | import android.view.KeyEvent;
4 |
5 | import com.sjl.scanner.listener.OnScanListener;
6 | import com.sjl.scanner.util.LogUtils;
7 |
8 | /**
9 | * usb键盘,自动感应扫码封装
10 | *
11 | * @author song
12 | */
13 | public class UsbKeyboardAutoScan {
14 | protected OnScanListener mOnScanListener;
15 |
16 | //延迟500ms,判断扫码是否完成。
17 | private final static long MESSAGE_DELAY = 500;
18 | private boolean intercept;
19 | //扫码内容
20 | private StringBuffer mStringBufferResult = new StringBuffer();
21 | private final Runnable mScanningFishedRunnable = new Runnable() {
22 | @Override
23 | public void run() {
24 | performScanSuccess();
25 | }
26 | };
27 |
28 | //返回扫描结果
29 | private void performScanSuccess() {
30 | String barcode = mStringBufferResult.toString().trim();
31 | disposeScanData(barcode);
32 | mStringBufferResult.setLength(0);
33 | }
34 |
35 | /**
36 | * 处理扫码数据并验证,运行在主线程
37 | *
38 | * @param barcode
39 | */
40 | protected void disposeScanData(String barcode) {
41 | LogUtils.i("扫码结果:" + barcode);
42 | if (mOnScanListener != null) {
43 | mOnScanListener.onScanSuccess(barcode);
44 | }
45 | }
46 |
47 |
48 | /**
49 | * key事件处理
50 | *
51 | * @param event
52 | */
53 | public void analysisKeyEvent(KeyEvent event) {
54 | int keyCode = event.getKeyCode();
55 | if (event.getAction() == KeyEvent.ACTION_DOWN) {
56 | char pressedKey = (char) event.getUnicodeChar();
57 | if (pressedKey != 0) {
58 | mStringBufferResult.append(pressedKey);
59 | }
60 | if (keyCode == KeyEvent.KEYCODE_ENTER) {
61 | //若为回车键,直接返回
62 | MainThreadExecutor.getHandler().removeCallbacks(mScanningFishedRunnable);
63 | MainThreadExecutor.getHandler().post(mScanningFishedRunnable);
64 | } else {
65 | //延迟post,若500ms内,有其他事件
66 | MainThreadExecutor.getHandler().removeCallbacks(mScanningFishedRunnable);
67 | MainThreadExecutor.getHandler().postDelayed(mScanningFishedRunnable, MESSAGE_DELAY);
68 | }
69 |
70 | }
71 |
72 | }
73 |
74 |
75 | /**
76 | * 设置扫码监听
77 | *
78 | * @param onScanListener
79 | * @return
80 | */
81 | public void setOnScanListener(OnScanListener onScanListener) {
82 | intercept = true;
83 | mOnScanListener = onScanListener;
84 | }
85 |
86 | /**
87 | * 移除扫码监听
88 | */
89 | public void removeScanListener() {
90 | intercept = false;
91 | if (mOnScanListener != null) {
92 | mOnScanListener = null;
93 | }
94 | }
95 |
96 | public boolean isIntercept() {
97 | return intercept;
98 | }
99 |
100 | /**
101 | * 取消扫码回调
102 | */
103 | public void cancel() {
104 | removeScanListener();
105 | MainThreadExecutor.getHandler().removeCallbacks(mScanningFishedRunnable);
106 | }
107 |
108 |
109 | }
110 |
--------------------------------------------------------------------------------
/scanner/src/main/java/com/sjl/scanner/UsbScanHelper.java:
--------------------------------------------------------------------------------
1 | package com.sjl.scanner;
2 |
3 | import android.app.PendingIntent;
4 | import android.content.BroadcastReceiver;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.IntentFilter;
8 | import android.hardware.usb.UsbDevice;
9 | import android.hardware.usb.UsbManager;
10 |
11 | import com.sjl.scanner.listener.UsbPermissionListener;
12 | import com.sjl.scanner.listener.UsbPlugListener;
13 | import com.sjl.scanner.util.LogUtils;
14 |
15 | import java.util.ArrayList;
16 | import java.util.HashMap;
17 | import java.util.List;
18 |
19 | /**
20 | * TODO
21 | *
22 | * @author Kelly
23 | * @version 1.0.0
24 | * @filename UsbScanHelper
25 | * @time 2022/6/9 11:37
26 | * @copyright(C) 2022 song
27 | */
28 | public class UsbScanHelper {
29 |
30 | static volatile UsbScanHelper usbScanHelper;
31 | private static UsbPermission usbPermission = UsbPermission.Unknown;
32 | private Context mContext;
33 |
34 | public enum UsbPermission {Unknown, Requested, Granted, Denied}
35 |
36 | private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
37 |
38 | private UsbPermissionListener usbPermissionListener;
39 | private UsbPlugListener usbPlugListener;
40 |
41 | public static UsbScanHelper getInstance() {
42 | if (usbScanHelper == null) {
43 | synchronized (UsbScanHelper.class) {
44 | if (usbScanHelper == null) {
45 | usbScanHelper = new UsbScanHelper();
46 | }
47 | }
48 | }
49 | return usbScanHelper;
50 | }
51 |
52 | /**
53 | * 初始化上下文
54 | * @param context
55 | */
56 | public void init(Context context) {
57 | mContext = context;
58 | }
59 |
60 |
61 | /**
62 | * 申请usb设备权限
63 | *
64 | * @param usbDevice
65 | */
66 | public void requestPermission(UsbDevice usbDevice) {
67 | requestPermission(usbDevice, null);
68 | }
69 |
70 | /**
71 | * 申请usb设备权限
72 | *
73 | * @param usbDevice
74 | * @param usbPermissionListener
75 | */
76 | public void requestPermission(UsbDevice usbDevice, UsbPermissionListener usbPermissionListener) {
77 | this.usbPermissionListener = usbPermissionListener;
78 | checkContext(mContext);
79 | UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
80 | PendingIntent usbPermissionIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_USB_PERMISSION), 0);
81 | usbManager.requestPermission(usbDevice, usbPermissionIntent);
82 | }
83 |
84 | private void checkContext(Context context) {
85 | if (context == null){
86 | throw new RuntimeException("context未初始化");
87 | }
88 | }
89 |
90 | /**
91 | * 是否有usb设备权限
92 | *
93 | * @param usbDevice
94 | * @return
95 | */
96 | public boolean hasPermission(UsbDevice usbDevice) {
97 | checkContext(mContext);
98 | UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
99 | return usbManager.hasPermission(usbDevice);
100 | }
101 |
102 | /**
103 | * 返回usb设备列表
104 | *
105 | * @return
106 | */
107 | public List getDeviceList() {
108 | checkContext(mContext);
109 | UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
110 | HashMap deviceList = usbManager.getDeviceList();
111 | for (UsbDevice usbDevice : deviceList.values()) {
112 | LogUtils.i("id:" + usbDevice.getDeviceId() + ",mName:" + usbDevice.getDeviceName() + ",vendorID:" + usbDevice.getVendorId() + ",ProductId:" + usbDevice.getProductId());
113 | }
114 | return new ArrayList<>(deviceList.values());
115 | }
116 |
117 |
118 | /**
119 | * 注册usb广播
120 | */
121 | public void registerUsbReceiver() {
122 | checkContext(mContext);
123 | IntentFilter filter = new IntentFilter();
124 | filter.addAction(ACTION_USB_PERMISSION);
125 | filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
126 | filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
127 | mContext.registerReceiver(mBroadcastReceiver, filter);
128 | }
129 |
130 | /**
131 | * 反注册usb广播
132 | */
133 | public void unregisterUsbReceiver() {
134 | if (mBroadcastReceiver != null) {
135 | mContext.unregisterReceiver(mBroadcastReceiver);
136 | }
137 | usbPermissionListener = null;
138 | usbPlugListener = null;
139 | }
140 |
141 | private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
142 |
143 | @Override
144 | public void onReceive(Context context, Intent intent) {
145 | String action = intent.getAction();
146 |
147 | if (ACTION_USB_PERMISSION.equals(action)) {
148 | UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
149 | if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
150 | usbPermission = UsbPermission.Granted;
151 | if (usbPermissionListener != null) {
152 | usbPermissionListener.onGranted();
153 | }
154 | } else {
155 | LogUtils.w("usb 授权拒绝: " + device.getDeviceName());
156 | usbPermission = UsbPermission.Denied;
157 | if (usbPermissionListener != null) {
158 | usbPermissionListener.onDenied();
159 | }
160 | }
161 |
162 | } else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
163 | UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
164 | LogUtils.w("USB插入:" + device.toString());
165 | if (usbPlugListener != null) {
166 | usbPlugListener.onAttached(device);
167 | }
168 | } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
169 | UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
170 | LogUtils.w("USB拔出:" + device.toString());
171 | if (usbPlugListener != null) {
172 | usbPlugListener.onDetached(device);
173 | }
174 | }
175 | }
176 | };
177 |
178 | /**
179 | * usb插拔监听
180 | *
181 | * @param usbPlugListener
182 | */
183 | public void setUsbPlugListener(UsbPlugListener usbPlugListener) {
184 | this.usbPlugListener = usbPlugListener;
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/scanner/src/main/java/com/sjl/scanner/listener/OnScanListener.java:
--------------------------------------------------------------------------------
1 | package com.sjl.scanner.listener;
2 |
3 | /**
4 | * TODO
5 | *
6 | * @author Kelly
7 | * @version 1.0.0
8 | * @filename OnScanListener
9 | * @time 2020/7/1 11:35
10 | * @copyright(C) 2020 song
11 | */
12 | public interface OnScanListener {
13 | /**
14 | * 正常扫码成功回调
15 | *
16 | * @param barcode
17 | */
18 | void onScanSuccess(String barcode);
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/scanner/src/main/java/com/sjl/scanner/listener/UsbPermissionListener.java:
--------------------------------------------------------------------------------
1 | package com.sjl.scanner.listener;
2 |
3 | /**
4 | * USB设备权限申请回调
5 | *
6 | * @author Kelly
7 | * @version 1.0.0
8 | * @filename UsbPer
9 | * @time 2022/6/7 14:31
10 | * @copyright(C) 2022 song
11 | */
12 | public interface UsbPermissionListener {
13 | void onGranted();
14 |
15 | void onDenied();
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/scanner/src/main/java/com/sjl/scanner/listener/UsbPlugListener.java:
--------------------------------------------------------------------------------
1 | package com.sjl.scanner.listener;
2 |
3 | import android.hardware.usb.UsbDevice;
4 |
5 | /**
6 | * USB设备插拔回调
7 | *
8 | * @author Kelly
9 | * @version 1.0.0
10 | * @filename UsbPlugListener
11 | * @time 2022/6/9 11:51
12 | * @copyright(C) 2022 song
13 | */
14 | public interface UsbPlugListener {
15 |
16 | void onAttached(UsbDevice usbDevice);
17 |
18 | void onDetached(UsbDevice usbDevice);
19 | }
20 |
--------------------------------------------------------------------------------
/scanner/src/main/java/com/sjl/scanner/util/ByteUtils.java:
--------------------------------------------------------------------------------
1 | package com.sjl.scanner.util;
2 |
3 | /**
4 | * TODO
5 | *
6 | * @author Kelly
7 | * @version 1.0.0
8 | * @filename ByteUtils
9 | * @time 2020/8/29 19:32
10 | * @copyright(C) 2020 song
11 | */
12 | public class ByteUtils {
13 | /**
14 | * 16进制字符串转字节数组
15 | *
16 | * @param hex
17 | * @return
18 | */
19 | public static byte[] hexStringToByteArr(String hex) {
20 | int l = hex.length() / 2;
21 | byte[] ret = new byte[l];
22 | for (int i = 0; i < l; i++) {
23 | ret[i] = (byte) Integer.valueOf(hex.substring(i * 2, i * 2 + 2), 16).byteValue();
24 | }
25 | return ret;
26 | }
27 |
28 | /**
29 | * 字节数组转16进制字符串
30 | * @param b
31 | * @return
32 | */
33 | public static String byteArrToHexString(byte[] b) {
34 | StringBuilder result = new StringBuilder();
35 | for (int i = 0; i < b.length; i++) {
36 | result.append(Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1));
37 | }
38 | return result.toString().toUpperCase();
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/scanner/src/main/java/com/sjl/scanner/util/LogUtils.java:
--------------------------------------------------------------------------------
1 | package com.sjl.scanner.util;
2 |
3 | import android.util.Log;
4 |
5 | /**
6 | * TODO
7 | *
8 | * @author Kelly
9 | * @version 1.0.0
10 | * @filename LogUtils
11 | * @time 2020/8/29 18:56
12 | * @copyright(C) 2020 song
13 | */
14 | public class LogUtils {
15 | private static boolean debug = false;
16 | private LogUtils() {
17 |
18 | }
19 | public static void init(boolean debug){
20 | LogUtils.debug = debug;
21 | }
22 | private static final String TAG = "SCAN_LOGGER";
23 |
24 | public static void i(String str) {
25 | Log.i(TAG, str);
26 | }
27 |
28 | public static void w(String str) {
29 | Log.w(TAG, str);
30 | }
31 |
32 | public static void e(String str) {
33 | Log.e(TAG, str);
34 | }
35 |
36 | public static void e(String str, Exception e) {
37 | Log.e(TAG, str, e);
38 | }
39 |
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/scanner/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/scanner/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | library
3 |
4 |
--------------------------------------------------------------------------------
/scanner/src/test/java/com/sjl/scanner/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.sjl.scanner;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':scanner'
--------------------------------------------------------------------------------