优化扫码枪支付
This commit is contained in:
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user