Compare commits
18
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86c3c07e42 | ||
|
|
aa96455f66 | ||
|
|
786f2a123c | ||
|
|
f2a6de55b9 | ||
|
|
e51864d400 | ||
|
|
e63d3f7624 | ||
|
|
0a110020b9 | ||
|
|
f99538890c | ||
|
|
6d8ca7c83f | ||
|
|
c6bcdc03ba | ||
|
|
3b88b011a3 | ||
|
|
b96f9872cb | ||
|
|
eef13ff56e | ||
|
|
6c937c5f5f | ||
|
|
513f4dbaea | ||
|
|
037816c303 | ||
|
|
dca9496bee | ||
|
|
08f1963054 |
@@ -14,6 +14,7 @@ import androidx.activity.enableEdgeToEdge
|
|||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.viewbinding.ViewBinding
|
import androidx.viewbinding.ViewBinding
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.sw.inbound.utils.DateTimeUtils
|
import com.sw.inbound.utils.DateTimeUtils
|
||||||
@@ -417,8 +418,8 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
|||||||
|
|
||||||
// private val initialDelay = 5 * 60 * 1000L
|
// private val initialDelay = 5 * 60 * 1000L
|
||||||
// private val dealyMillis = 10 * 60 * 1000L
|
// private val dealyMillis = 10 * 60 * 1000L
|
||||||
private val initialDelay = 5 * 60 * 1000L
|
private val initialDelay = 1 * 1000L
|
||||||
private val dealyMillis = 5 * 60 * 1000L
|
private val dealyMillis = 10 * 1000L
|
||||||
fun startFaceTask(block: (List<String?>) -> Unit) {
|
fun startFaceTask(block: (List<String?>) -> Unit) {
|
||||||
faceTaskJob =
|
faceTaskJob =
|
||||||
intervalExecutor.startIntervalTaskWithInitialDelay(initialDelay, dealyMillis) {
|
intervalExecutor.startIntervalTaskWithInitialDelay(initialDelay, dealyMillis) {
|
||||||
@@ -431,9 +432,9 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
|||||||
//private var taskPageNo = 1
|
//private var taskPageNo = 1
|
||||||
private fun getFaceIncrementList(pageNo: Int = 1, action:(List<String?>)-> Unit) {
|
private fun getFaceIncrementList(pageNo: Int = 1, action:(List<String?>)-> Unit) {
|
||||||
val timestamp = SpTool.lastFaceTimestamp
|
val timestamp = SpTool.lastFaceTimestamp
|
||||||
if (timestamp == 0L) {
|
// if (timestamp == 0L) {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
settingViewModel.getFaceIncrementList(
|
settingViewModel.getFaceIncrementList(
|
||||||
pageNo = pageNo,
|
pageNo = pageNo,
|
||||||
timestamp = timestamp
|
timestamp = timestamp
|
||||||
@@ -441,6 +442,12 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
|||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
if (pageNo == 1 && list.isEmpty()) {
|
if (pageNo == 1 && list.isEmpty()) {
|
||||||
//未查询到增量数据
|
//未查询到增量数据
|
||||||
|
// Thread{
|
||||||
|
// val faceDao = FaceDatabase.getInstance(this).faceDao()
|
||||||
|
// if (faceDao.getFaceCount() == 0) {
|
||||||
|
// FaceEntity("-1", null, byteArrayOf(-1)).also { it.userType = "-1" }
|
||||||
|
// }
|
||||||
|
// }.start()
|
||||||
return@runOnUiThread
|
return@runOnUiThread
|
||||||
}
|
}
|
||||||
updateFaceData(list)
|
updateFaceData(list)
|
||||||
@@ -462,28 +469,26 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
|||||||
|
|
||||||
public val recognizeViewModel by viewModels<RecognizeViewModel>()
|
public val recognizeViewModel by viewModels<RecognizeViewModel>()
|
||||||
private fun updateFaceData(list: List<UserFaceModel>) {
|
private fun updateFaceData(list: List<UserFaceModel>) {
|
||||||
Thread {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
val faceList = mutableListOf<FaceEntity>()
|
val faceList = mutableListOf<FaceEntity>()
|
||||||
val faceDao = FaceDatabase.getInstance(this).faceDao()
|
val faceDao = FaceDatabase.getInstance(this@BaseActivity).faceDao()
|
||||||
try {
|
try {
|
||||||
list.forEach { model ->
|
list.forEach { model ->
|
||||||
if (model.faceDeleted == true) {
|
if (model.faceDeleted == true) {
|
||||||
//删除数据
|
//删除数据
|
||||||
faceDao.deleteFaceById(model.userId)
|
faceDao.deleteFaceById(model.userId)
|
||||||
|
// if (faceDao.getFaceCount() == 0) {
|
||||||
|
// FaceEntity("-1", null, byteArrayOf(-1)).also { it.userType = "-1" }
|
||||||
|
// }
|
||||||
} else {
|
} else {
|
||||||
val faceData = faceDao.queryByUserName(model.userId)
|
// 获取该用户所有已存特征,判断当前特征是否已存在
|
||||||
if (faceData == null) {
|
val existList = faceDao.queryAllByUserName(model.userId)
|
||||||
//保存数据
|
val featureMatches = existList.map { Base64.encode(it.featureData) == model.faceFeatureStr }
|
||||||
val faceEntity = FaceEntity(
|
Timber.d("userId=${model.userId}, 已存记录数=${existList.size}, 各条特征是否与服务器一致=$featureMatches")
|
||||||
model.userId,
|
val alreadyExists = featureMatches.any { it }
|
||||||
null,
|
if (!alreadyExists) {
|
||||||
Base64.decode(model.faceFeatureStr)
|
//当前特征不存在,保存
|
||||||
).also {
|
faceList.add(getNewFaceEntity(model))
|
||||||
//1-会员、2-临时用户
|
|
||||||
it.userType = if (model.isMember) "1" else "2"
|
|
||||||
it.cardNo = model.cardNo
|
|
||||||
}
|
|
||||||
faceList.add(faceEntity)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -492,13 +497,25 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (faceList.isNotEmpty()) {
|
if (faceList.isNotEmpty()) {
|
||||||
FaceDatabase.getInstance(this).faceDao().insert(faceList)
|
FaceDatabase.getInstance(this@BaseActivity).faceDao().insert(faceList)
|
||||||
recognizeViewModel.refreshFaceList()
|
|
||||||
}
|
}
|
||||||
|
recognizeViewModel.refreshFaceList()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
}.start()
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getNewFaceEntity(model: UserFaceModel) : FaceEntity {
|
||||||
|
return FaceEntity(
|
||||||
|
model.userId,
|
||||||
|
null,
|
||||||
|
Base64.decode(model.faceFeatureStr)
|
||||||
|
).also {
|
||||||
|
//1-会员、2-临时用户
|
||||||
|
it.userType = if (model.member) "1" else "2"
|
||||||
|
it.cardNo = model.cardNo
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun initPlateList() {
|
fun initPlateList() {
|
||||||
@@ -518,6 +535,10 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
|||||||
val faceDao = FaceDatabase.getInstance(App.getContext()).faceDao()
|
val faceDao = FaceDatabase.getInstance(App.getContext()).faceDao()
|
||||||
faceDao.deleteAll()
|
faceDao.deleteAll()
|
||||||
faceDao.resetId()
|
faceDao.resetId()
|
||||||
|
// faceDao.insert(
|
||||||
|
// FaceEntity("-1", null, byteArrayOf(-1)).also { it.userType = "-1" }
|
||||||
|
// )
|
||||||
|
recognizeViewModel.refreshFaceList()
|
||||||
runOnUiThread { block() }
|
runOnUiThread { block() }
|
||||||
}.start()
|
}.start()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import com.sw.plate.utils.arcface.FaceApi
|
|||||||
import com.sw.platecabinet.GlobalData
|
import com.sw.platecabinet.GlobalData
|
||||||
import com.sw.platecabinet.databinding.ActivityDeviceInitBinding
|
import com.sw.platecabinet.databinding.ActivityDeviceInitBinding
|
||||||
import com.sw.platecabinet.databinding.ItemTitleTimeBinding
|
import com.sw.platecabinet.databinding.ItemTitleTimeBinding
|
||||||
|
import com.sw.platecabinet.utils.SpTool
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@@ -48,9 +49,11 @@ class DeviceInitActivity : BaseActivity<ActivityDeviceInitBinding>() {
|
|||||||
showWaitingDialog("加载中……")
|
showWaitingDialog("加载中……")
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
withContext(Dispatchers.Default) {
|
withContext(Dispatchers.Default) {
|
||||||
val count = FaceApi().queryFaceCount()
|
//val count = FaceApi().queryFaceCount()
|
||||||
if (count == 0) {
|
//if (count == 0) {
|
||||||
|
if (SpTool.firstGetFace) {
|
||||||
userViewModel.getUserFaceCache { status, msg ->
|
userViewModel.getUserFaceCache { status, msg ->
|
||||||
|
SpTool.firstGetFace = false
|
||||||
getDeviceConfig()
|
getDeviceConfig()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package com.sw.platecabinet.activity
|
package com.sw.platecabinet.activity
|
||||||
|
|
||||||
|
import androidx.activity.viewModels
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.sw.plate.utils.Base64
|
||||||
import com.sw.plate.utils.ToastUtils
|
import com.sw.plate.utils.ToastUtils
|
||||||
|
import com.sw.plate.utils.arcface.facedb.FaceDatabase
|
||||||
import com.sw.platecabinet.MyApp
|
import com.sw.platecabinet.MyApp
|
||||||
import com.sw.platecabinet.databinding.ActivityFunBinding
|
import com.sw.platecabinet.databinding.ActivityFunBinding
|
||||||
import com.sw.platecabinet.databinding.ItemTitleTimeBinding
|
import com.sw.platecabinet.databinding.ItemTitleTimeBinding
|
||||||
@@ -9,11 +12,12 @@ import com.sw.platecabinet.dialog.CustomDialog
|
|||||||
import com.sw.platecabinet.ext.clickWithDebounce
|
import com.sw.platecabinet.ext.clickWithDebounce
|
||||||
import com.sw.platecabinet.utils.PlateUtils
|
import com.sw.platecabinet.utils.PlateUtils
|
||||||
import com.sw.platecabinet.utils.SpTool
|
import com.sw.platecabinet.utils.SpTool
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import com.sw.platecabinet.viewmodel.UserViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import kotlin.getValue
|
||||||
|
|
||||||
class FunActivity : BaseActivity<ActivityFunBinding>() {
|
class FunActivity : BaseActivity<ActivityFunBinding>() {
|
||||||
|
|
||||||
@@ -32,6 +36,28 @@ class FunActivity : BaseActivity<ActivityFunBinding>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
binding.btnPutPlate.clickWithDebounce { putPlate() }
|
binding.btnPutPlate.clickWithDebounce { putPlate() }
|
||||||
|
|
||||||
|
queryUserCount()
|
||||||
|
|
||||||
|
binding.btnTestFeature.clickWithDebounce {
|
||||||
|
testFeature()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private fun testFeature() {
|
||||||
|
userViewModel.getUserFace(1, 1) {list ->
|
||||||
|
lifecycleScope.launch {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
if (list.isEmpty()) return@withContext
|
||||||
|
val userId = list[0].userId
|
||||||
|
val base64Feature = list[0].faceFeatureStr
|
||||||
|
val faceDao = FaceDatabase.getInstance(this@FunActivity).faceDao()
|
||||||
|
val entityList = faceDao.queryAllByUserName(userId)
|
||||||
|
val resultList = entityList.map { base64Feature == Base64.encode(it.featureData) }
|
||||||
|
val isEqual = resultList.any { true }
|
||||||
|
ToastUtils.showToast("比较结果:$isEqual")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun clearFace() {
|
private fun clearFace() {
|
||||||
@@ -42,6 +68,7 @@ class FunActivity : BaseActivity<ActivityFunBinding>() {
|
|||||||
{
|
{
|
||||||
hideWaitingDialog()
|
hideWaitingDialog()
|
||||||
ToastUtils.showToast("已清除")
|
ToastUtils.showToast("已清除")
|
||||||
|
queryUserCount()
|
||||||
}, 500
|
}, 500
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -57,6 +84,7 @@ class FunActivity : BaseActivity<ActivityFunBinding>() {
|
|||||||
ToastUtils.showToast(
|
ToastUtils.showToast(
|
||||||
if (status) "请求成功,$msg" else "请求失败,$msg"
|
if (status) "请求成功,$msg" else "请求失败,$msg"
|
||||||
)
|
)
|
||||||
|
queryUserCount()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,4 +139,14 @@ class FunActivity : BaseActivity<ActivityFunBinding>() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
@@ -10,10 +12,8 @@ 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 android.widget.ImageView
|
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.arcsoft.face.ErrorInfo
|
import com.arcsoft.face.ErrorInfo
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
@@ -49,7 +49,6 @@ 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.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
|
||||||
@@ -58,12 +57,14 @@ 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 kotlinx.coroutines.withContext
|
||||||
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
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.Arrays
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -89,22 +90,13 @@ 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()
|
||||||
|
|
||||||
initArcViewModel()
|
initArcViewModel()
|
||||||
@@ -174,8 +166,14 @@ 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 val leftDebouncer = Debouncer(30 * 1000)
|
||||||
|
|
||||||
|
/** 识别超时倒计时(15秒无操作跳回初始页) */
|
||||||
|
private val recognizeCountDownUtil = CountDownUtil()
|
||||||
|
|
||||||
|
/** 用户离开画面后的倒计时(llCollectButton 显示状态下无人脸时触发) */
|
||||||
|
private val leftCountDownUtil = CountDownUtil()
|
||||||
|
|
||||||
private var registerHandler: RegisterCallbackHandler? = null
|
private var registerHandler: RegisterCallbackHandler? = null
|
||||||
|
|
||||||
@@ -213,7 +211,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
recognizeViewModel.ftInitCode.observe(this, Observer { ftInitCode: Int? ->
|
recognizeViewModel.ftInitCode.observe(this) { 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",
|
||||||
@@ -222,8 +220,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",
|
||||||
@@ -232,8 +230,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",
|
||||||
@@ -242,31 +240,26 @@ 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}")
|
||||||
loadFaceRecognizeResult3(result)
|
loadFaceRecognizeResult3(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
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 识别失败重试次数
|
* 识别失败重试次数
|
||||||
*/
|
*/
|
||||||
@@ -277,11 +270,6 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
*/
|
*/
|
||||||
private val maxFailCount = 5
|
private val maxFailCount = 5
|
||||||
|
|
||||||
/**
|
|
||||||
* 人脸失败阈值
|
|
||||||
*/
|
|
||||||
private val faceFailThreshold = 0.3
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 配置阈值,默认值为0.8
|
* 配置阈值,默认值为0.8
|
||||||
*/
|
*/
|
||||||
@@ -295,8 +283,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
|
|
||||||
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
|
||||||
@@ -309,7 +296,6 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (similar <= faceFailThreshold || retryFailCount >= maxFailCount) {
|
|
||||||
if (retryFailCount >= maxFailCount) {
|
if (retryFailCount >= maxFailCount) {
|
||||||
loadLogInfo(
|
loadLogInfo(
|
||||||
"collectFace,recognizeUserId 识别失败,开始采集 similar=$similar, " +
|
"collectFace,recognizeUserId 识别失败,开始采集 similar=$similar, " +
|
||||||
@@ -351,7 +337,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun recognizeFail() {
|
private fun recognizeFail() {
|
||||||
CountDownManager.cancelCountDown()
|
recognizeCountDownUtil.cancelCountDown()
|
||||||
binding.tvToInit.text = ""
|
binding.tvToInit.text = ""
|
||||||
binding.tvSimilarValue.text = ""
|
binding.tvSimilarValue.text = ""
|
||||||
binding.llCollectButton.visible()
|
binding.llCollectButton.visible()
|
||||||
@@ -363,6 +349,20 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
binding.ivCollectPhoto.visible()
|
binding.ivCollectPhoto.visible()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun newUserCollect() {
|
||||||
|
recognizeCountDownUtil.cancelCountDown()
|
||||||
|
binding.tvToInit.text = ""
|
||||||
|
binding.tvSimilarValue.text = ""
|
||||||
|
binding.llCollectButton.visible()
|
||||||
|
binding.btnRetry.gone()
|
||||||
|
binding.btnCollect.run {
|
||||||
|
visible()
|
||||||
|
text = "新用户采集"
|
||||||
|
}
|
||||||
|
binding.ivCollectPhoto.gone()
|
||||||
|
resumeCamera()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询餐盘数据
|
* 查询餐盘数据
|
||||||
*/
|
*/
|
||||||
@@ -404,12 +404,12 @@ 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()
|
goInitActivity()
|
||||||
}
|
}
|
||||||
CountDownManager.cancelCountDown()
|
recognizeCountDownUtil.cancelCountDown()
|
||||||
}
|
}
|
||||||
currentPlateInfo = item
|
currentPlateInfo = item
|
||||||
MyApp.plateList = items
|
MyApp.plateList = items
|
||||||
@@ -417,21 +417,19 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// private val debouncer by lazy { Debouncer(3000) }
|
private val openDebouncer by lazy { Debouncer(5000) }
|
||||||
|
|
||||||
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
|
rgbCameraHelper?.release()
|
||||||
if (rgbCameraHelper != null) {
|
rgbCameraHelper = null
|
||||||
rgbCameraHelper!!.release()
|
|
||||||
rgbCameraHelper = null
|
|
||||||
}
|
|
||||||
|
|
||||||
recognizeViewModel.destroy()
|
recognizeViewModel.destroy()
|
||||||
|
|
||||||
@@ -474,7 +472,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 {
|
||||||
@@ -510,8 +508,8 @@ 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,
|
||||||
@@ -545,7 +543,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)
|
||||||
@@ -607,8 +605,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()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -626,15 +624,15 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun openRectInfoDraw(view: View?) {
|
// fun openRectInfoDraw(view: View?) {
|
||||||
openRectInfoDraw = !openRectInfoDraw
|
// openRectInfoDraw = !openRectInfoDraw
|
||||||
recognizeViewModel.setDrawRectInfoTextValue(openRectInfoDraw)
|
// recognizeViewModel.setDrawRectInfoTextValue(openRectInfoDraw)
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
override fun onGlobalLayout() {
|
override fun onGlobalLayout() {
|
||||||
loadLogInfo("onGlobalLayout")
|
loadLogInfo("onGlobalLayout")
|
||||||
binding.dualCameraTexturePreviewRgb.getViewTreeObserver().removeOnGlobalLayoutListener(this)
|
binding.dualCameraTexturePreviewRgb.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||||
// 请求权限
|
// 请求权限
|
||||||
checkCameraPermission()
|
checkCameraPermission()
|
||||||
}
|
}
|
||||||
@@ -663,38 +661,52 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
* @param height 图像高度(如 1080)
|
* @param height 图像高度(如 1080)
|
||||||
* @return 合法的全黑帧字节数组
|
* @return 合法的全黑帧字节数组
|
||||||
*/
|
*/
|
||||||
fun createBlackNV21Frame(width: Int, height: Int): ByteArray {
|
private fun createBlackNV21Frame(width: Int, height: Int): ByteArray {
|
||||||
val frameSize = width * height
|
val frameSize = width * height
|
||||||
val nv21Data = ByteArray(frameSize * 3 / 2) // NV21 格式长度 = 宽×高×1.5
|
val nv21Data = ByteArray(frameSize * 3 / 2) // NV21 格式长度 = 宽×高×1.5
|
||||||
|
|
||||||
// Y 分量(亮度)设为 0(全黑),UV 分量设为 128(默认值)
|
// Y 分量(亮度)设为 0(全黑),UV 分量设为 128(默认值)
|
||||||
// 1. 填充 Y 分量(前 frameSize 个字节)
|
// 1. 填充 Y 分量(前 frameSize 个字节)
|
||||||
Arrays.fill(nv21Data, 0, frameSize, 0.toByte())
|
// Arrays.fill(nv21Data, 0, frameSize, 0.toByte())
|
||||||
|
nv21Data.fill(0, 0, frameSize)
|
||||||
// 2. 填充 UV 分量(后 frameSize/2 个字节)
|
// 2. 填充 UV 分量(后 frameSize/2 个字节)
|
||||||
Arrays.fill(nv21Data, frameSize, nv21Data.size, 128.toByte())
|
// Arrays.fill(nv21Data, frameSize, nv21Data.size, 128.toByte())
|
||||||
|
nv21Data.fill(128.toByte(), frameSize, nv21Data.size)
|
||||||
|
|
||||||
return nv21Data
|
return nv21Data
|
||||||
}
|
}
|
||||||
|
|
||||||
// 调用示例:构造 1920×1080 的全黑帧(长度 1382400)
|
// 调用示例:构造 1920×1080 的全黑帧(长度 1382400)
|
||||||
val emptyFrame = createBlackNV21Frame(1920, 1080)
|
// private val emptyFrame = createBlackNV21Frame(1920, 1080)
|
||||||
|
private val emptyFrame = createBlackNV21Frame(1280, 720)
|
||||||
// 传递“空帧” recognizeViewModel.onPreviewFrame(emptyFrame, true)
|
// 传递“空帧” recognizeViewModel.onPreviewFrame(emptyFrame, true)
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* 临时用户id
|
// * 临时用户id
|
||||||
*/
|
// */
|
||||||
private var tempUserId: String = ""
|
// private var tempUserId: String = ""
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
try {
|
try {
|
||||||
if (isFirstOpen) {
|
if (isFirstOpen) {
|
||||||
|
isFirstOpen = false
|
||||||
goInitActivity()
|
goInitActivity()
|
||||||
binding.root.postDelayed({
|
|
||||||
isFirstOpen = false
|
|
||||||
}, 1000)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
resetRecognizeState()
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
try {
|
||||||
|
val faceDao = FaceDatabase.getInstance(this@LoginByFaceActivity).faceDao()
|
||||||
|
val faceCount = withContext(Dispatchers.IO) { faceDao.faceCount }
|
||||||
|
if (faceCount == 0) {
|
||||||
|
newUserCollect()
|
||||||
|
} else {
|
||||||
|
resetRecognizeState()
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
@@ -714,12 +726,13 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
isCollectFace = false
|
isCollectFace = false
|
||||||
plateEnable = true
|
plateEnable = true
|
||||||
collectCount = 0
|
collectCount = 0
|
||||||
tempUserId = "${System.currentTimeMillis()}"
|
// tempUserId = "${System.currentTimeMillis()}"
|
||||||
resumeCamera()
|
resumeCamera()
|
||||||
registerHandler = RegisterCallbackHandler()
|
registerHandler = RegisterCallbackHandler()
|
||||||
|
|
||||||
CountDownManager.startCountDown(
|
recognizeCountDownUtil.startCountDown(
|
||||||
totalSeconds = 15,
|
total = 15,
|
||||||
|
lifecycleScope = lifecycleScope,
|
||||||
onTick = { remaining ->
|
onTick = { remaining ->
|
||||||
loadLogInfo("resetRecognizeState剩余时间:$remaining 秒")
|
loadLogInfo("resetRecognizeState剩余时间:$remaining 秒")
|
||||||
if (judgeRecognizeState()) return@startCountDown
|
if (judgeRecognizeState()) return@startCountDown
|
||||||
@@ -741,10 +754,10 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
|
|
||||||
private var isFirstOpen = true
|
private var isFirstOpen = true
|
||||||
|
|
||||||
protected override fun onPause() {
|
override fun onPause() {
|
||||||
pauseCamera()
|
pauseCamera()
|
||||||
super.onPause()
|
super.onPause()
|
||||||
CountDownManager.cancelCountDown()
|
recognizeCountDownUtil.cancelCountDown()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun pauseCamera() {
|
private fun pauseCamera() {
|
||||||
@@ -752,14 +765,11 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
// recognizeViewModel.onPreviewFrame(emptyFrame, true)
|
// recognizeViewModel.onPreviewFrame(emptyFrame, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handleLoginSuccess(equipmentUserInfo: EquipmentUserInfo, isAdmin: Boolean) {
|
|
||||||
super.handleLoginSuccess(equipmentUserInfo, isAdmin)
|
|
||||||
// goInitActivity()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun judgeRecognizeState(): Boolean {
|
private fun judgeRecognizeState(): Boolean {
|
||||||
val rectColor = binding.dualCameraFaceRectView.rectColor
|
val rectColor = binding.dualCameraFaceRectView.rectColor
|
||||||
if (rectColor == RecognizeColor.COLOR_SUCCESS && currentUserId.isNullOrBlank().not() && facePreviewInfoList.isNullOrEmpty().not()) {
|
if (rectColor == RecognizeColor.COLOR_SUCCESS && currentUserId.isNullOrBlank()
|
||||||
|
.not() && facePreviewInfoList.isNullOrEmpty().not()
|
||||||
|
) {
|
||||||
//人未离开,再次识别
|
//人未离开,再次识别
|
||||||
loadLogInfo("userId=$currentUserId")
|
loadLogInfo("userId=$currentUserId")
|
||||||
openPlateDebouncer.debounce {
|
openPlateDebouncer.debounce {
|
||||||
@@ -823,14 +833,15 @@ 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 {
|
runOnUiThread {
|
||||||
Glide.with(this@LoginByFaceActivity).load(imageFile).into(binding.ivCollectPhoto)
|
Glide.with(this@LoginByFaceActivity).load(imageFile)
|
||||||
|
.into(binding.ivCollectPhoto)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uploadFaceDebouncer.reset()
|
uploadFaceDebouncer.reset()
|
||||||
}.start()
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -866,7 +877,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
|
|
||||||
private var currentUserId: String? = null
|
private var currentUserId: String? = null
|
||||||
|
|
||||||
private fun saveFaceInfo(faceModel: UserFaceModel, block:()-> Unit= {}) {
|
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()
|
||||||
@@ -876,13 +887,18 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
Base64.decode(faceModel.faceFeatureStr)
|
Base64.decode(faceModel.faceFeatureStr)
|
||||||
).also {
|
).also {
|
||||||
//1-会员、2-临时用户
|
//1-会员、2-临时用户
|
||||||
it.userType = if (faceModel.isMember) "1" else "2"
|
it.userType = if (faceModel.member) "1" else "2"
|
||||||
}
|
}
|
||||||
faceDao.insert(faceEntity)
|
val faceId = faceDao.insert(faceEntity)
|
||||||
|
faceEntity.faceId = faceId
|
||||||
loadLogInfo("collectFace,saveFaceInfo: ----------10,人脸数据保存完成-----------")
|
loadLogInfo("collectFace,saveFaceInfo: ----------10,人脸数据保存完成-----------")
|
||||||
recognizeViewModel.refreshFaceList()
|
recognizeViewModel.refreshFaceList()
|
||||||
loadLogInfo("collectFace,saveFaceInfo: ----------11,刷新人脸数据-----------")
|
loadLogInfo("collectFace,saveFaceInfo: ----------11,刷新人脸数据-----------")
|
||||||
runOnUiThread { recognizeViewModel.onPreviewFrame(emptyFrame, true) }
|
runOnUiThread {
|
||||||
|
//采集完成,不再识别,直接提示成功
|
||||||
|
isRecognition = false
|
||||||
|
recognizeSuccess("${System.currentTimeMillis()}")
|
||||||
|
}
|
||||||
loadLogInfo("---------------------------------")
|
loadLogInfo("---------------------------------")
|
||||||
collectCount = 0
|
collectCount = 0
|
||||||
block()
|
block()
|
||||||
@@ -899,22 +915,20 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
|
|
||||||
private var isCollectFace = false
|
private var isCollectFace = false
|
||||||
private fun leftCountDown() {
|
private fun leftCountDown() {
|
||||||
CountDownUtil.startCountDown(total = 15, lifecycleScope = lifecycleScope, onStart = {
|
leftCountDownUtil.startCountDown(total = 15, lifecycleScope = lifecycleScope, onStart = {
|
||||||
loadLogInfo("onPreview-----------leftCountDown-------------")
|
loadLogInfo("onPreview-----------leftCountDown-------------")
|
||||||
}, onTick = {
|
}, onTick = {
|
||||||
loadLogInfo("leftCountDown.onTick:$it")
|
loadLogInfo("leftCountDown.onTick:$it")
|
||||||
if (facePreviewInfoList.isNullOrEmpty().not()) {
|
if (facePreviewInfoList.isNullOrEmpty().not()) {
|
||||||
loadLogInfo("leftCountDown.onTick-cancel()")
|
loadLogInfo("leftCountDown.onTick-cancel()")
|
||||||
CountDownUtil.cancelCountDown()
|
leftCountDownUtil.cancelCountDown()
|
||||||
return@startCountDown
|
return@startCountDown
|
||||||
}
|
}
|
||||||
}, onFinish = {
|
}, onFinish = {
|
||||||
if (facePreviewInfoList.isNullOrEmpty().not()) {
|
if (facePreviewInfoList.isNullOrEmpty().not()) {
|
||||||
loadLogInfo("leftCountDown.onFinish-cancel()")
|
loadLogInfo("leftCountDown.onFinish-人脸未离开,不跳转")
|
||||||
CountDownUtil.cancelCountDown()
|
|
||||||
return@startCountDown
|
return@startCountDown
|
||||||
}
|
}
|
||||||
CountDownUtil.cancelCountDown()
|
|
||||||
goInitActivity()
|
goInitActivity()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package com.sw.platecabinet.model.response
|
|||||||
|
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.google.gson.annotations.SerializedName
|
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,5 +37,5 @@ data class UserFaceModel(
|
|||||||
/**
|
/**
|
||||||
* 是否会员
|
* 是否会员
|
||||||
*/
|
*/
|
||||||
val isMember: Boolean
|
val member: Boolean
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
@@ -12,37 +12,29 @@ 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()
|
||||||
.client(okHttpClient)
|
.baseUrl(GlobalData.appBaseUrl)
|
||||||
.addConverterFactory(GsonConverterFactory.create())
|
.client(okHttpClient)
|
||||||
// .addCallAdapterFactory(CoroutineCallAdapterFactory()) // 协程适配器
|
.addConverterFactory(GsonConverterFactory.create())
|
||||||
.build()
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
val apiService: ApiService by lazy {
|
val apiService: ApiService by lazy {
|
||||||
retrofit.create(ApiService::class.java)
|
retrofit.create(ApiService::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,135 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +1,26 @@
|
|||||||
package com.sw.platecabinet.utils
|
package com.sw.platecabinet.utils
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.*
|
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 java.util.concurrent.atomic.AtomicBoolean
|
||||||
import kotlin.ranges.downTo
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 人脸识别防抖倒计时工具类(单例+防重复+无死循环)
|
* 基于协程的倒计时工具类(实例化使用,各调用方持有独立实例,互不干扰)
|
||||||
|
* 修复:onCompletion 区分正常结束与取消,手动取消时不触发 onFinish
|
||||||
*/
|
*/
|
||||||
object CountDownUtil {
|
class CountDownUtil {
|
||||||
// 原子布尔值:标记是否正在倒计时(防重复启动)
|
|
||||||
private val isCounting = AtomicBoolean(false)
|
|
||||||
|
|
||||||
// 当前倒计时Job(用于取消)
|
private val isCounting = AtomicBoolean(false)
|
||||||
private var currentJob: Job? = null
|
private var currentJob: Job? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 启动/重启防抖倒计时(同一时间仅一个倒计时运行)
|
* 启动倒计时(若已在运行则忽略,需重启请先调用 cancelCountDown)
|
||||||
* @param totalSeconds 倒计时总秒数(如15)
|
* @param total 总秒数
|
||||||
* @param lifecycleScope Activity/Fragment的lifecycleScope(必须)
|
* @param lifecycleScope Activity/Fragment 的 lifecycleScope
|
||||||
|
* @param onStart 启动回调(主线程)
|
||||||
* @param onTick 每秒回调剩余秒数(主线程)
|
* @param onTick 每秒回调剩余秒数(主线程)
|
||||||
* @param onFinish 倒计时结束回调(主线程)
|
* @param onFinish 正常结束回调(主线程,手动取消时不触发)
|
||||||
*/
|
*/
|
||||||
fun startCountDown(
|
fun startCountDown(
|
||||||
total: Int = 15,
|
total: Int = 15,
|
||||||
@@ -36,46 +29,37 @@ object CountDownUtil {
|
|||||||
onTick: (Int) -> Unit = {},
|
onTick: (Int) -> Unit = {},
|
||||||
onFinish: () -> Unit
|
onFinish: () -> Unit
|
||||||
) {
|
) {
|
||||||
// 1. 防重复:如果正在倒计时,先取消旧任务
|
if (isCounting.get()) return
|
||||||
if (isCounting.get()) {
|
|
||||||
//cancelCountDown()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 标记为正在倒计时
|
|
||||||
isCounting.set(true)
|
isCounting.set(true)
|
||||||
|
|
||||||
// 3. 启动新倒计时
|
|
||||||
currentJob = flow {
|
currentJob = flow {
|
||||||
Log.d("CountDownUtil", "倒计时启动,总时长:$total 秒")
|
|
||||||
for (i in total downTo 1) {
|
for (i in total downTo 1) {
|
||||||
emit(i)
|
emit(i)
|
||||||
delay(1000) // IO线程等待1秒,无死循环
|
delay(1000L)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.flowOn(Dispatchers.IO) // 上游IO线程,避免阻塞主线程
|
.flowOn(Dispatchers.IO)
|
||||||
.onStart {
|
.onStart {
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) { onStart() }
|
||||||
onStart.invoke()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.onEach { remaining ->
|
.onEach { remaining ->
|
||||||
// 主线程回调剩余时间
|
withContext(Dispatchers.Main) { onTick(remaining) }
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
onTick.invoke(remaining)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.onCompletion {
|
.onCompletion { cause ->
|
||||||
// 倒计时结束:重置状态+回调
|
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
isCounting.set(false) // 重置标记
|
isCounting.set(false)
|
||||||
currentJob = null // 置空Job
|
currentJob = null
|
||||||
Log.d("CountDownUtil", "倒计时正常结束")
|
// cause == null 为正常结束,有异常(含 CancellationException)时不触发 onFinish
|
||||||
onFinish.invoke()
|
if (cause == null) {
|
||||||
|
Log.d("CountDownUtil", "倒计时正常结束")
|
||||||
|
onFinish()
|
||||||
|
} else {
|
||||||
|
Log.d("CountDownUtil", "倒计时被取消:${cause.message}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.catch { e ->
|
.catch { e ->
|
||||||
// 捕获异常:避免崩溃,重置状态
|
|
||||||
Log.e("CountDownUtil", "倒计时异常:${e.message}", e)
|
Log.e("CountDownUtil", "倒计时异常:${e.message}", e)
|
||||||
isCounting.set(false)
|
isCounting.set(false)
|
||||||
currentJob = null
|
currentJob = null
|
||||||
@@ -84,12 +68,12 @@ object CountDownUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 取消倒计时(手动停止)
|
* 取消倒计时(不触发 onFinish)
|
||||||
*/
|
*/
|
||||||
fun cancelCountDown() {
|
fun cancelCountDown() {
|
||||||
currentJob?.cancel() // 取消Job
|
currentJob?.cancel()
|
||||||
currentJob = null // 置空,避免残留
|
currentJob = null
|
||||||
isCounting.set(false) // 重置标记
|
isCounting.set(false)
|
||||||
Log.d("CountDownUtil", "倒计时被手动取消")
|
Log.d("CountDownUtil", "倒计时已取消")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,14 @@ import com.sw.inbound.utils.SPUtil
|
|||||||
import com.sw.inbound.utils.SPUtil.Companion.getInstance
|
import com.sw.inbound.utils.SPUtil.Companion.getInstance
|
||||||
import com.sw.platecabinet.MyApp
|
import com.sw.platecabinet.MyApp
|
||||||
import com.sw.platecabinet.model.response.EquipmentUserInfo
|
import com.sw.platecabinet.model.response.EquipmentUserInfo
|
||||||
|
import kotlin.text.get
|
||||||
|
|
||||||
object SpTool {
|
object SpTool {
|
||||||
const val LAST_FACE_TIMESTAMP: String = "faceTimestamp"
|
const val LAST_FACE_TIMESTAMP: String = "faceTimestamp"
|
||||||
const val PLATE_BOX_DATA: String = "plateBoxBind"
|
const val PLATE_BOX_DATA: String = "plateBoxBind"
|
||||||
|
|
||||||
|
const val IS_FIRST_GET_FACE = "isFirstGetFace"
|
||||||
|
|
||||||
private var spUtil: SPUtil? = null
|
private var spUtil: SPUtil? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -39,4 +43,10 @@ object SpTool {
|
|||||||
return GsonUtils.fromJsonList(jsonData, EquipmentUserInfo::class.java)
|
return GsonUtils.fromJsonList(jsonData, EquipmentUserInfo::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var firstGetFace: Boolean
|
||||||
|
get() = spUtil?.get(IS_FIRST_GET_FACE, true)?:true
|
||||||
|
set(value) {
|
||||||
|
spUtil?.boolean(IS_FIRST_GET_FACE, value)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,16 +9,12 @@ import com.sw.plate.utils.ToastUtils
|
|||||||
import com.sw.plate.utils.arcface.FaceApi
|
import com.sw.plate.utils.arcface.FaceApi
|
||||||
import com.sw.plate.utils.arcface.facedb.entity.FaceEntity
|
import com.sw.plate.utils.arcface.facedb.entity.FaceEntity
|
||||||
import com.sw.platecabinet.GlobalData
|
import com.sw.platecabinet.GlobalData
|
||||||
import com.sw.platecabinet.GlobalData.globalEquipmentCode
|
|
||||||
import com.sw.platecabinet.GlobalKey
|
import com.sw.platecabinet.GlobalKey
|
||||||
import com.sw.platecabinet.model.DeviceConfig
|
import com.sw.platecabinet.model.DeviceConfig
|
||||||
import com.sw.platecabinet.model.request.LoginParam
|
import com.sw.platecabinet.model.request.LoginParam
|
||||||
import com.sw.platecabinet.model.response.EquipmentUserInfo
|
|
||||||
import com.sw.platecabinet.model.response.UserFaceModel
|
import com.sw.platecabinet.model.response.UserFaceModel
|
||||||
import com.sw.platecabinet.utils.SpTool
|
import com.sw.platecabinet.utils.SpTool
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
@@ -55,6 +51,19 @@ class UserViewModel : BaseViewModel() {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
fun getUserFace(
|
||||||
|
pageNo: Int,
|
||||||
|
pageSize: Int,
|
||||||
|
callback: (List<UserFaceModel>) -> Unit
|
||||||
|
) {
|
||||||
|
launchWithLoading{
|
||||||
|
val response = repository.getUserFaceCache(pageNum = pageNo, pageSize = pageSize)
|
||||||
|
val status = parseResponse(response)
|
||||||
|
if (status) callback(response.data?:emptyList())
|
||||||
|
else emptyList<UserFaceModel>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取人脸数据
|
* 获取人脸数据
|
||||||
*/
|
*/
|
||||||
@@ -79,9 +88,15 @@ class UserViewModel : BaseViewModel() {
|
|||||||
return@launchWithLoading
|
return@launchWithLoading
|
||||||
}
|
}
|
||||||
val faceEntityList = list.map {
|
val faceEntityList = list.map {
|
||||||
FaceEntity(it.userId, null, Base64.decode(it.faceFeatureStr))
|
FaceEntity(it.userId, null, Base64.decode(it.faceFeatureStr)).apply {
|
||||||
|
userType = if (it.member) "1" else "2"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.Default) {
|
withContext(Dispatchers.Default) {
|
||||||
|
if (currentPageNo == 1) {
|
||||||
|
//第一页数据删除本地数据库
|
||||||
|
faceApi.clearFaceData()
|
||||||
|
}
|
||||||
faceApi.updateFaceData(pageNo, faceEntityList)
|
faceApi.updateFaceData(pageNo, faceEntityList)
|
||||||
if (list.size >= pageSize) {
|
if (list.size >= pageSize) {
|
||||||
faceTimestamp = list.last().faceUpdateTimestamp ?: 0
|
faceTimestamp = list.last().faceUpdateTimestamp ?: 0
|
||||||
@@ -103,6 +118,7 @@ class UserViewModel : BaseViewModel() {
|
|||||||
|
|
||||||
private var faceTimestamp = 0L
|
private var faceTimestamp = 0L
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 激活人脸识别引擎
|
* 激活人脸识别引擎
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -3,12 +3,45 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@drawable/bg"
|
android:background="@drawable/bg"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<include
|
<include
|
||||||
android:id="@+id/includeHeader"
|
android:id="@+id/includeHeader"
|
||||||
layout="@layout/item_title_time" />
|
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
|
<TextView
|
||||||
android:id="@+id/btnClearFace"
|
android:id="@+id/btnClearFace"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -32,7 +65,7 @@
|
|||||||
android:layout_marginHorizontal="50dp"
|
android:layout_marginHorizontal="50dp"
|
||||||
android:paddingHorizontal="30dp"
|
android:paddingHorizontal="30dp"
|
||||||
android:background="@drawable/btn_outline"
|
android:background="@drawable/btn_outline"
|
||||||
android:text="清除并重新拉取数据"
|
android:text="清除并拉取全量数据"
|
||||||
android:textColor="#FFCC99" />
|
android:textColor="#FFCC99" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -60,4 +93,18 @@
|
|||||||
android:background="@drawable/btn_outline"
|
android:background="@drawable/btn_outline"
|
||||||
android:text="模拟放置餐盘"
|
android:text="模拟放置餐盘"
|
||||||
android:textColor="#FFCC99" />
|
android:textColor="#FFCC99" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/btnTestFeature"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
android:layout_marginTop="30dp"
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_marginHorizontal="50dp"
|
||||||
|
android:paddingHorizontal="30dp"
|
||||||
|
android:background="@drawable/btn_outline"
|
||||||
|
android:text="人脸特征比较"
|
||||||
|
android:textColor="#FFCC99"
|
||||||
|
android:visibility="gone"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -40,6 +40,12 @@ public class FaceApi {
|
|||||||
faceDao.insert(list);
|
faceDao.insert(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void clearFaceData() {
|
||||||
|
FaceDao faceDao = FaceDatabase.getInstance(App.getContext()).faceDao();
|
||||||
|
faceDao.deleteAll();
|
||||||
|
faceDao.resetId();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 激活arcsoft 人脸
|
* 激活arcsoft 人脸
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -92,9 +92,25 @@ public interface FaceDao {
|
|||||||
@Query("SELECT * FROM face WHERE user_name = :userName limit 1")
|
@Query("SELECT * FROM face WHERE user_name = :userName limit 1")
|
||||||
FaceEntity queryByUserName(String userName);
|
FaceEntity queryByUserName(String userName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询指定用户的所有人脸记录(同一用户可能存有多条特征数据)
|
||||||
|
*
|
||||||
|
* @param userName 用户ID
|
||||||
|
* @return 该用户所有人脸记录列表
|
||||||
|
*/
|
||||||
|
@Query("SELECT * FROM face WHERE user_name = :userName")
|
||||||
|
List<FaceEntity> queryAllByUserName(String userName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return 删除临时用户人脸
|
* @return 删除临时用户人脸
|
||||||
*/
|
*/
|
||||||
@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