Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eef13ff56e | ||
|
|
6c937c5f5f | ||
|
|
513f4dbaea | ||
|
|
037816c303 | ||
|
|
dca9496bee | ||
|
|
08f1963054 | ||
|
|
4ea95092f5 | ||
|
|
cc88dccaa6 | ||
|
|
e979322697 | ||
|
|
1e6ea1517c | ||
|
|
2ee4d2d320 |
Generated
+2
-2
@@ -4,10 +4,10 @@
|
|||||||
<selectionStates>
|
<selectionStates>
|
||||||
<SelectionState runConfigName="app">
|
<SelectionState runConfigName="app">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<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">
|
<Target type="DEFAULT_BOOT">
|
||||||
<handle>
|
<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>
|
</handle>
|
||||||
</Target>
|
</Target>
|
||||||
</DropdownSelection>
|
</DropdownSelection>
|
||||||
|
|||||||
@@ -69,6 +69,7 @@
|
|||||||
android:name=".activity.MainActivity"
|
android:name=".activity.MainActivity"
|
||||||
android:exported="false">
|
android:exported="false">
|
||||||
</activity>
|
</activity>
|
||||||
|
<activity android:name="com.sw.platecabinet.activity.FunActivity" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -493,8 +493,8 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
|||||||
try {
|
try {
|
||||||
if (faceList.isNotEmpty()) {
|
if (faceList.isNotEmpty()) {
|
||||||
FaceDatabase.getInstance(this).faceDao().insert(faceList)
|
FaceDatabase.getInstance(this).faceDao().insert(faceList)
|
||||||
recognizeViewModel.refreshFaceList()
|
|
||||||
}
|
}
|
||||||
|
recognizeViewModel.refreshFaceList()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,128 @@
|
|||||||
|
package com.sw.platecabinet.activity
|
||||||
|
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
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 kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
package com.sw.platecabinet.activity
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
@@ -6,14 +8,15 @@ import android.graphics.Bitmap
|
|||||||
import android.graphics.Point
|
import android.graphics.Point
|
||||||
import android.hardware.Camera
|
import android.hardware.Camera
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.CountDownTimer
|
|
||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.ViewTreeObserver
|
import android.view.ViewTreeObserver
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.lifecycle.Observer
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.arcsoft.face.ErrorInfo
|
import com.arcsoft.face.ErrorInfo
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.sw.inbound.utils.DateTimeUtils
|
import com.sw.inbound.utils.DateTimeUtils
|
||||||
import com.sw.plate.utils.Base64
|
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.CameraListener
|
||||||
import com.sw.plate.utils.arcface.camera.DualCameraHelper
|
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.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.CompareResult
|
||||||
import com.sw.plate.utils.arcface.face.model.FacePreviewInfo
|
import com.sw.plate.utils.arcface.face.model.FacePreviewInfo
|
||||||
import com.sw.plate.utils.arcface.face.model.RecognizeConfiguration
|
import com.sw.plate.utils.arcface.face.model.RecognizeConfiguration
|
||||||
@@ -39,9 +43,14 @@ import com.sw.platecabinet.MyApp
|
|||||||
import com.sw.platecabinet.R
|
import com.sw.platecabinet.R
|
||||||
import com.sw.platecabinet.databinding.ActivityLoginFaceBinding
|
import com.sw.platecabinet.databinding.ActivityLoginFaceBinding
|
||||||
import com.sw.platecabinet.databinding.ItemTitleTimeBinding
|
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.EquipmentUserInfo
|
||||||
import com.sw.platecabinet.model.response.UserFaceModel
|
import com.sw.platecabinet.model.response.UserFaceModel
|
||||||
import com.sw.platecabinet.utils.BitmapSaver
|
import com.sw.platecabinet.utils.BitmapSaver
|
||||||
|
import com.sw.platecabinet.utils.CountDownManager
|
||||||
|
import com.sw.platecabinet.utils.CountDownUtil
|
||||||
import com.sw.platecabinet.utils.Debouncer
|
import com.sw.platecabinet.utils.Debouncer
|
||||||
import com.sw.platecabinet.utils.LogFileUtil
|
import com.sw.platecabinet.utils.LogFileUtil
|
||||||
import com.sw.platecabinet.utils.PermissionHelper
|
import com.sw.platecabinet.utils.PermissionHelper
|
||||||
@@ -49,6 +58,8 @@ import com.sw.platecabinet.utils.PlateUtils
|
|||||||
import com.sw.platecabinet.utils.RegisterCallbackHandler
|
import com.sw.platecabinet.utils.RegisterCallbackHandler
|
||||||
import com.sw.platecabinet.utils.SoundPoolUtil
|
import com.sw.platecabinet.utils.SoundPoolUtil
|
||||||
import com.sw.platecabinet.utils.SpTool
|
import com.sw.platecabinet.utils.SpTool
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
import org.greenrobot.eventbus.Subscribe
|
import org.greenrobot.eventbus.Subscribe
|
||||||
import org.greenrobot.eventbus.ThreadMode
|
import org.greenrobot.eventbus.ThreadMode
|
||||||
@@ -61,8 +72,6 @@ import java.util.concurrent.atomic.AtomicBoolean
|
|||||||
*/
|
*/
|
||||||
class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
||||||
ViewTreeObserver.OnGlobalLayoutListener {
|
ViewTreeObserver.OnGlobalLayoutListener {
|
||||||
// private val recognizeViewModel by viewModels<RecognizeViewModel>()
|
|
||||||
private var countDownTimer: CountDownTimer? = null
|
|
||||||
|
|
||||||
private val CAMERA_PERMISSION_REQUEST_CODE = 100
|
private val CAMERA_PERMISSION_REQUEST_CODE = 100
|
||||||
private val REQUIRED_PERMISSIONS: Array<String> = arrayOf(
|
private val REQUIRED_PERMISSIONS: Array<String> = arrayOf(
|
||||||
@@ -81,64 +90,22 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "LoginByFaceActivity"
|
private const val TAG = "LoginByFaceActivity"
|
||||||
private var instance: LoginByFaceActivity? = null
|
|
||||||
|
|
||||||
private const val COLLECT_SUCCESS = "collect_success"
|
private const val COLLECT_SUCCESS = "collect_success"
|
||||||
private const val RECOGNIZE_SUCCESS = "recognize_success"
|
private const val RECOGNIZE_SUCCESS = "recognize_success"
|
||||||
private const val NO_PLATE_REMIND = "no_plate_remind";
|
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() {
|
override fun initialize() {
|
||||||
instance = this
|
|
||||||
userViewModel.activeEngine()
|
userViewModel.activeEngine()
|
||||||
// addSocketListener()
|
|
||||||
initCountTime()
|
|
||||||
initArcViewModel()
|
initArcViewModel()
|
||||||
initArcView()
|
initArcView()
|
||||||
openRectInfoDraw = true
|
openRectInfoDraw = true
|
||||||
recognizeViewModel.setDrawRectInfoTextValue(true)
|
recognizeViewModel.setDrawRectInfoTextValue(true)
|
||||||
//viewModel.generateToken()
|
|
||||||
|
|
||||||
// binding.llToPwd.setOnClickListener {
|
|
||||||
// val intent = Intent(this, LoginByPwdActivity::class.java)
|
|
||||||
// startActivity(intent)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TaskManager.startTask()
|
|
||||||
|
|
||||||
EventBus.getDefault().register(this)
|
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 {
|
startFaceTask {
|
||||||
loadLogInfo("collectFace增量接口数据用户id:${Gson().toJson(it)}")
|
loadLogInfo("collectFace增量接口数据用户id:${Gson().toJson(it)}")
|
||||||
@@ -148,7 +115,22 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
soundMap[COLLECT_SUCCESS] = R.raw.collect_success
|
soundMap[COLLECT_SUCCESS] = R.raw.collect_success
|
||||||
soundMap[RECOGNIZE_SUCCESS] = R.raw.recognize_success
|
soundMap[RECOGNIZE_SUCCESS] = R.raw.recognize_success
|
||||||
soundMap[NO_PLATE_REMIND] = R.raw.no_plate_remind
|
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,46 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
private var rgbFaceRectTransformer: FaceRectTransformer? = null
|
private var rgbFaceRectTransformer: FaceRectTransformer? = null
|
||||||
private val livenessType = LivenessType.RGB
|
private val livenessType = LivenessType.RGB
|
||||||
private var openRectInfoDraw = false
|
private var openRectInfoDraw = false
|
||||||
private val regDebouncer = Debouncer(3000)
|
|
||||||
|
private val leftDebouncer = Debouncer(30*1000)
|
||||||
|
|
||||||
private var registerHandler: RegisterCallbackHandler? = null
|
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() {
|
private fun initArcViewModel() {
|
||||||
recognizeViewModel.setLiveType(livenessType)
|
recognizeViewModel.setLiveType(livenessType)
|
||||||
recognizeViewModel.prepareRegister()
|
recognizeViewModel.prepareRegister()
|
||||||
recognizeViewModel.setOnRegisterFinishedCallback { facePreviewInfo, userFaceInfo ->
|
recognizeViewModel.setOnRegisterFinishedCallback { facePreviewInfo, userFaceInfo ->
|
||||||
registerHandler?.setCallback {
|
registerHandler?.setCallback {
|
||||||
if (!isCollectFace) {
|
if (!isCollectFace || !isCollecting.get()) {
|
||||||
|
return@setCallback
|
||||||
|
}
|
||||||
|
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
|
return@setCallback
|
||||||
}
|
}
|
||||||
if (this.userFaceInfo == null && userFaceInfo != null) {
|
|
||||||
recognizeViewModel.updateRegisterStatus(RecognizeViewModel.REGISTER_STATUS_DONE)
|
recognizeViewModel.updateRegisterStatus(RecognizeViewModel.REGISTER_STATUS_DONE)
|
||||||
this.userFaceInfo = userFaceInfo
|
this.userFaceInfo = userFaceInfo
|
||||||
getFaceDataBlock { faceData, headBmp ->
|
getFaceDataBlock()
|
||||||
isCollecting.set(false)
|
|
||||||
isCollectFace = false
|
|
||||||
sendFaceData(faceData, headBmp)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
recognizeViewModel.ftInitCode.observe(this) { ftInitCode: Int? ->
|
||||||
recognizeViewModel.ftInitCode.observe(this, Observer { ftInitCode: Int? ->
|
|
||||||
if (ftInitCode != ErrorInfo.MOK) {
|
if (ftInitCode != ErrorInfo.MOK) {
|
||||||
val error: String? = getString(
|
val error: String? = getString(
|
||||||
R.string.specific_engine_init_failed, "ftEngine",
|
R.string.specific_engine_init_failed, "ftEngine",
|
||||||
@@ -215,8 +214,8 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
Timber.e("ftInitCode observe = $error")
|
Timber.e("ftInitCode observe = $error")
|
||||||
ToastUtils.showToast(error)
|
ToastUtils.showToast(error)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
recognizeViewModel.frInitCode.observe(this, Observer { frInitCode: Int? ->
|
recognizeViewModel.frInitCode.observe(this) { frInitCode: Int? ->
|
||||||
if (frInitCode != ErrorInfo.MOK) {
|
if (frInitCode != ErrorInfo.MOK) {
|
||||||
val error: String? = getString(
|
val error: String? = getString(
|
||||||
R.string.specific_engine_init_failed, "frEngine",
|
R.string.specific_engine_init_failed, "frEngine",
|
||||||
@@ -225,8 +224,8 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
Timber.e("frInitCode observe = $error")
|
Timber.e("frInitCode observe = $error")
|
||||||
ToastUtils.showToast(error)
|
ToastUtils.showToast(error)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
recognizeViewModel.flInitCode.observe(this, Observer { flInitCode: Int? ->
|
recognizeViewModel.flInitCode.observe(this) { flInitCode: Int? ->
|
||||||
if (flInitCode != ErrorInfo.MOK) {
|
if (flInitCode != ErrorInfo.MOK) {
|
||||||
val error: String? = getString(
|
val error: String? = getString(
|
||||||
R.string.specific_engine_init_failed, "flEngine",
|
R.string.specific_engine_init_failed, "flEngine",
|
||||||
@@ -235,34 +234,25 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
Timber.e("flInitCode observe = $error")
|
Timber.e("flInitCode observe = $error")
|
||||||
ToastUtils.showToast(error)
|
ToastUtils.showToast(error)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
recognizeViewModel.recognizeConfiguration
|
recognizeViewModel.recognizeConfiguration
|
||||||
.observe(this, Observer { recognizeConfiguration: RecognizeConfiguration? ->
|
.observe(this) { recognizeConfiguration: RecognizeConfiguration? ->
|
||||||
Timber.i("recognizeConfiguration: observe = ${recognizeConfiguration.toString()}")
|
Timber.i("recognizeConfiguration: observe = ${recognizeConfiguration.toString()}")
|
||||||
})
|
}
|
||||||
recognizeViewModel.recognizeNotice.observe(this, Observer { notice: String? ->
|
recognizeViewModel.recognizeNotice.observe(this) { notice: String? ->
|
||||||
Timber.i("recognizeNotice observe notice = $notice")
|
Timber.i("recognizeNotice observe notice = $notice")
|
||||||
})
|
}
|
||||||
|
|
||||||
recognizeViewModel.recognizeUserId.observe(this, Observer { result: CompareResult? ->
|
recognizeViewModel.recognizeUserId.observe(this) { result: CompareResult? ->
|
||||||
loadLogInfo("collectFace,recognizeUserId observe userId = ${result?.faceEntity?.userName}")
|
loadLogInfo("collectFace,recognizeUserId observe userId = ${result?.faceEntity?.userName}")
|
||||||
if (faceUseSwitch) {
|
loadFaceRecognizeResult3(result)
|
||||||
loadFaceRecognizeResult2(result)
|
|
||||||
return@Observer
|
|
||||||
}
|
}
|
||||||
loadFaceRecognizeResult(result)
|
|
||||||
})
|
|
||||||
|
|
||||||
recognizeViewModel.drawRectInfoText.observe(this, Observer { info ->
|
recognizeViewModel.drawRectInfoText.observe(this) { info ->
|
||||||
Timber.i("drawRectInfoText observe info = $info")
|
Timber.i("drawRectInfoText observe info = $info")
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
* 人脸识别开关,true-loadFaceRecognizeResult2,false-loadFaceRecognizeResult
|
|
||||||
*/
|
|
||||||
private var faceUseSwitch = true
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 识别失败重试次数
|
* 识别失败重试次数
|
||||||
@@ -274,37 +264,51 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
*/
|
*/
|
||||||
private val maxFailCount = 5
|
private val maxFailCount = 5
|
||||||
|
|
||||||
/**
|
|
||||||
* 人脸失败阈值
|
|
||||||
*/
|
|
||||||
private val faceFailThreshold = 0.3
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 配置阈值,默认值为0.8
|
* 配置阈值,默认值为0.8
|
||||||
*/
|
*/
|
||||||
private val faceSuccessThreshold by lazy { ConfigUtil.getRecognizeThreshold(this) }
|
private val faceSuccessThreshold by lazy { ConfigUtil.getRecognizeThreshold(this) }
|
||||||
|
|
||||||
|
|
||||||
private val openPlateDebouncer = Debouncer(5000)
|
private val openPlateDebouncer = Debouncer(5000)
|
||||||
private val isCollecting = AtomicBoolean(false)
|
private val isCollecting = AtomicBoolean(false)
|
||||||
|
|
||||||
/**
|
private fun loadFaceRecognizeResult3(result: CompareResult?) {
|
||||||
* 根据相似度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
|
val similar = result?.similar ?: return
|
||||||
|
|
||||||
binding.tvSimilarValue.text = "$similar,${retryFailCount}次"
|
binding.tvSimilarValue.text = "$similar,${retryFailCount}次"
|
||||||
loadLogInfo(
|
loadLogInfo(
|
||||||
"collectFace,recognizeUserId 阈值 success=$faceSuccessThreshold, " +
|
"collectFace,recognizeUserId 阈值 success=$faceSuccessThreshold, similar=$similar, retry=$retryFailCount"
|
||||||
"fail=$faceFailThreshold, similar=$similar, retry=$retryFailCount"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (similar >= faceSuccessThreshold) {
|
if (similar >= faceSuccessThreshold) {
|
||||||
val userId = result.faceEntity?.userName
|
val userId = result.faceEntity?.userName
|
||||||
|
recognizeSuccess(userId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCollectFace || isCollecting.get()) {
|
||||||
|
loadLogInfo("collectFace,recognizeUserId 正在采集中,忽略失败结果 similar=$similar")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retryFailCount >= maxFailCount) {
|
||||||
|
loadLogInfo(
|
||||||
|
"collectFace,recognizeUserId 识别失败,开始采集 similar=$similar, " +
|
||||||
|
"retry=$retryFailCount, time=${DateTimeUtils.getDateTimeString()}"
|
||||||
|
)
|
||||||
|
if (!isCollecting.compareAndSet(false, true)) {
|
||||||
|
loadLogInfo("collectFace 已在采集中,忽略重复触发")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
recognizeFail()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
retryFailCount++
|
||||||
|
loadLogInfo("collectFace,recognizeUserId 中间态重试,retryFailCount=$retryFailCount")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun recognizeSuccess(userId: String?) {
|
||||||
if (userId.isNullOrBlank()) {
|
if (userId.isNullOrBlank()) {
|
||||||
loadLogInfo("collectFace,recognizeUserId 识别成功但 userId 为空,忽略")
|
loadLogInfo("collectFace,recognizeUserId 识别成功但 userId 为空,忽略")
|
||||||
return
|
return
|
||||||
@@ -324,85 +328,19 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
openPlateDebouncer.debounce {
|
openPlateDebouncer.debounce {
|
||||||
openPlate(userId)
|
openPlate(userId)
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCollectFace) {
|
private fun recognizeFail() {
|
||||||
loadLogInfo("collectFace,recognizeUserId 正在采集中,忽略失败结果 similar=$similar")
|
CountDownManager.cancelCountDown()
|
||||||
return
|
binding.tvToInit.text = ""
|
||||||
|
binding.tvSimilarValue.text = ""
|
||||||
|
binding.llCollectButton.visible()
|
||||||
|
binding.btnRetry.visible()
|
||||||
|
binding.btnCollect.run {
|
||||||
|
visible()
|
||||||
|
text = "新用户采集"
|
||||||
}
|
}
|
||||||
|
binding.ivCollectPhoto.visible()
|
||||||
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) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -446,29 +384,31 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
item.updateTime = DateTimeUtils.getDateTimeString()
|
item.updateTime = DateTimeUtils.getDateTimeString()
|
||||||
item.plateNumber = null
|
item.plateNumber = null
|
||||||
plateEnable = false
|
plateEnable = false
|
||||||
Debouncer(5000).debounce {
|
openDebouncer.debounce {
|
||||||
//打开柜子
|
//打开柜子
|
||||||
PlateUtils.open(this, item.equipmentBoxCode, false)
|
PlateUtils.open(this, item.equipmentBoxCode, false) {
|
||||||
|
goInitActivity()
|
||||||
|
}
|
||||||
|
CountDownManager.cancelCountDown()
|
||||||
}
|
}
|
||||||
currentPlateInfo = item
|
currentPlateInfo = item
|
||||||
MyApp.plateList = items
|
MyApp.plateList = items
|
||||||
SpTool.savePlateData(items)
|
SpTool.savePlateData(items)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private val openDebouncer by lazy { Debouncer(5000) }
|
||||||
// private val debouncer by lazy { Debouncer(3000) }
|
|
||||||
|
|
||||||
private fun initArcView() {
|
private fun initArcView() {
|
||||||
//在布局结束后才做初始化操作
|
//在布局结束后才做初始化操作
|
||||||
binding.dualCameraTexturePreviewRgb.getViewTreeObserver().addOnGlobalLayoutListener(this)
|
binding.dualCameraTexturePreviewRgb.viewTreeObserver.addOnGlobalLayoutListener(this)
|
||||||
recognizeViewModel.getCompareResultList().getValue()
|
val list = recognizeViewModel.getCompareResultList().getValue()
|
||||||
|
loadLogInfo("initArcView:list.size=${list?.size}")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
loadLogInfo("onDestroy")
|
loadLogInfo("onDestroy")
|
||||||
instance = null
|
|
||||||
if (rgbCameraHelper != null) {
|
if (rgbCameraHelper != null) {
|
||||||
rgbCameraHelper!!.release()
|
rgbCameraHelper?.release()
|
||||||
rgbCameraHelper = null
|
rgbCameraHelper = null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -513,7 +453,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
layoutParams.width = (measuredHeight * ratio).toInt()
|
layoutParams.width = (measuredHeight * ratio).toInt()
|
||||||
}
|
}
|
||||||
if (scale < 1f) {
|
if (scale < 1f) {
|
||||||
val rgbParam = rgbPreview.getLayoutParams()
|
val rgbParam = rgbPreview.layoutParams
|
||||||
layoutParams.width = (rgbParam.width * scale).toInt()
|
layoutParams.width = (rgbParam.width * scale).toInt()
|
||||||
layoutParams.height = (rgbParam.height * scale).toInt()
|
layoutParams.height = (rgbParam.height * scale).toInt()
|
||||||
} else {
|
} else {
|
||||||
@@ -549,12 +489,13 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
isMirror: Boolean
|
isMirror: Boolean
|
||||||
) {
|
) {
|
||||||
loadLogInfo("initRgbCamera Rgb onCameraOpened: cameraId = $cameraId, displayOrientation = $displayOrientation")
|
loadLogInfo("initRgbCamera Rgb onCameraOpened: cameraId = $cameraId, displayOrientation = $displayOrientation")
|
||||||
runOnUiThread({
|
runOnUiThread {
|
||||||
val previewSizeRgb = camera.getParameters().getPreviewSize()
|
val previewSizeRgb = camera.parameters.previewSize
|
||||||
val layoutParams = adjustPreviewViewSize(
|
val layoutParams = adjustPreviewViewSize(
|
||||||
binding.dualCameraTexturePreviewRgb,
|
binding.dualCameraTexturePreviewRgb,
|
||||||
binding.dualCameraTexturePreviewRgb, binding.dualCameraFaceRectView,
|
binding.dualCameraTexturePreviewRgb, binding.dualCameraFaceRectView,
|
||||||
previewSizeRgb, displayOrientation, 0.6f
|
previewSizeRgb, displayOrientation, 0.7f
|
||||||
|
//0.6f
|
||||||
)
|
)
|
||||||
loadLogInfo("initRgbCamera previewSizeRgb = ${previewSizeRgb.width}-${previewSizeRgb.height}")
|
loadLogInfo("initRgbCamera previewSizeRgb = ${previewSizeRgb.width}-${previewSizeRgb.height}")
|
||||||
loadLogInfo("initRgbCamera layoutParams = ${layoutParams.width}-${layoutParams.height}")
|
loadLogInfo("initRgbCamera layoutParams = ${layoutParams.width}-${layoutParams.height}")
|
||||||
@@ -583,7 +524,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
|
|
||||||
recognizeViewModel.onRgbCameraOpened(camera)
|
recognizeViewModel.onRgbCameraOpened(camera)
|
||||||
recognizeViewModel.setRgbFaceRectTransformer(rgbFaceRectTransformer)
|
recognizeViewModel.setRgbFaceRectTransformer(rgbFaceRectTransformer)
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.Q)
|
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||||
@@ -600,6 +541,13 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
currentPlateInfo = null
|
currentPlateInfo = null
|
||||||
currentUserId = null
|
currentUserId = null
|
||||||
userFaceInfo = null
|
userFaceInfo = null
|
||||||
|
binding.btnCollect.tag = ""
|
||||||
|
binding.tvSimilarValue.text = ""
|
||||||
|
retryFailCount = 0
|
||||||
|
|
||||||
|
if (binding.llCollectButton.isVisible) {
|
||||||
|
leftDebouncer.debounce { leftCountDown() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
recognizeViewModel.clearLeftFace(facePreviewInfoList)
|
recognizeViewModel.clearLeftFace(facePreviewInfoList)
|
||||||
}
|
}
|
||||||
@@ -638,8 +586,8 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
.previewOn(binding.dualCameraTexturePreviewRgb)
|
.previewOn(binding.dualCameraTexturePreviewRgb)
|
||||||
.cameraListener(cameraListener)
|
.cameraListener(cameraListener)
|
||||||
.build()
|
.build()
|
||||||
rgbCameraHelper!!.init()
|
rgbCameraHelper?.init()
|
||||||
rgbCameraHelper!!.start()
|
rgbCameraHelper?.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -665,28 +613,60 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
|
|
||||||
override fun onGlobalLayout() {
|
override fun onGlobalLayout() {
|
||||||
loadLogInfo("onGlobalLayout")
|
loadLogInfo("onGlobalLayout")
|
||||||
binding.dualCameraTexturePreviewRgb.getViewTreeObserver().removeOnGlobalLayoutListener(this)
|
binding.dualCameraTexturePreviewRgb.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||||
// 请求权限
|
// 请求权限
|
||||||
checkCameraPermission()
|
checkCameraPermission()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resumeCamera() {
|
private fun resumeCamera() {
|
||||||
isRecognition = true
|
// isRecognition = true
|
||||||
if (rgbCameraHelper != null && rgbCameraHelper!!.isStopped) {
|
if (rgbCameraHelper != null && rgbCameraHelper!!.isStopped) {
|
||||||
loadLogInfo("collectFace,resumeCamera: ")
|
loadLogInfo("collectFace,resumeCamera: ")
|
||||||
rgbCameraHelper!!.start()
|
isRecognition = true
|
||||||
|
rgbCameraHelper?.start()
|
||||||
} else {
|
} 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)
|
||||||
|
// 传递“空帧” recognizeViewModel.onPreviewFrame(emptyFrame, true)
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 临时用户id
|
||||||
|
// */
|
||||||
|
// private var tempUserId: String = ""
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
registerHandler = RegisterCallbackHandler()
|
|
||||||
try {
|
try {
|
||||||
if (isFirstOpen) {
|
if (isFirstOpen) {
|
||||||
goInitActivity()
|
goInitActivity()
|
||||||
@@ -695,56 +675,75 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
}, 1000)
|
}, 1000)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
resetRecognizeState()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置识别状态
|
||||||
|
*/
|
||||||
|
private fun resetRecognizeState() {
|
||||||
|
binding.ivCollectPhoto.gone()
|
||||||
|
binding.ivCollectPhoto.setImageDrawable(null)
|
||||||
|
binding.tvToInit.text = "15"
|
||||||
|
binding.llCollectButton.gone()
|
||||||
retryFailCount = 0
|
retryFailCount = 0
|
||||||
binding.tvSimilarValue.text = ""
|
binding.tvSimilarValue.text = ""
|
||||||
isCollecting.set(false)
|
isCollecting.set(false)
|
||||||
isCollectFace = false
|
isCollectFace = false
|
||||||
plateEnable = true
|
plateEnable = true
|
||||||
collectCount = 0
|
collectCount = 0
|
||||||
tempUserId = "${System.currentTimeMillis()}"
|
// tempUserId = "${System.currentTimeMillis()}"
|
||||||
resumeCamera()
|
resumeCamera()
|
||||||
userViewModel.resetUserInfo()
|
registerHandler = RegisterCallbackHandler()
|
||||||
countDownTimer?.let {
|
|
||||||
it.cancel()
|
CountDownManager.startCountDown(
|
||||||
it.start()
|
totalSeconds = 15,
|
||||||
|
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()
|
||||||
}
|
}
|
||||||
// initFaceScheduler()
|
|
||||||
// checkFaceState()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var isFirstOpen = true
|
private var isFirstOpen = true
|
||||||
|
|
||||||
protected override fun onPause() {
|
override fun onPause() {
|
||||||
pauseCamera()
|
pauseCamera()
|
||||||
super.onPause()
|
super.onPause()
|
||||||
countDownTimer?.cancel()
|
CountDownManager.cancelCountDown()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun pauseCamera() {
|
private fun pauseCamera() {
|
||||||
isRecognition = false
|
isRecognition = false
|
||||||
recognizeViewModel.onPreviewFrame(ByteArray(1382400), true)
|
// recognizeViewModel.onPreviewFrame(emptyFrame, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handleLoginSuccess(equipmentUserInfo: EquipmentUserInfo, isAdmin: Boolean) {
|
private fun judgeRecognizeState(): Boolean {
|
||||||
super.handleLoginSuccess(equipmentUserInfo, isAdmin)
|
val rectColor = binding.dualCameraFaceRectView.rectColor
|
||||||
// goInitActivity()
|
if (rectColor == RecognizeColor.COLOR_SUCCESS && currentUserId.isNullOrBlank().not() && facePreviewInfoList.isNullOrEmpty().not()) {
|
||||||
}
|
//人未离开,再次识别
|
||||||
|
loadLogInfo("userId=$currentUserId")
|
||||||
private val totalTimeInMillis = 15 * 1000L
|
openPlateDebouncer.debounce {
|
||||||
fun initCountTime() {
|
openPlate(currentUserId)
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun goInitActivity() {
|
fun goInitActivity() {
|
||||||
@@ -785,12 +784,6 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
uploadFaceDebouncer.debounce {
|
uploadFaceDebouncer.debounce {
|
||||||
addUserFace(faceData, imageFile)
|
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) {
|
private fun addUserFace(faceData: String, imageFile: File) {
|
||||||
@@ -806,23 +799,33 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
loadLogInfo("collectFace,sendFaceData: ----------12,发送人脸数据成功但接口返回为null,faceData=${faceData}")
|
loadLogInfo("collectFace,sendFaceData: ----------12,发送人脸数据成功但接口返回为null,faceData=${faceData}")
|
||||||
return@addUserFace
|
return@addUserFace
|
||||||
}
|
}
|
||||||
Thread {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
saveFaceInfo(faceModel)
|
saveFaceInfo(faceModel) {
|
||||||
|
runOnUiThread {
|
||||||
|
Glide.with(this@LoginByFaceActivity).load(imageFile).into(binding.ivCollectPhoto)
|
||||||
|
}
|
||||||
|
}
|
||||||
uploadFaceDebouncer.reset()
|
uploadFaceDebouncer.reset()
|
||||||
}.start()
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getFaceDataBlock(block: (String, Bitmap) -> Unit) {
|
private fun getFaceDataBlock() {
|
||||||
if (userFaceInfo == null || userFaceInfo?.faceFeature?.featureData == null) {
|
if (userFaceInfo?.faceFeature?.featureData == null) {
|
||||||
|
loadLogInfo("collectFace,getFaceData: ----------7,拿到人脸提取特征数据为null-----------")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val faceData: String? = Base64.encode(userFaceInfo?.faceFeature?.featureData)
|
val faceData: String? = Base64.encode(userFaceInfo?.faceFeature?.featureData)
|
||||||
if (faceData.isNullOrBlank()) {
|
if (faceData.isNullOrBlank()) {
|
||||||
|
loadLogInfo("collectFace,getFaceData: ----------7,拿到人脸提取特征数据Base64编码为空")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
loadLogInfo("collectFace,getFaceData: ----------7,拿到人脸提取特征数据时间:${DateTimeUtils.getDateTimeString()}-----------")
|
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 +842,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
|
|
||||||
private var currentUserId: String? = null
|
private var currentUserId: String? = null
|
||||||
|
|
||||||
private fun saveFaceInfo(faceModel: UserFaceModel) {
|
private fun saveFaceInfo(faceModel: UserFaceModel, block: () -> Unit = {}) {
|
||||||
try {
|
try {
|
||||||
loadLogInfo("collectFace,saveFaceInfo: ----------8,保存人脸取特征数据-----------")
|
loadLogInfo("collectFace,saveFaceInfo: ----------8,保存人脸取特征数据-----------")
|
||||||
val faceDao = FaceDatabase.getInstance(this).faceDao()
|
val faceDao = FaceDatabase.getInstance(this).faceDao()
|
||||||
@@ -855,9 +858,10 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
loadLogInfo("collectFace,saveFaceInfo: ----------10,人脸数据保存完成-----------")
|
loadLogInfo("collectFace,saveFaceInfo: ----------10,人脸数据保存完成-----------")
|
||||||
recognizeViewModel.refreshFaceList()
|
recognizeViewModel.refreshFaceList()
|
||||||
loadLogInfo("collectFace,saveFaceInfo: ----------11,刷新人脸数据-----------")
|
loadLogInfo("collectFace,saveFaceInfo: ----------11,刷新人脸数据-----------")
|
||||||
runOnUiThread { recognizeViewModel.onPreviewFrame(ByteArray(1382400), true) }
|
runOnUiThread { recognizeViewModel.onPreviewFrame(emptyFrame, true) }
|
||||||
loadLogInfo("---------------------------------")
|
loadLogInfo("---------------------------------")
|
||||||
collectCount = 0
|
collectCount = 0
|
||||||
|
block()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
loadLogInfo("collectFace,saveFaceInfo: ----------13,人脸采集异常:${e.message}-----------")
|
loadLogInfo("collectFace,saveFaceInfo: ----------13,人脸采集异常:${e.message}-----------")
|
||||||
@@ -869,78 +873,25 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
|
|
||||||
private var collectCount = 0
|
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 var isCollectFace = false
|
||||||
|
private fun leftCountDown() {
|
||||||
|
CountDownUtil.startCountDown(total = 15, lifecycleScope = lifecycleScope, onStart = {
|
||||||
|
loadLogInfo("onPreview-----------leftCountDown-------------")
|
||||||
|
}, onTick = {
|
||||||
|
loadLogInfo("leftCountDown.onTick:$it")
|
||||||
|
if (facePreviewInfoList.isNullOrEmpty().not()) {
|
||||||
|
loadLogInfo("leftCountDown.onTick-cancel()")
|
||||||
|
CountDownUtil.cancelCountDown()
|
||||||
|
return@startCountDown
|
||||||
|
}
|
||||||
|
}, onFinish = {
|
||||||
|
if (facePreviewInfoList.isNullOrEmpty().not()) {
|
||||||
|
loadLogInfo("leftCountDown.onFinish-cancel()")
|
||||||
|
CountDownUtil.cancelCountDown()
|
||||||
|
return@startCountDown
|
||||||
|
}
|
||||||
|
CountDownUtil.cancelCountDown()
|
||||||
|
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() {
|
override fun onRightDoubleClick() {
|
||||||
if (pageType == PageType.SETTING_LIST) {
|
if (pageType == PageType.SETTING_LIST) {
|
||||||
loadClearDialog()
|
startActivity(Intent(this, FunActivity::class.java))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,25 +104,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
|||||||
super.registerDataChange()
|
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -70,3 +70,35 @@ fun View.clickWithCoroutines(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//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 // 恢复可点击
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,35 +12,27 @@ import timber.log.Timber
|
|||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
object ApiClient {
|
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)
|
.connectTimeout(TIME_OUT, TimeUnit.SECONDS)
|
||||||
.readTimeout(TIME_OUT, TimeUnit.SECONDS)
|
.readTimeout(TIME_OUT, TimeUnit.SECONDS)
|
||||||
.writeTimeout(TIME_OUT, TimeUnit.SECONDS)
|
.writeTimeout(TIME_OUT, TimeUnit.SECONDS)
|
||||||
.addNetworkInterceptor(HttpLoggingInterceptor(logger = {
|
.addNetworkInterceptor(HttpLoggingInterceptor { Timber.d("okhttp ==>${it}") }.apply {
|
||||||
Timber.d("okhttp logger ==>${it}")
|
level = if (MyApp.DEBUG) HttpLoggingInterceptor.Level.BODY
|
||||||
}).apply {
|
else HttpLoggingInterceptor.Level.NONE
|
||||||
level = if (MyApp.DEBUG) {
|
|
||||||
HttpLoggingInterceptor.Level.BODY
|
|
||||||
} else {
|
|
||||||
HttpLoggingInterceptor.Level.NONE
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.addInterceptor(RequestInterceptor())
|
.addInterceptor(RequestInterceptor())
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
private val retrofit = Retrofit.Builder()
|
private val retrofit by lazy {
|
||||||
.baseUrl(DEVICE_BASE_URL)
|
Retrofit.Builder()
|
||||||
|
.baseUrl(GlobalData.appBaseUrl)
|
||||||
.client(okHttpClient)
|
.client(okHttpClient)
|
||||||
.addConverterFactory(GsonConverterFactory.create())
|
.addConverterFactory(GsonConverterFactory.create())
|
||||||
// .addCallAdapterFactory(CoroutineCallAdapterFactory()) // 协程适配器
|
|
||||||
.build()
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
val apiService: ApiService by lazy {
|
val apiService: ApiService by lazy {
|
||||||
retrofit.create(ApiService::class.java)
|
retrofit.create(ApiService::class.java)
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
package com.sw.platecabinet.utils
|
||||||
|
|
||||||
|
import android.os.CountDownTimer
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import android.os.Message
|
||||||
|
import android.util.Log
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 无状态残留的CountDownTimer封装(解决时间残留问题)
|
||||||
|
* @param millisInFuture 总时长(毫秒)
|
||||||
|
* @param countDownInterval 倒计时间隔(毫秒)
|
||||||
|
* @param onTick 倒计时回调(剩余毫秒)
|
||||||
|
* @param onFinish 结束回调
|
||||||
|
*/
|
||||||
|
class SafeCountDownTimer(
|
||||||
|
private val millisInFuture: Long,
|
||||||
|
private val countDownInterval: Long,
|
||||||
|
private val onTick: (Long) -> Unit,
|
||||||
|
private val onFinish: () -> Unit
|
||||||
|
) {
|
||||||
|
// 独立Handler,避免与其他CountDownTimer复用消息队列
|
||||||
|
private val handler = object : Handler(Looper.getMainLooper()) {
|
||||||
|
override fun handleMessage(msg: Message) {
|
||||||
|
synchronized(this@SafeCountDownTimer) {
|
||||||
|
// 仅当timer未取消时处理消息,避免残留消息触发
|
||||||
|
if (!isCancelled) {
|
||||||
|
super.handleMessage(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 核心:标记是否已取消,避免残留消息触发
|
||||||
|
@Volatile
|
||||||
|
private var isCancelled = false
|
||||||
|
// 实际的CountDownTimer实例
|
||||||
|
private var timer: CountDownTimer? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动倒计时(启动前自动清空旧状态)
|
||||||
|
*/
|
||||||
|
fun start() {
|
||||||
|
// 第一步:强制取消旧实例+清空所有残留消息(核心!)
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
// 第二步:重置取消标记
|
||||||
|
isCancelled = false
|
||||||
|
|
||||||
|
// 第三步:新建CountDownTimer实例,绑定独立Handler
|
||||||
|
timer = object : CountDownTimer(millisInFuture, countDownInterval) {
|
||||||
|
override fun onTick(millisUntilFinished: Long) {
|
||||||
|
// 双重校验:仅未取消时触发回调
|
||||||
|
if (!isCancelled) {
|
||||||
|
onTick.invoke(millisUntilFinished)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFinish() {
|
||||||
|
if (!isCancelled) {
|
||||||
|
onFinish.invoke()
|
||||||
|
// 结束后主动清空,避免残留
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第四步:启动新实例
|
||||||
|
timer?.start()
|
||||||
|
Log.d("SafeCountDownTimer", "倒计时启动,总时长:$millisInFuture 毫秒")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消倒计时(彻底清空所有状态)
|
||||||
|
*/
|
||||||
|
fun cancel() {
|
||||||
|
// 标记为已取消,拦截后续消息
|
||||||
|
isCancelled = true
|
||||||
|
|
||||||
|
// 取消timer实例
|
||||||
|
timer?.cancel()
|
||||||
|
timer = null
|
||||||
|
|
||||||
|
// 清空Handler所有残留消息(核心:解决旧消息触发问题)
|
||||||
|
handler.removeCallbacksAndMessages(null)
|
||||||
|
|
||||||
|
Log.d("SafeCountDownTimer", "倒计时已取消,清空所有残留状态")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 封装成工具类,方便全局调用 ==========
|
||||||
|
object CountDownManager {
|
||||||
|
// 保存当前倒计时实例,避免重复启动
|
||||||
|
private var currentTimer: SafeCountDownTimer? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动/重启倒计时(适配人脸识别防抖场景)
|
||||||
|
* @param totalSeconds 总秒数(如15)
|
||||||
|
* @param onTick 每秒回调剩余秒数
|
||||||
|
* @param onFinish 结束回调
|
||||||
|
*/
|
||||||
|
fun startCountDown(
|
||||||
|
totalSeconds: Int = 15,
|
||||||
|
onTick: (Int) -> Unit = {},
|
||||||
|
onFinish: () -> Unit
|
||||||
|
) {
|
||||||
|
// 先取消旧倒计时
|
||||||
|
cancelCountDown()
|
||||||
|
|
||||||
|
// 新建安全倒计时实例
|
||||||
|
currentTimer = SafeCountDownTimer(
|
||||||
|
millisInFuture = totalSeconds * 1000L,
|
||||||
|
countDownInterval = 1000L,
|
||||||
|
onTick = { millis ->
|
||||||
|
// 转换为秒数回调
|
||||||
|
val sec = (millis / 1000).toInt() + 1
|
||||||
|
onTick.invoke(sec)
|
||||||
|
},
|
||||||
|
onFinish = {
|
||||||
|
onFinish.invoke()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 启动倒计时
|
||||||
|
currentTimer?.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消当前倒计时
|
||||||
|
*/
|
||||||
|
fun cancelCountDown() {
|
||||||
|
currentTimer?.cancel()
|
||||||
|
currentTimer = null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package com.sw.platecabinet.utils
|
||||||
|
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.flow.*
|
||||||
|
import android.util.Log
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onCompletion
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.flow.onStart
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
import kotlin.ranges.downTo
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 人脸识别防抖倒计时工具类(单例+防重复+无死循环)
|
||||||
|
*/
|
||||||
|
object CountDownUtil {
|
||||||
|
// 原子布尔值:标记是否正在倒计时(防重复启动)
|
||||||
|
private val isCounting = AtomicBoolean(false)
|
||||||
|
|
||||||
|
// 当前倒计时Job(用于取消)
|
||||||
|
private var currentJob: Job? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动/重启防抖倒计时(同一时间仅一个倒计时运行)
|
||||||
|
* @param totalSeconds 倒计时总秒数(如15)
|
||||||
|
* @param lifecycleScope Activity/Fragment的lifecycleScope(必须)
|
||||||
|
* @param onTick 每秒回调剩余秒数(主线程)
|
||||||
|
* @param onFinish 倒计时结束回调(主线程)
|
||||||
|
*/
|
||||||
|
fun startCountDown(
|
||||||
|
total: Int = 15,
|
||||||
|
lifecycleScope: CoroutineScope,
|
||||||
|
onStart: () -> Unit = {},
|
||||||
|
onTick: (Int) -> Unit = {},
|
||||||
|
onFinish: () -> Unit
|
||||||
|
) {
|
||||||
|
// 1. 防重复:如果正在倒计时,先取消旧任务
|
||||||
|
if (isCounting.get()) {
|
||||||
|
//cancelCountDown()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 标记为正在倒计时
|
||||||
|
isCounting.set(true)
|
||||||
|
|
||||||
|
// 3. 启动新倒计时
|
||||||
|
currentJob = flow {
|
||||||
|
Log.d("CountDownUtil", "倒计时启动,总时长:$total 秒")
|
||||||
|
for (i in total downTo 1) {
|
||||||
|
emit(i)
|
||||||
|
delay(1000) // IO线程等待1秒,无死循环
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.flowOn(Dispatchers.IO) // 上游IO线程,避免阻塞主线程
|
||||||
|
.onStart {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
onStart.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onEach { remaining ->
|
||||||
|
// 主线程回调剩余时间
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
onTick.invoke(remaining)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onCompletion {
|
||||||
|
// 倒计时结束:重置状态+回调
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
isCounting.set(false) // 重置标记
|
||||||
|
currentJob = null // 置空Job
|
||||||
|
Log.d("CountDownUtil", "倒计时正常结束")
|
||||||
|
onFinish.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.catch { e ->
|
||||||
|
// 捕获异常:避免崩溃,重置状态
|
||||||
|
Log.e("CountDownUtil", "倒计时异常:${e.message}", e)
|
||||||
|
isCounting.set(false)
|
||||||
|
currentJob = null
|
||||||
|
}
|
||||||
|
.launchIn(lifecycleScope)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消倒计时(手动停止)
|
||||||
|
*/
|
||||||
|
fun cancelCountDown() {
|
||||||
|
currentJob?.cancel() // 取消Job
|
||||||
|
currentJob = null // 置空,避免残留
|
||||||
|
isCounting.set(false) // 重置标记
|
||||||
|
Log.d("CountDownUtil", "倒计时被手动取消")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,8 @@ object PlateUtils {
|
|||||||
fragmentActivity: FragmentActivity,
|
fragmentActivity: FragmentActivity,
|
||||||
code: String?,
|
code: String?,
|
||||||
isScanCode: Boolean,
|
isScanCode: Boolean,
|
||||||
isSetting: Boolean = false
|
isSetting: Boolean = false,
|
||||||
|
callback: () -> Unit = {}
|
||||||
) {
|
) {
|
||||||
val activity = fragmentActivity as BaseActivity<*>
|
val activity = fragmentActivity as BaseActivity<*>
|
||||||
Timber.d("itemClick llOpen equipmentBoxCode = ${code}")
|
Timber.d("itemClick llOpen equipmentBoxCode = ${code}")
|
||||||
@@ -36,15 +37,30 @@ object PlateUtils {
|
|||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
ToastUtils.showToast("柜门打开成功")
|
ToastUtils.showToast("柜门打开成功")
|
||||||
|
callback()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFail(e: Exception?) {
|
override fun onFail(e: Exception?) {
|
||||||
activity.hideWaitingDialog()
|
activity.hideWaitingDialog()
|
||||||
ToastUtils.showToast("柜门打开失败")
|
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?) {
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
<?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" />
|
||||||
|
</LinearLayout>
|
||||||
@@ -105,6 +105,46 @@
|
|||||||
android:textSize="36sp"
|
android:textSize="36sp"
|
||||||
tools:text="30" />
|
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-->
|
<!-- <LinearLayout-->
|
||||||
<!-- android:layout_width="wrap_content"-->
|
<!-- android:layout_width="wrap_content"-->
|
||||||
<!-- android:layout_height="wrap_content"-->
|
<!-- android:layout_height="wrap_content"-->
|
||||||
@@ -173,6 +213,14 @@
|
|||||||
<!-- android:src="@drawable/ic_log_show"-->
|
<!-- android:src="@drawable/ic_log_show"-->
|
||||||
<!-- android:textSize="16sp" />-->
|
<!-- 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>
|
</FrameLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -62,6 +62,6 @@ dependencies {
|
|||||||
implementation("com.google.code.gson:gson:2.8.6")
|
implementation("com.google.code.gson:gson:2.8.6")
|
||||||
|
|
||||||
val glideVersion = "4.12.0"
|
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")
|
annotationProcessor("com.github.bumptech.glide:compiler:$glideVersion")
|
||||||
}
|
}
|
||||||
@@ -46,6 +46,10 @@ public class FaceRectView extends View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getRectColor() {
|
||||||
|
return paint.getColor();
|
||||||
|
}
|
||||||
|
|
||||||
public void clearFaceInfo() {
|
public void clearFaceInfo() {
|
||||||
drawInfoList.clear();
|
drawInfoList.clear();
|
||||||
postInvalidate();
|
postInvalidate();
|
||||||
|
|||||||
@@ -97,4 +97,11 @@ public interface FaceDao {
|
|||||||
*/
|
*/
|
||||||
@Query("DELETE from face WHERE user_type = '2'")
|
@Query("DELETE from face WHERE user_type = '2'")
|
||||||
int deleteTempUserFaceData();
|
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