Compare commits
23
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86c3c07e42 | ||
|
|
aa96455f66 | ||
|
|
786f2a123c | ||
|
|
f2a6de55b9 | ||
|
|
e51864d400 | ||
|
|
e63d3f7624 | ||
|
|
0a110020b9 | ||
|
|
f99538890c | ||
|
|
6d8ca7c83f | ||
|
|
c6bcdc03ba | ||
|
|
3b88b011a3 | ||
|
|
b96f9872cb | ||
|
|
eef13ff56e | ||
|
|
6c937c5f5f | ||
|
|
513f4dbaea | ||
|
|
037816c303 | ||
|
|
dca9496bee | ||
|
|
08f1963054 | ||
|
|
4ea95092f5 | ||
|
|
cc88dccaa6 | ||
|
|
e979322697 | ||
|
|
1e6ea1517c | ||
|
|
2ee4d2d320 |
Generated
+2
-2
@@ -4,10 +4,10 @@
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DropdownSelection timestamp="2026-01-22T07:28:00.026342Z">
|
||||
<DropdownSelection timestamp="2026-02-05T08:42:12.462597100Z">
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="Default" identifier="serial=192.168.1.128:5555;connection=c168ec92" />
|
||||
<DeviceId pluginId="Default" identifier="serial=192.168.3.114:5555;connection=a41f05a9" />
|
||||
</handle>
|
||||
</Target>
|
||||
</DropdownSelection>
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
android:name=".activity.MainActivity"
|
||||
android:exported="false">
|
||||
</activity>
|
||||
<activity android:name="com.sw.platecabinet.activity.FunActivity" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -14,6 +14,7 @@ import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.google.gson.Gson
|
||||
import com.sw.inbound.utils.DateTimeUtils
|
||||
@@ -417,8 +418,8 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
||||
|
||||
// private val initialDelay = 5 * 60 * 1000L
|
||||
// private val dealyMillis = 10 * 60 * 1000L
|
||||
private val initialDelay = 5 * 60 * 1000L
|
||||
private val dealyMillis = 5 * 60 * 1000L
|
||||
private val initialDelay = 1 * 1000L
|
||||
private val dealyMillis = 10 * 1000L
|
||||
fun startFaceTask(block: (List<String?>) -> Unit) {
|
||||
faceTaskJob =
|
||||
intervalExecutor.startIntervalTaskWithInitialDelay(initialDelay, dealyMillis) {
|
||||
@@ -431,9 +432,9 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
||||
//private var taskPageNo = 1
|
||||
private fun getFaceIncrementList(pageNo: Int = 1, action:(List<String?>)-> Unit) {
|
||||
val timestamp = SpTool.lastFaceTimestamp
|
||||
if (timestamp == 0L) {
|
||||
return
|
||||
}
|
||||
// if (timestamp == 0L) {
|
||||
// return
|
||||
// }
|
||||
settingViewModel.getFaceIncrementList(
|
||||
pageNo = pageNo,
|
||||
timestamp = timestamp
|
||||
@@ -441,6 +442,12 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
||||
runOnUiThread {
|
||||
if (pageNo == 1 && list.isEmpty()) {
|
||||
//未查询到增量数据
|
||||
// Thread{
|
||||
// val faceDao = FaceDatabase.getInstance(this).faceDao()
|
||||
// if (faceDao.getFaceCount() == 0) {
|
||||
// FaceEntity("-1", null, byteArrayOf(-1)).also { it.userType = "-1" }
|
||||
// }
|
||||
// }.start()
|
||||
return@runOnUiThread
|
||||
}
|
||||
updateFaceData(list)
|
||||
@@ -462,28 +469,26 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
||||
|
||||
public val recognizeViewModel by viewModels<RecognizeViewModel>()
|
||||
private fun updateFaceData(list: List<UserFaceModel>) {
|
||||
Thread {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val faceList = mutableListOf<FaceEntity>()
|
||||
val faceDao = FaceDatabase.getInstance(this).faceDao()
|
||||
val faceDao = FaceDatabase.getInstance(this@BaseActivity).faceDao()
|
||||
try {
|
||||
list.forEach { model ->
|
||||
if (model.faceDeleted == true) {
|
||||
//删除数据
|
||||
faceDao.deleteFaceById(model.userId)
|
||||
// if (faceDao.getFaceCount() == 0) {
|
||||
// FaceEntity("-1", null, byteArrayOf(-1)).also { it.userType = "-1" }
|
||||
// }
|
||||
} else {
|
||||
val faceData = faceDao.queryByUserName(model.userId)
|
||||
if (faceData == null) {
|
||||
//保存数据
|
||||
val faceEntity = FaceEntity(
|
||||
model.userId,
|
||||
null,
|
||||
Base64.decode(model.faceFeatureStr)
|
||||
).also {
|
||||
//1-会员、2-临时用户
|
||||
it.userType = if (model.isMember) "1" else "2"
|
||||
it.cardNo = model.cardNo
|
||||
}
|
||||
faceList.add(faceEntity)
|
||||
// 获取该用户所有已存特征,判断当前特征是否已存在
|
||||
val existList = faceDao.queryAllByUserName(model.userId)
|
||||
val featureMatches = existList.map { Base64.encode(it.featureData) == model.faceFeatureStr }
|
||||
Timber.d("userId=${model.userId}, 已存记录数=${existList.size}, 各条特征是否与服务器一致=$featureMatches")
|
||||
val alreadyExists = featureMatches.any { it }
|
||||
if (!alreadyExists) {
|
||||
//当前特征不存在,保存
|
||||
faceList.add(getNewFaceEntity(model))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -492,13 +497,25 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
||||
}
|
||||
try {
|
||||
if (faceList.isNotEmpty()) {
|
||||
FaceDatabase.getInstance(this).faceDao().insert(faceList)
|
||||
recognizeViewModel.refreshFaceList()
|
||||
FaceDatabase.getInstance(this@BaseActivity).faceDao().insert(faceList)
|
||||
}
|
||||
recognizeViewModel.refreshFaceList()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getNewFaceEntity(model: UserFaceModel) : FaceEntity {
|
||||
return FaceEntity(
|
||||
model.userId,
|
||||
null,
|
||||
Base64.decode(model.faceFeatureStr)
|
||||
).also {
|
||||
//1-会员、2-临时用户
|
||||
it.userType = if (model.member) "1" else "2"
|
||||
it.cardNo = model.cardNo
|
||||
}
|
||||
}
|
||||
|
||||
fun initPlateList() {
|
||||
@@ -518,6 +535,10 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
||||
val faceDao = FaceDatabase.getInstance(App.getContext()).faceDao()
|
||||
faceDao.deleteAll()
|
||||
faceDao.resetId()
|
||||
// faceDao.insert(
|
||||
// FaceEntity("-1", null, byteArrayOf(-1)).also { it.userType = "-1" }
|
||||
// )
|
||||
recognizeViewModel.refreshFaceList()
|
||||
runOnUiThread { block() }
|
||||
}.start()
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.sw.plate.utils.arcface.FaceApi
|
||||
import com.sw.platecabinet.GlobalData
|
||||
import com.sw.platecabinet.databinding.ActivityDeviceInitBinding
|
||||
import com.sw.platecabinet.databinding.ItemTitleTimeBinding
|
||||
import com.sw.platecabinet.utils.SpTool
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
@@ -48,9 +49,11 @@ class DeviceInitActivity : BaseActivity<ActivityDeviceInitBinding>() {
|
||||
showWaitingDialog("加载中……")
|
||||
lifecycleScope.launch {
|
||||
withContext(Dispatchers.Default) {
|
||||
val count = FaceApi().queryFaceCount()
|
||||
if (count == 0) {
|
||||
//val count = FaceApi().queryFaceCount()
|
||||
//if (count == 0) {
|
||||
if (SpTool.firstGetFace) {
|
||||
userViewModel.getUserFaceCache { status, msg ->
|
||||
SpTool.firstGetFace = false
|
||||
getDeviceConfig()
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
package com.sw.platecabinet.activity
|
||||
|
||||
import androidx.activity.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.sw.plate.utils.Base64
|
||||
import com.sw.plate.utils.ToastUtils
|
||||
import com.sw.plate.utils.arcface.facedb.FaceDatabase
|
||||
import com.sw.platecabinet.MyApp
|
||||
import com.sw.platecabinet.databinding.ActivityFunBinding
|
||||
import com.sw.platecabinet.databinding.ItemTitleTimeBinding
|
||||
import com.sw.platecabinet.dialog.CustomDialog
|
||||
import com.sw.platecabinet.ext.clickWithDebounce
|
||||
import com.sw.platecabinet.utils.PlateUtils
|
||||
import com.sw.platecabinet.utils.SpTool
|
||||
import com.sw.platecabinet.viewmodel.UserViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.getValue
|
||||
|
||||
class FunActivity : BaseActivity<ActivityFunBinding>() {
|
||||
|
||||
override fun inflateTitleBinding(): ItemTitleTimeBinding? {
|
||||
return binding.includeHeader
|
||||
}
|
||||
|
||||
override fun inflateViewBinding() = ActivityFunBinding.inflate(layoutInflater)
|
||||
|
||||
override fun initialize() {
|
||||
binding.btnClearFace.clickWithDebounce { clearFace() }
|
||||
binding.btnClearAndGetAll.clickWithDebounce { clearFaceAndGetAll() }
|
||||
binding.btnOpenAllPlate.clickWithDebounce {
|
||||
lifecycleScope.launch {
|
||||
openAllPlate()
|
||||
}
|
||||
}
|
||||
binding.btnPutPlate.clickWithDebounce { putPlate() }
|
||||
|
||||
queryUserCount()
|
||||
|
||||
binding.btnTestFeature.clickWithDebounce {
|
||||
testFeature()
|
||||
}
|
||||
}
|
||||
private fun testFeature() {
|
||||
userViewModel.getUserFace(1, 1) {list ->
|
||||
lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
if (list.isEmpty()) return@withContext
|
||||
val userId = list[0].userId
|
||||
val base64Feature = list[0].faceFeatureStr
|
||||
val faceDao = FaceDatabase.getInstance(this@FunActivity).faceDao()
|
||||
val entityList = faceDao.queryAllByUserName(userId)
|
||||
val resultList = entityList.map { base64Feature == Base64.encode(it.featureData) }
|
||||
val isEqual = resultList.any { true }
|
||||
ToastUtils.showToast("比较结果:$isEqual")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearFace() {
|
||||
loadClearDialog("请确认是否清除本地所有数据?") {
|
||||
showWaitingDialog("加载中……")
|
||||
clearAllFace {
|
||||
binding.root.postDelayed(
|
||||
{
|
||||
hideWaitingDialog()
|
||||
ToastUtils.showToast("已清除")
|
||||
queryUserCount()
|
||||
}, 500
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearFaceAndGetAll() {
|
||||
loadClearDialog("请确认是否清除本地所有数据,并重新拉取全量人脸数据?") {
|
||||
showWaitingDialog("加载中……")
|
||||
clearAllFace {
|
||||
userViewModel.getUserFaceCache { status, msg ->
|
||||
hideWaitingDialog()
|
||||
ToastUtils.showToast(
|
||||
if (status) "请求成功,$msg" else "请求失败,$msg"
|
||||
)
|
||||
queryUserCount()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun openAllPlate() {
|
||||
MyApp.plateList?.let { plateList ->
|
||||
val boxCodeList = plateList.filter { it.equipmentBoxCode.isNullOrBlank().not() }
|
||||
.map { it.equipmentBoxCode }
|
||||
boxCodeList.forEach { boxCode ->
|
||||
withContext(Dispatchers.Default) {
|
||||
PlateUtils.openDirect(boxCode)
|
||||
delay(500)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun putPlate() {
|
||||
showWaitingDialog("加载中……")
|
||||
MyApp.plateList?.forEachIndexed { index, item ->
|
||||
val num = index + 1
|
||||
val realNum = if (num < 10) "0${num}" else "${num}"
|
||||
item.plateNumber = realNum + realNum + realNum
|
||||
item.faceId = null
|
||||
item.updateTime = null
|
||||
}
|
||||
SpTool.savePlateData(MyApp.plateList!!)
|
||||
binding.root.postDelayed({
|
||||
hideWaitingDialog()
|
||||
ToastUtils.showToast("已完成")
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载清除数据弹窗
|
||||
*/
|
||||
private fun loadClearDialog(msg: String, callback: () -> Unit) {
|
||||
CustomDialog(
|
||||
activity = this,
|
||||
content = msg,
|
||||
onConfirm = {
|
||||
callback()
|
||||
}).show()
|
||||
}
|
||||
|
||||
override fun onLeftDoubleClick() {
|
||||
finish()
|
||||
}
|
||||
|
||||
override fun onRightDoubleClick() {
|
||||
|
||||
}
|
||||
|
||||
private fun queryUserCount() {
|
||||
lifecycleScope.launch {
|
||||
val faceDao = FaceDatabase.getInstance(this@FunActivity).faceDao()
|
||||
val memberUserCount = withContext(Dispatchers.IO) { faceDao.getFaceCount("1") }
|
||||
val tempUserCount = withContext(Dispatchers.IO) { faceDao.getFaceCount("2") }
|
||||
binding.tvMemberUserCount.text = "会员用户:${memberUserCount}人"
|
||||
binding.tvTempUserCount.text = "临时用户:${tempUserCount}人"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package com.sw.platecabinet.activity
|
||||
|
||||
import android.Manifest
|
||||
@@ -6,14 +8,15 @@ import android.graphics.Bitmap
|
||||
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
|
||||
import android.view.ViewTreeObserver
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.arcsoft.face.ErrorInfo
|
||||
import com.bumptech.glide.Glide
|
||||
import com.google.gson.Gson
|
||||
import com.sw.inbound.utils.DateTimeUtils
|
||||
import com.sw.plate.utils.Base64
|
||||
@@ -27,6 +30,7 @@ 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.constants.RecognizeColor
|
||||
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
|
||||
@@ -39,9 +43,13 @@ 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.clickWithDebounce
|
||||
import com.sw.platecabinet.ext.gone
|
||||
import com.sw.platecabinet.ext.visible
|
||||
import com.sw.platecabinet.model.response.EquipmentUserInfo
|
||||
import com.sw.platecabinet.model.response.UserFaceModel
|
||||
import com.sw.platecabinet.utils.BitmapSaver
|
||||
import com.sw.platecabinet.utils.CountDownUtil
|
||||
import com.sw.platecabinet.utils.Debouncer
|
||||
import com.sw.platecabinet.utils.LogFileUtil
|
||||
import com.sw.platecabinet.utils.PermissionHelper
|
||||
@@ -49,6 +57,9 @@ import com.sw.platecabinet.utils.PlateUtils
|
||||
import com.sw.platecabinet.utils.RegisterCallbackHandler
|
||||
import com.sw.platecabinet.utils.SoundPoolUtil
|
||||
import com.sw.platecabinet.utils.SpTool
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
@@ -61,8 +72,6 @@ import java.util.concurrent.atomic.AtomicBoolean
|
||||
*/
|
||||
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(
|
||||
@@ -81,64 +90,22 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
||||
|
||||
companion object {
|
||||
private const val TAG = "LoginByFaceActivity"
|
||||
private var instance: LoginByFaceActivity? = null
|
||||
|
||||
private const val COLLECT_SUCCESS = "collect_success"
|
||||
private const val RECOGNIZE_SUCCESS = "recognize_success"
|
||||
private const val NO_PLATE_REMIND = "no_plate_remind";
|
||||
|
||||
fun goInitActivity() {
|
||||
instance?.goInitActivity()
|
||||
}
|
||||
private const val NO_PLATE_REMIND = "no_plate_remind"
|
||||
}
|
||||
|
||||
// private var logList: MutableList<String> = mutableListOf()
|
||||
// private val logAdapter by lazy { LogAdapter(logList) }
|
||||
|
||||
override fun initialize() {
|
||||
instance = this
|
||||
userViewModel.activeEngine()
|
||||
// addSocketListener()
|
||||
initCountTime()
|
||||
|
||||
initArcViewModel()
|
||||
initArcView()
|
||||
openRectInfoDraw = true
|
||||
recognizeViewModel.setDrawRectInfoTextValue(true)
|
||||
//viewModel.generateToken()
|
||||
|
||||
// binding.llToPwd.setOnClickListener {
|
||||
// val intent = Intent(this, LoginByPwdActivity::class.java)
|
||||
// startActivity(intent)
|
||||
// }
|
||||
|
||||
// TaskManager.startTask()
|
||||
|
||||
EventBus.getDefault().register(this)
|
||||
|
||||
// binding.btnCollectFace.setOnClickListener { collectFace() }
|
||||
|
||||
|
||||
// binding.rvLogInfo.let {
|
||||
// it.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, true)
|
||||
// it.adapter = logAdapter
|
||||
// }
|
||||
//
|
||||
// binding.btnLogState.let {
|
||||
// it.tag = false
|
||||
// it.setImageResource(R.drawable.ic_log_show)
|
||||
// it.setOnClickListener { v ->
|
||||
// binding.rvLogInfo.run {
|
||||
// val state = it.tag.toString().toBooleanStrictOrNull() ?: false
|
||||
// if (state) visible() else gone()
|
||||
// }
|
||||
// val state = it.tag.toString().toBooleanStrictOrNull() ?: false
|
||||
// it.tag = state
|
||||
// it.setImageResource(
|
||||
// if (state) R.drawable.ic_log_hide else R.drawable.ic_log_show
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
|
||||
//开启人脸增量数据定时任务
|
||||
startFaceTask {
|
||||
loadLogInfo("collectFace增量接口数据用户id:${Gson().toJson(it)}")
|
||||
@@ -148,7 +115,22 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
||||
soundMap[COLLECT_SUCCESS] = R.raw.collect_success
|
||||
soundMap[RECOGNIZE_SUCCESS] = R.raw.recognize_success
|
||||
soundMap[NO_PLATE_REMIND] = R.raw.no_plate_remind
|
||||
SoundPoolUtil.getInstance().loadR(this, soundMap);
|
||||
SoundPoolUtil.getInstance().loadR(this, soundMap)
|
||||
|
||||
binding.btnCollect.clickWithDebounce {
|
||||
//先重置,后采集
|
||||
resetRecognizeState()
|
||||
//采集数据按钮
|
||||
retryFailCount = 0
|
||||
isCollectFace = true
|
||||
isCollecting.set(true)
|
||||
collectFace()
|
||||
}
|
||||
|
||||
binding.btnRetry.clickWithDebounce {
|
||||
//重新识别按钮
|
||||
resetRecognizeState()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -184,29 +166,52 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
||||
private var rgbFaceRectTransformer: FaceRectTransformer? = null
|
||||
private val livenessType = LivenessType.RGB
|
||||
private var openRectInfoDraw = false
|
||||
private val regDebouncer = Debouncer(3000)
|
||||
|
||||
private val leftDebouncer = Debouncer(30 * 1000)
|
||||
|
||||
/** 识别超时倒计时(15秒无操作跳回初始页) */
|
||||
private val recognizeCountDownUtil = CountDownUtil()
|
||||
|
||||
/** 用户离开画面后的倒计时(llCollectButton 显示状态下无人脸时触发) */
|
||||
private val leftCountDownUtil = CountDownUtil()
|
||||
|
||||
private var registerHandler: RegisterCallbackHandler? = null
|
||||
|
||||
private fun isFitData(userFaceInfo: UserFaceInfo?): Boolean {
|
||||
if (userFaceInfo == null) return false
|
||||
val headBmp = userFaceInfo.headBmp
|
||||
if (headBmp == null) return false
|
||||
// if (headBmp.width <= 200 || headBmp.height <= 200) {
|
||||
// loadLogInfo("collectFace,isFitData:拿到人脸提取特征数据照片过小-----------")
|
||||
// return false
|
||||
// }
|
||||
return true
|
||||
}
|
||||
|
||||
private fun initArcViewModel() {
|
||||
recognizeViewModel.setLiveType(livenessType)
|
||||
recognizeViewModel.prepareRegister()
|
||||
recognizeViewModel.setOnRegisterFinishedCallback { facePreviewInfo, userFaceInfo ->
|
||||
registerHandler?.setCallback {
|
||||
if (!isCollectFace) {
|
||||
if (!isCollectFace || !isCollecting.get()) {
|
||||
return@setCallback
|
||||
}
|
||||
if (this.userFaceInfo == null && userFaceInfo != null) {
|
||||
if (this.userFaceInfo == null && isFitData(userFaceInfo)) {
|
||||
val headBmp = userFaceInfo.headBmp
|
||||
if (headBmp.width <= 200 || headBmp.height <= 200) {
|
||||
loadLogInfo("collectFace,isFitData:拿到人脸提取特征数据照片过小w:${headBmp.width},h:${headBmp.height}-----------")
|
||||
binding.llCollectButton.visible()
|
||||
binding.btnRetry.gone()
|
||||
binding.btnCollect.text = "重新采集"
|
||||
return@setCallback
|
||||
}
|
||||
recognizeViewModel.updateRegisterStatus(RecognizeViewModel.REGISTER_STATUS_DONE)
|
||||
this.userFaceInfo = userFaceInfo
|
||||
getFaceDataBlock { faceData, headBmp ->
|
||||
isCollecting.set(false)
|
||||
isCollectFace = false
|
||||
sendFaceData(faceData, headBmp)
|
||||
}
|
||||
getFaceDataBlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
recognizeViewModel.ftInitCode.observe(this, Observer { ftInitCode: Int? ->
|
||||
recognizeViewModel.ftInitCode.observe(this) { ftInitCode: Int? ->
|
||||
if (ftInitCode != ErrorInfo.MOK) {
|
||||
val error: String? = getString(
|
||||
R.string.specific_engine_init_failed, "ftEngine",
|
||||
@@ -215,8 +220,8 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
||||
Timber.e("ftInitCode observe = $error")
|
||||
ToastUtils.showToast(error)
|
||||
}
|
||||
})
|
||||
recognizeViewModel.frInitCode.observe(this, Observer { frInitCode: Int? ->
|
||||
}
|
||||
recognizeViewModel.frInitCode.observe(this) { frInitCode: Int? ->
|
||||
if (frInitCode != ErrorInfo.MOK) {
|
||||
val error: String? = getString(
|
||||
R.string.specific_engine_init_failed, "frEngine",
|
||||
@@ -225,8 +230,8 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
||||
Timber.e("frInitCode observe = $error")
|
||||
ToastUtils.showToast(error)
|
||||
}
|
||||
})
|
||||
recognizeViewModel.flInitCode.observe(this, Observer { flInitCode: Int? ->
|
||||
}
|
||||
recognizeViewModel.flInitCode.observe(this) { flInitCode: Int? ->
|
||||
if (flInitCode != ErrorInfo.MOK) {
|
||||
val error: String? = getString(
|
||||
R.string.specific_engine_init_failed, "flEngine",
|
||||
@@ -235,35 +240,26 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
||||
Timber.e("flInitCode observe = $error")
|
||||
ToastUtils.showToast(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
recognizeViewModel.recognizeConfiguration
|
||||
.observe(this, Observer { recognizeConfiguration: RecognizeConfiguration? ->
|
||||
.observe(this) { recognizeConfiguration: RecognizeConfiguration? ->
|
||||
Timber.i("recognizeConfiguration: observe = ${recognizeConfiguration.toString()}")
|
||||
})
|
||||
recognizeViewModel.recognizeNotice.observe(this, Observer { notice: String? ->
|
||||
Timber.i("recognizeNotice observe notice = $notice")
|
||||
})
|
||||
|
||||
recognizeViewModel.recognizeUserId.observe(this, Observer { result: CompareResult? ->
|
||||
loadLogInfo("collectFace,recognizeUserId observe userId = ${result?.faceEntity?.userName}")
|
||||
if (faceUseSwitch) {
|
||||
loadFaceRecognizeResult2(result)
|
||||
return@Observer
|
||||
}
|
||||
loadFaceRecognizeResult(result)
|
||||
})
|
||||
recognizeViewModel.recognizeNotice.observe(this) { notice: String? ->
|
||||
Timber.i("recognizeNotice observe notice = $notice")
|
||||
}
|
||||
|
||||
recognizeViewModel.drawRectInfoText.observe(this, Observer { info ->
|
||||
recognizeViewModel.recognizeUserId.observe(this) { result: CompareResult? ->
|
||||
loadLogInfo("collectFace,recognizeUserId observe userId = ${result?.faceEntity?.userName}")
|
||||
loadFaceRecognizeResult3(result)
|
||||
}
|
||||
|
||||
recognizeViewModel.drawRectInfoText.observe(this) { info ->
|
||||
Timber.i("drawRectInfoText observe info = $info")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 人脸识别开关,true-loadFaceRecognizeResult2,false-loadFaceRecognizeResult
|
||||
*/
|
||||
private var faceUseSwitch = true
|
||||
|
||||
/**
|
||||
* 识别失败重试次数
|
||||
*/
|
||||
@@ -274,65 +270,33 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
||||
*/
|
||||
private val maxFailCount = 5
|
||||
|
||||
/**
|
||||
* 人脸失败阈值
|
||||
*/
|
||||
private val faceFailThreshold = 0.3
|
||||
|
||||
/**
|
||||
* 配置阈值,默认值为0.8
|
||||
*/
|
||||
private val faceSuccessThreshold by lazy { ConfigUtil.getRecognizeThreshold(this) }
|
||||
|
||||
|
||||
private val openPlateDebouncer = Debouncer(5000)
|
||||
private val isCollecting = AtomicBoolean(false)
|
||||
|
||||
/**
|
||||
* 根据相似度similar>设定阈值0.8作为识别成功判断,
|
||||
* 如果similar<0.3作为识别失败判断,
|
||||
* 如果similar>=0.3&&similar<=0.8时,暂时不作为失败情况,再识别5次如果similar<0.8则作为识别失败处理
|
||||
*/
|
||||
private fun loadFaceRecognizeResult2(result: CompareResult?) {
|
||||
// 无有效结果直接忽略
|
||||
private fun loadFaceRecognizeResult3(result: CompareResult?) {
|
||||
val similar = result?.similar ?: return
|
||||
|
||||
binding.tvSimilarValue.text = "$similar,${retryFailCount}次"
|
||||
loadLogInfo(
|
||||
"collectFace,recognizeUserId 阈值 success=$faceSuccessThreshold, " +
|
||||
"fail=$faceFailThreshold, similar=$similar, retry=$retryFailCount"
|
||||
"collectFace,recognizeUserId 阈值 success=$faceSuccessThreshold, similar=$similar, retry=$retryFailCount"
|
||||
)
|
||||
|
||||
if (similar >= faceSuccessThreshold) {
|
||||
val userId = result.faceEntity?.userName
|
||||
if (userId.isNullOrBlank()) {
|
||||
loadLogInfo("collectFace,recognizeUserId 识别成功但 userId 为空,忽略")
|
||||
return
|
||||
}
|
||||
|
||||
loadLogInfo(
|
||||
"collectFace,recognizeUserId 识别成功 userId=$userId, time=${DateTimeUtils.getDateTimeString()}"
|
||||
)
|
||||
|
||||
// 成功后必须重置状态
|
||||
retryFailCount = 0
|
||||
isCollecting.set(false)
|
||||
isCollectFace = false
|
||||
currentUserId = userId
|
||||
|
||||
// 防抖,防止重复开闸
|
||||
openPlateDebouncer.debounce {
|
||||
openPlate(userId)
|
||||
}
|
||||
recognizeSuccess(userId)
|
||||
return
|
||||
}
|
||||
|
||||
if (isCollectFace) {
|
||||
if (isCollectFace || isCollecting.get()) {
|
||||
loadLogInfo("collectFace,recognizeUserId 正在采集中,忽略失败结果 similar=$similar")
|
||||
return
|
||||
}
|
||||
|
||||
if (similar <= faceFailThreshold || retryFailCount >= maxFailCount) {
|
||||
if (retryFailCount >= maxFailCount) {
|
||||
loadLogInfo(
|
||||
"collectFace,recognizeUserId 识别失败,开始采集 similar=$similar, " +
|
||||
"retry=$retryFailCount, time=${DateTimeUtils.getDateTimeString()}"
|
||||
@@ -342,9 +306,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
||||
return
|
||||
}
|
||||
|
||||
retryFailCount = 0
|
||||
isCollectFace = true
|
||||
collectFace()//采集人脸
|
||||
recognizeFail()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -352,57 +314,53 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
||||
loadLogInfo("collectFace,recognizeUserId 中间态重试,retryFailCount=$retryFailCount")
|
||||
}
|
||||
|
||||
// private fun loadFaceRecognizeResult2(result: CompareResult?) {
|
||||
//
|
||||
// if(isCollectFace){
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// val similar = result?.similar ?: 0f
|
||||
// binding.tvSimilarValue.text = "$similar,${retryFailCount}次"
|
||||
// loadLogInfo("collectFace,recognizeUserId 人脸识别配置阈值为:${faceSuccessThreshold},失败阈值为:${faceFailThreshold},相似度为:${similar},失败次数为:${retryFailCount}")
|
||||
// if (similar > faceSuccessThreshold) {
|
||||
// loadLogInfo("collectFace,recognizeUserId 相似度为:${similar},识别成功时间:${DateTimeUtils.getDateTimeString()}")
|
||||
// //大 于识别阈值,作为识别成功判断
|
||||
// currentUserId = result?.faceEntity?.userName
|
||||
// Debouncer(5000).debounce { openPlate(currentUserId) }
|
||||
// return
|
||||
// }
|
||||
// loadLogInfo("collectFace,recognizeUserId 人脸识别相似度为:${similar},失败次数为:${retryFailCount}")
|
||||
// if (similar < faceFailThreshold || retryFailCount >= maxFailCount) {
|
||||
// //相似度低于识别错误阈值进行采集、识别失败次数大于等于最大失败次数,作为识别失败判断
|
||||
// if (isCollectFace.not()) {
|
||||
// //非采集中
|
||||
// loadLogInfo("collectFace,recognizeUserId 未识别到人脸信息,相似度为:${similar},开始采集时间:${DateTimeUtils.getDateTimeString()}")
|
||||
// isCollectFace = true
|
||||
// collectFace()
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
// //满足similar >= faceFailThreshold && similar <= successThreshold时进行重试
|
||||
// retryFailCount++
|
||||
// loadLogInfo("collectFace,recognizeUserId 人脸识别失败次数为:${retryFailCount}")
|
||||
// }
|
||||
|
||||
/**
|
||||
* 根据相似度similar>设定阈值0.8作为识别是否成功来加载人脸识别结果
|
||||
*/
|
||||
private fun loadFaceRecognizeResult(result: CompareResult?) {
|
||||
val similar = result?.similar ?: 0f
|
||||
binding.tvSimilarValue.text = "$similar"
|
||||
val similarPass = result?.isSimilarPass ?: false
|
||||
if (similarPass.not()) {
|
||||
loadLogInfo("collectFace,recognizeUserId 未识别到人脸信息,开始采集")
|
||||
isCollectFace = true
|
||||
collectFace()
|
||||
private fun recognizeSuccess(userId: String?) {
|
||||
if (userId.isNullOrBlank()) {
|
||||
loadLogInfo("collectFace,recognizeUserId 识别成功但 userId 为空,忽略")
|
||||
return
|
||||
}
|
||||
currentUserId = result?.faceEntity?.userName
|
||||
if (tempUserId != currentUserId) {
|
||||
SoundPoolUtil.getInstance().play("success", 0)
|
||||
|
||||
loadLogInfo(
|
||||
"collectFace,recognizeUserId 识别成功 userId=$userId, time=${DateTimeUtils.getDateTimeString()}"
|
||||
)
|
||||
|
||||
// 成功后必须重置状态
|
||||
retryFailCount = 0
|
||||
isCollecting.set(false)
|
||||
isCollectFace = false
|
||||
currentUserId = userId
|
||||
|
||||
// 防抖,防止重复开闸
|
||||
openPlateDebouncer.debounce {
|
||||
openPlate(userId)
|
||||
}
|
||||
// plateEnable = true
|
||||
Debouncer(5000).debounce { openPlate(currentUserId) }
|
||||
}
|
||||
|
||||
private fun recognizeFail() {
|
||||
recognizeCountDownUtil.cancelCountDown()
|
||||
binding.tvToInit.text = ""
|
||||
binding.tvSimilarValue.text = ""
|
||||
binding.llCollectButton.visible()
|
||||
binding.btnRetry.visible()
|
||||
binding.btnCollect.run {
|
||||
visible()
|
||||
text = "新用户采集"
|
||||
}
|
||||
binding.ivCollectPhoto.visible()
|
||||
}
|
||||
|
||||
private fun newUserCollect() {
|
||||
recognizeCountDownUtil.cancelCountDown()
|
||||
binding.tvToInit.text = ""
|
||||
binding.tvSimilarValue.text = ""
|
||||
binding.llCollectButton.visible()
|
||||
binding.btnRetry.gone()
|
||||
binding.btnCollect.run {
|
||||
visible()
|
||||
text = "新用户采集"
|
||||
}
|
||||
binding.ivCollectPhoto.gone()
|
||||
resumeCamera()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -446,9 +404,12 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
||||
item.updateTime = DateTimeUtils.getDateTimeString()
|
||||
item.plateNumber = null
|
||||
plateEnable = false
|
||||
Debouncer(5000).debounce {
|
||||
openDebouncer.debounce {
|
||||
//打开柜子
|
||||
PlateUtils.open(this, item.equipmentBoxCode, false)
|
||||
PlateUtils.open(this, item.equipmentBoxCode, false) {
|
||||
goInitActivity()
|
||||
}
|
||||
recognizeCountDownUtil.cancelCountDown()
|
||||
}
|
||||
currentPlateInfo = item
|
||||
MyApp.plateList = items
|
||||
@@ -456,21 +417,19 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
||||
}
|
||||
}
|
||||
|
||||
// private val debouncer by lazy { Debouncer(3000) }
|
||||
private val openDebouncer by lazy { Debouncer(5000) }
|
||||
|
||||
private fun initArcView() {
|
||||
//在布局结束后才做初始化操作
|
||||
binding.dualCameraTexturePreviewRgb.getViewTreeObserver().addOnGlobalLayoutListener(this)
|
||||
recognizeViewModel.getCompareResultList().getValue()
|
||||
binding.dualCameraTexturePreviewRgb.viewTreeObserver.addOnGlobalLayoutListener(this)
|
||||
val list = recognizeViewModel.getCompareResultList().getValue()
|
||||
loadLogInfo("initArcView:list.size=${list?.size}")
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
loadLogInfo("onDestroy")
|
||||
instance = null
|
||||
if (rgbCameraHelper != null) {
|
||||
rgbCameraHelper!!.release()
|
||||
rgbCameraHelper = null
|
||||
}
|
||||
rgbCameraHelper?.release()
|
||||
rgbCameraHelper = null
|
||||
|
||||
recognizeViewModel.destroy()
|
||||
|
||||
@@ -513,7 +472,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
||||
layoutParams.width = (measuredHeight * ratio).toInt()
|
||||
}
|
||||
if (scale < 1f) {
|
||||
val rgbParam = rgbPreview.getLayoutParams()
|
||||
val rgbParam = rgbPreview.layoutParams
|
||||
layoutParams.width = (rgbParam.width * scale).toInt()
|
||||
layoutParams.height = (rgbParam.height * scale).toInt()
|
||||
} else {
|
||||
@@ -549,12 +508,13 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
||||
isMirror: Boolean
|
||||
) {
|
||||
loadLogInfo("initRgbCamera Rgb onCameraOpened: cameraId = $cameraId, displayOrientation = $displayOrientation")
|
||||
runOnUiThread({
|
||||
val previewSizeRgb = camera.getParameters().getPreviewSize()
|
||||
runOnUiThread {
|
||||
val previewSizeRgb = camera.parameters.previewSize
|
||||
val layoutParams = adjustPreviewViewSize(
|
||||
binding.dualCameraTexturePreviewRgb,
|
||||
binding.dualCameraTexturePreviewRgb, binding.dualCameraFaceRectView,
|
||||
previewSizeRgb, displayOrientation, 0.6f
|
||||
previewSizeRgb, displayOrientation, 0.7f
|
||||
//0.6f
|
||||
)
|
||||
loadLogInfo("initRgbCamera previewSizeRgb = ${previewSizeRgb.width}-${previewSizeRgb.height}")
|
||||
loadLogInfo("initRgbCamera layoutParams = ${layoutParams.width}-${layoutParams.height}")
|
||||
@@ -583,7 +543,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
||||
|
||||
recognizeViewModel.onRgbCameraOpened(camera)
|
||||
recognizeViewModel.setRgbFaceRectTransformer(rgbFaceRectTransformer)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||
@@ -600,6 +560,13 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
||||
currentPlateInfo = null
|
||||
currentUserId = null
|
||||
userFaceInfo = null
|
||||
binding.btnCollect.tag = ""
|
||||
binding.tvSimilarValue.text = ""
|
||||
retryFailCount = 0
|
||||
|
||||
if (binding.llCollectButton.isVisible) {
|
||||
leftDebouncer.debounce { leftCountDown() }
|
||||
}
|
||||
}
|
||||
recognizeViewModel.clearLeftFace(facePreviewInfoList)
|
||||
}
|
||||
@@ -638,8 +605,8 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
||||
.previewOn(binding.dualCameraTexturePreviewRgb)
|
||||
.cameraListener(cameraListener)
|
||||
.build()
|
||||
rgbCameraHelper!!.init()
|
||||
rgbCameraHelper!!.start()
|
||||
rgbCameraHelper?.init()
|
||||
rgbCameraHelper?.start()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -657,94 +624,160 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
||||
}
|
||||
}
|
||||
|
||||
fun openRectInfoDraw(view: View?) {
|
||||
openRectInfoDraw = !openRectInfoDraw
|
||||
recognizeViewModel.setDrawRectInfoTextValue(openRectInfoDraw)
|
||||
}
|
||||
// fun openRectInfoDraw(view: View?) {
|
||||
// openRectInfoDraw = !openRectInfoDraw
|
||||
// recognizeViewModel.setDrawRectInfoTextValue(openRectInfoDraw)
|
||||
// }
|
||||
|
||||
|
||||
override fun onGlobalLayout() {
|
||||
loadLogInfo("onGlobalLayout")
|
||||
binding.dualCameraTexturePreviewRgb.getViewTreeObserver().removeOnGlobalLayoutListener(this)
|
||||
binding.dualCameraTexturePreviewRgb.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
// 请求权限
|
||||
checkCameraPermission()
|
||||
}
|
||||
|
||||
private fun resumeCamera() {
|
||||
isRecognition = true
|
||||
// isRecognition = true
|
||||
if (rgbCameraHelper != null && rgbCameraHelper!!.isStopped) {
|
||||
loadLogInfo("collectFace,resumeCamera: ")
|
||||
isRecognition = true
|
||||
rgbCameraHelper!!.start()
|
||||
} else {
|
||||
recognizeViewModel.onPreviewFrame(ByteArray(1382400), true)
|
||||
//recognizeViewModel.onPreviewFrame(ByteArray(1382400), true)
|
||||
|
||||
recognizeViewModel.onPreviewFrame(emptyFrame, true)
|
||||
binding.dualCameraFaceRectView.clearFaceInfo()
|
||||
recognizeViewModel.clearLeftFace(facePreviewInfoList)
|
||||
binding.root.postDelayed({
|
||||
isRecognition = true
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 临时用户id
|
||||
* 构造符合分辨率的 NV21 格式全黑帧
|
||||
* @param width 图像宽度(如 1920)
|
||||
* @param height 图像高度(如 1080)
|
||||
* @return 合法的全黑帧字节数组
|
||||
*/
|
||||
private var tempUserId: String = ""
|
||||
private fun createBlackNV21Frame(width: Int, height: Int): ByteArray {
|
||||
val frameSize = width * height
|
||||
val nv21Data = ByteArray(frameSize * 3 / 2) // NV21 格式长度 = 宽×高×1.5
|
||||
|
||||
// Y 分量(亮度)设为 0(全黑),UV 分量设为 128(默认值)
|
||||
// 1. 填充 Y 分量(前 frameSize 个字节)
|
||||
// Arrays.fill(nv21Data, 0, frameSize, 0.toByte())
|
||||
nv21Data.fill(0, 0, frameSize)
|
||||
// 2. 填充 UV 分量(后 frameSize/2 个字节)
|
||||
// Arrays.fill(nv21Data, frameSize, nv21Data.size, 128.toByte())
|
||||
nv21Data.fill(128.toByte(), frameSize, nv21Data.size)
|
||||
|
||||
return nv21Data
|
||||
}
|
||||
|
||||
// 调用示例:构造 1920×1080 的全黑帧(长度 1382400)
|
||||
// private val emptyFrame = createBlackNV21Frame(1920, 1080)
|
||||
private val emptyFrame = createBlackNV21Frame(1280, 720)
|
||||
// 传递“空帧” recognizeViewModel.onPreviewFrame(emptyFrame, true)
|
||||
|
||||
// /**
|
||||
// * 临时用户id
|
||||
// */
|
||||
// private var tempUserId: String = ""
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
registerHandler = RegisterCallbackHandler()
|
||||
try {
|
||||
if (isFirstOpen) {
|
||||
isFirstOpen = false
|
||||
goInitActivity()
|
||||
binding.root.postDelayed({
|
||||
isFirstOpen = false
|
||||
}, 1000)
|
||||
return
|
||||
}
|
||||
retryFailCount = 0
|
||||
binding.tvSimilarValue.text = ""
|
||||
isCollecting.set(false)
|
||||
isCollectFace = false
|
||||
plateEnable = true
|
||||
collectCount = 0
|
||||
tempUserId = "${System.currentTimeMillis()}"
|
||||
resumeCamera()
|
||||
userViewModel.resetUserInfo()
|
||||
countDownTimer?.let {
|
||||
it.cancel()
|
||||
it.start()
|
||||
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
val faceDao = FaceDatabase.getInstance(this@LoginByFaceActivity).faceDao()
|
||||
val faceCount = withContext(Dispatchers.IO) { faceDao.faceCount }
|
||||
if (faceCount == 0) {
|
||||
newUserCollect()
|
||||
} else {
|
||||
resetRecognizeState()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
// initFaceScheduler()
|
||||
// checkFaceState()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置识别状态
|
||||
*/
|
||||
private fun resetRecognizeState() {
|
||||
binding.ivCollectPhoto.gone()
|
||||
binding.ivCollectPhoto.setImageDrawable(null)
|
||||
binding.tvToInit.text = "15"
|
||||
binding.llCollectButton.gone()
|
||||
retryFailCount = 0
|
||||
binding.tvSimilarValue.text = ""
|
||||
isCollecting.set(false)
|
||||
isCollectFace = false
|
||||
plateEnable = true
|
||||
collectCount = 0
|
||||
// tempUserId = "${System.currentTimeMillis()}"
|
||||
resumeCamera()
|
||||
registerHandler = RegisterCallbackHandler()
|
||||
|
||||
recognizeCountDownUtil.startCountDown(
|
||||
total = 15,
|
||||
lifecycleScope = lifecycleScope,
|
||||
onTick = { remaining ->
|
||||
loadLogInfo("resetRecognizeState剩余时间:$remaining 秒")
|
||||
if (judgeRecognizeState()) return@startCountDown
|
||||
loadLogInfo("initCountTime onTick = $remaining")
|
||||
binding.tvToInit.text = "$remaining"
|
||||
},
|
||||
onFinish = {
|
||||
loadLogInfo("resetRecognizeState倒计时结束")
|
||||
if (judgeRecognizeState()) return@startCountDown
|
||||
if (facePreviewInfoList.isNullOrEmpty()) {
|
||||
goInitActivity()
|
||||
} else {
|
||||
loadLogInfo("recognizeFail---initCountTime")
|
||||
recognizeFail()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private var isFirstOpen = true
|
||||
|
||||
protected override fun onPause() {
|
||||
override fun onPause() {
|
||||
pauseCamera()
|
||||
super.onPause()
|
||||
countDownTimer?.cancel()
|
||||
recognizeCountDownUtil.cancelCountDown()
|
||||
}
|
||||
|
||||
private fun pauseCamera() {
|
||||
isRecognition = false
|
||||
recognizeViewModel.onPreviewFrame(ByteArray(1382400), true)
|
||||
// recognizeViewModel.onPreviewFrame(emptyFrame, true)
|
||||
}
|
||||
|
||||
override fun handleLoginSuccess(equipmentUserInfo: EquipmentUserInfo, isAdmin: Boolean) {
|
||||
super.handleLoginSuccess(equipmentUserInfo, isAdmin)
|
||||
// goInitActivity()
|
||||
}
|
||||
|
||||
private val totalTimeInMillis = 15 * 1000L
|
||||
fun initCountTime() {
|
||||
countDownTimer = object : CountDownTimer(totalTimeInMillis, 1000) {
|
||||
override fun onTick(millisUntilFinished: Long) {
|
||||
loadLogInfo("initCountTime onTick = ${(millisUntilFinished / 1000).toInt()}")
|
||||
binding.tvToInit.text = "${(millisUntilFinished / 1000).toInt() + 1}"
|
||||
}
|
||||
|
||||
override fun onFinish() {
|
||||
goInitActivity()
|
||||
private fun judgeRecognizeState(): Boolean {
|
||||
val rectColor = binding.dualCameraFaceRectView.rectColor
|
||||
if (rectColor == RecognizeColor.COLOR_SUCCESS && currentUserId.isNullOrBlank()
|
||||
.not() && facePreviewInfoList.isNullOrEmpty().not()
|
||||
) {
|
||||
//人未离开,再次识别
|
||||
loadLogInfo("userId=$currentUserId")
|
||||
openPlateDebouncer.debounce {
|
||||
openPlate(currentUserId)
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun goInitActivity() {
|
||||
@@ -785,12 +818,6 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
||||
uploadFaceDebouncer.debounce {
|
||||
addUserFace(faceData, imageFile)
|
||||
}
|
||||
// // 给某个客户端发送
|
||||
// val jsonObject = JSONObject().also {
|
||||
// it.put("type", "faceFeature")
|
||||
// it.put("content", faceData)
|
||||
// }
|
||||
// tcpClient?.send(jsonObject)
|
||||
}
|
||||
|
||||
private fun addUserFace(faceData: String, imageFile: File) {
|
||||
@@ -806,23 +833,34 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
||||
loadLogInfo("collectFace,sendFaceData: ----------12,发送人脸数据成功但接口返回为null,faceData=${faceData}")
|
||||
return@addUserFace
|
||||
}
|
||||
Thread {
|
||||
saveFaceInfo(faceModel)
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
saveFaceInfo(faceModel) {
|
||||
runOnUiThread {
|
||||
Glide.with(this@LoginByFaceActivity).load(imageFile)
|
||||
.into(binding.ivCollectPhoto)
|
||||
}
|
||||
}
|
||||
uploadFaceDebouncer.reset()
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFaceDataBlock(block: (String, Bitmap) -> Unit) {
|
||||
if (userFaceInfo == null || userFaceInfo?.faceFeature?.featureData == null) {
|
||||
private fun getFaceDataBlock() {
|
||||
if (userFaceInfo?.faceFeature?.featureData == null) {
|
||||
loadLogInfo("collectFace,getFaceData: ----------7,拿到人脸提取特征数据为null-----------")
|
||||
return
|
||||
}
|
||||
val faceData: String? = Base64.encode(userFaceInfo?.faceFeature?.featureData)
|
||||
if (faceData.isNullOrBlank()) {
|
||||
loadLogInfo("collectFace,getFaceData: ----------7,拿到人脸提取特征数据Base64编码为空")
|
||||
return
|
||||
}
|
||||
loadLogInfo("collectFace,getFaceData: ----------7,拿到人脸提取特征数据时间:${DateTimeUtils.getDateTimeString()}-----------")
|
||||
block(faceData, userFaceInfo!!.headBmp)
|
||||
val headBmp = userFaceInfo!!.headBmp
|
||||
//block(faceData, headBmp)
|
||||
isCollecting.set(false)
|
||||
isCollectFace = false
|
||||
sendFaceData(faceData, headBmp)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -839,7 +877,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
||||
|
||||
private var currentUserId: String? = null
|
||||
|
||||
private fun saveFaceInfo(faceModel: UserFaceModel) {
|
||||
private fun saveFaceInfo(faceModel: UserFaceModel, block: () -> Unit = {}) {
|
||||
try {
|
||||
loadLogInfo("collectFace,saveFaceInfo: ----------8,保存人脸取特征数据-----------")
|
||||
val faceDao = FaceDatabase.getInstance(this).faceDao()
|
||||
@@ -849,15 +887,21 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
||||
Base64.decode(faceModel.faceFeatureStr)
|
||||
).also {
|
||||
//1-会员、2-临时用户
|
||||
it.userType = if (faceModel.isMember) "1" else "2"
|
||||
it.userType = if (faceModel.member) "1" else "2"
|
||||
}
|
||||
faceDao.insert(faceEntity)
|
||||
val faceId = faceDao.insert(faceEntity)
|
||||
faceEntity.faceId = faceId
|
||||
loadLogInfo("collectFace,saveFaceInfo: ----------10,人脸数据保存完成-----------")
|
||||
recognizeViewModel.refreshFaceList()
|
||||
loadLogInfo("collectFace,saveFaceInfo: ----------11,刷新人脸数据-----------")
|
||||
runOnUiThread { recognizeViewModel.onPreviewFrame(ByteArray(1382400), true) }
|
||||
runOnUiThread {
|
||||
//采集完成,不再识别,直接提示成功
|
||||
isRecognition = false
|
||||
recognizeSuccess("${System.currentTimeMillis()}")
|
||||
}
|
||||
loadLogInfo("---------------------------------")
|
||||
collectCount = 0
|
||||
block()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
loadLogInfo("collectFace,saveFaceInfo: ----------13,人脸采集异常:${e.message}-----------")
|
||||
@@ -869,78 +913,23 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
||||
|
||||
private var collectCount = 0
|
||||
|
||||
// private var tcpClient: TcpClient? = null
|
||||
//
|
||||
// private fun addSocketListener() {
|
||||
//// val ipAddress = NetworkUtils.getIPAddress(true)
|
||||
//// loadLogInfo( "addSocketListener,ipAddress: $ipAddress");
|
||||
// tcpClient = TcpClient(
|
||||
// "192.168.1.95",
|
||||
// 5000,
|
||||
// GlobalData.deviceId, // clientId
|
||||
// 5000, // connectTimeoutMs
|
||||
// 10 * 1000, // heartbeatIntervalMs
|
||||
// 30 * 1000 // heartbeatTimeoutMs
|
||||
// )
|
||||
//
|
||||
// tcpClient?.setListener(object : TcpClientListenerImpl() {
|
||||
// override fun onSendSuccess(json: JSONObject?) {
|
||||
// super.onSendSuccess(json)
|
||||
// loadLogInfo("collectFace,TcpClientListener发送成功:$json")
|
||||
//// toast("发送成功")
|
||||
// }
|
||||
//
|
||||
// override fun onSendFailed(json: JSONObject?, e: java.lang.Exception?) {
|
||||
// super.onSendFailed(json, e)
|
||||
// ToastUtils.showToast("发送失败${e?.message}")
|
||||
// loadLogInfo("collectFace,TcpClientListener人脸数据发送失败:${e?.message}")
|
||||
// }
|
||||
//
|
||||
// override fun onMessage(json: JSONObject?) {
|
||||
// super.onMessage(json)
|
||||
// loadLogInfo("collectFace,TcpClientListener收到消息:$json")
|
||||
// val type = json?.getInt("type")
|
||||
// // 根据服务端消息类型处理删除新增临时用户人脸数据---------------------
|
||||
// when (type) {
|
||||
// LanServer.TYPE_ADD_FACE_DATA -> {
|
||||
// //采集设备id自己生成,不接收服务端的,在本地只判断是否存在同一人脸数据
|
||||
// loadLogInfo("collectFace,TcpClientListener收到消息,新增用户:采集设备id自己生成,不接收服务端的,在本地只判断是否存在同一人脸数据")
|
||||
// }
|
||||
//
|
||||
// LanServer.TYPE_CLEAR_FACE_DATA -> {
|
||||
// clearFaceData()
|
||||
// }
|
||||
//
|
||||
// else -> {}
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// tcpClient?.start()
|
||||
//
|
||||
// try {
|
||||
// LanServer.getInstance().let {
|
||||
// it.setListener(object : LanServerListenerImpl() {
|
||||
// override fun onMessageReceived(clientId: String?, message: JSONObject?) {
|
||||
// super.onMessageReceived(clientId, message)
|
||||
// ToastUtils.showToast("收到消息clientId=$clientId")
|
||||
// }
|
||||
// })
|
||||
// it.start()
|
||||
// }
|
||||
// } catch (e: Exception) {
|
||||
// e.printStackTrace()
|
||||
// }
|
||||
// }
|
||||
|
||||
private fun clearFaceData() {
|
||||
Thread {
|
||||
val faceDao = FaceDatabase.getInstance(this).faceDao()
|
||||
faceDao.deleteTempUserFaceData()
|
||||
recognizeViewModel.refreshFaceList()
|
||||
loadLogInfo("collectFace,TcpClientListener收到消息,已删除并刷新人脸数据")
|
||||
}.start()
|
||||
}
|
||||
|
||||
private var isCollectFace = false
|
||||
|
||||
private fun leftCountDown() {
|
||||
leftCountDownUtil.startCountDown(total = 15, lifecycleScope = lifecycleScope, onStart = {
|
||||
loadLogInfo("onPreview-----------leftCountDown-------------")
|
||||
}, onTick = {
|
||||
loadLogInfo("leftCountDown.onTick:$it")
|
||||
if (facePreviewInfoList.isNullOrEmpty().not()) {
|
||||
loadLogInfo("leftCountDown.onTick-cancel()")
|
||||
leftCountDownUtil.cancelCountDown()
|
||||
return@startCountDown
|
||||
}
|
||||
}, onFinish = {
|
||||
if (facePreviewInfoList.isNullOrEmpty().not()) {
|
||||
loadLogInfo("leftCountDown.onFinish-人脸未离开,不跳转")
|
||||
return@startCountDown
|
||||
}
|
||||
goInitActivity()
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,946 @@
|
||||
//package com.sw.platecabinet.activity
|
||||
//
|
||||
//import android.Manifest
|
||||
//import android.content.Intent
|
||||
//import android.graphics.Bitmap
|
||||
//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
|
||||
//import android.view.ViewTreeObserver
|
||||
//import androidx.annotation.RequiresApi
|
||||
//import androidx.lifecycle.Observer
|
||||
//import com.arcsoft.face.ErrorInfo
|
||||
//import com.google.gson.Gson
|
||||
//import com.sw.inbound.utils.DateTimeUtils
|
||||
//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
|
||||
//import com.sw.plate.utils.arcface.FaceRectTransformer
|
||||
//import com.sw.plate.utils.arcface.FaceRectView
|
||||
//import com.sw.plate.utils.arcface.FaceRectView.DrawInfo
|
||||
//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.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.model.response.EquipmentUserInfo
|
||||
//import com.sw.platecabinet.model.response.UserFaceModel
|
||||
//import com.sw.platecabinet.utils.BitmapSaver
|
||||
//import com.sw.platecabinet.utils.Debouncer
|
||||
//import com.sw.platecabinet.utils.LogFileUtil
|
||||
//import com.sw.platecabinet.utils.PermissionHelper
|
||||
//import com.sw.platecabinet.utils.PlateUtils
|
||||
//import com.sw.platecabinet.utils.RegisterCallbackHandler
|
||||
//import com.sw.platecabinet.utils.SoundPoolUtil
|
||||
//import com.sw.platecabinet.utils.SpTool
|
||||
//import org.greenrobot.eventbus.EventBus
|
||||
//import org.greenrobot.eventbus.Subscribe
|
||||
//import org.greenrobot.eventbus.ThreadMode
|
||||
//import timber.log.Timber
|
||||
//import java.io.File
|
||||
//import java.util.concurrent.atomic.AtomicBoolean
|
||||
//
|
||||
///**
|
||||
// * 人脸识别
|
||||
// */
|
||||
//class LoginByFaceActivityBak : 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(
|
||||
// Manifest.permission.CAMERA,
|
||||
// Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||
// Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
// )
|
||||
//
|
||||
// override fun inflateViewBinding(): ActivityLoginFaceBinding {
|
||||
// return ActivityLoginFaceBinding.inflate(layoutInflater)
|
||||
// }
|
||||
//
|
||||
// override fun inflateTitleBinding(): ItemTitleTimeBinding {
|
||||
// return binding.includeHeader
|
||||
// }
|
||||
//
|
||||
// companion object {
|
||||
// private const val TAG = "LoginByFaceActivity"
|
||||
// private var instance: LoginByFaceActivityBak? = null
|
||||
//
|
||||
// private const val COLLECT_SUCCESS = "collect_success"
|
||||
// private const val RECOGNIZE_SUCCESS = "recognize_success"
|
||||
// private const val NO_PLATE_REMIND = "no_plate_remind";
|
||||
//
|
||||
// fun goInitActivity() {
|
||||
// instance?.goInitActivity()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//// private var logList: MutableList<String> = mutableListOf()
|
||||
//// private val logAdapter by lazy { LogAdapter(logList) }
|
||||
//
|
||||
// override fun initialize() {
|
||||
// instance = this
|
||||
// userViewModel.activeEngine()
|
||||
//// addSocketListener()
|
||||
// initCountTime()
|
||||
// initArcViewModel()
|
||||
// initArcView()
|
||||
// openRectInfoDraw = true
|
||||
// recognizeViewModel.setDrawRectInfoTextValue(true)
|
||||
// //viewModel.generateToken()
|
||||
//
|
||||
//// binding.llToPwd.setOnClickListener {
|
||||
//// val intent = Intent(this, LoginByPwdActivity::class.java)
|
||||
//// startActivity(intent)
|
||||
//// }
|
||||
//
|
||||
//// TaskManager.startTask()
|
||||
//
|
||||
// EventBus.getDefault().register(this)
|
||||
//
|
||||
//// binding.btnCollectFace.setOnClickListener { collectFace() }
|
||||
//
|
||||
//
|
||||
//// binding.rvLogInfo.let {
|
||||
//// it.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, true)
|
||||
//// it.adapter = logAdapter
|
||||
//// }
|
||||
////
|
||||
//// binding.btnLogState.let {
|
||||
//// it.tag = false
|
||||
//// it.setImageResource(R.drawable.ic_log_show)
|
||||
//// it.setOnClickListener { v ->
|
||||
//// binding.rvLogInfo.run {
|
||||
//// val state = it.tag.toString().toBooleanStrictOrNull() ?: false
|
||||
//// if (state) visible() else gone()
|
||||
//// }
|
||||
//// val state = it.tag.toString().toBooleanStrictOrNull() ?: false
|
||||
//// it.tag = state
|
||||
//// it.setImageResource(
|
||||
//// if (state) R.drawable.ic_log_hide else R.drawable.ic_log_show
|
||||
//// )
|
||||
//// }
|
||||
//// }
|
||||
//
|
||||
// //开启人脸增量数据定时任务
|
||||
// startFaceTask {
|
||||
// loadLogInfo("collectFace增量接口数据用户id:${Gson().toJson(it)}")
|
||||
// }
|
||||
//
|
||||
// val soundMap = hashMapOf<String, Int>()
|
||||
// soundMap[COLLECT_SUCCESS] = R.raw.collect_success
|
||||
// soundMap[RECOGNIZE_SUCCESS] = R.raw.recognize_success
|
||||
// soundMap[NO_PLATE_REMIND] = R.raw.no_plate_remind
|
||||
// SoundPoolUtil.getInstance().loadR(this, soundMap);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// private fun checkCameraPermission() {
|
||||
// Timber.i("checkCameraPermission")
|
||||
// val permissionHelper =
|
||||
// PermissionHelper.with(this, REQUIRED_PERMISSIONS, CAMERA_PERMISSION_REQUEST_CODE)
|
||||
// .onGranted {
|
||||
// loadLogInfo("checkCameraPermission 权限已允许")
|
||||
// // 权限已授予
|
||||
// recognizeViewModel.init()
|
||||
// initRgbCamera()
|
||||
// resumeCamera()
|
||||
// }
|
||||
// .build()
|
||||
// registerPermissionHelper(permissionHelper)
|
||||
// permissionHelper.checkAndRequest()
|
||||
// }
|
||||
//
|
||||
// @Subscribe(threadMode = ThreadMode.MAIN)
|
||||
// fun getFaceEntity(insertEntity: FaceEntity) {
|
||||
// Timber.tag("performSync").e("-time=%s", insertEntity.registerTime)
|
||||
// recognizeViewModel.addFace(insertEntity)
|
||||
// Timber.tag("performSync over").e("-time=%s", insertEntity.registerTime)
|
||||
// }
|
||||
//
|
||||
// override fun onLeftDoubleClick() {
|
||||
// finish()
|
||||
// }
|
||||
//
|
||||
// private var isRecognition = false
|
||||
// private var rgbCameraHelper: DualCameraHelper? = null
|
||||
// private var rgbFaceRectTransformer: FaceRectTransformer? = null
|
||||
// private val livenessType = LivenessType.RGB
|
||||
// private var openRectInfoDraw = false
|
||||
// private val regDebouncer = Debouncer(3000)
|
||||
// private var registerHandler: RegisterCallbackHandler? = null
|
||||
//
|
||||
// private fun initArcViewModel() {
|
||||
// recognizeViewModel.setLiveType(livenessType)
|
||||
// recognizeViewModel.prepareRegister()
|
||||
// recognizeViewModel.setOnRegisterFinishedCallback { facePreviewInfo, userFaceInfo ->
|
||||
// registerHandler?.setCallback {
|
||||
// if (!isCollectFace) {
|
||||
// return@setCallback
|
||||
// }
|
||||
// if (this.userFaceInfo == null && userFaceInfo != null) {
|
||||
// recognizeViewModel.updateRegisterStatus(RecognizeViewModel.REGISTER_STATUS_DONE)
|
||||
// this.userFaceInfo = userFaceInfo
|
||||
// getFaceDataBlock { faceData, headBmp ->
|
||||
// isCollecting.set(false)
|
||||
// isCollectFace = false
|
||||
// sendFaceData(faceData, headBmp)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// recognizeViewModel.ftInitCode.observe(this, Observer { ftInitCode: Int? ->
|
||||
// if (ftInitCode != ErrorInfo.MOK) {
|
||||
// val error: String? = getString(
|
||||
// R.string.specific_engine_init_failed, "ftEngine",
|
||||
// ftInitCode, ErrorCodeUtil.arcFaceErrorCodeToFieldName(ftInitCode!!)
|
||||
// )
|
||||
// Timber.e("ftInitCode observe = $error")
|
||||
// ToastUtils.showToast(error)
|
||||
// }
|
||||
// })
|
||||
// recognizeViewModel.frInitCode.observe(this, Observer { frInitCode: Int? ->
|
||||
// if (frInitCode != ErrorInfo.MOK) {
|
||||
// val error: String? = getString(
|
||||
// R.string.specific_engine_init_failed, "frEngine",
|
||||
// frInitCode, ErrorCodeUtil.arcFaceErrorCodeToFieldName(frInitCode!!)
|
||||
// )
|
||||
// Timber.e("frInitCode observe = $error")
|
||||
// ToastUtils.showToast(error)
|
||||
// }
|
||||
// })
|
||||
// recognizeViewModel.flInitCode.observe(this, Observer { flInitCode: Int? ->
|
||||
// if (flInitCode != ErrorInfo.MOK) {
|
||||
// val error: String? = getString(
|
||||
// R.string.specific_engine_init_failed, "flEngine",
|
||||
// flInitCode, ErrorCodeUtil.arcFaceErrorCodeToFieldName(flInitCode!!)
|
||||
// )
|
||||
// Timber.e("flInitCode observe = $error")
|
||||
// ToastUtils.showToast(error)
|
||||
// }
|
||||
// })
|
||||
//
|
||||
// recognizeViewModel.recognizeConfiguration
|
||||
// .observe(this, Observer { recognizeConfiguration: RecognizeConfiguration? ->
|
||||
// Timber.i("recognizeConfiguration: observe = ${recognizeConfiguration.toString()}")
|
||||
// })
|
||||
// recognizeViewModel.recognizeNotice.observe(this, Observer { notice: String? ->
|
||||
// Timber.i("recognizeNotice observe notice = $notice")
|
||||
// })
|
||||
//
|
||||
// recognizeViewModel.recognizeUserId.observe(this, Observer { result: CompareResult? ->
|
||||
// loadLogInfo("collectFace,recognizeUserId observe userId = ${result?.faceEntity?.userName}")
|
||||
// if (faceUseSwitch) {
|
||||
// loadFaceRecognizeResult2(result)
|
||||
// return@Observer
|
||||
// }
|
||||
// loadFaceRecognizeResult(result)
|
||||
// })
|
||||
//
|
||||
// recognizeViewModel.drawRectInfoText.observe(this, Observer { info ->
|
||||
// Timber.i("drawRectInfoText observe info = $info")
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 人脸识别开关,true-loadFaceRecognizeResult2,false-loadFaceRecognizeResult
|
||||
// */
|
||||
// private var faceUseSwitch = true
|
||||
//
|
||||
// /**
|
||||
// * 识别失败重试次数
|
||||
// */
|
||||
// private var retryFailCount = 0
|
||||
//
|
||||
// /**
|
||||
// * 识别失败最大次数
|
||||
// */
|
||||
// private val maxFailCount = 5
|
||||
//
|
||||
// /**
|
||||
// * 人脸失败阈值
|
||||
// */
|
||||
// private val faceFailThreshold = 0.3
|
||||
//
|
||||
// /**
|
||||
// * 配置阈值,默认值为0.8
|
||||
// */
|
||||
// private val faceSuccessThreshold by lazy { ConfigUtil.getRecognizeThreshold(this) }
|
||||
//
|
||||
//
|
||||
// private val openPlateDebouncer = Debouncer(5000)
|
||||
// private val isCollecting = AtomicBoolean(false)
|
||||
//
|
||||
// /**
|
||||
// * 根据相似度similar>设定阈值0.8作为识别成功判断,
|
||||
// * 如果similar<0.3作为识别失败判断,
|
||||
// * 如果similar>=0.3&&similar<=0.8时,暂时不作为失败情况,再识别5次如果similar<0.8则作为识别失败处理
|
||||
// */
|
||||
// private fun loadFaceRecognizeResult2(result: CompareResult?) {
|
||||
// // 无有效结果直接忽略
|
||||
// val similar = result?.similar ?: return
|
||||
//
|
||||
// binding.tvSimilarValue.text = "$similar,${retryFailCount}次"
|
||||
// loadLogInfo(
|
||||
// "collectFace,recognizeUserId 阈值 success=$faceSuccessThreshold, " +
|
||||
// "fail=$faceFailThreshold, similar=$similar, retry=$retryFailCount"
|
||||
// )
|
||||
//
|
||||
// if (similar >= faceSuccessThreshold) {
|
||||
// val userId = result.faceEntity?.userName
|
||||
// if (userId.isNullOrBlank()) {
|
||||
// loadLogInfo("collectFace,recognizeUserId 识别成功但 userId 为空,忽略")
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// loadLogInfo(
|
||||
// "collectFace,recognizeUserId 识别成功 userId=$userId, time=${DateTimeUtils.getDateTimeString()}"
|
||||
// )
|
||||
//
|
||||
// // 成功后必须重置状态
|
||||
// retryFailCount = 0
|
||||
// isCollecting.set(false)
|
||||
// isCollectFace = false
|
||||
// currentUserId = userId
|
||||
//
|
||||
// // 防抖,防止重复开闸
|
||||
// openPlateDebouncer.debounce {
|
||||
// openPlate(userId)
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// if (isCollectFace) {
|
||||
// loadLogInfo("collectFace,recognizeUserId 正在采集中,忽略失败结果 similar=$similar")
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// if (similar <= faceFailThreshold || retryFailCount >= maxFailCount) {
|
||||
// loadLogInfo(
|
||||
// "collectFace,recognizeUserId 识别失败,开始采集 similar=$similar, " +
|
||||
// "retry=$retryFailCount, time=${DateTimeUtils.getDateTimeString()}"
|
||||
// )
|
||||
// if (!isCollecting.compareAndSet(false, true)) {
|
||||
// loadLogInfo("collectFace 已在采集中,忽略重复触发")
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// retryFailCount = 0
|
||||
// isCollectFace = true
|
||||
// collectFace()//采集人脸
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// retryFailCount++
|
||||
// loadLogInfo("collectFace,recognizeUserId 中间态重试,retryFailCount=$retryFailCount")
|
||||
// }
|
||||
//
|
||||
//// private fun loadFaceRecognizeResult2(result: CompareResult?) {
|
||||
////
|
||||
//// if(isCollectFace){
|
||||
//// return
|
||||
//// }
|
||||
////
|
||||
//// val similar = result?.similar ?: 0f
|
||||
//// binding.tvSimilarValue.text = "$similar,${retryFailCount}次"
|
||||
//// loadLogInfo("collectFace,recognizeUserId 人脸识别配置阈值为:${faceSuccessThreshold},失败阈值为:${faceFailThreshold},相似度为:${similar},失败次数为:${retryFailCount}")
|
||||
//// if (similar > faceSuccessThreshold) {
|
||||
//// loadLogInfo("collectFace,recognizeUserId 相似度为:${similar},识别成功时间:${DateTimeUtils.getDateTimeString()}")
|
||||
//// //大 于识别阈值,作为识别成功判断
|
||||
//// currentUserId = result?.faceEntity?.userName
|
||||
//// Debouncer(5000).debounce { openPlate(currentUserId) }
|
||||
//// return
|
||||
//// }
|
||||
//// loadLogInfo("collectFace,recognizeUserId 人脸识别相似度为:${similar},失败次数为:${retryFailCount}")
|
||||
//// if (similar < faceFailThreshold || retryFailCount >= maxFailCount) {
|
||||
//// //相似度低于识别错误阈值进行采集、识别失败次数大于等于最大失败次数,作为识别失败判断
|
||||
//// if (isCollectFace.not()) {
|
||||
//// //非采集中
|
||||
//// loadLogInfo("collectFace,recognizeUserId 未识别到人脸信息,相似度为:${similar},开始采集时间:${DateTimeUtils.getDateTimeString()}")
|
||||
//// isCollectFace = true
|
||||
//// collectFace()
|
||||
//// return
|
||||
//// }
|
||||
//// }
|
||||
//// //满足similar >= faceFailThreshold && similar <= successThreshold时进行重试
|
||||
//// retryFailCount++
|
||||
//// loadLogInfo("collectFace,recognizeUserId 人脸识别失败次数为:${retryFailCount}")
|
||||
//// }
|
||||
//
|
||||
// /**
|
||||
// * 根据相似度similar>设定阈值0.8作为识别是否成功来加载人脸识别结果
|
||||
// */
|
||||
// private fun loadFaceRecognizeResult(result: CompareResult?) {
|
||||
// val similar = result?.similar ?: 0f
|
||||
// binding.tvSimilarValue.text = "$similar"
|
||||
// val similarPass = result?.isSimilarPass ?: false
|
||||
// if (similarPass.not()) {
|
||||
// loadLogInfo("collectFace,recognizeUserId 未识别到人脸信息,开始采集")
|
||||
// isCollectFace = true
|
||||
// collectFace()
|
||||
// return
|
||||
// }
|
||||
// currentUserId = result?.faceEntity?.userName
|
||||
// if (tempUserId != currentUserId) {
|
||||
// SoundPoolUtil.getInstance().play("success", 0)
|
||||
// }
|
||||
//// plateEnable = true
|
||||
// Debouncer(5000).debounce { openPlate(currentUserId) }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 查询餐盘数据
|
||||
// */
|
||||
// private fun getPlateData(block: (List<EquipmentUserInfo>) -> Unit) {
|
||||
// MyApp.plateList?.let {
|
||||
// block(it)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private var currentPlateInfo: EquipmentUserInfo? = null
|
||||
// private var plateEnable = false
|
||||
//
|
||||
// /**
|
||||
// * 打开餐盘柜
|
||||
// */
|
||||
// private fun openPlate(userId: String?) {
|
||||
// if (plateEnable.not()) return
|
||||
// loadLogInfo("collectFace,openPlate: userId=$userId")
|
||||
// getPlateData { items ->
|
||||
// loadLogInfo("collectFace,openPlate: items:${Gson().toJson(items)}")
|
||||
// // 未查询到当前用户,查询第一个未绑定的餐盘
|
||||
// val item = items.firstOrNull { it.plateNumber.isNullOrEmpty().not() }
|
||||
// loadLogInfo("collectFace,openPlate: item2:${item}")
|
||||
// if (item == null) {
|
||||
// //暂无餐盘
|
||||
// loadLogInfo("collectFace,openPlate: 暂无餐盘,请联系管理人员,isCollectFace=$isCollectFace")
|
||||
// //ToastUtils.showToast("暂无餐盘,请联系管理人员")
|
||||
// SoundPoolUtil.getInstance().play(NO_PLATE_REMIND, 0)
|
||||
// goInitActivity()
|
||||
// return@getPlateData
|
||||
// }
|
||||
//// if (tempUserId != currentUserId) {
|
||||
// SoundPoolUtil.getInstance().play(RECOGNIZE_SUCCESS, 0)
|
||||
//// } else {
|
||||
//// SoundPoolUtil.getInstance().play(COLLECT_SUCCESS, 0)
|
||||
//// }
|
||||
// loadLogInfo("collectFace,openPlate: item3:${item}")
|
||||
// item.faceId = userId
|
||||
// item.updateTime = DateTimeUtils.getDateTimeString()
|
||||
// item.plateNumber = null
|
||||
// plateEnable = false
|
||||
// Debouncer(5000).debounce {
|
||||
// //打开柜子
|
||||
// PlateUtils.open(this, item.equipmentBoxCode, false)
|
||||
// }
|
||||
// currentPlateInfo = item
|
||||
// MyApp.plateList = items
|
||||
// SpTool.savePlateData(items)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//// private val debouncer by lazy { Debouncer(3000) }
|
||||
//
|
||||
// private fun initArcView() {
|
||||
// //在布局结束后才做初始化操作
|
||||
// binding.dualCameraTexturePreviewRgb.getViewTreeObserver().addOnGlobalLayoutListener(this)
|
||||
// recognizeViewModel.getCompareResultList().getValue()
|
||||
// }
|
||||
//
|
||||
// override fun onDestroy() {
|
||||
// loadLogInfo("onDestroy")
|
||||
// instance = null
|
||||
// if (rgbCameraHelper != null) {
|
||||
// rgbCameraHelper!!.release()
|
||||
// rgbCameraHelper = null
|
||||
// }
|
||||
//
|
||||
// recognizeViewModel.destroy()
|
||||
//
|
||||
// EventBus.getDefault().unregister(this)
|
||||
// super.onDestroy()
|
||||
// }
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * 调整View的宽高,使预览显示正常且采集框固定为380x380
|
||||
// *
|
||||
// * @param rgbPreview RGB预览View
|
||||
// * @param previewView 显示预览数据的view
|
||||
// * @param faceRectView 画框的view (380x380)
|
||||
// * @param previewSize 预览大小
|
||||
// * @param displayOrientation 相机旋转角度
|
||||
// * @param scale 缩放比例
|
||||
// * @return 调整后的LayoutParams
|
||||
// */
|
||||
// private fun adjustPreviewViewSize(
|
||||
// rgbPreview: View,
|
||||
// previewView: View,
|
||||
// faceRectView: FaceRectView,
|
||||
// previewSize: Camera.Size,
|
||||
// displayOrientation: Int,
|
||||
// scale: Float
|
||||
// ): ViewGroup.LayoutParams {
|
||||
// val layoutParams = previewView.layoutParams
|
||||
// val measuredWidth = previewView.measuredWidth
|
||||
// val measuredHeight = previewView.measuredHeight
|
||||
// var ratio = (previewSize.height.toFloat()) / previewSize.width.toFloat()
|
||||
// if (ratio > 1) {
|
||||
// ratio = 1 / ratio
|
||||
// }
|
||||
// if (displayOrientation % 180 == 0) {
|
||||
// layoutParams.width = measuredWidth
|
||||
// layoutParams.height = (measuredWidth * ratio).toInt()
|
||||
// } else {
|
||||
// layoutParams.height = measuredHeight
|
||||
// layoutParams.width = (measuredHeight * ratio).toInt()
|
||||
// }
|
||||
// if (scale < 1f) {
|
||||
// val rgbParam = rgbPreview.getLayoutParams()
|
||||
// layoutParams.width = (rgbParam.width * scale).toInt()
|
||||
// layoutParams.height = (rgbParam.height * scale).toInt()
|
||||
// } else {
|
||||
// layoutParams.width = (layoutParams.width * scale).toInt()
|
||||
// layoutParams.height = (layoutParams.height * scale).toInt()
|
||||
// }
|
||||
//
|
||||
// val metrics = DisplayMetrics()
|
||||
// windowManager.defaultDisplay.getMetrics(metrics)
|
||||
//
|
||||
// if (layoutParams.width >= metrics.widthPixels) {
|
||||
// val viewRatio = layoutParams.width / (metrics.widthPixels.toFloat())
|
||||
// layoutParams.width = (layoutParams.width / viewRatio).toInt()
|
||||
// layoutParams.height = (layoutParams.height / viewRatio).toInt()
|
||||
// }
|
||||
// if (layoutParams.height >= metrics.heightPixels) {
|
||||
// val viewRatio = layoutParams.height / (metrics.heightPixels.toFloat())
|
||||
// layoutParams.width = (layoutParams.width / viewRatio).toInt()
|
||||
// layoutParams.height = (layoutParams.height / viewRatio).toInt()
|
||||
// }
|
||||
//
|
||||
// previewView.setLayoutParams(layoutParams)
|
||||
// faceRectView.setLayoutParams(layoutParams)
|
||||
// return layoutParams
|
||||
// }
|
||||
//
|
||||
// private fun initRgbCamera() {
|
||||
// val cameraListener: CameraListener = object : CameraListener {
|
||||
// override fun onCameraOpened(
|
||||
// camera: Camera,
|
||||
// cameraId: Int,
|
||||
// displayOrientation: Int,
|
||||
// isMirror: Boolean
|
||||
// ) {
|
||||
// loadLogInfo("initRgbCamera Rgb onCameraOpened: cameraId = $cameraId, displayOrientation = $displayOrientation")
|
||||
// runOnUiThread({
|
||||
// val previewSizeRgb = camera.getParameters().getPreviewSize()
|
||||
// val layoutParams = adjustPreviewViewSize(
|
||||
// binding.dualCameraTexturePreviewRgb,
|
||||
// binding.dualCameraTexturePreviewRgb, binding.dualCameraFaceRectView,
|
||||
// previewSizeRgb, displayOrientation, 0.6f
|
||||
// )
|
||||
// loadLogInfo("initRgbCamera previewSizeRgb = ${previewSizeRgb.width}-${previewSizeRgb.height}")
|
||||
// loadLogInfo("initRgbCamera layoutParams = ${layoutParams.width}-${layoutParams.height}")
|
||||
// loadLogInfo(
|
||||
// "initRgbCamera isMirror = ${isMirror}, isDrawRgbRectHorizontalMirror = ${
|
||||
// ConfigUtil.isDrawRgbRectHorizontalMirror(
|
||||
// context
|
||||
// )
|
||||
// }, isDrawRgbRectVerticalMirror = ${
|
||||
// ConfigUtil.isDrawRgbRectVerticalMirror(
|
||||
// context
|
||||
// )
|
||||
// }"
|
||||
// )
|
||||
// rgbFaceRectTransformer = FaceRectTransformer(
|
||||
// previewSizeRgb.width,
|
||||
// previewSizeRgb.height,
|
||||
// layoutParams.width,
|
||||
// layoutParams.height,
|
||||
// 90,
|
||||
// cameraId,
|
||||
// isMirror,
|
||||
// true,
|
||||
// true
|
||||
// )
|
||||
//
|
||||
// recognizeViewModel.onRgbCameraOpened(camera)
|
||||
// recognizeViewModel.setRgbFaceRectTransformer(rgbFaceRectTransformer)
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// @RequiresApi(api = Build.VERSION_CODES.Q)
|
||||
// override fun onPreview(nv21: ByteArray?, camera: Camera?) {
|
||||
// if (!isRecognition) {
|
||||
// return
|
||||
// }
|
||||
// binding.dualCameraFaceRectView.clearFaceInfo()
|
||||
// facePreviewInfoList = recognizeViewModel.onPreviewFrame(nv21, true)
|
||||
// if (facePreviewInfoList != null && rgbFaceRectTransformer != null) {
|
||||
// drawPreviewInfo(facePreviewInfoList)
|
||||
// }
|
||||
// if (facePreviewInfoList.isNullOrEmpty()) {
|
||||
// currentPlateInfo = null
|
||||
// currentUserId = null
|
||||
// userFaceInfo = null
|
||||
// }
|
||||
// recognizeViewModel.clearLeftFace(facePreviewInfoList)
|
||||
// }
|
||||
//
|
||||
// override fun onCameraClosed() {
|
||||
// Timber.i("initRgbCamera onCameraClosed: ")
|
||||
// }
|
||||
//
|
||||
// override fun onCameraError(e: java.lang.Exception) {
|
||||
// Timber.i("initRgbCamera onCameraError: %s", e.message)
|
||||
// e.printStackTrace()
|
||||
// }
|
||||
//
|
||||
// override fun onCameraConfigurationChanged(cameraID: Int, displayOrientation: Int) {
|
||||
// Timber.i("initRgbCamera onCameraConfigurationChanged: threadName = ${Thread.currentThread().name}")
|
||||
// if (rgbFaceRectTransformer != null) {
|
||||
// rgbFaceRectTransformer!!.cameraDisplayOrientation = displayOrientation
|
||||
// }
|
||||
// Timber.i("initRgbCamera onCameraConfigurationChanged: $cameraID $displayOrientation")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// val previewConfig: PreviewConfig = recognizeViewModel.previewConfig
|
||||
// rgbCameraHelper = DualCameraHelper.Builder()
|
||||
// .previewViewSize(
|
||||
// Point(
|
||||
// binding.dualCameraTexturePreviewRgb.measuredWidth,
|
||||
// binding.dualCameraTexturePreviewRgb.measuredHeight
|
||||
// )
|
||||
// )
|
||||
// .rotation(windowManager.defaultDisplay.rotation)
|
||||
// .additionalRotation(0) // 角度
|
||||
// .previewSize(recognizeViewModel.loadPreviewSize())
|
||||
// .specificCameraId(previewConfig.rgbCameraId)
|
||||
// .isMirror(true)
|
||||
// .previewOn(binding.dualCameraTexturePreviewRgb)
|
||||
// .cameraListener(cameraListener)
|
||||
// .build()
|
||||
// rgbCameraHelper!!.init()
|
||||
// rgbCameraHelper!!.start()
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 绘制RGB、IR画面的实时人脸信息
|
||||
// *
|
||||
// * @param facePreviewInfoList RGB画面的实时人脸信息
|
||||
// */
|
||||
// private fun drawPreviewInfo(facePreviewInfoList: MutableList<FacePreviewInfo>?) {
|
||||
// if (rgbFaceRectTransformer != null) {
|
||||
// val rgbDrawInfoList: MutableList<DrawInfo>? = recognizeViewModel.getDrawInfo(
|
||||
// facePreviewInfoList, LivenessType.RGB, openRectInfoDraw
|
||||
// )
|
||||
// // 识别成功
|
||||
// binding.dualCameraFaceRectView.drawRealtimeFaceInfo(rgbDrawInfoList)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// fun openRectInfoDraw(view: View?) {
|
||||
// openRectInfoDraw = !openRectInfoDraw
|
||||
// recognizeViewModel.setDrawRectInfoTextValue(openRectInfoDraw)
|
||||
// }
|
||||
//
|
||||
//
|
||||
// override fun onGlobalLayout() {
|
||||
// loadLogInfo("onGlobalLayout")
|
||||
// binding.dualCameraTexturePreviewRgb.getViewTreeObserver().removeOnGlobalLayoutListener(this)
|
||||
// // 请求权限
|
||||
// checkCameraPermission()
|
||||
// }
|
||||
//
|
||||
// private fun resumeCamera() {
|
||||
// isRecognition = true
|
||||
// if (rgbCameraHelper != null && rgbCameraHelper!!.isStopped) {
|
||||
// loadLogInfo("collectFace,resumeCamera: ")
|
||||
// rgbCameraHelper!!.start()
|
||||
// } else {
|
||||
// recognizeViewModel.onPreviewFrame(ByteArray(1382400), true)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 临时用户id
|
||||
// */
|
||||
// private var tempUserId: String = ""
|
||||
// override fun onResume() {
|
||||
// super.onResume()
|
||||
// registerHandler = RegisterCallbackHandler()
|
||||
// try {
|
||||
// if (isFirstOpen) {
|
||||
// goInitActivity()
|
||||
// binding.root.postDelayed({
|
||||
// isFirstOpen = false
|
||||
// }, 1000)
|
||||
// return
|
||||
// }
|
||||
// retryFailCount = 0
|
||||
// binding.tvSimilarValue.text = ""
|
||||
// isCollecting.set(false)
|
||||
// isCollectFace = false
|
||||
// plateEnable = true
|
||||
// collectCount = 0
|
||||
// tempUserId = "${System.currentTimeMillis()}"
|
||||
// resumeCamera()
|
||||
// userViewModel.resetUserInfo()
|
||||
// countDownTimer?.let {
|
||||
// it.cancel()
|
||||
// it.start()
|
||||
// }
|
||||
//// initFaceScheduler()
|
||||
//// checkFaceState()
|
||||
// } catch (e: Exception) {
|
||||
// e.printStackTrace()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private var isFirstOpen = true
|
||||
//
|
||||
// protected override fun onPause() {
|
||||
// pauseCamera()
|
||||
// super.onPause()
|
||||
// countDownTimer?.cancel()
|
||||
// }
|
||||
//
|
||||
// private fun pauseCamera() {
|
||||
// isRecognition = false
|
||||
// recognizeViewModel.onPreviewFrame(ByteArray(1382400), true)
|
||||
// }
|
||||
//
|
||||
// override fun handleLoginSuccess(equipmentUserInfo: EquipmentUserInfo, isAdmin: Boolean) {
|
||||
// super.handleLoginSuccess(equipmentUserInfo, isAdmin)
|
||||
//// goInitActivity()
|
||||
// }
|
||||
//
|
||||
// private val totalTimeInMillis = 15 * 1000L
|
||||
// fun initCountTime() {
|
||||
// countDownTimer = object : CountDownTimer(totalTimeInMillis, 1000) {
|
||||
// override fun onTick(millisUntilFinished: Long) {
|
||||
// loadLogInfo("initCountTime onTick = ${(millisUntilFinished / 1000).toInt()}")
|
||||
// binding.tvToInit.text = "${(millisUntilFinished / 1000).toInt() + 1}"
|
||||
// }
|
||||
//
|
||||
// override fun onFinish() {
|
||||
// goInitActivity()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// fun goInitActivity() {
|
||||
// val intent = Intent(context, InitActivity::class.java)
|
||||
// startActivity(intent)
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 采集人脸
|
||||
// */
|
||||
// private fun collectFace() {
|
||||
// loadLogInfo("collectFace: -----------1,开始采集人脸,开始时间:${DateTimeUtils.getDateTimeString()}----------")
|
||||
// if (isFirstOpen) return
|
||||
// loadLogInfo("collectFace: ----------2,isFirstOpen=false-----------")
|
||||
// loadLogInfo("collectFace: ----------4,检测到人脸信息-----------")
|
||||
// currentUserId = null
|
||||
// userFaceInfo = null
|
||||
// recognizeViewModel.updateRegisterStatus(REGISTER_STATUS_READY)
|
||||
// loadLogInfo("collectFace: ----------5,人脸采集中-----------")
|
||||
// }
|
||||
//
|
||||
// private val uploadFaceDebouncer = Debouncer(10000)
|
||||
//
|
||||
// /**
|
||||
// * 改为上传人脸数据,不需要发生人脸数据到收银端
|
||||
// */
|
||||
// private fun sendFaceData(faceData: String, headBmp: Bitmap) {
|
||||
// loadLogInfo("collectFace,sendFaceData: ----------11,发送人脸数据-----------")
|
||||
//
|
||||
// val imageFile = BitmapSaver.saveToAppFilesDir(
|
||||
// headBmp, this, "IMG_CROP_${System.currentTimeMillis()}.jpg"
|
||||
// )
|
||||
// loadLogInfo("collectFace,sendFaceData:${imageFile?.absolutePath}")
|
||||
// if (imageFile == null) {
|
||||
// ToastUtils.showToast("获取人脸照片失败")
|
||||
// return
|
||||
// }
|
||||
// uploadFaceDebouncer.debounce {
|
||||
// addUserFace(faceData, imageFile)
|
||||
// }
|
||||
//// // 给某个客户端发送
|
||||
//// val jsonObject = JSONObject().also {
|
||||
//// it.put("type", "faceFeature")
|
||||
//// it.put("content", faceData)
|
||||
//// }
|
||||
//// tcpClient?.send(jsonObject)
|
||||
// }
|
||||
//
|
||||
// private fun addUserFace(faceData: String, imageFile: File) {
|
||||
// settingViewModel.addUserFace(faceData, imageFile) { status, faceModel ->
|
||||
// loadLogInfo("collectFace,saveFaceInfo: ----------12,人脸数据发送完成时间:${DateTimeUtils.getDateTimeString()},结果:$status-----------")
|
||||
// if (status.not()) {
|
||||
// ToastUtils.showToast("新增数据失败")
|
||||
// loadLogInfo("collectFace,sendFaceData: ----------12,发送人脸数据失败,faceData=${faceData}")
|
||||
// return@addUserFace
|
||||
// }
|
||||
// if (faceModel == null) {
|
||||
// ToastUtils.showToast("新增数据成功,但未拿到接口数据")
|
||||
// loadLogInfo("collectFace,sendFaceData: ----------12,发送人脸数据成功但接口返回为null,faceData=${faceData}")
|
||||
// return@addUserFace
|
||||
// }
|
||||
// Thread {
|
||||
// saveFaceInfo(faceModel)
|
||||
// uploadFaceDebouncer.reset()
|
||||
// }.start()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private fun getFaceDataBlock(block: (String, Bitmap) -> Unit) {
|
||||
// if (userFaceInfo == null || userFaceInfo?.faceFeature?.featureData == null) {
|
||||
// return
|
||||
// }
|
||||
// val faceData: String? = Base64.encode(userFaceInfo?.faceFeature?.featureData)
|
||||
// if (faceData.isNullOrBlank()) {
|
||||
// return
|
||||
// }
|
||||
// loadLogInfo("collectFace,getFaceData: ----------7,拿到人脸提取特征数据时间:${DateTimeUtils.getDateTimeString()}-----------")
|
||||
// block(faceData, userFaceInfo!!.headBmp)
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 加载日志
|
||||
// * @param msg 日志内容
|
||||
// */
|
||||
// private fun loadLogInfo(msg: String) {
|
||||
// Timber.tag(TAG).d(msg)
|
||||
// LogFileUtil.saveLog(msg)
|
||||
// }
|
||||
//
|
||||
// private var facePreviewInfoList: MutableList<FacePreviewInfo>? = null
|
||||
// private var userFaceInfo: UserFaceInfo? = null
|
||||
//
|
||||
// private var currentUserId: String? = null
|
||||
//
|
||||
// private fun saveFaceInfo(faceModel: UserFaceModel) {
|
||||
// try {
|
||||
// loadLogInfo("collectFace,saveFaceInfo: ----------8,保存人脸取特征数据-----------")
|
||||
// val faceDao = FaceDatabase.getInstance(this).faceDao()
|
||||
// val faceEntity = FaceEntity(
|
||||
// faceModel.userId,
|
||||
// null,
|
||||
// Base64.decode(faceModel.faceFeatureStr)
|
||||
// ).also {
|
||||
// //1-会员、2-临时用户
|
||||
// it.userType = if (faceModel.isMember) "1" else "2"
|
||||
// }
|
||||
// faceDao.insert(faceEntity)
|
||||
// loadLogInfo("collectFace,saveFaceInfo: ----------10,人脸数据保存完成-----------")
|
||||
// recognizeViewModel.refreshFaceList()
|
||||
// loadLogInfo("collectFace,saveFaceInfo: ----------11,刷新人脸数据-----------")
|
||||
// runOnUiThread { recognizeViewModel.onPreviewFrame(ByteArray(1382400), true) }
|
||||
// loadLogInfo("---------------------------------")
|
||||
// collectCount = 0
|
||||
// } catch (e: Exception) {
|
||||
// e.printStackTrace()
|
||||
// loadLogInfo("collectFace,saveFaceInfo: ----------13,人脸采集异常:${e.message}-----------")
|
||||
// collectCount++
|
||||
// loadLogInfo("collectFace,saveFaceInfo: ----------14,${collectCount}次人脸采集异常:${e.message}-----------")
|
||||
// ToastUtils.showToast("人脸采集异常:${e.message}")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private var collectCount = 0
|
||||
//
|
||||
//// private var tcpClient: TcpClient? = null
|
||||
////
|
||||
//// private fun addSocketListener() {
|
||||
////// val ipAddress = NetworkUtils.getIPAddress(true)
|
||||
////// loadLogInfo( "addSocketListener,ipAddress: $ipAddress");
|
||||
//// tcpClient = TcpClient(
|
||||
//// "192.168.1.95",
|
||||
//// 5000,
|
||||
//// GlobalData.deviceId, // clientId
|
||||
//// 5000, // connectTimeoutMs
|
||||
//// 10 * 1000, // heartbeatIntervalMs
|
||||
//// 30 * 1000 // heartbeatTimeoutMs
|
||||
//// )
|
||||
////
|
||||
//// tcpClient?.setListener(object : TcpClientListenerImpl() {
|
||||
//// override fun onSendSuccess(json: JSONObject?) {
|
||||
//// super.onSendSuccess(json)
|
||||
//// loadLogInfo("collectFace,TcpClientListener发送成功:$json")
|
||||
////// toast("发送成功")
|
||||
//// }
|
||||
////
|
||||
//// override fun onSendFailed(json: JSONObject?, e: java.lang.Exception?) {
|
||||
//// super.onSendFailed(json, e)
|
||||
//// ToastUtils.showToast("发送失败${e?.message}")
|
||||
//// loadLogInfo("collectFace,TcpClientListener人脸数据发送失败:${e?.message}")
|
||||
//// }
|
||||
////
|
||||
//// override fun onMessage(json: JSONObject?) {
|
||||
//// super.onMessage(json)
|
||||
//// loadLogInfo("collectFace,TcpClientListener收到消息:$json")
|
||||
//// val type = json?.getInt("type")
|
||||
//// // 根据服务端消息类型处理删除新增临时用户人脸数据---------------------
|
||||
//// when (type) {
|
||||
//// LanServer.TYPE_ADD_FACE_DATA -> {
|
||||
//// //采集设备id自己生成,不接收服务端的,在本地只判断是否存在同一人脸数据
|
||||
//// loadLogInfo("collectFace,TcpClientListener收到消息,新增用户:采集设备id自己生成,不接收服务端的,在本地只判断是否存在同一人脸数据")
|
||||
//// }
|
||||
////
|
||||
//// LanServer.TYPE_CLEAR_FACE_DATA -> {
|
||||
//// clearFaceData()
|
||||
//// }
|
||||
////
|
||||
//// else -> {}
|
||||
//// }
|
||||
//// }
|
||||
//// })
|
||||
//// tcpClient?.start()
|
||||
////
|
||||
//// try {
|
||||
//// LanServer.getInstance().let {
|
||||
//// it.setListener(object : LanServerListenerImpl() {
|
||||
//// override fun onMessageReceived(clientId: String?, message: JSONObject?) {
|
||||
//// super.onMessageReceived(clientId, message)
|
||||
//// ToastUtils.showToast("收到消息clientId=$clientId")
|
||||
//// }
|
||||
//// })
|
||||
//// it.start()
|
||||
//// }
|
||||
//// } catch (e: Exception) {
|
||||
//// e.printStackTrace()
|
||||
//// }
|
||||
//// }
|
||||
//
|
||||
// private fun clearFaceData() {
|
||||
// Thread {
|
||||
// val faceDao = FaceDatabase.getInstance(this).faceDao()
|
||||
// faceDao.deleteTempUserFaceData()
|
||||
// recognizeViewModel.refreshFaceList()
|
||||
// loadLogInfo("collectFace,TcpClientListener收到消息,已删除并刷新人脸数据")
|
||||
// }.start()
|
||||
// }
|
||||
//
|
||||
// private var isCollectFace = false
|
||||
//
|
||||
//}
|
||||
@@ -86,7 +86,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
|
||||
override fun onRightDoubleClick() {
|
||||
if (pageType == PageType.SETTING_LIST) {
|
||||
loadClearDialog()
|
||||
startActivity(Intent(this, FunActivity::class.java))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,25 +104,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
super.registerDataChange()
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载清除数据弹窗
|
||||
*/
|
||||
private fun loadClearDialog() {
|
||||
CustomDialog(
|
||||
activity = this,
|
||||
content = "请确认是否清除本地所有数据,并重新拉取全量人脸数据?",
|
||||
onConfirm = {
|
||||
showWaitingDialog("加载中……")
|
||||
clearAllFace {
|
||||
userViewModel.getUserFaceCache { status, msg ->
|
||||
hideWaitingDialog()
|
||||
ToastUtils.showToast(
|
||||
if (status) "请求成功,$msg" else "请求失败,$msg"
|
||||
)
|
||||
}
|
||||
}
|
||||
}).show()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -69,4 +69,36 @@ fun View.clickWithCoroutines(
|
||||
lastClickTime = currentTime
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//fun View.clickWithDebounce(delay: Long = 500, action: () -> Unit) {
|
||||
// var job: Job? = null
|
||||
// setOnClickListener {
|
||||
// job?.cancel()
|
||||
// job = CoroutineScope(Dispatchers.Main).launch {
|
||||
// delay(delay)
|
||||
// action()
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
/**
|
||||
* 极简版防重复点击扩展函数
|
||||
* @param delay 防抖时间(默认300ms)
|
||||
* @param action 点击执行逻辑
|
||||
*/
|
||||
fun View.clickWithDebounce(delay: Long = 500, action: () -> Unit) {
|
||||
setOnClickListener {
|
||||
// 用View的tag存储是否可点击的状态(默认可点击)
|
||||
if (tag as? Boolean ?: true) {
|
||||
tag = false // 标记为不可点击
|
||||
action() // 立即执行点击逻辑
|
||||
|
||||
// 启动协程,延迟后恢复可点击状态
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
delay(delay)
|
||||
tag = true // 恢复可点击
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package com.sw.platecabinet.model.response
|
||||
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
/**
|
||||
@@ -38,5 +37,5 @@ data class UserFaceModel(
|
||||
/**
|
||||
* 是否会员
|
||||
*/
|
||||
val isMember: Boolean
|
||||
val member: Boolean
|
||||
) : Parcelable
|
||||
@@ -12,37 +12,29 @@ import timber.log.Timber
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
object ApiClient {
|
||||
//private const val BASE_URL = "https://vip.shuziweidao.com"
|
||||
// private const val DEVICE_BASE_URL = "http://device.shuziweidao.com:8889/"
|
||||
private var DEVICE_BASE_URL = GlobalData.appBaseUrl
|
||||
// private const val BASE_URL = "http://192.168.1.8:9092"
|
||||
// private const val TIME_OUT = 30L // 超时时间(秒)
|
||||
private const val TIME_OUT = 60L // 超时时间(秒)
|
||||
|
||||
val okHttpClient = OkHttpClient.Builder()
|
||||
private const val TIME_OUT = 60L // 超时时间(秒)
|
||||
|
||||
private val okHttpClient = OkHttpClient.Builder()
|
||||
.connectTimeout(TIME_OUT, TimeUnit.SECONDS)
|
||||
.readTimeout(TIME_OUT, TimeUnit.SECONDS)
|
||||
.writeTimeout(TIME_OUT, TimeUnit.SECONDS)
|
||||
.addNetworkInterceptor(HttpLoggingInterceptor(logger = {
|
||||
Timber.d("okhttp logger ==>${it}")
|
||||
}).apply {
|
||||
level = if (MyApp.DEBUG) {
|
||||
HttpLoggingInterceptor.Level.BODY
|
||||
} else {
|
||||
HttpLoggingInterceptor.Level.NONE
|
||||
}
|
||||
.addNetworkInterceptor(HttpLoggingInterceptor { Timber.d("okhttp ==>${it}") }.apply {
|
||||
level = if (MyApp.DEBUG) HttpLoggingInterceptor.Level.BODY
|
||||
else HttpLoggingInterceptor.Level.NONE
|
||||
})
|
||||
.addInterceptor(RequestInterceptor())
|
||||
.build()
|
||||
|
||||
private val retrofit = Retrofit.Builder()
|
||||
.baseUrl(DEVICE_BASE_URL)
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
// .addCallAdapterFactory(CoroutineCallAdapterFactory()) // 协程适配器
|
||||
.build()
|
||||
private val retrofit by lazy {
|
||||
Retrofit.Builder()
|
||||
.baseUrl(GlobalData.appBaseUrl)
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build()
|
||||
}
|
||||
|
||||
val apiService: ApiService by lazy {
|
||||
retrofit.create(ApiService::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.sw.platecabinet.utils
|
||||
|
||||
import android.util.Log
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
/**
|
||||
* 基于协程的倒计时工具类(实例化使用,各调用方持有独立实例,互不干扰)
|
||||
* 修复:onCompletion 区分正常结束与取消,手动取消时不触发 onFinish
|
||||
*/
|
||||
class CountDownUtil {
|
||||
|
||||
private val isCounting = AtomicBoolean(false)
|
||||
private var currentJob: Job? = null
|
||||
|
||||
/**
|
||||
* 启动倒计时(若已在运行则忽略,需重启请先调用 cancelCountDown)
|
||||
* @param total 总秒数
|
||||
* @param lifecycleScope Activity/Fragment 的 lifecycleScope
|
||||
* @param onStart 启动回调(主线程)
|
||||
* @param onTick 每秒回调剩余秒数(主线程)
|
||||
* @param onFinish 正常结束回调(主线程,手动取消时不触发)
|
||||
*/
|
||||
fun startCountDown(
|
||||
total: Int = 15,
|
||||
lifecycleScope: CoroutineScope,
|
||||
onStart: () -> Unit = {},
|
||||
onTick: (Int) -> Unit = {},
|
||||
onFinish: () -> Unit
|
||||
) {
|
||||
if (isCounting.get()) return
|
||||
|
||||
isCounting.set(true)
|
||||
|
||||
currentJob = flow {
|
||||
for (i in total downTo 1) {
|
||||
emit(i)
|
||||
delay(1000L)
|
||||
}
|
||||
}
|
||||
.flowOn(Dispatchers.IO)
|
||||
.onStart {
|
||||
withContext(Dispatchers.Main) { onStart() }
|
||||
}
|
||||
.onEach { remaining ->
|
||||
withContext(Dispatchers.Main) { onTick(remaining) }
|
||||
}
|
||||
.onCompletion { cause ->
|
||||
withContext(Dispatchers.Main) {
|
||||
isCounting.set(false)
|
||||
currentJob = null
|
||||
// cause == null 为正常结束,有异常(含 CancellationException)时不触发 onFinish
|
||||
if (cause == null) {
|
||||
Log.d("CountDownUtil", "倒计时正常结束")
|
||||
onFinish()
|
||||
} else {
|
||||
Log.d("CountDownUtil", "倒计时被取消:${cause.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
.catch { e ->
|
||||
Log.e("CountDownUtil", "倒计时异常:${e.message}", e)
|
||||
isCounting.set(false)
|
||||
currentJob = null
|
||||
}
|
||||
.launchIn(lifecycleScope)
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消倒计时(不触发 onFinish)
|
||||
*/
|
||||
fun cancelCountDown() {
|
||||
currentJob?.cancel()
|
||||
currentJob = null
|
||||
isCounting.set(false)
|
||||
Log.d("CountDownUtil", "倒计时已取消")
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,8 @@ object PlateUtils {
|
||||
fragmentActivity: FragmentActivity,
|
||||
code: String?,
|
||||
isScanCode: Boolean,
|
||||
isSetting: Boolean = false
|
||||
isSetting: Boolean = false,
|
||||
callback: () -> Unit = {}
|
||||
) {
|
||||
val activity = fragmentActivity as BaseActivity<*>
|
||||
Timber.d("itemClick llOpen equipmentBoxCode = ${code}")
|
||||
@@ -36,15 +37,30 @@ object PlateUtils {
|
||||
)
|
||||
} else {
|
||||
ToastUtils.showToast("柜门打开成功")
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFail(e: Exception?) {
|
||||
activity.hideWaitingDialog()
|
||||
ToastUtils.showToast("柜门打开失败")
|
||||
callback()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun openDirect(boxCode: String?) {
|
||||
val boxNumber = boxCode?.toIntOrNull() ?: -1
|
||||
if (boxNumber < 0) return
|
||||
SerialApi.openPlate(boxNumber, object : SerialPortManager.SendCallback {
|
||||
override fun onSuccess() {
|
||||
|
||||
}
|
||||
|
||||
override fun onFail(e: Exception?) {
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,10 +6,14 @@ import com.sw.inbound.utils.SPUtil
|
||||
import com.sw.inbound.utils.SPUtil.Companion.getInstance
|
||||
import com.sw.platecabinet.MyApp
|
||||
import com.sw.platecabinet.model.response.EquipmentUserInfo
|
||||
import kotlin.text.get
|
||||
|
||||
object SpTool {
|
||||
const val LAST_FACE_TIMESTAMP: String = "faceTimestamp"
|
||||
const val PLATE_BOX_DATA: String = "plateBoxBind"
|
||||
|
||||
const val IS_FIRST_GET_FACE = "isFirstGetFace"
|
||||
|
||||
private var spUtil: SPUtil? = null
|
||||
|
||||
init {
|
||||
@@ -39,4 +43,10 @@ object SpTool {
|
||||
return GsonUtils.fromJsonList(jsonData, EquipmentUserInfo::class.java)
|
||||
}
|
||||
|
||||
var firstGetFace: Boolean
|
||||
get() = spUtil?.get(IS_FIRST_GET_FACE, true)?:true
|
||||
set(value) {
|
||||
spUtil?.boolean(IS_FIRST_GET_FACE, value)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,16 +9,12 @@ import com.sw.plate.utils.ToastUtils
|
||||
import com.sw.plate.utils.arcface.FaceApi
|
||||
import com.sw.plate.utils.arcface.facedb.entity.FaceEntity
|
||||
import com.sw.platecabinet.GlobalData
|
||||
import com.sw.platecabinet.GlobalData.globalEquipmentCode
|
||||
import com.sw.platecabinet.GlobalKey
|
||||
import com.sw.platecabinet.model.DeviceConfig
|
||||
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.SpTool
|
||||
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
|
||||
@@ -55,6 +51,19 @@ class UserViewModel : BaseViewModel() {
|
||||
// }
|
||||
// }
|
||||
|
||||
fun getUserFace(
|
||||
pageNo: Int,
|
||||
pageSize: Int,
|
||||
callback: (List<UserFaceModel>) -> Unit
|
||||
) {
|
||||
launchWithLoading{
|
||||
val response = repository.getUserFaceCache(pageNum = pageNo, pageSize = pageSize)
|
||||
val status = parseResponse(response)
|
||||
if (status) callback(response.data?:emptyList())
|
||||
else emptyList<UserFaceModel>()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取人脸数据
|
||||
*/
|
||||
@@ -79,9 +88,15 @@ class UserViewModel : BaseViewModel() {
|
||||
return@launchWithLoading
|
||||
}
|
||||
val faceEntityList = list.map {
|
||||
FaceEntity(it.userId, null, Base64.decode(it.faceFeatureStr))
|
||||
FaceEntity(it.userId, null, Base64.decode(it.faceFeatureStr)).apply {
|
||||
userType = if (it.member) "1" else "2"
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Default) {
|
||||
if (currentPageNo == 1) {
|
||||
//第一页数据删除本地数据库
|
||||
faceApi.clearFaceData()
|
||||
}
|
||||
faceApi.updateFaceData(pageNo, faceEntityList)
|
||||
if (list.size >= pageSize) {
|
||||
faceTimestamp = list.last().faceUpdateTimestamp ?: 0
|
||||
@@ -103,6 +118,7 @@ class UserViewModel : BaseViewModel() {
|
||||
|
||||
private var faceTimestamp = 0L
|
||||
|
||||
|
||||
/**
|
||||
* 激活人脸识别引擎
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/bg"
|
||||
android:orientation="vertical"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<include
|
||||
android:id="@+id/includeHeader"
|
||||
layout="@layout/item_title_time" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginTop="30dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvMemberUserCount"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/white"
|
||||
android:gravity="center"
|
||||
tools:text="会员用户:100人"/>
|
||||
|
||||
<View
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="30dp"
|
||||
android:background="@color/white"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTempUserCount"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/white"
|
||||
android:gravity="center"
|
||||
tools:text="临时用户:100人"/>
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/btnClearFace"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:textSize="24sp"
|
||||
android:gravity="center"
|
||||
android:layout_marginHorizontal="50dp"
|
||||
android:paddingHorizontal="30dp"
|
||||
android:background="@drawable/btn_outline"
|
||||
android:text="清除人脸数据"
|
||||
android:textColor="#FFCC99" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/btnClearAndGetAll"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:textSize="24sp"
|
||||
android:gravity="center"
|
||||
android:layout_marginHorizontal="50dp"
|
||||
android:paddingHorizontal="30dp"
|
||||
android:background="@drawable/btn_outline"
|
||||
android:text="清除并拉取全量数据"
|
||||
android:textColor="#FFCC99" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/btnOpenAllPlate"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:textSize="24sp"
|
||||
android:gravity="center"
|
||||
android:layout_marginHorizontal="50dp"
|
||||
android:paddingHorizontal="30dp"
|
||||
android:background="@drawable/btn_outline"
|
||||
android:text="打开全部柜子"
|
||||
android:textColor="#FFCC99" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/btnPutPlate"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:textSize="24sp"
|
||||
android:gravity="center"
|
||||
android:layout_marginHorizontal="50dp"
|
||||
android:paddingHorizontal="30dp"
|
||||
android:background="@drawable/btn_outline"
|
||||
android:text="模拟放置餐盘"
|
||||
android:textColor="#FFCC99" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/btnTestFeature"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:textSize="24sp"
|
||||
android:gravity="center"
|
||||
android:layout_marginHorizontal="50dp"
|
||||
android:paddingHorizontal="30dp"
|
||||
android:background="@drawable/btn_outline"
|
||||
android:text="人脸特征比较"
|
||||
android:textColor="#FFCC99"
|
||||
android:visibility="gone"/>
|
||||
</LinearLayout>
|
||||
@@ -105,6 +105,46 @@
|
||||
android:textSize="36sp"
|
||||
tools:text="30" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llCollectButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginBottom="100dp"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/btnRetry"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="60dp"
|
||||
android:text="再次重试"
|
||||
android:textColor="#FFCC99"
|
||||
android:textSize="24sp"
|
||||
android:layout_marginHorizontal="12dp"
|
||||
android:layout_marginVertical="10dp"
|
||||
android:paddingHorizontal="45dp"
|
||||
android:gravity="center"
|
||||
android:background="@drawable/btn_outline"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/btnCollect"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="60dp"
|
||||
android:text="新用户采集"
|
||||
android:textColor="#FFCC99"
|
||||
android:textSize="24sp"
|
||||
android:layout_marginHorizontal="12dp"
|
||||
android:layout_marginVertical="10dp"
|
||||
android:paddingHorizontal="45dp"
|
||||
android:gravity="center"
|
||||
android:background="@drawable/btn_outline"
|
||||
tools:ignore="HardcodedText" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- <LinearLayout-->
|
||||
<!-- android:layout_width="wrap_content"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
@@ -173,6 +213,14 @@
|
||||
<!-- android:src="@drawable/ic_log_show"-->
|
||||
<!-- android:textSize="16sp" />-->
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivCollectPhoto"
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="200dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:layout_gravity="top|start"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -62,6 +62,6 @@ dependencies {
|
||||
implementation("com.google.code.gson:gson:2.8.6")
|
||||
|
||||
val glideVersion = "4.12.0"
|
||||
implementation("com.github.bumptech.glide:glide:$glideVersion")
|
||||
api("com.github.bumptech.glide:glide:$glideVersion")
|
||||
annotationProcessor("com.github.bumptech.glide:compiler:$glideVersion")
|
||||
}
|
||||
@@ -40,6 +40,12 @@ public class FaceApi {
|
||||
faceDao.insert(list);
|
||||
}
|
||||
|
||||
public void clearFaceData() {
|
||||
FaceDao faceDao = FaceDatabase.getInstance(App.getContext()).faceDao();
|
||||
faceDao.deleteAll();
|
||||
faceDao.resetId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活arcsoft 人脸
|
||||
*
|
||||
|
||||
@@ -46,6 +46,10 @@ public class FaceRectView extends View {
|
||||
}
|
||||
}
|
||||
|
||||
public int getRectColor() {
|
||||
return paint.getColor();
|
||||
}
|
||||
|
||||
public void clearFaceInfo() {
|
||||
drawInfoList.clear();
|
||||
postInvalidate();
|
||||
|
||||
@@ -92,9 +92,25 @@ public interface FaceDao {
|
||||
@Query("SELECT * FROM face WHERE user_name = :userName limit 1")
|
||||
FaceEntity queryByUserName(String userName);
|
||||
|
||||
/**
|
||||
* 查询指定用户的所有人脸记录(同一用户可能存有多条特征数据)
|
||||
*
|
||||
* @param userName 用户ID
|
||||
* @return 该用户所有人脸记录列表
|
||||
*/
|
||||
@Query("SELECT * FROM face WHERE user_name = :userName")
|
||||
List<FaceEntity> queryAllByUserName(String userName);
|
||||
|
||||
/**
|
||||
* @return 删除临时用户人脸
|
||||
*/
|
||||
@Query("DELETE from face WHERE user_type = '2'")
|
||||
int deleteTempUserFaceData();
|
||||
|
||||
/**
|
||||
* 查询用户人脸数
|
||||
* @param userType 1-会员,2-临时用户
|
||||
*/
|
||||
@Query("SELECT COUNT(1) FROM face WHERE user_type = :userType")
|
||||
int getFaceCount(String userType);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user