Android电子班牌USB免驱读取卡号数据

 

最近做了一个Android外接USB读卡器刷手环读取数据,模拟键盘输入事件的项目;

借鉴了https://github.com/githubRonda/BarcodeScanner

连接电子牌板子调试,可以将板子上OTG跳帽取下,然后用一根双USB口的线连接电脑就可以调试了(ps:板子上一般连接靠近网线的USB口)

因为之前公司的Android系统的电子牌的读卡器是通过串口开发的,最近由于换了电子牌的厂商,手环读取方式也更改了,无奈研究了一番,从网上找了相关文章,但是没有找到具体的,其中根据某个大神的外接扫码器的项目,结合实际终于实现了;

不管外接扫码枪还是外接读卡器,其实原理就我了解的原理是一样的,都是会将扫描到或者刷卡读取到的数据模拟成键盘输入的事件,你会发现,当Android界面有一个焦点输入框时候,扫码或者刷卡,数据都会自动填充输入框, 那问题也随之而来,我们需要做的其实就是监听按键输入事件,然后获取扫到或者读取到的数据,之后进行一些其他方面的操作.

回归正文:

 

后台无障碍服务AccessibilityService配置

import android.accessibilityservice.AccessibilityService;
import android.util.Log;
import android.view.KeyEvent;
import android.view.accessibility.AccessibilityEvent;
 
/**
 * Created by Administrator on 2019/4/22.
 */
public class ReadCardService extends AccessibilityService {
 
    private static final String TAG = ReadCardService.class.getSimpleName();
 
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        Log.e(TAG, "onAccessibilityEvent --> " + event);
    }
 
    @Override
    public void onInterrupt() {
        Log.e(TAG, "onInterrupt");
    }
 
    /**
     * 复写这个方法可以捕获按键事件
     *
     * @param event
     * @return
     */
    @Override
    protected boolean onKeyEvent(KeyEvent event) {
        int keyCode = event.getKeyCode();
        Log.e(TAG, "keyEvent:" + event + "keyCode: " + keyCode + "char: " + KeyEvent.keyCodeToString(keyCode));
 
        return super.onKeyEvent(event);
    }
}
在AndroidManifest.xml文件中记得配置

<service
            android:name=".readService.ReadCardService"
            android:enabled="true"
            android:exported="true"
            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
            <intent-filter>
                <action android:name="android.accessibilityservice.AccessibilityService"/>
            </intent-filter>
            <meta-data
                android:name="android.accessibilityservice"
                android:resource="@xml/accessibility"/>
        </service>
res目录下创建xml文件夹,并在xml文件夹内创建accessibility.xml文件内容如下

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:accessibilityFlags="flagRequestFilterKeyEvents"
    android:canRetrieveWindowContent="true"
    android:canRequestFilterKeyEvents="true"
    android:description="@string/accessibility_description"
    android:packageNames="com.xxx.xxxdemo你的项目包名"/>
 
accessibility_description在res的values文件夹内strings中设置你想要说明的,例如
<string name="accessibility_description">xxx按键监听的无障碍辅助服务</string>
ReadCardUtils工具类

import android.content.Context;
import android.content.res.Configuration;
import android.os.Handler;
import android.util.Log;
import android.view.InputDevice;
import android.view.KeyEvent;
 
/**
 * Created by Administrator on 2019/4/22.
 *
 * 使用说明:
 *  * 1. 在Activity中先创建ReadCardUtils对象,并设置扫码成功监听器: setReadSuccessListener() [一般在onCreate()方法中初始化]
 *  * 2. 接着在Activity#dispatchKeyEvent() 或者 Activity#onKeyDown() 中调用本类中的resolveKeyEvent()方法。当扫码结束之后,会自动回调第一步设置的监听器中的方法
 *  *
 *  * 原理分析:
 *  * 1. 扫码枪就是一个外部的输入设备(和键盘一样)。扫码的时候,就是在极短的时间内输入了一系列的数字或字母
 *  * 2. 这样就可以在键盘事件中抓捕这些输入的字符,但是又会产生一个问题(快速扫两次的情形):在键盘事件中应该抓捕多少个字符呢?即一个条码应该在哪个位置结束呢? (有的扫码枪会以一个回车或者换行作为一次扫码的结束符,但是有的就纯粹的是一系列的条码。这个得需要设置)
 *  * 所以为了兼容性,应当是当短时间内不再输入字符的时候,就表示扫码已结束。这样只能定性描述,不能定量,只能自己在程序中用一个具体的数字来表示这个“短时间”,eg:500ms。(如果每个条码结束的时候都有一个结束符那该多好,直接判断这个结束符,就可以知道当前扫码已完成)
 *  *
 *  * 接下来就产生了ReadCardUtils这个类。
 *  * 核心原理就一句话:在Activity的键盘监听事件中,每抓捕到一个字符的时候,就先向 Handler 一次一个runnable对象,再延迟500ms发送一个runnable. 这样若两个输入字符的间隔时间超过了500ms,则视为两次扫码
 *
 */
public class ReadCardUtils {
 
    private static final String TAG = ReadCardUtils.class.getSimpleName();
 
    // 若500ms之内无字符输入,则表示扫码完成. (若觉得时间还长,则可以设置成更小的值)
    private final static long MESSAGE_DELAY = 500;
 
