Android外接扫码器

最近做了一个关于Android设备Usb外接扫码器的项目,在此记录下。
言归正传,首先扫码器有这两种模式:USB HID-KBW,USB 虚拟串口。

  • USB HID-KBW:扫码器会将扫描出来的内容模拟成键盘事件,就是Android中就是KeyEvent里面对应的常量(比如:KeyEvent.KEYCODE_A 表示小写字母a,如果要表示大写字母A,就会组合KeyEvent.KEYCODE_SHIFT_LEFT或者KeyEvent.KEYCODE_SHIFT_RIGHT + KeyEvent.KEYCODE_A)。
    这种模式优点是使用简单,只要有焦点的文本输入框,系统会自动将扫码出的内容填充到文本框内,缺点是不支持中文!

  • USB 虚拟串口:这种模式相对来说复杂点,但也比较简单,Android系统屏蔽了底层串口通信(需要了解Android串口通信的可以看下android-serialport-api ),直接使用UsbDevice进行通信,连接到UsbDevice,读取数据,好处是支持中文扫码。

上代码:
USB HID-KBW,这种模式我们可以自定义EditText对内容进行拦截,过滤出我们想要的内容或者触发我们的业务逻辑。自定义EditText很简单,初始化的时候设置调用setOnKeyListener方法,设置OnKeyListener,通过onKey方法返回true来拦截键盘事件,至于拦截到的键盘事件通过KeyEvent的对应常量转换成相应的字符(注意这里需要让EditText获取到焦点,不然KeyEvent不会触发)。

public class CustomEditText extends android.support.v7.widget.AppCompatEditText {

    private ScanCodeListener codeListener;
    private StringBuffer buffer = new StringBuffer();

    public CustomEditText(Context context) {
        super(context);
        init();
    }

    public CustomEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CustomEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }


    private int preKeyCode = -1;

    private void init() {
        setOnKeyListener(new OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (event.getAction() == KeyEvent.ACTION_UP) {
                    Log.d("ooo", "Code:" + keyCode + ", display int:" + (int) event.getDisplayLabel() + ", char:" + event.getDisplayLabel());

                    if (keyCode == KeyEvent.KEYCODE_ENTER) {
                        String str = buffer.toString();
                        buffer.setLength(0);
                        preKeyCode = -1;
                        if (codeListener != null) {
                            codeListener.handleCode(str);
                        }
                    } else if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
                        preKeyCode = KeyEvent.KEYCODE_SHIFT_RIGHT;
                    } else {
                        if (preKeyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
                            String shiftChar = SHIFT_KEY_MAP.get(keyCode);
                            if (shiftChar != null) {
                                buffer.append(shiftChar);
                            }
                        } else {
                            char label = event.getDisplayLabel();
                            if (label != 0) {
                                buffer.append(toLowCase(label));
                            }
                        }
                        preKeyCode = -1;
                    }
                }
                return true;
            }
        });
    }

    private char toLowCase(char c) {
        if (c >= 'A' && c <= 'Z') {
            c += 32;
            return c;
        }
        return c;
    }

    public void setScanCodeListener(ScanCodeListener codeListener) {
        this.codeListener = codeListener;
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        return super.dispatchKeyEvent(event);
    }

    @Override
    public boolean dispatchKeyEventPreIme(KeyEvent event) {
        boolean value = super.dispatchKeyEventPreIme(event);
        return value;
    }

    @Override
    public void setText(CharSequence text, BufferType type) {
        super.setText(text, type);
    }

    @Override
    public void setOnKeyListener(OnKeyListener l) {
        super.setOnKeyListener(l);
    }

    public interface ScanCodeListener {
        void handleCode(String code);
    }

    static SparseArray SHIFT_KEY_MAP = new SparseArray<>();

    static {

        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_GRAVE, "~");

        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_0, ")");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_1, "!");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_2, "@");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_3, "#");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_4, "$");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_5, "%");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_6, "^");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_7, "&");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_8, "*");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_9, ")");

        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_MINUS, "_");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_EQUALS, "+");

        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_LEFT_BRACKET, "{");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_RIGHT_BRACKET, "}");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_BACKSLASH, "|");

        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_SEMICOLON, ":");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_APOSTROPHE, "\"");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_COMMA, "<");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_PERIOD, ">");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_SLASH, "?");

        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_A, "A");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_B, "B");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_C, "C");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_D, "D");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_E, "E");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_F, "F");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_G, "G");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_H, "H");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_I, "I");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_J, "J");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_K, "K");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_L, "L");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_M, "M");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_N, "N");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_O, "O");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_P, "P");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_Q, "Q");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_R, "R");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_S, "S");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_T, "T");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_U, "U");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_V, "V");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_W, "W");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_X, "X");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_Y, "Y");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_Z, "Z");
    }
}

