|
|
@@ -0,0 +1,613 @@
|
|
|
+package com.yingyang.personalcenter.personalcenter
|
|
|
+
|
|
|
+import android.Manifest
|
|
|
+import android.content.pm.PackageManager
|
|
|
+import android.graphics.BitmapFactory
|
|
|
+import android.graphics.Rect
|
|
|
+import android.os.Build
|
|
|
+import android.os.Bundle
|
|
|
+import android.os.Handler
|
|
|
+import android.os.Looper
|
|
|
+import android.util.Log
|
|
|
+import android.view.View
|
|
|
+import android.widget.Button
|
|
|
+import android.widget.ImageView
|
|
|
+import android.widget.ProgressBar
|
|
|
+import android.widget.TextView
|
|
|
+import android.widget.Toast
|
|
|
+import androidx.activity.result.contract.ActivityResultContracts
|
|
|
+import androidx.appcompat.app.AppCompatActivity
|
|
|
+import androidx.camera.core.*
|
|
|
+import androidx.camera.lifecycle.ProcessCameraProvider
|
|
|
+import androidx.camera.view.PreviewView
|
|
|
+import androidx.core.app.ActivityCompat
|
|
|
+import androidx.core.content.ContextCompat
|
|
|
+import androidx.lifecycle.ViewModelProvider
|
|
|
+import com.alibaba.android.arouter.facade.annotation.Route
|
|
|
+import com.google.mlkit.vision.common.InputImage
|
|
|
+import com.google.mlkit.vision.face.FaceDetection
|
|
|
+import com.google.mlkit.vision.face.FaceDetectorOptions
|
|
|
+import com.yingyang.personalcenter.R
|
|
|
+import com.yingyangfly.baselib.router.RouterUrlCommon
|
|
|
+import com.yingyangfly.baselib.utils.JumpUtil
|
|
|
+import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
|
|
+import okhttp3.MultipartBody
|
|
|
+import okhttp3.RequestBody.Companion.asRequestBody
|
|
|
+import java.io.File
|
|
|
+import java.util.concurrent.ExecutorService
|
|
|
+import java.util.concurrent.Executors
|
|
|
+import java.util.concurrent.atomic.AtomicInteger
|
|
|
+
|
|
|
+@Route(path = RouterUrlCommon.faceRegister)
|
|
|
+class FaceRegisterActivity : AppCompatActivity() {
|
|
|
+
|
|
|
+ // UI组件
|
|
|
+ private lateinit var previewView: PreviewView
|
|
|
+ private lateinit var captureButton: Button
|
|
|
+ private lateinit var statusTextView: TextView
|
|
|
+ private lateinit var progressBar: ProgressBar
|
|
|
+ private lateinit var iconBack: ImageView
|
|
|
+ private lateinit var faceGuideView: View // 人脸引导框
|
|
|
+
|
|
|
+ // CameraX
|
|
|
+ private lateinit var cameraProviderFuture: com.google.common.util.concurrent.ListenableFuture<ProcessCameraProvider>
|
|
|
+ private var imageCapture: ImageCapture? = null
|
|
|
+ private var imageAnalyzer: ImageAnalysis? = null
|
|
|
+ private var camera: Camera? = null
|
|
|
+ private lateinit var cameraExecutor: ExecutorService
|
|
|
+
|
|
|
+ // 人脸检测
|
|
|
+ private lateinit var faceDetector: com.google.mlkit.vision.face.FaceDetector
|
|
|
+
|
|
|
+ // 状态控制
|
|
|
+ private var isProcessingPhoto = false
|
|
|
+ private var isRequestInProgress = false
|
|
|
+ private val captureAttempts = AtomicInteger(0)
|
|
|
+ private val maxCaptureAttempts = 3
|
|
|
+ private var currentAttempt = 1
|
|
|
+
|
|
|
+ // 人脸质量检测(保留用于引导用户)
|
|
|
+ private var stableFaceFrames = 0
|
|
|
+ private val requiredStableFrames = 5
|
|
|
+ private var lastFaceSize = 0f
|
|
|
+ private var lastFacePositionX = 0f
|
|
|
+ private var lastFacePositionY = 0f
|
|
|
+ private var isFaceDetected = false
|
|
|
+ private var isFaceQualityGood = false
|
|
|
+
|
|
|
+ // Handler
|
|
|
+ private val mainHandler = Handler(Looper.getMainLooper())
|
|
|
+
|
|
|
+ // ViewModel
|
|
|
+ private lateinit var viewModel: PersonalCenterViewModel
|
|
|
+
|
|
|
+ // 权限请求
|
|
|
+ private val requestPermissionLauncher = registerForActivityResult(
|
|
|
+ ActivityResultContracts.RequestPermission()
|
|
|
+ ) { isGranted ->
|
|
|
+ if (isGranted) {
|
|
|
+ startCamera()
|
|
|
+ } else {
|
|
|
+ Toast.makeText(this, "需要相机权限才能使用人脸注册", Toast.LENGTH_LONG).show()
|
|
|
+ finish()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ companion object {
|
|
|
+ private const val TAG = "FaceRegister"
|
|
|
+ private const val FACE_SIZE_THRESHOLD = 0.15f
|
|
|
+ private const val FACE_MOVEMENT_THRESHOLD = 0.05f
|
|
|
+ private const val FACE_CENTER_THRESHOLD = 0.15f
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onCreate(savedInstanceState: Bundle?) {
|
|
|
+ super.onCreate(savedInstanceState)
|
|
|
+ setContentView(R.layout.activity_face_register_simple)
|
|
|
+
|
|
|
+ initViews()
|
|
|
+ initViewModel()
|
|
|
+ initFaceDetector()
|
|
|
+
|
|
|
+ if (allPermissionsGranted()) {
|
|
|
+ startCamera()
|
|
|
+ } else {
|
|
|
+ requestCameraPermission()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun initViews() {
|
|
|
+ previewView = findViewById(R.id.preview_view)
|
|
|
+ captureButton = findViewById(R.id.capture_button)
|
|
|
+ statusTextView = findViewById(R.id.status_text)
|
|
|
+ progressBar = findViewById(R.id.progress_bar)
|
|
|
+ iconBack = findViewById(R.id.icon_back)
|
|
|
+ faceGuideView = findViewById(R.id.overlay_view) // 人脸引导框
|
|
|
+
|
|
|
+ cameraExecutor = Executors.newSingleThreadExecutor()
|
|
|
+
|
|
|
+ captureButton.setOnClickListener {
|
|
|
+ if (!isProcessingPhoto && !isRequestInProgress) {
|
|
|
+ if (isFaceQualityGood) {
|
|
|
+ manualCaptureFace()
|
|
|
+ } else {
|
|
|
+ Toast.makeText(this, "请先调整人脸位置,确保在引导框内", Toast.LENGTH_SHORT).show()
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ Toast.makeText(this, "正在处理中,请稍候", Toast.LENGTH_SHORT).show()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ iconBack.setOnClickListener {
|
|
|
+ JumpUtil.jumpActivity(RouterUrlCommon.personalCenter, this)
|
|
|
+ finish()
|
|
|
+ }
|
|
|
+
|
|
|
+ updateUIState()
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun initViewModel() {
|
|
|
+ viewModel = ViewModelProvider(this)[PersonalCenterViewModel::class.java]
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun initFaceDetector() {
|
|
|
+ val options = FaceDetectorOptions.Builder()
|
|
|
+ .setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_FAST)
|
|
|
+ .setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_NONE)
|
|
|
+ .setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_NONE)
|
|
|
+ .setMinFaceSize(0.1f)
|
|
|
+ .build()
|
|
|
+
|
|
|
+ faceDetector = FaceDetection.getClient(options)
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun allPermissionsGranted(): Boolean {
|
|
|
+ return ContextCompat.checkSelfPermission(
|
|
|
+ this, Manifest.permission.CAMERA
|
|
|
+ ) == PackageManager.PERMISSION_GRANTED
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun requestCameraPermission() {
|
|
|
+ requestPermissionLauncher.launch(Manifest.permission.CAMERA)
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun startCamera() {
|
|
|
+ cameraProviderFuture = ProcessCameraProvider.getInstance(this)
|
|
|
+
|
|
|
+ cameraProviderFuture.addListener(Runnable {
|
|
|
+ val cameraProvider = cameraProviderFuture.get()
|
|
|
+ val rotation = previewView.display.rotation
|
|
|
+
|
|
|
+ // 预览配置
|
|
|
+ val preview = Preview.Builder()
|
|
|
+ .setTargetAspectRatio(AspectRatio.RATIO_4_3)
|
|
|
+ .setTargetRotation(rotation)
|
|
|
+ .build()
|
|
|
+ .also {
|
|
|
+ it.setSurfaceProvider(previewView.surfaceProvider)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 图像捕获配置
|
|
|
+ imageCapture = ImageCapture.Builder()
|
|
|
+ .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
|
|
|
+ .setTargetAspectRatio(AspectRatio.RATIO_4_3)
|
|
|
+ .setTargetRotation(rotation)
|
|
|
+ .setFlashMode(ImageCapture.FLASH_MODE_OFF)
|
|
|
+ .build()
|
|
|
+
|
|
|
+ // 图像分析配置(用于人脸质量检测)
|
|
|
+ imageAnalyzer = ImageAnalysis.Builder()
|
|
|
+ .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
|
|
+ .setTargetRotation(rotation)
|
|
|
+ .apply {
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
|
+ setTargetResolution(android.util.Size(640, 480))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .build()
|
|
|
+ .also { analyzer ->
|
|
|
+ analyzer.setAnalyzer(cameraExecutor, ImageAnalysis.Analyzer { imageProxy ->
|
|
|
+ analyzeFaceQuality(imageProxy)
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 选择前置摄像头
|
|
|
+ val cameraSelector = CameraSelector.Builder()
|
|
|
+ .requireLensFacing(CameraSelector.LENS_FACING_FRONT)
|
|
|
+ .build()
|
|
|
+
|
|
|
+ try {
|
|
|
+ cameraProvider.unbindAll()
|
|
|
+ camera = cameraProvider.bindToLifecycle(
|
|
|
+ this, cameraSelector, preview, imageCapture, imageAnalyzer
|
|
|
+ )
|
|
|
+ updateStatus("摄像头就绪,请将人脸对准引导框")
|
|
|
+ } catch (e: Exception) {
|
|
|
+ Log.e(TAG, "相机启动失败: ${e.message}")
|
|
|
+ updateStatus("相机启动失败")
|
|
|
+ Toast.makeText(this, "相机启动失败: ${e.message}", Toast.LENGTH_LONG).show()
|
|
|
+ }
|
|
|
+ }, ContextCompat.getMainExecutor(this))
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 人脸质量分析(保留校验功能,但不自动拍摄)
|
|
|
+ */
|
|
|
+ private fun analyzeFaceQuality(imageProxy: ImageProxy) {
|
|
|
+ if (isProcessingPhoto || isRequestInProgress) {
|
|
|
+ imageProxy.close()
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ val mediaImage = imageProxy.image
|
|
|
+ if (mediaImage == null) {
|
|
|
+ imageProxy.close()
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
|
|
|
+
|
|
|
+ faceDetector.process(image)
|
|
|
+ .addOnSuccessListener { faces ->
|
|
|
+ handleFaceDetectionResult(faces, imageProxy)
|
|
|
+ }
|
|
|
+ .addOnFailureListener { e ->
|
|
|
+ Log.e(TAG, "人脸检测失败: ${e.message}")
|
|
|
+ imageProxy.close()
|
|
|
+ }
|
|
|
+ .addOnCompleteListener {
|
|
|
+ imageProxy.close()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理人脸检测结果(仅用于质量提示,不自动拍摄)
|
|
|
+ */
|
|
|
+ private fun handleFaceDetectionResult(faces: List<com.google.mlkit.vision.face.Face>, imageProxy: ImageProxy) {
|
|
|
+ runOnUiThread {
|
|
|
+ if (faces.isEmpty()) {
|
|
|
+ // 未检测到人脸
|
|
|
+ stableFaceFrames = 0
|
|
|
+ isFaceDetected = false
|
|
|
+ isFaceQualityGood = false
|
|
|
+ updateStatus("未检测到人脸,请正对摄像头")
|
|
|
+ updateCaptureButtonState(false)
|
|
|
+ return@runOnUiThread
|
|
|
+ }
|
|
|
+
|
|
|
+ val face = faces.first()
|
|
|
+ val boundingBox = face.boundingBox
|
|
|
+
|
|
|
+ // 计算人脸位置和大小
|
|
|
+ val faceCenterX = (boundingBox.left + boundingBox.right) / 2f / imageProxy.width
|
|
|
+ val faceCenterY = (boundingBox.top + boundingBox.bottom) / 2f / imageProxy.height
|
|
|
+ val faceSize = boundingBox.width().toFloat() / imageProxy.width
|
|
|
+
|
|
|
+ // 检查人脸质量
|
|
|
+ val isFaceCentered = Math.abs(faceCenterX - 0.5f) < FACE_CENTER_THRESHOLD &&
|
|
|
+ Math.abs(faceCenterY - 0.5f) < FACE_CENTER_THRESHOLD
|
|
|
+ val isFaceGoodSize = faceSize > FACE_SIZE_THRESHOLD && faceSize < 0.6f
|
|
|
+ val isSingleFace = faces.size == 1
|
|
|
+
|
|
|
+ isFaceDetected = true
|
|
|
+
|
|
|
+ if (isFaceCentered && isFaceGoodSize && isSingleFace) {
|
|
|
+ // 检查人脸稳定性
|
|
|
+ if (stableFaceFrames == 0 ||
|
|
|
+ (Math.abs(faceSize - lastFaceSize) < FACE_MOVEMENT_THRESHOLD &&
|
|
|
+ Math.abs(faceCenterX - lastFacePositionX) < FACE_MOVEMENT_THRESHOLD &&
|
|
|
+ Math.abs(faceCenterY - lastFacePositionY) < FACE_MOVEMENT_THRESHOLD)) {
|
|
|
+
|
|
|
+ stableFaceFrames++
|
|
|
+
|
|
|
+ if (stableFaceFrames >= requiredStableFrames) {
|
|
|
+ // 人脸质量良好,可以拍摄
|
|
|
+ isFaceQualityGood = true
|
|
|
+ updateStatus("人脸质量良好,可以点击拍照")
|
|
|
+ updateCaptureButtonState(true)
|
|
|
+ } else {
|
|
|
+ // 人脸稳定中
|
|
|
+ isFaceQualityGood = false
|
|
|
+ updateStatus("请保持姿势稳定 (${stableFaceFrames}/${requiredStableFrames})")
|
|
|
+ updateCaptureButtonState(false)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 人脸移动,重置稳定性计数
|
|
|
+ stableFaceFrames = 1
|
|
|
+ isFaceQualityGood = false
|
|
|
+ updateStatus("检测到人脸,请保持稳定")
|
|
|
+ updateCaptureButtonState(false)
|
|
|
+ }
|
|
|
+
|
|
|
+ lastFaceSize = faceSize
|
|
|
+ lastFacePositionX = faceCenterX
|
|
|
+ lastFacePositionY = faceCenterY
|
|
|
+ } else {
|
|
|
+ // 人脸质量不符合要求
|
|
|
+ stableFaceFrames = 0
|
|
|
+ isFaceQualityGood = false
|
|
|
+ updateCaptureButtonState(false)
|
|
|
+
|
|
|
+ when {
|
|
|
+ !isSingleFace -> updateStatus("检测到多张人脸,请确保只有一人")
|
|
|
+ !isFaceCentered -> updateStatus("请将人脸对准引导框中心")
|
|
|
+ !isFaceGoodSize -> {
|
|
|
+ if (faceSize <= FACE_SIZE_THRESHOLD) {
|
|
|
+ updateStatus("请靠近摄像头")
|
|
|
+ } else {
|
|
|
+ updateStatus("请远离摄像头")
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新拍照按钮状态
|
|
|
+ */
|
|
|
+ private fun updateCaptureButtonState(enabled: Boolean) {
|
|
|
+ runOnUiThread {
|
|
|
+ if (enabled && !isProcessingPhoto && !isRequestInProgress) {
|
|
|
+ captureButton.isEnabled = true
|
|
|
+ captureButton.alpha = 1f
|
|
|
+ captureButton.text = "拍照"
|
|
|
+ } else {
|
|
|
+ captureButton.isEnabled = false
|
|
|
+ captureButton.alpha = 0.6f
|
|
|
+ if (isProcessingPhoto || isRequestInProgress) {
|
|
|
+ captureButton.text = "处理中..."
|
|
|
+ } else {
|
|
|
+ captureButton.text = "调整姿势"
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun manualCaptureFace() {
|
|
|
+ if (isProcessingPhoto || isRequestInProgress || !isFaceQualityGood) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ isProcessingPhoto = true
|
|
|
+ isRequestInProgress = true
|
|
|
+ updateStatus("正在拍照...")
|
|
|
+ showProgressBar(true)
|
|
|
+ updateUIState()
|
|
|
+
|
|
|
+ // 使用ImageCapture拍照
|
|
|
+ captureImageToFile()
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun captureImageToFile() {
|
|
|
+ val timestamp = System.currentTimeMillis()
|
|
|
+ val fileName = "face_register_$timestamp.jpg"
|
|
|
+ val file = File(cacheDir, fileName)
|
|
|
+
|
|
|
+ val outputFileOptions = ImageCapture.OutputFileOptions.Builder(file).build()
|
|
|
+
|
|
|
+ imageCapture?.takePicture(
|
|
|
+ outputFileOptions,
|
|
|
+ ContextCompat.getMainExecutor(this),
|
|
|
+ object : ImageCapture.OnImageSavedCallback {
|
|
|
+ override fun onImageSaved(output: ImageCapture.OutputFileResults) {
|
|
|
+ Log.d(TAG, "图片保存成功: ${file.absolutePath}")
|
|
|
+
|
|
|
+ runOnUiThread {
|
|
|
+ if (file.exists() && file.length() > 1024) {
|
|
|
+ processCapturedImage(file)
|
|
|
+ } else {
|
|
|
+ handleCaptureError("保存的图片文件无效")
|
|
|
+ if (file.exists()) file.delete()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onError(exception: ImageCaptureException) {
|
|
|
+ Log.e(TAG, "图片保存失败", exception)
|
|
|
+ runOnUiThread {
|
|
|
+ handleCaptureError("拍照失败: ${exception.message}")
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun processCapturedImage(file: File) {
|
|
|
+ updateStatus("正在处理图片...")
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 验证图片有效性
|
|
|
+ val bitmap = BitmapFactory.decodeFile(file.absolutePath)
|
|
|
+ if (bitmap == null) {
|
|
|
+ handleCaptureError("图片解码失败")
|
|
|
+ file.delete()
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查图片尺寸
|
|
|
+ if (bitmap.width < 100 || bitmap.height < 100) {
|
|
|
+ handleCaptureError("图片尺寸过小")
|
|
|
+ bitmap.recycle()
|
|
|
+ file.delete()
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ bitmap.recycle()
|
|
|
+
|
|
|
+ // 上传图片
|
|
|
+ uploadImageToServer(file)
|
|
|
+
|
|
|
+ } catch (e: Exception) {
|
|
|
+ Log.e(TAG, "图片处理失败", e)
|
|
|
+ handleCaptureError("图片处理失败: ${e.message}")
|
|
|
+ if (file.exists()) file.delete()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun uploadImageToServer(file: File) {
|
|
|
+ updateStatus("正在上传图片...")
|
|
|
+
|
|
|
+ try {
|
|
|
+ val requestFile = file.asRequestBody("image/jpeg".toMediaTypeOrNull())
|
|
|
+ val body = MultipartBody.Part.createFormData("file", file.name, requestFile)
|
|
|
+
|
|
|
+ viewModel.uploadFile(
|
|
|
+ body,
|
|
|
+ fail = { errorMessage ->
|
|
|
+ runOnUiThread {
|
|
|
+ // 删除临时文件
|
|
|
+ file.delete()
|
|
|
+ handleRegisterResponse(
|
|
|
+ success = false,
|
|
|
+ errorMessage = errorMessage
|
|
|
+ )
|
|
|
+ }
|
|
|
+ },
|
|
|
+ success = { result ->
|
|
|
+ runOnUiThread {
|
|
|
+ // 删除临时文件
|
|
|
+ file.delete()
|
|
|
+ viewModel.faceRegistration(result!!,
|
|
|
+ fail = { errorMessage ->
|
|
|
+ handleRegisterResponse(
|
|
|
+ success = false,
|
|
|
+ errorMessage = errorMessage
|
|
|
+ )
|
|
|
+ },
|
|
|
+ success = {
|
|
|
+ handleRegisterResponse(
|
|
|
+ success = true,
|
|
|
+ errorMessage = null
|
|
|
+ )
|
|
|
+ }
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
+
|
|
|
+ } catch (e: Exception) {
|
|
|
+ Log.e(TAG, "上传失败", e)
|
|
|
+ runOnUiThread {
|
|
|
+ if (file.exists()) file.delete()
|
|
|
+ handleRegisterResponse(success = false, errorMessage = "上传失败: ${e.message}")
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun handleRegisterResponse(success: Boolean, errorMessage: String?) {
|
|
|
+ isProcessingPhoto = false
|
|
|
+ isRequestInProgress = false
|
|
|
+ showProgressBar(false)
|
|
|
+ updateUIState()
|
|
|
+
|
|
|
+ // 重置人脸检测状态
|
|
|
+ stableFaceFrames = 0
|
|
|
+ isFaceDetected = false
|
|
|
+ isFaceQualityGood = false
|
|
|
+
|
|
|
+ if (success) {
|
|
|
+ // 注册成功
|
|
|
+ updateStatus("人脸注册成功!")
|
|
|
+ Toast.makeText(this, "人脸注册成功", Toast.LENGTH_SHORT).show()
|
|
|
+
|
|
|
+ // 2秒后返回个人中心
|
|
|
+ mainHandler.postDelayed({
|
|
|
+ JumpUtil.jumpActivity(RouterUrlCommon.personalCenter, this)
|
|
|
+ finish()
|
|
|
+ }, 2000)
|
|
|
+ } else {
|
|
|
+ // 注册失败
|
|
|
+ val attempts = captureAttempts.incrementAndGet()
|
|
|
+ val errorMsg = errorMessage ?: "注册失败"
|
|
|
+
|
|
|
+ if (attempts >= maxCaptureAttempts) {
|
|
|
+ // 达到最大尝试次数
|
|
|
+ updateStatus("注册失败,已达最大尝试次数")
|
|
|
+ Toast.makeText(this, errorMsg, Toast.LENGTH_LONG).show()
|
|
|
+ captureButton.isEnabled = false
|
|
|
+
|
|
|
+ // 5秒后返回
|
|
|
+ mainHandler.postDelayed({
|
|
|
+ JumpUtil.jumpActivity(RouterUrlCommon.personalCenter, this)
|
|
|
+ finish()
|
|
|
+ }, 5000)
|
|
|
+ } else {
|
|
|
+ // 还有机会,提示重试
|
|
|
+ currentAttempt++
|
|
|
+ updateStatus("注册失败,请重试 ($currentAttempt/$maxCaptureAttempts)")
|
|
|
+ Toast.makeText(this, "$errorMsg,请重试", Toast.LENGTH_SHORT).show()
|
|
|
+
|
|
|
+ // 3秒后重置状态
|
|
|
+ mainHandler.postDelayed({
|
|
|
+ if (!isProcessingPhoto && !isRequestInProgress) {
|
|
|
+ updateStatus("请将人脸对准引导框")
|
|
|
+ updateCaptureButtonState(false)
|
|
|
+ }
|
|
|
+ }, 3000)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun handleCaptureError(errorMessage: String) {
|
|
|
+ isProcessingPhoto = false
|
|
|
+ isRequestInProgress = false
|
|
|
+ showProgressBar(false)
|
|
|
+ updateUIState()
|
|
|
+
|
|
|
+ // 重置人脸检测状态
|
|
|
+ stableFaceFrames = 0
|
|
|
+ isFaceDetected = false
|
|
|
+ isFaceQualityGood = false
|
|
|
+
|
|
|
+ updateStatus("拍照失败")
|
|
|
+ Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT).show()
|
|
|
+
|
|
|
+ // 2秒后重置
|
|
|
+ mainHandler.postDelayed({
|
|
|
+ if (!isProcessingPhoto && !isRequestInProgress) {
|
|
|
+ updateStatus("请将人脸对准引导框")
|
|
|
+ updateCaptureButtonState(false)
|
|
|
+ }
|
|
|
+ }, 2000)
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun updateStatus(message: String) {
|
|
|
+ runOnUiThread {
|
|
|
+ statusTextView.text = message
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun showProgressBar(show: Boolean) {
|
|
|
+ runOnUiThread {
|
|
|
+ progressBar.visibility = if (show) View.VISIBLE else View.GONE
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun updateUIState() {
|
|
|
+ runOnUiThread {
|
|
|
+ if (isProcessingPhoto || isRequestInProgress) {
|
|
|
+ captureButton.isEnabled = false
|
|
|
+ captureButton.text = "处理中..."
|
|
|
+ captureButton.alpha = 0.6f
|
|
|
+ } else {
|
|
|
+ updateCaptureButtonState(isFaceQualityGood)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onDestroy() {
|
|
|
+ super.onDestroy()
|
|
|
+ isProcessingPhoto = false
|
|
|
+ isRequestInProgress = false
|
|
|
+ cameraExecutor.shutdown()
|
|
|
+ try {
|
|
|
+ if (!cameraExecutor.awaitTermination(1, java.util.concurrent.TimeUnit.SECONDS)) {
|
|
|
+ cameraExecutor.shutdownNow()
|
|
|
+ }
|
|
|
+ } catch (e: InterruptedException) {
|
|
|
+ cameraExecutor.shutdownNow()
|
|
|
+ }
|
|
|
+ faceDetector.close()
|
|
|
+ mainHandler.removeCallbacksAndMessages(null)
|
|
|
+ }
|
|
|
+}
|