Method for connecting Android to USB code scanning module

  • 2021-12-21 05:03:03
  • OfStack

Preface

USB code scanning module can be a scanning box or a code scanning gun, An USB line is used for power supply and data communication. Some code scanning modules support virtual serial port mode. It is easier to read data in virtual serial port mode, which can be operated like ordinary serial port, that is, data can be obtained through virtual serial port + baud rate. Here, we mainly talk about reading data in USB mode.

1. Read data in USB mode

The code scanning module in USB mode is equivalent to an external keyboard, That is, it must be in the place where there is a cursor to scan the code. And the scanned content is directly automatically input into the input box, It is not under our control, so we must find another way. There is such a side dispatchKeyEvent (KeyEvent event) in Android system, which is used to process the input events of our keyboard. If we intercept this method and give it to ourselves for processing, so that we can get the data transmitted from the wharf without Edittext.

It is worth noting that the scan code output characters are continuously output, that is to say, the scan code data does not output all the data in one sub-sub, but the output characters with intervals, which are relatively short, about 10ms. Some scan code modules have carriage return terminators, while others do not, so we need to package the code for use, as follows:


import android.os.Build;
import android.os.Handler;
import android.util.Log;
import android.view.KeyEvent;

import org.greenrobot.eventbus.EventBus;

import java.util.ArrayList;
import java.util.List;

public class ScanGunHelper {

    private final static long MESSAGE_DELAY = 200;

    private StringBuffer mStringBufferResult = new StringBuffer();

    private Handler mHandler = new Handler();

    private Runnable mScanningFishedRunnable = new Runnable() {
        @Override
        public void run() {
            performScanSuccess();
        }
    };


    private List<Integer> keyCodeList = new ArrayList<>();

    private static class SingletonHolder {
        private static final ScanGunHelper instance = new ScanGunHelper();
    }

    private ScanGunHelper() {

    }

    public static ScanGunHelper getInstance() {
        return ScanGunHelper.SingletonHolder.instance;
    }

    /**
     *  Return the result after successful code scanning 
     */
    private void performScanSuccess() {
        try {

            boolean mCaps = keyCodeList.contains(KeyEvent.KEYCODE_SHIFT_RIGHT) || keyCodeList.contains(KeyEvent.KEYCODE_SHIFT_LEFT);

            for (int keyCode : keyCodeList) {

                char aChar = getInputCode(keyCode, mCaps);
                if (aChar != 0) {
                    mStringBufferResult.append(aChar);
                }
            }
            String barcode = mStringBufferResult.toString();

            Log.e("ScanGunHelper", "barcode==" + barcode);

            mStringBufferResult.setLength(0);

            keyCodeList.clear();

            if (mHandler != null) {
                mHandler.removeCallbacks(mScanningFishedRunnable);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     *  Analysis of Code Scanning Gun Event 
     */
    public void analysisKeyEvent(KeyEvent event) {

        //Virtual Is the name of the built-in soft keyboard of the machine I use 
        // In this judgment, it is because conflicts with soft keyboards are avoided in the project (both code scanning guns and soft keyboards are key events) 
        int keyCode = event.getKeyCode();

        if (event.getAction() == KeyEvent.ACTION_UP) {

            keyCodeList.add(keyCode);

            if (keyCode == KeyEvent.KEYCODE_ENTER) {
                mHandler.removeCallbacks(mScanningFishedRunnable);
                mHandler.post(mScanningFishedRunnable);
            } else {
                mHandler.removeCallbacks(mScanningFishedRunnable);
                mHandler.postDelayed(mScanningFishedRunnable, MESSAGE_DELAY);
            }
        }
    }


    // Get scanned content 
    private char getInputCode(int keyCode, boolean mCaps) {
        char aChar;
        if (keyCode >= KeyEvent.KEYCODE_A && keyCode <= KeyEvent.KEYCODE_Z) {
            // Alphabet 
            aChar = (char) ((mCaps ? 'A' : 'a') + keyCode - KeyEvent.KEYCODE_A);
        } else if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {
            // Figures 
            aChar = (char) ('0' + keyCode - KeyEvent.KEYCODE_0);
        } else {
            // Other symbols 
            switch (keyCode) {
                case KeyEvent.KEYCODE_PERIOD:
                    aChar = '.';
                    break;
                case KeyEvent.KEYCODE_MINUS:
                    aChar = mCaps ? '_' : '-';
                    break;
                case KeyEvent.KEYCODE_SLASH:
                    aChar = '/';
                    break;
                case KeyEvent.KEYCODE_BACKSLASH:
                    aChar = mCaps ? '|' : '\\';
                    break;
                default:
                    aChar = 0;
                    break;
            }
        }
        return aChar;
    }

    /**
     *  Does the input device exist 
     */
    public static boolean isScanGunExist(KeyEvent event) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            Log.e("ScanGunHelper", "ProductId==" + event.getDevice().getProductId() + " , VendorId==" + event.getDevice().getVendorId());
            if (event.getDevice().getVendorId() == 1409 && event.getDevice().getProductId() == 262) {
                return true;
            }
        }
        return false;
    }

}

If the code scanning module used has certain ProductId and VendorId, it can be filtered directly. Writing dead ProductId and VendorId will make the program very inflexible. It is not suitable to change one code scanning module, so it is recommended to make it configurable to avoid a lot of trouble

If you don't use ProductId and VendorId to filter input events, you can distinguish the soft keyboard under 1. The DeviceId of the automatic software disk of the system is-1. As long as it is not equal to-1, it is the character input by the code scanning module.

The single-column mode encapsulates the code, mainly considering that it is impossible for an Android 1 body machine to access two code scanning modules, and it can be treated as the only device.

2. It is used in Activity of base class to ensure that every interface can intercept data, which is convenient to use and does not need to intercept every interface


import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.KeyEvent;
import android.view.WindowManager;

public abstract class BaseActivity extends AppCompatActivity {


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }


    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        if (event.getDeviceId() > 0) {
            ScanGunHelper.getInstance().analysisKeyEvent(event);
            return true;
        }
        return super.dispatchKeyEvent(event);
    }
}

Summary:

The current USB code scanning module is quite popular, However, the communication modes between the code scanning module and the terminal equipment are full of tricks, There are wifi, Bluetooth, USB, serial port and so on. But everything is the same, For Android devices, The code scanning module is an external keyboard, No matter how you communicate (except serial port), The final form is an external keyboard, Of course, we can also use Bluetooth to read scan code data. These are so esay, Serial port is simpler, It is better to have an actual serial port line, Virtual serial port will be troublesome for 1 point, Every time the virtual string number is plugged and unplugged, the string number displayed by different Android devices is not fixed, so the virtual serial port mode needs to do a lot of adaptation work, and one virtual string number cannot be written dead, which will be made configurable according to the inflexible and unsuitable program, so as to cope with the changeable bidding site or customer demand site.


Related articles: