增加人脸采集功能;

识别人脸成功后打开有餐盘的第一个柜子;
扫码打开已绑定的柜子,无则打开第一个空柜子并放入餐盘;
设置页面问题优化;
This commit is contained in:
mazengfei
2026-01-14 19:00:20 +08:00
parent 11a0a41e3c
commit aadba7efd5
24 changed files with 1327 additions and 908 deletions
@@ -7,13 +7,21 @@ import android.hardware.Camera
import android.os.Build
import android.os.CountDownTimer
import android.util.DisplayMetrics
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import androidx.activity.viewModels
import androidx.annotation.RequiresApi
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import com.arcsoft.face.ErrorInfo
import com.google.gson.Gson
import com.sw.inbound.utils.DateTimeUtils
import com.sw.inbound.utils.GsonUtils
import com.sw.plate.App
import com.sw.plate.utils.Base64
import com.sw.plate.utils.ToastUtils
import com.sw.plate.utils.arcface.ConfigUtil
import com.sw.plate.utils.arcface.ErrorCodeUtil
@@ -24,17 +32,31 @@ import com.sw.plate.utils.arcface.PreviewConfig
import com.sw.plate.utils.arcface.camera.CameraListener
import com.sw.plate.utils.arcface.camera.DualCameraHelper
import com.sw.plate.utils.arcface.face.constants.LivenessType
import com.sw.plate.utils.arcface.face.model.CompareResult
import com.sw.plate.utils.arcface.face.model.FacePreviewInfo
import com.sw.plate.utils.arcface.face.model.RecognizeConfiguration
import com.sw.plate.utils.arcface.faceserver.FaceServer
import com.sw.plate.utils.arcface.facedb.FaceDatabase
import com.sw.plate.utils.arcface.facedb.entity.FaceEntity
import com.sw.plate.utils.arcface.model.UserFaceInfo
import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel
import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel.REGISTER_STATUS_READY
import com.sw.platecabinet.MyApp
import com.sw.platecabinet.R
import com.sw.platecabinet.databinding.ActivityLoginFaceBinding
import com.sw.platecabinet.databinding.ItemTitleTimeBinding
import com.sw.platecabinet.ext.gone
import com.sw.platecabinet.ext.invisible
import com.sw.platecabinet.ext.visible
import com.sw.platecabinet.model.request.BindParam
import com.sw.platecabinet.model.response.EquipmentUserInfo
import com.sw.platecabinet.network.task.TaskManager
import com.sw.platecabinet.utils.IntervalExecutor
import com.sw.platecabinet.utils.PermissionHelper
import com.sw.platecabinet.utils.PlateUtils
import com.sw.platecabinet.utils.SpTool
import com.sw.platecabinet.utils.countDownByFlow
import com.sw.platecabinet.viewmodel.SettingViewModel
import kotlinx.coroutines.Job
import kotlinx.coroutines.runBlocking
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
@@ -64,6 +86,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
}
companion object {
private const val TAG = "LoginByFaceActivity"
private var instance: LoginByFaceActivity? = null
fun goInitActivity() {
@@ -82,17 +105,26 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
recognizeViewModel.setDrawRectInfoTextValue(true)
//viewModel.generateToken()
// viewModel.activeEngine()
viewModel.getUserFaceCache()
binding.llToPwd.setOnClickListener {
val intent = Intent(this, LoginByPwdActivity::class.java)
startActivity(intent)
}
// TODO: 暂时注释人脸接口数据,测试本地采集
// viewModel.getUserFaceCache()
// Thread {
// val faceDao = FaceDatabase.getInstance(App.getContext()).faceDao();
// faceDao.deleteAll()
// faceDao.resetId()
// }.start()
// TODO: 测试请求
TaskManager.startTask()
// binding.llToPwd.setOnClickListener {
// val intent = Intent(this, LoginByPwdActivity::class.java)
// startActivity(intent)
// }
// TaskManager.startTask()
EventBus.getDefault().register(this)
binding.btnCollectFace.setOnClickListener { collectFace() }
}
private fun checkCameraPermission() {
@@ -131,7 +163,11 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
private fun initArcViewModel() {
recognizeViewModel.setLiveType(livenessType)
recognizeViewModel.prepareRegister()
recognizeViewModel.setOnRegisterFinishedCallback { facePreviewInfo, userFaceInfo ->
this.userFaceInfo = userFaceInfo
Log.d(TAG, "initArcViewModel: userFaceInfo == null : ${userFaceInfo == null}")
}
recognizeViewModel.ftInitCode.observe(this, Observer { ftInitCode: Int? ->
if (ftInitCode != ErrorInfo.MOK) {
val error: String? = getString(
@@ -171,9 +207,19 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
Timber.i("recognizeNotice observe notice = $notice")
})
recognizeViewModel.recognizeUserId.observe(this, Observer { userId: String? ->
Timber.i("recognizeUserId observe userId = $userId")
viewModel.getUserInfoById(memberId = userId)
recognizeViewModel.recognizeUserId.observe(this, Observer { result: CompareResult? ->
Timber.i("recognizeUserId observe userId = ${result?.faceEntity?.userName}")
currentUserId = result?.faceEntity?.userName
// viewModel.getUserInfoById(memberId = userId)
//ToastUtils.showToast(userId)
// if (!currentUserId.isNullOrBlank()) {
//// ToastUtils.showToast("您已采集过人脸信息")
// return@Observer
// }
if (currentPlateInfo?.faceId == currentUserId) {
return@Observer
}
openPlate(currentUserId)
})
recognizeViewModel.drawRectInfoText.observe(this, Observer { info ->
@@ -181,6 +227,69 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
})
}
// private val settingViewModel by viewModels<SettingViewModel>()
/**
* 查询餐盘数据
*/
private fun getPlateData(block: (List<EquipmentUserInfo>) -> Unit) {
MyApp.plateList?.let {
block(it)
}
// settingViewModel.getEquipmentList { items ->
// block(items)
// }
}
private var currentPlateInfo: EquipmentUserInfo? = null
/**
* 打开餐盘柜
*/
private fun openPlate(userId: String?) {
getPlateData { items ->
Log.d(TAG, "openPlate: ${Gson().toJson(items)}")
var item = items.firstOrNull { it.faceId == userId }
if (item != null) {
//餐盘已取
ToastUtils.showToast("您已拿过餐盘!")
return@getPlateData
}
// 未查询到当前用户
item = items.firstOrNull { it.plateNumber.isNullOrEmpty().not() }
if (item == null) {
//暂无餐盘
ToastUtils.showToast("暂无餐盘,请联系管理人员!")
return@getPlateData
}
// TODO: 待定如何在设置页面显示plateNumber,可以增加字段处理
item.faceId = userId
item.updateTime = DateTimeUtils.getDateTimeString()
//打开柜子
PlateUtils.open(this, item.equipmentBoxCode)
currentPlateInfo = item
MyApp.plateList = items
SpTool.savePlateData(items)
}
}
// /**
// * 绑定餐盘
// */
// private fun bindPlate(item: EquipmentUserInfo) {
// val bindParam = BindParam(
// equipmentId = item.equipmentId,
// equipmentCode = item.equipmentCode,
// equipmentBoxCode = item.equipmentBoxCode,
// faceId = item.faceId,
// plateNumber = item.plateNumber
// )
// settingViewModel.bindPlate(bindParam) { pair ->
// Log.d(TAG, "openPlate: state:${pair.first},msg:${pair.second.code},${pair.second.msg}")
// }
// }
private fun initArcView() {
//在布局结束后才做初始化操作
binding.dualCameraTexturePreviewRgb.getViewTreeObserver().addOnGlobalLayoutListener(this)
@@ -198,6 +307,8 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
recognizeViewModel.destroy()
EventBus.getDefault().unregister(this)
countDownJob?.cancel()
super.onDestroy()
}
@@ -320,6 +431,10 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
if (facePreviewInfoList != null && rgbFaceRectTransformer != null) {
drawPreviewInfo(facePreviewInfoList)
}
if (facePreviewInfoList.isNullOrEmpty()) {
currentPlateInfo = null
currentUserId = null
}
recognizeViewModel.clearLeftFace(facePreviewInfoList)
}
@@ -444,5 +559,100 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
startActivity(intent)
}
private fun collectFace() {
//toast("currentUserId=$currentUserId")
if (currentUserId != null) {
ToastUtils.showToast("您已采集过人脸信息")
return
}
currentUserId = null
userFaceInfo = null
recognizeViewModel.updateRegisterStatus(REGISTER_STATUS_READY)
//showWaitingDialog("人脸信息中……")
// binding.tvFaceTip.gone()
binding.btnCollectFace.invisible()
binding.layoutState.visible()
binding.pbCollectLoading.visible()
binding.tvCollectState.text = "采集中......"
getFaceData { faceData ->
Log.d("LoginByFaceActivity", "faceData: $faceData")
runOnUiThread {
//hideWaitingDialog()
//toast("人脸信息已采集")
binding.pbCollectLoading.gone()
//binding.tvCollectState.text = "采集完成"
userFaceInfo = null
countDown()
}
// sendFaceData(faceData)
saveFaceInfo(faceData)
}
}
private fun getFaceData(block: (String) -> Unit) {
runBlocking {
job = executor.startIntervalTask(5) {
// if (currentUserId != null && currentUserId!!.isNotBlank()) {
// toast("您已采集过人脸信息")
// return@startIntervalTask
// }
if (userFaceInfo == null) {
return@startIntervalTask
}
val faceData = Base64.encode(userFaceInfo!!.faceFeature!!.featureData)
block(faceData)
job?.cancel()
}
}
}
private var userFaceInfo: UserFaceInfo? = null
private var countDownJob: Job? = null
private var job: Job? = null
val executor = IntervalExecutor()
private var currentUserId: String? = null
private fun countDown() {
countDownJob = countDownByFlow(
total = 3,
scope = lifecycleScope,
onStart = {
binding.tvCollectState.text = "采集完成(3s"
},
onTick = { seconds ->
binding.tvCollectState.text = "采集完成(${seconds}s"
},
onFinish = {
//binding.tvCollectState.text = "采集完成"
currentUserId = null
binding.layoutState.invisible()
// binding.tvFaceTip.visible()
binding.btnCollectFace.visible()
}
)
}
private fun saveFaceInfo(faceData: String) {
Thread {
try {
val tempId = "${System.currentTimeMillis()}"
val faceEntity = FaceEntity(
tempId,
null,
Base64.decode(faceData)
).also {
it.userType = "2"
}
FaceDatabase.getInstance(this).faceDao().insert(faceEntity)
recognizeViewModel.refreshFaceList();
runOnUiThread {
ToastUtils.showToast("你的人脸信息已采集")
}
} catch (e: Exception) {
e.printStackTrace()
}
}.start()
}
}