优化扫码枪支付

This commit is contained in:
mazengfei
2026-03-10 14:44:34 +08:00
parent 39c0ca7d72
commit 8a1630f2a4
8 changed files with 193 additions and 154 deletions
@@ -59,7 +59,7 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
}
override fun onDestroy() {
keyEventHelper?.onDestroy()
keyEventHelper?.release()
EventBus.getDefault().unregister(this)
super.onDestroy()
}
@@ -153,7 +153,7 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
*/
fun registerKeyEvent() {
keyEventHelper =
ScanGunKeyEventHelper(context, object : ScanGunKeyEventHelper.OnScanSuccessListener {
ScanGunKeyEventHelper( object : ScanGunKeyEventHelper.OnScanSuccessListener {
override fun onScanSuccess(barcode: String?) {
Timber.d("onScanSuccess barcode = $barcode")
if (barcode == null) return
@@ -166,10 +166,13 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
/**
* 处理扫描枪数据
*/
open fun scanGunKeyEventCallback(scanInfo: String) {
Timber.tag("TAG").d("handleScanKeyInfo: $scanInfo")
fun scanGunKeyEventCallback(scanInfo: String){
if (this is PayActivity) {
qrCodePay(scanInfo)
}
}
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
if (keyEventHelper != null) {
if (keyEventHelper!!.isScanGunEvent(event)) {
@@ -158,7 +158,6 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
}
setupCamera()
initData()
registerKeyEvent()
}
private fun initData() {
@@ -1,5 +1,7 @@
package com.sw.dualscreen.activity
import android.view.KeyEvent
import androidx.activity.OnBackPressedCallback
import androidx.activity.viewModels
import androidx.fragment.app.Fragment
import com.sw.dualscreen.GlobalKey
@@ -15,6 +17,7 @@ import com.sw.dualscreen.model.response.FoodInfo
import com.sw.dualscreen.model.response.MemberInfo
import com.sw.dualscreen.model.response.PaySuccessEvent
import com.sw.dualscreen.presentation.pay.PayPresentation
import com.sw.dualscreen.utils.Debouncer
import com.sw.dualscreen.utils.SPUtil
import com.sw.dualscreen.utils.SoundPoolUtil
import com.sw.dualscreen.viewmodel.BaseViewModel
@@ -81,7 +84,8 @@ class PayActivity : BaseActivity<ActivityPayBinding>() {
override fun initialize() {
super.initialize()
presentation.show()
registerKeyEvent()
addBackEventListener()
val soundMap = hashMapOf<String, Int>()
soundMap[PAY_SUCCESS] = R.raw.pay_success
SoundPoolUtil.getInstance().loadR(this, soundMap)
@@ -196,11 +200,15 @@ class PayActivity : BaseActivity<ActivityPayBinding>() {
presentation?.loadQrCodeImage(qrCodeUrl)
}
private var debouncer = Debouncer(5000)
fun showPaySuccess(isVip: Boolean) {
playSoundOnPaySuccess()
EventBus.getDefault().post(PaySuccessEvent())
showPayInfo(type = 2, isVip = isVip, memberInfo = memberInfo)
hidePayTab()
debouncer.debounce {
playSoundOnPaySuccess()
EventBus.getDefault().post(PaySuccessEvent())
showPayInfo(type = 2, isVip = isVip, memberInfo = memberInfo)
hidePayTab()
}
}
fun getQrCodeImg(
@@ -286,10 +294,11 @@ class PayActivity : BaseActivity<ActivityPayBinding>() {
}
}
override fun scanGunKeyEventCallback(scanInfo: String) {
super.scanGunKeyEventCallback(scanInfo)
fun qrCodePay(scanInfo: String) {
showWaitingDialog("支付中……")
userViewModel.qrCodePay(scanInfo, foodOrderId) { payState, backData ->
Timber.d("qrCodePay支付返回数据:state=$payState,backData=$backData")
hideWaitingDialog()
if (payState) {
showPaySuccess(false)
}
@@ -340,4 +349,12 @@ class PayActivity : BaseActivity<ActivityPayBinding>() {
// }
// }
// }
fun addBackEventListener() {
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
}
})
}
}
@@ -220,12 +220,10 @@ interface ApiService {
* 二维码支付
* @param orderType 0付款 1会员充值
*/
@GET
@POST
suspend fun qrCodePay(
@Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/pay/app/qrCodePay",
@Query("authCode") authCode: String,
@Query("outTradeNo") orderNo: String,
@Query("orderType") orderType:Int
@Body param: HashMap<String, String>
): ApiResponse<String?>
/**
@@ -191,7 +191,13 @@ class RemoteRepository constructor(
*/
suspend fun qrCodePay(authCode: String,orderNo: String): ApiResponse<String?> {
return safeApiCall {
apiService.qrCodePay(authCode = authCode, orderNo=orderNo, orderType = 0)
apiService.qrCodePay(
param = hashMapOf(
"authCode" to authCode,
"outTradeNo" to orderNo,
"orderType" to "0"
)
)
}
}
@@ -104,7 +104,7 @@ class UserViewModel : BaseViewModel() {
withContext(Dispatchers.Default) {
val list: List<UserFaceModel> = response.data ?: emptyList()
if (pageNo == 1 && list.isEmpty()) {
ToastUtils.showToast("查询人脸数据为空")
//ToastUtils.showToast("查询人脸数据为空")
return@withContext
}
//val item = list.map { it.member }
+2 -2
View File
@@ -1,8 +1,8 @@
#Mon Jul 14 11:28:37 CST 2025
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.11.1-all.zip
#distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
#distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.11.1-all.zip
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.14.3-all.zip
#distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.14.3-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
@@ -5,7 +5,10 @@ import static android.content.Context.INPUT_SERVICE;
import android.content.Context;
import android.hardware.input.InputManager;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import java.util.Arrays;
@@ -14,172 +17,185 @@ import java.util.List;
/**
* 扫描枪事件处理
*/
public class ScanGunKeyEventHelper {
private final static long MESSAGE_DELAY = 500; //延迟500ms,判断扫码是否完成。
private final StringBuffer mStringBufferResult; //扫码内容
private boolean mCaps; //大小写区分
private final Handler mHandler;
private final Runnable mScanningFishedRunnable;
private OnScanSuccessListener mOnScanSuccessListener;
private final Context mContext;
// private String mDeviceName = "TMC HIDKeyBoard";
//private String mDeviceName = "Linux 3.4.35 with ak-hsudc Composite Gadget (ACM + HID)";
//private String mDeviceName1 = "USBKey Chip USBKey Module";
private static final long SCAN_DELAY = 200; // 扫码结束判断时间
private String[] deviceNameArray = new String[]{
"CROWN CROWN Barcode KeyBorad FS",
"Linux 3.4.35 with ak-hsudc Composite Gadget (ACM + HID)",
"USBKey Chip USBKey Module"
};
private long lastTime = 0L;
private final StringBuilder resultBuffer = new StringBuilder();
private boolean caps;
public ScanGunKeyEventHelper(Context context, OnScanSuccessListener onScanSuccessListener) {
mContext = context;
mOnScanSuccessListener = onScanSuccessListener;
mStringBufferResult = new StringBuffer();
mHandler = new Handler();
mScanningFishedRunnable = this::performScanSuccess;
private final Handler handler = new Handler(Looper.getMainLooper());
private final Runnable scanFinishRunnable;
private OnScanSuccessListener listener;
private long lastScanTime = 0;
public ScanGunKeyEventHelper(OnScanSuccessListener listener) {
this.listener = listener;
scanFinishRunnable = this::handleScanFinish;
}
/**
* 返回扫码成功后的结果
* 扫码结束
*/
private void performScanSuccess() {
String barcode = mStringBufferResult.toString();
L.d("performScanSuccess barcode = " + barcode);
long currentTime = System.currentTimeMillis();
if (currentTime - lastTime < 5 * 1000) {
L.d("performScanSuccess 操作太频繁");
mStringBufferResult.setLength(0);
private void handleScanFinish() {
String barcode = resultBuffer.toString();
if (barcode.isEmpty()) {
return;
}
lastTime = currentTime;
if (mOnScanSuccessListener != null && !TextUtils.isEmpty(barcode))
mOnScanSuccessListener.onScanSuccess(barcode);
mStringBufferResult.setLength(0);
long now = System.currentTimeMillis();
// 防止重复扫描
if (now - lastScanTime < 1000) {
resultBuffer.setLength(0);
return;
}
lastScanTime = now;
if (listener != null) {
listener.onScanSuccess(barcode);
}
resultBuffer.setLength(0);
}
/**
* 扫码枪事件解析
*
* @param event
* 解析 KeyEvent
*/
public void analysisKeyEvent(KeyEvent event) {
int keyCode = event.getKeyCode();
//字母大小写判断
checkLetterStatus(event);
if (event.getAction() == KeyEvent.ACTION_DOWN) {
char aChar = getInputCode(event);
if (aChar != 0) {
mStringBufferResult.append(aChar);
}
if (keyCode == KeyEvent.KEYCODE_ENTER) {
//若为回车键,直接返回
mHandler.removeCallbacks(mScanningFishedRunnable);
mHandler.post(mScanningFishedRunnable);
} else {
//延迟post,若500ms内,有其他事件
mHandler.removeCallbacks(mScanningFishedRunnable);
mHandler.postDelayed(mScanningFishedRunnable, MESSAGE_DELAY);
}
if (event.getAction() != KeyEvent.ACTION_DOWN) {
return;
}
checkCaps(event);
int keyCode = event.getKeyCode();
if (keyCode == KeyEvent.KEYCODE_ENTER) {
handler.removeCallbacks(scanFinishRunnable);
handler.post(scanFinishRunnable);
return;
}
char c = getInputCode(event);
if (c != 0) {
resultBuffer.append(c);
}
handler.removeCallbacks(scanFinishRunnable);
handler.postDelayed(scanFinishRunnable, SCAN_DELAY);
}
//检查shift键
private void checkLetterStatus(KeyEvent event) {
/**
* 判断是否为扫码枪
*/
public boolean isScanGunEvent(KeyEvent event) {
if (event == null) return false;
InputDevice device = event.getDevice();
if (device == null) return false;
int sources = device.getSources();
// HID keyboard
boolean isKeyboard =
(sources & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD;
// 排除虚拟键盘
boolean notVirtual =
event.getDeviceId() != KeyCharacterMap.VIRTUAL_KEYBOARD;
return isKeyboard && notVirtual;
}
/**
* SHIFT 状态
*/
private void checkCaps(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;
}
if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
|| keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
caps = event.getAction() == KeyEvent.ACTION_DOWN;
}
}
/**
* 获取扫描内容
*
* @param event
* @return
* KeyEvent 转字符
*/
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);
return (char) ((caps ? '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 (char) ('0' + keyCode - KeyEvent.KEYCODE_0);
}
return aChar;
switch (keyCode) {
case KeyEvent.KEYCODE_MINUS:
return caps ? '_' : '-';
case KeyEvent.KEYCODE_EQUALS:
return caps ? '+' : '=';
case KeyEvent.KEYCODE_LEFT_BRACKET:
return caps ? '{' : '[';
case KeyEvent.KEYCODE_RIGHT_BRACKET:
return caps ? '}' : ']';
case KeyEvent.KEYCODE_BACKSLASH:
return caps ? '|' : '\\';
case KeyEvent.KEYCODE_SEMICOLON:
return caps ? ':' : ';';
case KeyEvent.KEYCODE_APOSTROPHE:
return caps ? '"' : '\'';
case KeyEvent.KEYCODE_COMMA:
return caps ? '<' : ',';
case KeyEvent.KEYCODE_PERIOD:
return caps ? '>' : '.';
case KeyEvent.KEYCODE_SLASH:
return caps ? '?' : '/';
case KeyEvent.KEYCODE_SPACE:
return ' ';
default:
return 0;
}
}
public void release() {
handler.removeCallbacks(scanFinishRunnable);
listener = null;
}
public interface OnScanSuccessListener {
void onScanSuccess(String barcode);
}
public void onDestroy() {
mHandler.removeCallbacks(mScanningFishedRunnable);
mOnScanSuccessListener = null;
}
/**
* 输入设备是否存在
*
* @param deviceName
* @return
*/
public boolean isInputDeviceExist(String deviceName) {
InputManager inputManager = (InputManager) mContext.getSystemService(INPUT_SERVICE);
int[] deviceIds = inputManager.getInputDeviceIds();
for (int id : deviceIds) {
if (inputManager.getInputDevice(id).getName().equals(deviceName)) {
return true;
}
}
return false;
}
/**
* 是否为扫码枪事件(部分机型KeyEvent获取的名字错误)
*
* @param event
* @return
*/
public boolean isScanGunEvent(KeyEvent event) {
if (event == null || event.getDevice() == null) return false;
String deviceName = event.getDevice().getName();
L.e("event===" + deviceName +
"===Char===" + event.getCharacters() +
"===Action===" + event.getAction());
//return deviceName.equals(mDeviceName) || deviceName.equals(mDeviceName1);
List<String> devices = Arrays.asList(deviceNameArray);
return devices.contains(deviceName);
}
}