USB 虚拟串口,这种模式稍稍复杂点,要自己去连接USB设备,读取数据,其实也很简单

  1. 找到设备
  2. 设备授权
  3. 连接设备
  4. 读取数据
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    public static final String ACTION_DEVICE_PERMISSION = "com.demo.USB_PERMISSION";

    private final int MY_PRODUCT_ID = 33286;//产品ID,根据你的设备修改
    private final int MY_VENDOR_ID = 7851;  //厂商ID,根据你的设备修改
    
    private UsbManager usbManager;
    private TextView textView;
    
    private UsbEndpoint inUsbEndpoint;
    private UsbEndpoint outUsbEndpoint;
    private UsbInterface mUsbInterface;
    private UsbDeviceConnection mUsbDeviceConnection;

    private Thread thread;
    private Buff buff = new Buff(); //负责分割扫码数据

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
        findViewById(R.id.list_usb_device).setOnClickListener(this);
        findViewById(R.id.connect).setOnClickListener(this);
        findViewById(R.id.read).setOnClickListener(this);
        findViewById(R.id.send).setOnClickListener(this);
        findViewById(R.id.clear).setOnClickListener(this);
        textView = (TextView) findViewById(R.id.device_info);

    }

    protected void onResume() {
        super.onResume();
        IntentFilter usbFilter = new IntentFilter();
        usbFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
        usbFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
        usbFilter.addAction(ACTION_DEVICE_PERMISSION);
        registerReceiver(mUsbReceiver, usbFilter);
    }
    
    @Override
    protected void onPause() {
        super.onPause();
        unregisterReceiver(mUsbReceiver);
    }

    private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();

            if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
                Log.d("xxx", "ACTION_USB_DEVICE_ATTACHED");
            } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
                Log.d("xxx", "ACTION_USB_DEVICE_DETACHED");
            } else if (ACTION_DEVICE_PERMISSION.equals(action)) {
                UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                    Toast.makeText(MainActivity.this, "已授权回调", Toast.LENGTH_SHORT).show();
                    if (device != null) {
                        initCommunication(device);
                    }
                } else {
                    Toast.makeText(MainActivity.this, "未授权回调", Toast.LENGTH_SHORT).show();
                }
            }
        }
    };

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.list_usb_device) {
            HashMap deviceList = usbManager.getDeviceList();
            if (deviceList != null) {
                Set keySet = deviceList.keySet();
                StringBuffer stringBuffer = new StringBuffer();
                for (String key : keySet) {
                    stringBuffer.append(deviceList.get(key).toString() + "\n");
                    stringBuffer.append("\n\n");
                }
                textView.setText(stringBuffer.toString());
            } else {
                textView.setText("没有设备");
            }
        } else if (v.getId() == R.id.connect) {
            HashMap deviceList = usbManager.getDeviceList();
            if (deviceList.size() > 0) {
                for (UsbDevice device : deviceList.values()) {
                    if (MY_VENDOR_ID == device.getVendorId() && MY_PRODUCT_ID == device.getProductId()) {
                        if (usbManager.hasPermission(device)) {
                            initCommunication(device);
                            continue;
                        } else {
                            requestUsbPermission(device);
                        }
                        break;
                    }
                }
            }
        } else if (v.getId() == R.id.read) {
            startRead();
        } else if (v.getId() == R.id.send) {
            sendData();
        } else if (v.getId() == R.id.clear) {
            textView.setText("");
        }
    }

    //申请授权
    private void requestUsbPermission(UsbDevice device) {
        PendingIntent mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_DEVICE_PERMISSION), 0);
        usbManager.requestPermission(device, mPermissionIntent);
    }
    
    //开启线程读取数据
    private void startRead() {
        if (mUsbDeviceConnection == null && !mUsbDeviceConnection.claimInterface(mUsbInterface, true))
            return;
        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                
                while (true) {
                    byte[] bytes = new byte[inUsbEndpoint.getMaxPacketSize()];
                    //读取数据, 这里注意最后一个参数为0时 为阻塞读取,知道读到数据为止
                    int ret = mUsbDeviceConnection.bulkTransfer(inUsbEndpoint, bytes, bytes.length, 0);
                    if (ret > 0) {
                        //这里因为扫码器的maxPacketSize很小(我的设备只有64byte),所以扫码到的结果可能分多次读取,
                        //所以这里将读取到的数据丢到Buff里面,Buff负责分割成单条数据
                        buff.append(Arrays.copyOf(bytes, ret));
                        //分割获取单条扫码数据
                        byte[] code = buff.getCode();
                        if (code != null) {
                            final String str = new String(code);
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    textView.setText(str);
                                }
                            });
                        }
                    } else {
                        Log.d("xxx", "Read empty data");
                    }
                }
            }
        });
        thread.start();
    }

    //初始化设备,找到对应的通信接口
    private void initCommunication(UsbDevice device) {
        int interfaceCount = device.getInterfaceCount();
        for (int interfaceIndex = 0; interfaceIndex < interfaceCount; interfaceIndex++) {
            UsbInterface usbInterface = device.getInterface(interfaceIndex);
            if ((UsbConstants.USB_CLASS_CDC_DATA != usbInterface.getInterfaceClass())
                    && (UsbConstants.USB_CLASS_COMM != usbInterface.getInterfaceClass())
                    && UsbConstants.USB_CLASS_HID != usbInterface.getInterfaceClass()) {
                continue;
            }
            for (int i = 0; i < usbInterface.getEndpointCount(); i++) {

                UsbEndpoint ep = usbInterface.getEndpoint(i);
                if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
                    if (ep.getDirection() == UsbConstants.USB_DIR_IN) {
                        inUsbEndpoint = ep;
                    } else {
                        outUsbEndpoint = ep;
                    }
                }

            }

            if (null == inUsbEndpoint) {
                inUsbEndpoint = null;
                outUsbEndpoint = null;
                mUsbInterface = null;
            } else {
                //连接成功
                mUsbInterface = usbInterface;
                mUsbDeviceConnection = usbManager.openDevice(device);
                initConnection();
                break;
            }
        }
        if (mUsbDeviceConnection == null) {
            Toast.makeText(this, "连接失败", Toast.LENGTH_SHORT).show();
        }
    }

    //初始化,设置比特率
    private void initConnection() {
        if (!mUsbDeviceConnection.claimInterface(mUsbInterface, true)) {
            return;
        }
        
        int ret = mUsbDeviceConnection.controlTransfer(0x21, 0x22, 0x00, 0, null, 0, 0);
        setCdcBaudrate(9600);
    }

    private void setCdcBaudrate(int baudrate) {
        byte[] baudByte = new byte[4];

        baudByte[0] = (byte) (baudrate & 0x000000FF);
        baudByte[1] = (byte) ((baudrate & 0x0000FF00) >> 8);
        baudByte[2] = (byte) ((baudrate & 0x00FF0000) >> 16);
        baudByte[3] = (byte) ((baudrate & 0xFF000000) >> 24);
        int ret = mUsbDeviceConnection.controlTransfer(0x21, 0x20, 0, 0, new byte[]{
                baudByte[0], baudByte[1], baudByte[2], baudByte[3], 0x00, 0x00,
                0x08
        }, 7, 0);
    }

    //发送数据
    private void sendData() {
        byte[] buffer = "test data".getBytes();
        int ret = mUsbDeviceConnection.controlTransfer(UsbConstants.USB_DIR_OUT, 1, 1, 1, buffer, buffer.length, 1000);
        Toast.makeText(this, "send " + ret + " bytes data", Toast.LENGTH_SHORT).show();
    }
}

Buff 用于 分割扫码数据,我用的设备是以回车符(0X )+换行符(0X0A)为扫码结束分隔符

public class Buff {
    private final int DELIMITER_1 = 0xd; //回车符
    private final int DELIMITER_2 = 0xa; //换行符
    private byte[] buffer;

    public void append(byte[] data) {
        if (buffer == null) {
            buffer = new byte[data.length];
            System.arraycopy(data, 0, buffer, 0, data.length);
        } else {
            byte[] temp = new byte[buffer.length + data.length];
            System.arraycopy(buffer, 0, temp, 0, buffer.length);
            System.arraycopy(data, 0, temp, buffer.length, data.length);
            buffer = temp;
        }
    }

    public byte[] getCode() {
        if (buffer == null)
            return null;
        if (buffer.length > 2) {
            boolean found = false;
            int i = 0;
            for (; i < buffer.length - 1; i++) {
                if (buffer[i] == DELIMITER_1 && buffer[i + 1] == DELIMITER_2) {
                    found = true;
                    break;
                }
            }
            if (found) {
                byte[] result = Arrays.copyOf(buffer, i);
                if (i < buffer.length - 2) {
                    buffer = Arrays.copyOfRange(buffer, i + 2, buffer.length);
                } else {
                    buffer = null;
                }
                return result;
            }
        }
        return null;
    }
}

你可能感兴趣的:(Android外接扫码器)