    private boolean mCaps;//大写或小写
    private StringBuilder mResult = new StringBuilder();//扫码内容
 
    private OnReadSuccessListener mOnReadSuccessListener;
    private Handler mHandler = new Handler();
 
    private final Runnable mReadingEndRunnable = new Runnable() {
        @Override
        public void run() {
            performScanSuccess();
        }
    };
 
    //调用回调方法
    private void performScanSuccess() {
        String barcode = mResult.toString();
        //Log.i(TAG, "performScanSuccess -> barcode: "+barcode);
        if (mOnReadSuccessListener != null) {
            mOnReadSuccessListener.onScanSuccess(barcode);
        }
        mResult.setLength(0);
    }
 
    //key事件处理
    public void resolveKeyEvent(KeyEvent event) {
 
        int keyCode = event.getKeyCode();
 
        checkLetterStatus(event);//字母大小写判断
 
        Log.w(TAG, "keyEvent:" + event + "keyCode: " + keyCode + "char: " + KeyEvent.keyCodeToString(keyCode));
        if (event.getAction() == KeyEvent.ACTION_DOWN) {
 
 
            char aChar = getInputCode(event);
            Log.w(TAG, "aChar: " + aChar);
 
            if (aChar != 0) {
                mResult.append(aChar);
            }
 
            if (keyCode == KeyEvent.KEYCODE_ENTER) {
                //若为回车键,直接返回
                mHandler.removeCallbacks(mReadingEndRunnable);
                mHandler.post(mReadingEndRunnable);
            } else {
                //延迟post,若500ms内,有其他事件
                mHandler.removeCallbacks(mReadingEndRunnable);
                mHandler.postDelayed(mReadingEndRunnable, MESSAGE_DELAY);
            }
 
        }
    }
 
    //检查shift键
    private void checkLetterStatus(KeyEvent event) {
        int keyCode = event.getKeyCode();
        if (keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT || keyCode == KeyEvent.KEYCODE_SHIFT_LEFT) {
            if (event.getAction() == KeyEvent.ACTION_DOWN) {
                //按着shift键,表示大写
                mCaps = true;
            } else {
                //松开shift键,表示小写
                mCaps = false;
            }
        }
    }
 
    //获取扫描内容
    private char getInputCode(KeyEvent event) {
        int keyCode = event.getKeyCode();
 
        char aChar;
        if (keyCode >= KeyEvent.KEYCODE_A && keyCode <= KeyEvent.KEYCODE_Z) {
            //字母
            aChar = (char) ((mCaps ? 'A' : 'a') + keyCode - KeyEvent.KEYCODE_A);
        } else if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {
            //数字
            aChar = (char) ('0' + keyCode - KeyEvent.KEYCODE_0);
        } else {
            //其他符号
            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;
    }
 
    /**
     * 检测输入设备是否是读卡器
     *
     * @param context
     * @return 是的话返回true,否则返回false
     */
    public static boolean isInputFromReader(Context context, KeyEvent event) {
        if (event.getDevice() == null) {
            return false;
        }
//        event.getDevice().getControllerNumber();
        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK || event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN || event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP) {
            //实体按键,若按键为返回、音量加减、返回false
            return false;
        }
        if (event.getDevice().getSources() == (InputDevice.SOURCE_KEYBOARD | InputDevice.SOURCE_DPAD | InputDevice.SOURCE_CLASS_BUTTON)) {
            //虚拟按键返回false
            return false;
        }
        Configuration cfg = context.getResources().getConfiguration();
        return cfg.keyboard != Configuration.KEYBOARD_UNDEFINED;
    }
 
 
    public interface OnReadSuccessListener {
        void onScanSuccess(String barcode);
    }
 
    public void setReadSuccessListener(OnReadSuccessListener onReadSuccessListener) {
        mOnReadSuccessListener = onReadSuccessListener;
    }
 
    public void removeScanSuccessListener() {
        mHandler.removeCallbacks(mReadingEndRunnable);
        mOnReadSuccessListener = null;
    }
 
}
在你的MainActivity类中

声明读取工具类ReadCardUtils服务,并在合适的位置初始化

    //U口读卡器,类似于外接键盘
    private ReadCardUtils readCardUtils;
初始化

    //读卡器声明
    readCardUtils = new ReadCardUtils();
    initCardReader();
 /**
     * 读卡器初始化
     */
    private void initCardReader() {
        readCardUtils.setReadSuccessListener(new ReadCardUtils.OnReadSuccessListener() {
            @Override
            public void onScanSuccess(String barcode) {
                Log.e(TAG, "barcode: " + barcode);
            }
        });
    }
 
    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        if (ReadCardUtils.isInputFromReader(this, event)) {
            if (readCardUtils != null){
                readCardUtils.resolveKeyEvent(event);
            }
        }
        return super.dispatchKeyEvent(event);
    }
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        return super.onKeyDown(keyCode, event);
    }
 
    @Override
    protected void onDestroy() {
        readCardUtils.removeScanSuccessListener();
        readCardUtils = null;
        super.onDestroy();
    }
 

安卓USB免驱读取卡号数据demo (demo无添加界面提示,需要在控制面板查看log输出)源码demo下载链接


--------------------- 
来源:CSDN 
原文:https://blog.csdn.net/joyu_/article/details/89641271 
版权声明:转载请附上博文链接!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值