Browse Source

refactor(更新)
- 人脸设置的逻辑优化

周文健 4 tháng trước cách đây
mục cha
commit
1fe449dd75

+ 53 - 19
app/src/main/java/com/grkj/iscs/features/main/fragment/user_info/SetFaceFragment.kt

@@ -11,6 +11,7 @@ import com.grkj.iscs.R
 import com.grkj.iscs.databinding.FragmentSetFaceBinding
 import com.grkj.iscs.features.main.viewmodel.user_info.UserInfoViewModel
 import com.grkj.shared.utils.ArcSoftUtil
+import com.grkj.shared.utils.CancellableTimer
 import com.grkj.ui_base.base.BaseFragment
 import com.sik.sikcore.date.TimeUtils
 import com.sik.sikcore.extension.file
@@ -18,7 +19,6 @@ import com.sik.sikcore.extension.setDebouncedClickListener
 import com.sik.sikcore.extension.toJson
 import com.sik.sikimage.ImageConvertUtils
 import dagger.hilt.android.AndroidEntryPoint
-import kotlin.math.log
 
 /**
  * 设置人脸
@@ -27,7 +27,26 @@ import kotlin.math.log
 class SetFaceFragment : BaseFragment<FragmentSetFaceBinding>() {
     private val viewModel: UserInfoViewModel by viewModels()
     private var mCapturedBitmap: Bitmap? = null
-    private var isFaceChecking: Boolean = false
+    private var isFaceDetect: Boolean = false
+    private var isInCountDown: Boolean = false
+    private val captureTimer = CancellableTimer(4000, 1000, {
+        binding.countDownTip.text = "${(3000 - it) / 1000}"
+    }) {
+        ArcSoftUtil.inDetecting = true
+        isFaceDetect = true
+        binding.previewLayout.visibility = View.INVISIBLE
+        binding.image.visibility = View.VISIBLE
+        binding.recapture.visibility = View.VISIBLE
+        binding.confirm.visibility = View.VISIBLE
+        binding.countDownTip.text = getString(R.string.detect_face_tip)
+        binding.countDownTip.isVisible = false
+    }
+    private val reCaptureTimer = CancellableTimer(2000, 1000, {}) {
+        isFaceDetect = false
+        ArcSoftUtil.inDetecting = false
+        isInCountDown = false
+    }
+
     override fun getLayoutId(): Int {
         return R.layout.fragment_set_face
     }
@@ -40,18 +59,19 @@ class SetFaceFragment : BaseFragment<FragmentSetFaceBinding>() {
         binding.setOrResetFace.setDebouncedClickListener {
             binding.faceViewLayout.isVisible = false
             binding.faceSetLayout.isVisible = true
-            isFaceChecking = false
+            binding.image.isVisible = false
+            binding.previewLayout.isVisible = true
             startFace()
         }
         binding.cancel.setDebouncedClickListener {
             releaseFace()
-            isFaceChecking = false
             binding.faceViewLayout.isVisible = true
             binding.faceSetLayout.isVisible = false
+            binding.image.isVisible = false
+            binding.previewLayout.isVisible = true
         }
         binding.confirm.setDebouncedClickListener {
             releaseFace()
-            isFaceChecking = false
             binding.faceViewLayout.isVisible = true
             binding.faceSetLayout.isVisible = false
             val saveFileName =
@@ -70,9 +90,12 @@ class SetFaceFragment : BaseFragment<FragmentSetFaceBinding>() {
             }
         }
         binding.recapture.setDebouncedClickListener {
-            isFaceChecking = false
+            reCaptureTimer.start()
+            binding.countDownTip.isVisible = false
             binding.image.isVisible = false
             binding.previewLayout.isVisible = true
+            binding.confirm.isVisible = false
+            binding.recapture.isVisible = false
         }
     }
 
@@ -114,35 +137,46 @@ class SetFaceFragment : BaseFragment<FragmentSetFaceBinding>() {
             binding.preview,
             true
         ) { bitmap, faceSize, alive ->
-            if (isFaceChecking) {
-                ArcSoftUtil.inDetecting = false
-                return@initCamera
-            }
-            isFaceChecking = true
             binding.tipTv.isVisible = faceSize > 1 || alive == false
             logger.info("人脸检测结果: ${bitmap == null},$faceSize,$alive")
             if (faceSize > 1) {
                 binding.tipTv.text = getString(R.string.only_one_person_allowed)
-                isFaceChecking = false
                 ArcSoftUtil.inDetecting = false
+                stopCountDown()
                 return@initCamera
             }
             if (alive == false) {
                 binding.tipTv.text =
                     getString(R.string.real_person_verification_required)
-                isFaceChecking = false
                 ArcSoftUtil.inDetecting = false
+                stopCountDown()
                 return@initCamera
             }
-            binding.previewLayout.visibility = View.INVISIBLE
-            binding.image.visibility = View.VISIBLE
-            mCapturedBitmap = bitmap
-            binding.image.setImageBitmap(bitmap)
-            binding.recapture.visibility = View.VISIBLE
-            binding.confirm.visibility = View.VISIBLE
+            if (!isInCountDown) {
+                startCountDown()
+            }
+            if (!isFaceDetect) {
+                mCapturedBitmap = bitmap
+                binding.image.setImageBitmap(bitmap)
+            }
+            ArcSoftUtil.inDetecting = false
         }
     }
 
+    private fun startCountDown() {
+        isInCountDown = true
+        binding.countDownTip.text = getString(R.string.detect_face_tip)
+        binding.countDownTip.isVisible = true
+        captureTimer.start()
+    }
+
+    private fun stopCountDown() {
+        isInCountDown = false
+        binding.countDownTip.text = getString(R.string.detect_face_tip)
+        binding.countDownTip.isVisible = false
+        captureTimer.cancel()
+    }
+
     private fun releaseFace() {
         ArcSoftUtil.stop()
     }

+ 27 - 13
app/src/main/res/layout-land/fragment_set_face.xml

@@ -128,7 +128,7 @@
                             android:id="@+id/preview_layout"
                             android:layout_width="match_parent"
                             android:layout_height="match_parent"
-                            android:visibility="invisible">
+                            android:visibility="visible">
 
                             <TextureView
                                 android:id="@+id/preview"
@@ -140,6 +140,32 @@
                                 android:layout_height="match_parent"
                                 android:scaleType="fitXY"
                                 android:src="@drawable/mask_vector_circle" />
+
+                            <TextView
+                                android:id="@+id/count_down_tip"
+                                android:layout_width="wrap_content"
+                                android:layout_height="wrap_content"
+                                android:layout_gravity="center|bottom"
+                                android:layout_marginBottom="@dimen/common_spacing"
+                                android:text="@string/detect_face_tip"
+                                android:textColor="@color/dialogxColorBlue"
+                                android:textSize="@dimen/common_text_size_big"
+                                android:textStyle="bold"
+                                android:visibility="gone"
+                                tools:text="检测到人脸,即将拍摄" />
+                            <TextView
+                                android:id="@+id/tip_tv"
+                                android:layout_width="match_parent"
+                                android:layout_height="wrap_content"
+                                android:layout_marginTop="10dp"
+                                android:gravity="center"
+                                android:text="@string/only_one_person_allowed"
+                                android:textColor="@color/common_status_red"
+                                android:textSize="@dimen/common_text_size_big"
+                                android:layout_gravity="center|bottom"
+                                android:layout_marginBottom="@dimen/common_spacing"
+                                android:visibility="gone"
+                                tools:text="请保证画面中只有自己" />
                         </FrameLayout>
 
                         <ImageView
@@ -187,18 +213,6 @@
                             android:textColor="@color/black"
                             android:textSize="@dimen/common_text_size_small" />
 
-                        <TextView
-                            android:id="@+id/tip_tv"
-                            android:layout_width="match_parent"
-                            android:layout_height="wrap_content"
-                            android:layout_marginTop="10dp"
-                            android:background="@color/common_status_red"
-                            android:gravity="center"
-                            android:text="@string/only_one_person_allowed"
-                            android:textColor="@color/white"
-                            android:textSize="@dimen/common_text_size_big"
-                            android:visibility="gone"
-                            tools:text="请保证画面中只有自己" />
                     </LinearLayout>
                 </LinearLayout>
 

+ 28 - 14
app/src/main/res/layout/fragment_set_face.xml

@@ -122,7 +122,7 @@
                         android:id="@+id/preview_layout"
                         android:layout_width="match_parent"
                         android:layout_height="match_parent"
-                        android:visibility="invisible">
+                        android:visibility="visible">
 
                         <TextureView
                             android:id="@+id/preview"
@@ -134,6 +134,33 @@
                             android:layout_height="match_parent"
                             android:scaleType="fitXY"
                             android:src="@drawable/mask_vector_circle" />
+
+                        <TextView
+                            android:id="@+id/count_down_tip"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_gravity="center|bottom"
+                            android:layout_marginBottom="@dimen/common_spacing"
+                            android:textColor="@color/dialogxColorBlue"
+                            android:textSize="@dimen/common_text_size_big"
+                            android:textStyle="bold"
+                            android:visibility="gone"
+                            android:text="@string/detect_face_tip"
+                            tools:text="检测到人脸,即将拍摄" />
+
+                        <TextView
+                            android:id="@+id/tip_tv"
+                            android:layout_width="match_parent"
+                            android:layout_height="wrap_content"
+                            android:layout_marginTop="10dp"
+                            android:gravity="center"
+                            android:text="@string/only_one_person_allowed"
+                            android:textColor="@color/common_status_red"
+                            android:textSize="@dimen/common_text_size_big"
+                            android:layout_gravity="center|bottom"
+                            android:layout_marginBottom="@dimen/common_spacing"
+                            android:visibility="gone"
+                            tools:text="请保证画面中只有自己" />
                     </FrameLayout>
 
                     <ImageView
@@ -180,19 +207,6 @@
                         android:text="@string/capture_tip_content"
                         android:textColor="@color/black"
                         android:textSize="@dimen/common_text_size_small" />
-
-                    <TextView
-                        android:id="@+id/tip_tv"
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:layout_marginTop="10dp"
-                        android:background="@color/common_status_red"
-                        android:gravity="center"
-                        android:text="@string/only_one_person_allowed"
-                        android:textColor="@color/white"
-                        android:textSize="@dimen/common_text_size_big"
-                        android:visibility="gone"
-                        tools:text="请保证画面中只有自己" />
                 </LinearLayout>
 
                 <androidx.constraintlayout.widget.ConstraintLayout

+ 1 - 0
app/src/main/res/values-en/strings.xml

@@ -452,5 +452,6 @@
     <string name="create_job_failed">Create job failed</string>
     <string name="please_press_the_fingerprint_again">Please press the fingerprint again</string>
     <string name="fingerprint_register_failed">Fingerprint input failed</string>
+    <string name="detect_face_tip">Detected face, about to shoot</string>
 
 </resources>

+ 1 - 0
app/src/main/res/values-zh/strings.xml

@@ -452,5 +452,6 @@
     <string name="create_job_failed">创建作业失败</string>
     <string name="please_press_the_fingerprint_again">请再次按压指纹</string>
     <string name="fingerprint_register_failed">指纹录入失败</string>
+    <string name="detect_face_tip">检测到人脸,即将拍摄</string>
 
 </resources>

+ 1 - 0
app/src/main/res/values/strings.xml

@@ -455,5 +455,6 @@
     <string name="create_job_failed">创建作业失败</string>
     <string name="please_press_the_fingerprint_again">请再次按压指纹</string>
     <string name="fingerprint_register_failed">指纹录入失败</string>
+    <string name="detect_face_tip">检测到人脸,即将拍摄</string>
 
 </resources>

+ 5 - 5
shared/src/main/java/com/grkj/shared/utils/ArcSoftUtil.kt

@@ -201,13 +201,13 @@ object ArcSoftUtil {
                         previewSize!!.width,
                         previewSize!!.height
                     )
-                    val faceRect = faceInfoList[0].rect.expandToPadCenter()
+//                    val faceRect = faceInfoList[0].rect.expandToPadCenter()
                     logger.debug("人脸检测结果-识别结果 : ${bitmap == null} - $faceInfoList")
                     callBack(bitmap, faceInfoList.size, true)
-                    bitmap?.let {
-                        val faceBitmap = CropImageUtils.cropBitmap(bitmap, faceRect)
-                        callBack(faceBitmap, faceInfoList.size, true)
-                    } ?: callBack(null, faceInfoList.size, true)
+//                    bitmap?.let {
+//                        val faceBitmap = CropImageUtils.cropBitmap(bitmap, faceRect)
+//                        callBack(faceBitmap, faceInfoList.size, true)
+//                    } ?: callBack(null, faceInfoList.size, true)
                 } else {
                     inDetecting = false
                     return

+ 50 - 0
shared/src/main/java/com/grkj/shared/utils/CancellableTimer.kt

@@ -0,0 +1,50 @@
+package com.grkj.shared.utils
+
+import android.os.Handler
+import android.os.Looper
+
+/**
+ * 可取消的重复定时器:
+ * – 每隔 intervalMillis 调用 onTick()
+ * – 累计时间 >= delayMillis 时,调用一次 onFinish() 并自动停止
+ *
+ * @param delayMillis    总时长(毫秒),当累计时间达到或超过这个值时触发 onFinish
+ * @param intervalMillis 触发 onTick 的间隔(毫秒)
+ * @param onTick         每次间隔到时的回调
+ * @param onFinish       总时长到时的回调(触发一次后不再继续)
+ */
+class CancellableTimer(
+    private val delayMillis: Long,
+    private val intervalMillis: Long,
+    private val onTick: (Long) -> Unit,
+    private val onFinish: () -> Unit
+) {
+    private val handler = Handler(Looper.getMainLooper())
+    private var elapsed = 0L
+
+    private val job = object : Runnable {
+        override fun run() {
+            elapsed += intervalMillis
+            if (elapsed < delayMillis) {
+                // 还没到总时长,继续 onTick 并重发
+                onTick(elapsed)
+                handler.postDelayed(this, intervalMillis)
+            } else {
+                // 累计到达或超过总时长,触发一次 onFinish 并不再重发
+                onFinish()
+            }
+        }
+    }
+
+    /** 启动定时器(若已在运行会先取消并重置) */
+    fun start() {
+        cancel()
+        elapsed = 0L
+        handler.postDelayed(job, intervalMillis)
+    }
+
+    /** 取消定时器(之后不会再触发 onTick 或 onFinish) */
+    fun cancel() {
+        handler.removeCallbacks(job)
+    }
+}