添加init界面,调整了认证后逻辑,调整了扫描枪扫描处理

This commit is contained in:
zxj
2025-08-01 18:23:54 +08:00
parent d20ee771b0
commit 47f6e814a6
22 changed files with 279 additions and 197 deletions
@@ -26,6 +26,9 @@ object GlobalData {
*/
var arrayMode: Int = 0
var appId = "Hkz1rBk6PZXbS8KwKr67K2eZtsz8bRoMHLg64bUdgCZj"
var sdkKey = "AabXs3sHM8UhhE7oCGf4LVMLrdkGb1nJNksbjjTdVt7k"
var activeKey = "085F-118G-Q391-53YL"
}
/**
@@ -318,7 +318,7 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
}
if (equipmentUserInfo.equipmentBoxCode?.isNotEmpty() == true) {
SerialApi.openPlate(
equipmentUserInfo.equipmentBoxCode.toInt(),
equipmentUserInfo.equipmentBoxCode!!.toInt(),
object : SerialPortManager.SendCallback {
override fun onSuccess() {
MainActivity.start(context, pageType = PageType.PLATE_TIP)
@@ -343,6 +343,7 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
}
override fun onDestroy() {
SerialApi.close()
timeJob?.cancel()
keyEventHelper?.onDestroy()
super.onDestroy()
@@ -0,0 +1,28 @@
package com.sw.platecabinet.activity
import android.content.Intent
import com.sw.platecabinet.databinding.ActivityInitBinding
import com.sw.platecabinet.databinding.ItemTitleTimeBinding
class InitActivity : BaseActivity<ActivityInitBinding>() {
override fun inflateViewBinding(): ActivityInitBinding {
return ActivityInitBinding.inflate(layoutInflater)
}
override fun inflateTitleBinding(): ItemTitleTimeBinding? {
return binding.includeHeader
}
override fun initialize() {
binding.main.setOnClickListener {
val intent = Intent(this, LoginByFaceActivity::class.java)
startActivity(intent)
}
}
override fun onLeftDoubleClick() {
finish()
}
}
@@ -5,6 +5,7 @@ import android.content.Intent
import android.graphics.Point
import android.hardware.Camera
import android.os.Build
import android.os.CountDownTimer
import android.util.DisplayMetrics
import android.view.View
import android.view.ViewGroup
@@ -29,8 +30,8 @@ import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel
import com.sw.platecabinet.R
import com.sw.platecabinet.databinding.ActivityLoginFaceBinding
import com.sw.platecabinet.databinding.ItemTitleTimeBinding
import com.sw.platecabinet.model.response.EquipmentUserInfo
import com.sw.platecabinet.utils.PermissionHelper
import com.sw.platecabinet.utils.ThreadUtils
import timber.log.Timber
/**
@@ -39,6 +40,7 @@ import timber.log.Timber
class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
ViewTreeObserver.OnGlobalLayoutListener {
private val recognizeViewModel by viewModels<RecognizeViewModel>()
private var countDownTimer: CountDownTimer? = null
private val CAMERA_PERMISSION_REQUEST_CODE = 100
private val REQUIRED_PERMISSIONS: Array<String> = arrayOf(
@@ -55,11 +57,19 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
return binding.includeHeader
}
override fun initialize() {
ThreadUtils.launch {
viewModel.activeEngine()
}
companion object {
private var instance: LoginByFaceActivity? = null
fun finishActivity() {
instance?.finish()
}
}
override fun initialize() {
instance = this
viewModel.activeEngine()
initCountTime()
initArcViewModel()
initArcView()
openRectInfoDraw = true
@@ -157,6 +167,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
}
override fun onDestroy() {
instance = null
if (rgbCameraHelper != null) {
rgbCameraHelper!!.release()
rgbCameraHelper = null
@@ -356,6 +367,10 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
super.onResume()
resumeCamera()
viewModel.resetUserInfo()
countDownTimer?.let {
it.cancel()
it.start()
}
}
protected override fun onPause() {
@@ -369,4 +384,21 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
recognizeViewModel.onPreviewFrame(ByteArray(1382400), true)
}
override fun handleLoginSuccess(equipmentUserInfo: EquipmentUserInfo, isAdmin: Boolean) {
super.handleLoginSuccess(equipmentUserInfo, isAdmin)
finish()
}
fun initCountTime() {
val totalTimeInMillis = 30L * 1000
countDownTimer = object : CountDownTimer(totalTimeInMillis, 1000) {
override fun onTick(millisUntilFinished: Long) {
binding.tvToInit.text = "${(millisUntilFinished / 1000).toInt() + 1}"
}
override fun onFinish() {
finish()
}
}.start()
}
}
@@ -41,6 +41,7 @@ class LoginByPwdActivity : BaseActivity<ActivityLoginByPwdBinding>() {
override fun handleLoginSuccess(equipmentUserInfo: EquipmentUserInfo, isAdmin: Boolean) {
super.handleLoginSuccess(equipmentUserInfo, isAdmin)
LoginByFaceActivity.finishActivity()
finish()
}
}
@@ -1,16 +1,17 @@
package com.sw.platecabinet.fragment
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.text.Editable
import android.text.TextUtils
import android.text.TextWatcher
import android.view.inputmethod.EditorInfo
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.sw.plate.utils.ToastUtils
import com.sw.plate.utils.comn.SerialApi
import com.sw.plate.utils.comn.SerialPortManager
import com.sw.platecabinet.R
import com.sw.platecabinet.activity.MainActivity
import com.sw.platecabinet.activity.PageType
@@ -23,6 +24,7 @@ import com.sw.platecabinet.ext.maskName
import com.sw.platecabinet.ext.maskPhone
import com.sw.platecabinet.model.response.EquipmentUserInfo
import com.sw.platecabinet.model.response.SearchResult
import com.sw.platecabinet.utils.Debouncer
import com.sw.platecabinet.utils.KeyboardUtils
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.launch
@@ -39,6 +41,7 @@ class BindPlateFragment : BaseFragment<FragmentBindPlateBinding>(
private var info: EquipmentUserInfo? = null
private var checkedItem: SearchResult.Member? = null
private var lastText = ""
private val debouncer = Debouncer(2000)
companion object {
@JvmStatic
@@ -94,21 +97,25 @@ class BindPlateFragment : BaseFragment<FragmentBindPlateBinding>(
}
})
(activity as MainActivity).registerKeyEventInfo {
binding.etCode.text = Editable.Factory.getInstance().newEditable(it)
if (binding.etUserInfo.isFocused) return@registerKeyEventInfo
binding.etCode.text.clear()
binding.etCode.postDelayed({
binding.etCode.text.clear()
binding.etCode.text = Editable.Factory.getInstance().newEditable(it)
binding.etUserInfo.requestFocus()
}, 50)
}
}
private fun initListener() {
binding.etUserInfo.addTextChangedListener(object : TextWatcher {
private val handler = Handler(Looper.getMainLooper())
private val debounceDelay = 500L // 延迟 500 毫秒
binding.etCode.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(
s: CharSequence?,
start: Int,
count: Int,
after: Int
) {
Timber.d("beforeTextChanged s = ${s.toString()}, start = $start, count = $count, after = $after")
}
override fun onTextChanged(
@@ -117,20 +124,32 @@ class BindPlateFragment : BaseFragment<FragmentBindPlateBinding>(
before: Int,
count: Int
) {
Timber.d("onTextChanged s = ${s.toString()}, start = $start, count = $count, before = $before")
}
override fun afterTextChanged(s: Editable?) {
val currentText = s.toString()
if (currentText == lastText) return // 内容未变化时不处理
lastText = currentText
handler.removeCallbacksAndMessages(null) // 取消之前的延迟任务
handler.postDelayed({
// if (currentText.isNotEmpty()) {
viewModel.getSearchMemberList(param = currentText, pageNum = 1)
// }
}, debounceDelay)
Timber.d("afterTextChanged s = ${s.toString()}")
}
})
binding.etUserInfo.setOnEditorActionListener { _, actionId, keyEvent ->
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
val currentText = binding.etUserInfo.text.toString()
val containsNewlines = currentText.contains('\n') || currentText.contains('\r')
if (containsNewlines) {
Timber.d("非用户主动搜索")
true
}
debouncer.debounce {
// 执行搜索操作
viewModel.getSearchMemberList(param = currentText, pageNum = 1)
// 隐藏键盘
KeyboardUtils.hideKeyboard(requireActivity())
}
true // 表示已处理该事件
} else {
false // 未处理其他动作
}
}
binding.llBind.setOnClickListener {
if (info == null || info!!.equipmentCode?.isEmpty() == true || info!!.equipmentBoxCode?.isEmpty() == true) {
ToastUtils.showToast("传入的设备信息异常")
@@ -176,15 +195,29 @@ class BindPlateFragment : BaseFragment<FragmentBindPlateBinding>(
if (it.first == null) return@collect // 解绑状态过滤
val errorInfo = it.second
if (errorInfo.isSuccess()) {
if (errorInfo.equipmentUserInfo?.isOtherEquipment() == true) {
if (errorInfo.equipmentUserInfo == null) {
ToastUtils.showToast("数据异常")
return@collect
}
if (errorInfo.equipmentUserInfo.isOtherEquipment() == true) {
MainActivity.start(
requireContext(),
pageType = PageType.PLATE_TIP,
equipmentUserInfo = errorInfo.equipmentUserInfo
)
} else {
ToastUtils.showToast("绑定成功")
activity?.finish()
SerialApi.openPlate(
errorInfo.equipmentUserInfo.equipmentBoxCode!!.toInt(),
object : SerialPortManager.SendCallback {
override fun onSuccess() {
ToastUtils.showToast("绑定成功")
activity?.finish()
}
override fun onFail(e: Exception?) {
ToastUtils.showToast("绑定失败")
}
})
}
} else {
ToastUtils.showToast(errorInfo.msg)
@@ -15,12 +15,12 @@ data class EquipmentUserInfo(
* 设备盒子编号
*/
@SerializedName("equipmentBoxCode")
val equipmentBoxCode: String? = "",
var equipmentBoxCode: String? = "",
/**
* 设备编号
*/
@SerializedName("equipmentCode")
val equipmentCode: String? = "",
var equipmentCode: String? = "",
/**
*
* 设备Id
@@ -71,7 +71,7 @@ data class EquipmentUserInfo(
* 是否是其他设备
*/
fun isOtherEquipment(): Boolean {
return GlobalData.globalEquipmentCode != equipmentCode
return equipmentCode != null && equipmentCode != "" && GlobalData.globalEquipmentCode != equipmentCode
}
fun isPlaceholder(): Boolean {
@@ -0,0 +1,25 @@
package com.sw.platecabinet.utils
class Debouncer(private val delayMillis: Long) {
private var lastActionTime = 0L
/**
* 执行防抖操作
* @param action 要执行的操作
* @return Boolean 是否执行了操作 (true=已执行, false=被防抖)
*/
fun debounce(action: () -> Unit): Boolean {
val currentTime = System.currentTimeMillis()
if (currentTime - lastActionTime >= delayMillis) {
lastActionTime = currentTime
action()
return true
}
return false
}
// 重置防抖计时
fun reset() {
lastActionTime = 0L
}
}
@@ -1,124 +0,0 @@
package com.sw.platecabinet.utils
import android.os.Handler
import android.os.Looper
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import kotlin.coroutines.CoroutineContext
/**
* 多功能线程工具类
* 结合协程、Handler和线程池实现线程切换
*/
object ThreadUtils : CoroutineScope {
// 主线程Handler
private val mainHandler by lazy { Handler(Looper.getMainLooper()) }
// 后台线程池(IO密集型任务)
private val ioThreadPool: ExecutorService by lazy {
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2)
}
// CPU密集型线程池
private val cpuThreadPool: ExecutorService by lazy {
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())
}
// 协程Job管理
private val job = Job()
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job
// ========== Handler相关方法 ==========
/**
* 在主线程执行任务
* @param delayMillis 延迟时间(毫秒)
*/
fun runOnUiThread(delayMillis: Long = 0, block: () -> Unit) {
if (delayMillis > 0) {
mainHandler.postDelayed(block, delayMillis)
} else {
if (isOnMainThread()) {
block()
} else {
mainHandler.post(block)
}
}
}
/**
* 移除主线程任务
*/
fun removeUiThreadTask(block: () -> Unit) {
mainHandler.removeCallbacks(block)
}
// ========== 线程池相关方法 ==========
/**
* 在IO线程执行任务
*/
fun runOnIoThread(block: () -> Unit) {
ioThreadPool.execute(block)
}
/**
* 在CPU计算线程执行任务
*/
fun runOnCpuThread(block: () -> Unit) {
cpuThreadPool.execute(block)
}
// ========== 协程相关方法 ==========
/**
* 启动协程(默认在主线程)
*/
fun launch(block: suspend CoroutineScope.() -> Unit): Job {
return launch(coroutineContext, block = block)
}
/**
* 在IO线程启动协程
*/
fun launchOnIo(block: suspend CoroutineScope.() -> Unit): Job {
return launch(Dispatchers.IO, block = block)
}
/**
* 切换到主线程(协程环境)
*/
suspend fun <T> switchToMain(block: suspend CoroutineScope.() -> T): T {
return withContext(Dispatchers.Main, block)
}
/**
* 切换到IO线程(协程环境)
*/
suspend fun <T> switchToIo(block: suspend CoroutineScope.() -> T): T {
return withContext(Dispatchers.IO, block)
}
/**
* 是否在主线程
*/
fun isOnMainThread(): Boolean {
return Looper.myLooper() == Looper.getMainLooper()
}
/**
* 释放资源
*/
fun release() {
job.cancel()
ioThreadPool.shutdown()
cpuThreadPool.shutdown()
}
}
@@ -99,7 +99,13 @@ class SettingViewModel : BaseViewModel() {
)
val response = repository.bindEquipment(bindParam)
if (parseResponse(response)) {
val userInfo = response.data
var userInfo = response.data
if (userInfo == null) {
userInfo = EquipmentUserInfo()
userInfo.equipmentBoxCode = equipmentBoxCode
userInfo.plateNumber = plateNumber
userInfo.equipmentCode = equipmentCode
}
_bindStateChange.value = true to ErrorInfo(equipmentUserInfo = userInfo)
} else {
_bindStateChange.value = true to ErrorInfo(response.code, response.msg.toString())
@@ -1,5 +1,6 @@
package com.sw.platecabinet.viewmodel
import androidx.lifecycle.viewModelScope
import com.arcsoft.face.ErrorInfo
import com.sw.inbound.utils.SPUtil
import com.sw.plate.App
@@ -13,10 +14,10 @@ import com.sw.platecabinet.GlobalKey
import com.sw.platecabinet.model.request.LoginParam
import com.sw.platecabinet.model.response.EquipmentUserInfo
import com.sw.platecabinet.model.response.UserFaceModel
import com.sw.platecabinet.utils.ThreadUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
@@ -70,18 +71,17 @@ class UserViewModel : BaseViewModel() {
*/
fun activeEngine() {
Timber.d("activeEngine")
var appId = "Hkz1rBk6PZXbS8KwKr67K2eZtsz8bRoMHLg64bUdgCZj"
var sdkKey = "AabXs3sHM8UhhE7oCGf4LVMLrdkGb1nJNksbjjTdVt7k"
var activeKey = "085F-118G-Q391-53YL"
faceApi.activeEngine(
App.getContext(),
appId,
sdkKey,
activeKey,
GlobalData.appId,
GlobalData.sdkKey,
GlobalData.activeKey,
object : FaceApi.ActiveCallback {
override fun onSuccess(activeCode: Int) {
Timber.d("activeEngine activeCode = $activeCode")
ThreadUtils.launch {
// ThreadUtils.launch {
viewModelScope.launch(Dispatchers.Main) {
when (activeCode) {
ErrorInfo.MOK -> {
ToastUtils.showToast("激活引擎成功")
@@ -99,7 +99,7 @@ class UserViewModel : BaseViewModel() {
}
override fun onFail(e: Exception?) {
ThreadUtils.launch {
viewModelScope.launch(Dispatchers.Main) {
ToastUtils.showToast("激活引擎异常,${e?.message}")
}
}