添加了界面及逻辑

This commit is contained in:
zxj
2025-07-22 15:48:12 +08:00
parent 9d14b5499b
commit 5ad82eb224
40 changed files with 2157 additions and 182 deletions
@@ -2,30 +2,53 @@ package com.sw.platecabinet.activity
import android.Manifest
import android.content.Intent
import android.graphics.Outline
import android.graphics.Point
import android.hardware.Camera
import android.os.Build
import android.util.DisplayMetrics
import android.view.View
import android.view.ViewOutlineProvider
import android.view.ViewGroup
import android.view.ViewTreeObserver
import androidx.activity.viewModels
import androidx.camera.core.CameraSelector
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import com.google.common.util.concurrent.ListenableFuture
import androidx.annotation.RequiresApi
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import com.arcsoft.face.ErrorInfo
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.FacePreviewInfo
import com.sw.plate.utils.arcface.face.model.RecognizeConfiguration
import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel
import com.sw.platecabinet.R
import com.sw.platecabinet.databinding.ActivityLoginFaceBinding
import com.sw.platecabinet.databinding.ItemTitleTimeBinding
import com.sw.platecabinet.utils.PermissionHelper
import com.sw.platecabinet.viewmodel.UserViewModel
import kotlinx.coroutines.launch
import timber.log.Timber
/**
* 人脸识别
*/
class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>() {
private lateinit var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>
class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
ViewTreeObserver.OnGlobalLayoutListener {
private val viewModel by viewModels<UserViewModel>()
private val recognizeViewModel by viewModels<RecognizeViewModel>()
private val CAMERA_PERMISSION_REQUEST_CODE = 100
private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
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)
@@ -38,7 +61,10 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>() {
override fun initialize() {
// 请求权限
checkCameraPermission()
initView()
initArcViewModel()
initArcView()
openRectInfoDraw = true
recognizeViewModel.setDrawRectInfoTextValue(true)
viewModel.generateToken()
binding.llToPwd.setOnClickListener {
val intent = Intent(this, LoginByPwdActivity::class.java)
@@ -46,53 +72,314 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>() {
}
}
private fun checkCameraPermission() {
PermissionHelper.with(this, REQUIRED_PERMISSIONS, CAMERA_PERMISSION_REQUEST_CODE)
.onGranted {
// 权限已授予
startCamera()
override fun registerDataChange() {
lifecycleScope.launch {
viewModel.currentUserInfo.collect {
Timber.d("currentUserInfo it = $it")
if (it != null) {
ToastUtils.showToast("登录成功")
MainActivity.start(context, pageType = PageType.PLATE_CABINET_FULL)
finish()
}
}
.checkAndRequest()
}
private fun initView() {
binding.previewView.outlineProvider = object : ViewOutlineProvider() {
override fun getOutline(view: View, outline: Outline) {
// outline.setRoundRect(0, 0, view.width, view.height, 12f.dp)
outline.setOval(0, 0, view.width, view.height)
}
lifecycleScope.launch {
viewModel.showLoading.collect {
if (it) {
showWaitingDialog("")
} else {
hideWaitingDialog()
}
}
}
binding.previewView.clipToOutline = true
}
private fun startCamera() {
Timber.d("startCamera")
cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
val cameraProvider = cameraProviderFuture.get()
// 创建预览用例
val preview = Preview.Builder()
.build()
.also {
it.setSurfaceProvider(binding.previewView.surfaceProvider)
private fun checkCameraPermission() {
val permissionHelper =
PermissionHelper.with(this, REQUIRED_PERMISSIONS, CAMERA_PERMISSION_REQUEST_CODE)
.onGranted {
// 权限已授予
recognizeViewModel.init()
initRgbCamera()
resumeCamera()
}
.build()
registerPermissionHelper(permissionHelper)
permissionHelper.checkAndRequest()
}
// 选择后置摄像头
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
private var isRecognition = false
private var rgbCameraHelper: DualCameraHelper? = null
private var rgbFaceRectTransformer: FaceRectTransformer? = null
private val livenessType = LivenessType.RGB
private var openRectInfoDraw = false
try {
// 解绑所有用例
cameraProvider.unbindAll()
private fun initArcViewModel() {
// 绑定到生命周期
cameraProvider.bindToLifecycle(
this, cameraSelector, preview
recognizeViewModel.setLiveType(livenessType)
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!!)
)
} catch (exc: Exception) {
Timber.e(exc, "Use case binding failed")
Timber.e("ftInitCode observe = $error")
ToastUtils.showToast(error)
}
}, ContextCompat.getMainExecutor(this))
})
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 { userId: String? ->
Timber.i("recognizeUserId observe userId = $userId")
viewModel.getUserInfoById(memberId = userId?.toInt() ?: 0)
})
}
private fun initArcView() {
//在布局结束后才做初始化操作
binding.dualCameraTexturePreviewRgb.getViewTreeObserver().addOnGlobalLayoutListener(this)
recognizeViewModel.getCompareResultList().getValue()
}
override fun onDestroy() {
if (rgbCameraHelper != null) {
rgbCameraHelper!!.release()
rgbCameraHelper = null
}
recognizeViewModel.destroy()
super.onDestroy()
}
/**
* 调整View的宽高,使2个预览同时显示
*
* @param previewView 显示预览数据的view
* @param faceRectView 画框的view
* @param previewSize 预览大小
* @param displayOrientation 相机旋转角度
* @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
) {
runOnUiThread({
val previewSizeRgb = camera.getParameters().getPreviewSize()
val layoutParams = adjustPreviewViewSize(
binding.dualCameraTexturePreviewRgb,
binding.dualCameraTexturePreviewRgb, binding.dualCameraFaceRectView,
previewSizeRgb, displayOrientation, 1.0f
)
rgbFaceRectTransformer = FaceRectTransformer(
previewSizeRgb.width,
previewSizeRgb.height,
layoutParams.width,
layoutParams.height,
displayOrientation,
cameraId,
isMirror,
ConfigUtil.isDrawRgbRectHorizontalMirror(context),
ConfigUtil.isDrawRgbRectVerticalMirror(context)
)
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()
val facePreviewInfoList: MutableList<FacePreviewInfo?>? =
recognizeViewModel.onPreviewFrame(nv21, true)
if (facePreviewInfoList != null && rgbFaceRectTransformer != null) {
drawPreviewInfo(facePreviewInfoList)
}
recognizeViewModel.clearLeftFace(facePreviewInfoList)
}
override fun onCameraClosed() {
Timber.i("onCameraClosed: ")
}
override fun onCameraError(e: java.lang.Exception) {
Timber.i("onCameraError: %s", e.message)
e.printStackTrace()
}
override fun onCameraConfigurationChanged(cameraID: Int, displayOrientation: Int) {
Timber.i("onCameraConfigurationChanged: threadName = ${Thread.currentThread().name}")
if (rgbFaceRectTransformer != null) {
rgbFaceRectTransformer!!.cameraDisplayOrientation = displayOrientation
}
Timber.i("onCameraConfigurationChanged: $cameraID $displayOrientation")
}
}
val previewConfig: PreviewConfig = recognizeViewModel.previewConfig
rgbCameraHelper = DualCameraHelper.Builder()
.previewViewSize(
Point(
binding.dualCameraTexturePreviewRgb.measuredWidth,
binding.dualCameraTexturePreviewRgb.measuredHeight
)
)
.rotation(windowManager.defaultDisplay.rotation)
.additionalRotation(90) // 角度
.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() {
binding.dualCameraTexturePreviewRgb.getViewTreeObserver().removeOnGlobalLayoutListener(this)
recognizeViewModel.init()
initRgbCamera()
resumeCamera()
// countDownTimer.start();
}
// @Override
// protected void onResume() {
// super.onResume();
// resumeCamera();
// }
private fun resumeCamera() {
isRecognition = true
if (rgbCameraHelper != null && rgbCameraHelper!!.isStopped) {
rgbCameraHelper!!.start()
}
}
protected override fun onPause() {
pauseCamera()
super.onPause()
}
private fun pauseCamera() {
isRecognition = false
recognizeViewModel.onPreviewFrame(ByteArray(1382400), true)
}
}