瀏覽代碼

1. 重写人脸检测功能
2. 修改登录流程

JaycePC 1 年之前
父節點
當前提交
e36c444603

+ 9 - 5
app/build.gradle

@@ -131,12 +131,16 @@ dependencies {
     implementation(name: 'flowlayout-1.0.0', ext: 'aar')
 
     implementation 'com.blankj:utilcodex:1.31.1'
-//    implementation 'com.github.jenly1314.MLKit:mlkit-common:2.2.1'
-//    implementation 'com.github.jenly1314.MLKit:mlkit-face-detection:2.2.1'
-//    implementation 'com.github.jenly1314:camera-scan:1.2.0'
 
-    implementation 'com.google.mlkit:face-detection:16.1.7'
-    implementation 'com.otaliastudios:cameraview:2.7.2'
+//    implementation 'com.google.mlkit:face-detection:16.1.7'
+//    implementation 'com.otaliastudios:cameraview:2.7.2'
 
 
+    //公共库 (*必须)
+    implementation 'com.github.jenly1314.MLKit:mlkit-common:2.2.1'
+    //条码识别 (可选)
+    implementation 'com.github.jenly1314.MLKit:mlkit-barcode-scanning:2.2.1'
+    //人脸检测 (可选)
+    implementation 'com.github.jenly1314.MLKit:mlkit-face-detection:2.2.1'
+
 }

+ 1 - 0
app/src/main/AndroidManifest.xml

@@ -44,6 +44,7 @@
         android:theme="@style/Theme.AppFullTheme"
         android:usesCleartextTraffic="true"
         tools:targetApi="m">
+
         <receiver
             android:name=".broadcast.BootBroadcastReceiver"
             android:exported="true">

+ 2 - 8
app/src/main/java/com/dlc/eboard/ui/SplashActivity.kt

@@ -113,8 +113,6 @@ class SplashActivity : RcBaseActivity<ActivitySplashBinding>() {
     }
 
 
-    private var lastErrTime: Long = 0L
-
     /**
      * 登录获取token
      */
@@ -130,12 +128,8 @@ class SplashActivity : RcBaseActivity<ActivitySplashBinding>() {
                     authLogin()
                 }
             }, { throwable ->
-                val nowTime = System.currentTimeMillis()
-                if (0L == lastErrTime && nowTime - lastErrTime > 20000) {
-                    lastErrTime = nowTime
-                    //初始化进入后网络反应太慢 重新调用
-                    authLogin()
-                }
+                //初始化进入后网络反应太慢 重新调用
+                authLogin()
                 showNetError(throwable)
             })
         addDisposable(disposable)

+ 3 - 3
app/src/main/java/com/dlc/eboard/ui/auth/DualAuthActivity.kt

@@ -14,7 +14,7 @@ import com.dlc.eboard.serial.OnSerialScanListener
 import com.dlc.eboard.serial.SerialPortHelper
 import com.dlc.eboard.ui.MainActivity
 import com.dlc.eboard.ui.auth.fragment.CardAuthFragment
-import com.dlc.eboard.ui.auth.fragment.FaceAuthFragment
+import com.dlc.eboard.ui.auth.fragment.FaceDetectionFragment
 import com.dlc.eboard.ui.auth.fragment.PasswordAuthFragment
 import com.dlc.eboard.ui.common.BaseCountDownActivity
 import com.dlc.eboard.ui.common.TabFragmentPagerAdapter
@@ -79,11 +79,11 @@ class DualAuthActivity :
         return when (mAuthTypeConfig) {
             AuthTypeConfig.FACE_PASSWORD.type -> {
                 mPasswordAuthFragment = PasswordAuthFragment.newInstance()
-                mutableListOf(FaceAuthFragment.newInstance(), mPasswordAuthFragment!!)
+                mutableListOf(FaceDetectionFragment.newInstance(), mPasswordAuthFragment!!)
             }
             AuthTypeConfig.FACE_CARD.type -> {
                 mCardAuthFragment = CardAuthFragment.newInstance(smaller = true)
-                mutableListOf(FaceAuthFragment.newInstance(), mCardAuthFragment!!)
+                mutableListOf(FaceDetectionFragment.newInstance(), mCardAuthFragment!!)
             }
             AuthTypeConfig.CARD_PASSWORD.type -> {
                 mCardAuthFragment = CardAuthFragment.newInstance(smaller = true)

+ 4 - 3
app/src/main/java/com/dlc/eboard/ui/auth/FourChoiceAuthActivity.kt

@@ -12,10 +12,9 @@ import com.dlc.eboard.common.Constants
 import com.dlc.eboard.databinding.ActivityAuthFourChoiceBinding
 import com.dlc.eboard.serial.OnSerialScanListener
 import com.dlc.eboard.serial.SerialPortHelper
-import com.dlc.eboard.ui.HomeActivity
 import com.dlc.eboard.ui.MainActivity
 import com.dlc.eboard.ui.auth.fragment.CardAuthFragment
-import com.dlc.eboard.ui.auth.fragment.FaceAuthFragment
+import com.dlc.eboard.ui.auth.fragment.FaceDetectionFragment
 import com.dlc.eboard.ui.auth.fragment.FingerAuthFragment
 import com.dlc.eboard.ui.common.BaseCountDownActivity
 import com.dlc.eboard.ui.common.TabFragmentPagerAdapter
@@ -44,7 +43,9 @@ class FourChoiceAuthActivity :
         super.initViews(savedInstanceState)
 
         val tabArray = mutableListOf(
-            FaceAuthFragment.newInstance(),
+//            FacesAuthFragment.newInstance(),
+            FaceDetectionFragment.newInstance(),
+//            FaceAuthFragment.newInstance(),
             FingerAuthFragment.newInstance(),
             CardAuthFragment.newInstance()/*,
             PasswordAuthFragment.newInstance()*/

+ 2 - 2
app/src/main/java/com/dlc/eboard/ui/auth/TwoChoiceAuthActivity.kt

@@ -5,7 +5,7 @@ import android.os.Bundle
 import com.dlc.eboard.LabApp
 import com.dlc.eboard.databinding.ActivityAuthTwoChoiceBinding
 import com.dlc.eboard.ui.MainActivity
-import com.dlc.eboard.ui.auth.fragment.FaceAuthFragment
+import com.dlc.eboard.ui.auth.fragment.FaceDetectionFragment
 import com.dlc.eboard.ui.auth.fragment.FingerAuthFragment
 import com.dlc.eboard.ui.common.BaseCountDownActivity
 import com.dlc.eboard.ui.common.TabFragmentPagerAdapter
@@ -33,7 +33,7 @@ class TwoChoiceAuthActivity :
 
         viewBinding.labelText.text = "切换指纹验证"
         val tabArray = mutableListOf(
-            FaceAuthFragment.newInstance(),
+            FaceDetectionFragment.newInstance(),
             FingerAuthFragment.newInstance()
         )
         val adapter = TabFragmentPagerAdapter(supportFragmentManager, tabArray)

+ 198 - 244
app/src/main/java/com/dlc/eboard/ui/auth/fragment/FaceAuthFragment.kt

@@ -1,244 +1,198 @@
-package com.dlc.eboard.ui.auth.fragment
-
-import android.graphics.Matrix
-import android.graphics.RectF
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.ViewGroup
-import androidx.appcompat.app.AlertDialog
-import com.dlc.eboard.LabApp
-import com.dlc.eboard.databinding.FragmentAuthFaceMatchingBinding
-import com.dlc.eboard.ui.auth.AuthType
-import com.dlc.eboard.ui.auth.OnAuthResultListener
-import com.dlc.eboard.utils.SharedPreferencesUtil
-import com.google.mlkit.vision.common.InputImage
-import com.google.mlkit.vision.face.Face
-import com.google.mlkit.vision.face.FaceDetection
-import com.google.mlkit.vision.face.FaceDetectorOptions
-import com.otaliastudios.cameraview.CameraListener
-import com.otaliastudios.cameraview.PictureResult
-import com.otaliastudios.cameraview.controls.Facing
-import com.otaliastudios.cameraview.frame.Frame
-import com.otaliastudios.cameraview.frame.FrameProcessor
-import com.otaliastudios.cameraview.size.Size
-import com.otaliastudios.cameraview.size.SizeSelector
-import com.rc.core.ui.fragment.RcBaseFragment
-import com.rc.core.util.MediaUtils
-import com.rc.httpcore.client.ApiRepository
-import com.rc.httpcore.exception.NetException
-import java.io.File
-
-/**
- * 人脸识别
- *
- * @author ReiChin_
- */
-class FaceAuthFragment :
-    RcBaseFragment<FragmentAuthFaceMatchingBinding>() {
-
-    companion object {
-        const val TAG = "FaceAuthFragment"
-
-        private const val WHAT_TAKE_PICTURE = 1
-
-        private const val TIMEOUT_TAKE_PICTURE = 200L
-
-        fun newInstance() = FaceAuthFragment()
-    }
-
-    /**
-     * A: 是否需要人脸验证
-     * B: 是否已完成验证
-     */
-    private var isInComparison: Pair<Boolean, Boolean> = Pair(true, false)
-    private var faceDetectorOptions: FaceDetectorOptions? = null
-    override fun createViewBinding(
-        inflater: LayoutInflater,
-        container: ViewGroup?
-    ): FragmentAuthFaceMatchingBinding {
-        return FragmentAuthFaceMatchingBinding.inflate(inflater, container, false)
-    }
-
-    override fun initViews(savedInstanceState: Bundle?) {
-        // 相机选择
-        val retrievedValue = SharedPreferencesUtil.getString(requireActivity(), "0")
-        if (retrievedValue == "0") {
-            viewBinding.mlCameraView.facing = Facing.FRONT
-        } else if (retrievedValue == "1") {
-            viewBinding.mlCameraView.facing = Facing.BACK
-        }
-        viewBinding.mlCameraView.setPreviewStreamSize(SizeSelector { source ->
-            val sizeList: MutableList<Size> = ArrayList()
-            for (size in source) {
-                val width = size.width
-                if (width == 800) {
-                    sizeList.add(size)
-                }
-            }
-            if (sizeList.isEmpty()) {
-                return@SizeSelector source
-            }
-            sizeList
-        })
-        isInComparison = Pair(true, false)
-        faceDetectorOptions = FaceDetectorOptions.Builder()
-            .setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL)
-            .setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_ALL).build()
-    }
-
-    /**
-     * 人脸角度是否符合预期
-     */
-    private fun isHeadEulerAngleAccord(face: Face): Boolean {
-        return face.headEulerAngleY < 10 && face.headEulerAngleY > -10
-                && face.headEulerAngleX < 10 && face.headEulerAngleX > -10
-                && face.headEulerAngleZ < 10 && face.headEulerAngleZ > -10
-    }
-
-    override fun onResume() {
-        super.onResume()
-        isInComparison = Pair(true, false)
-
-        viewBinding.mlCameraView.addCameraListener(cameraListener)
-        viewBinding.mlCameraView.addFrameProcessor(frameProcessor)
-        viewBinding.mlCameraView.open()
-    }
-
-    /**
-     * 帧捕获
-     */
-    private var frameProcessor: FrameProcessor = FrameProcessor { frame ->
-        onProcess(frame)
-    }
-    private var matrix: Matrix = Matrix()
-
-    /**
-     * 帧处理
-     */
-    private fun onProcess(frame: Frame) {
-        if (isInComparison.first) {
-            val size = frame.size
-            val image = InputImage.fromByteArray(
-                frame.getData(),
-                size.width,
-                size.height,
-                frame.rotation,
-                InputImage.IMAGE_FORMAT_NV21
-            )
-            val faceDetector = FaceDetection.getClient(faceDetectorOptions!!)
-            // 发起人脸识别
-            faceDetector.process(image).addOnSuccessListener { faces: MutableList<Face> ->
-                // 是否检测到人脸
-                if (faces.isNotEmpty()) {
-                    val face = faces[0]
-                    val srcRect = RectF(face.boundingBox);
-                    val dstRect = RectF(0f, 0f, 0f, 0f)
-                    matrix.mapRect(dstRect, srcRect)
-                    viewBinding.detectView.onDetectFace(dstRect);
-                    // 人脸角度是否符合
-                    if (isHeadEulerAngleAccord(face)) {
-                        if (null != face.leftEyeOpenProbability && null != face.rightEyeOpenProbability) {
-                            // 睁眼检测
-                            if (face.leftEyeOpenProbability!! > 0.5 && face.rightEyeOpenProbability!! > 0.5) {
-                                // 需要人脸识别,也没有在比对中
-                                if (isInComparison.first && !isInComparison.second) {
-                                    // 需要人脸识别,正在比对中
-                                    isInComparison = Pair(true, true)
-                                    // 快速捕获一帧图片
-                                    viewBinding.mlCameraView.takePicture()
-                                }
-                            }
-                        }
-                    }
-                } else {
-                    viewBinding.detectView.onDetectFace(RectF(0f, 0f, 0f, 0f));
-                }
-            }
-        }
-    }
-
-    /**
-     * 相机监听
-     */
-    private var cameraListener: CameraListener = object : CameraListener() {
-        /**
-         * 拍照监听
-         */
-        override fun onPictureTaken(result: PictureResult) {
-            super.onPictureTaken(result)
-            val photoFile = File(
-                requireActivity().getExternalFilesDir("photos"),
-                "${System.currentTimeMillis()}.jpg"
-            )
-            result.toFile(photoFile) { file: File? ->
-                callFaceMatchingApi(file)
-            }
-        }
-    }
-
-    override fun onPause() {
-        super.onPause()
-        viewBinding.mlCameraView.removeCameraListener(cameraListener)
-        viewBinding.mlCameraView.removeFrameProcessor(frameProcessor)
-        viewBinding.mlCameraView.close()
-    }
-
-    private fun callFaceMatchingApi(photoFile: File?) {
-        // 没有人脸文件则重新发起
-        if (photoFile == null || !photoFile.exists()) {
-            isInComparison = Pair(true, false)
-            return
-        }
-
-        val disposable = ApiRepository.authFace(photoFile, LabApp.sLabConfig!!.labId)
-            .subscribe({ userVo ->
-                MediaUtils.deleteFile(photoFile)
-                if (!isResumed) return@subscribe
-
-                val activity = requireActivity()
-                if (activity is OnAuthResultListener) {
-                    if (!activity.onAuthSuccess(AuthType.FACE, userVo)) {
-                        isInComparison = Pair(false, false)
-                    }
-                }
-            }, { throwable ->
-                MediaUtils.deleteFile(photoFile)
-                if (!isResumed) return@subscribe
-                isInComparison = Pair(true, false)
-                if (!pretreatmentError(throwable)) {
-                    throwable.printStackTrace()
-                    showNetError(throwable)
-                }
-            })
-        addDisposable(disposable)
-    }
-
-    private fun pretreatmentError(throwable: Throwable): Boolean {
-        if (throwable is NetException) {
-            if ("500" == throwable.code &&
-                true == throwable.message?.startsWith("人脸对比不符")
-            ) {
-                // 500- 人脸对比不符!
-                AlertDialog.Builder(requireActivity())
-                    .setCancelable(false)
-                    .setTitle("提示")
-                    .setMessage(throwable.message)
-                    .setPositiveButton("确定") { _, _ ->
-                    }.show()
-                return true
-            } else if ("500" == throwable.code
-                && throwable.message == "识别中"
-            ) {
-                return true
-            }
-        }
-        return false
-    }
-
-    override fun onDestroy() {
-        super.onDestroy()
-        viewBinding.mlCameraView.clearCameraListeners();
-        viewBinding.mlCameraView.destroy();
-    }
-
-}
+//package com.dlc.eboard.ui.auth.fragment
+//
+//import android.os.Bundle
+//import android.view.LayoutInflater
+//import android.view.ViewGroup
+//import com.dlc.eboard.databinding.FragmentAuthFaceMatchingBinding
+//import com.rc.core.ui.fragment.RcBaseFragment
+//
+///**
+// * 人脸识别
+// *
+// * @author ReiChin_
+// */
+//class FaceAuthFragment :
+//    RcBaseFragment<FragmentAuthFaceMatchingBinding>() {
+//
+//    companion object {
+//        const val TAG = "FaceAuthFragment"
+//
+//        private const val WHAT_TAKE_PICTURE = 1
+//
+//        private const val TIMEOUT_TAKE_PICTURE = 200L
+//
+//        fun newInstance() = FaceAuthFragment()
+//    }
+//
+////    private var faceDetectorOptions: FaceDetectorOptions? = null
+////
+//    override fun createViewBinding(
+//    inflater: LayoutInflater,
+//    container: ViewGroup?
+//    ): FragmentAuthFaceMatchingBinding {
+//        return FragmentAuthFaceMatchingBinding.inflate(inflater, container, false)
+//    }
+//
+//    override fun initViews(savedInstanceState: Bundle?) {
+////        // 相机选择
+////        val retrievedValue = SharedPreferencesUtil.getString(requireActivity(), "0")
+////        if (retrievedValue == "0") {
+////            viewBinding.mlCameraView.facing = Facing.FRONT
+////        } else if (retrievedValue == "1") {
+////            viewBinding.mlCameraView.facing = Facing.BACK
+////        }
+////        viewBinding.mlCameraView.setPreviewStreamSize(SizeSelector { source ->
+////            val sizeList: MutableList<Size> = ArrayList()
+////            for (size in source) {
+////                val width = size.width
+////                if (width == 800) {
+////                    sizeList.add(size)
+////                }
+////            }
+////            if (sizeList.isEmpty()) {
+////                return@SizeSelector source
+////            }
+////            sizeList
+////        })
+////        faceDetectorOptions = FaceDetectorOptions.Builder()
+////            .setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL)
+////            .setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_ALL).build()
+//    }
+////
+////    override fun onResume() {
+////        super.onResume()
+//////        viewBinding.mlCameraView.addCameraListener(cameraListener)
+//////        viewBinding.mlCameraView.addFrameProcessor(frameProcessor)
+//////        viewBinding.mlCameraView.open()
+////    }
+////
+////    /**
+////     * 帧捕获
+////     */
+////    private var frameProcessor: FrameProcessor = FrameProcessor { frame ->
+////        onProcess(frame)
+////    }
+////    private var matrix: Matrix = Matrix()
+////
+////    private var lastPicture = 0L
+////
+////    /**
+////     * 帧处理
+////     */
+////    private fun onProcess(frame: Frame) {
+////        val size = frame.size
+////
+////        val image = InputImage.fromByteArray(
+////            frame.getData(),
+////            size.width,
+////            size.height,
+////            frame.rotation,
+////            InputImage.IMAGE_FORMAT_NV21
+////        )
+////        val faceDetector = FaceDetection.getClient(faceDetectorOptions!!)
+////        // 发起人脸识别
+////        faceDetector.process(image).addOnSuccessListener { faces: MutableList<Face> ->
+////            // 是否检测到人脸
+////            if (faces.isNotEmpty()) {
+////                val face = faces[0]
+//////                val srcRect = RectF(face.boundingBox);
+//////                val dstRect = RectF(0f, 0f, 0f, 0f)
+//////                matrix.mapRect(dstRect, srcRect)
+//////                // 绘制人脸框
+//////                viewBinding.detectView.onDetectFace(dstRect);
+////
+////                val nowTime = System.currentTimeMillis()
+////                if (lastPicture == 0L || nowTime - lastPicture > 3000) {
+////                    lastPicture = nowTime
+////                    // 快速捕获一帧图片
+////                    viewBinding.mlCameraView.takePicture()
+////                }
+////            } else {
+//////                viewBinding.detectView.onDetectFace(RectF(0f, 0f, 0f, 0f));
+////            }
+////        }
+////    }
+////
+////    /**
+////     * 相机监听
+////     */
+////    private var cameraListener: CameraListener = object : CameraListener() {
+////        /**
+////         * 拍照监听
+////         */
+////        override fun onPictureTaken(result: PictureResult) {
+////            super.onPictureTaken(result)
+////            val photoFile = File(
+////                requireActivity().getExternalFilesDir("photos"),
+////                "${System.currentTimeMillis()}.jpg"
+////            )
+////            LogUtils.json("Jayce", result.format)
+////            result.toFile(photoFile) { file: File? ->
+////                callFaceMatchingApi(file)
+////            }
+////        }
+////    }
+////
+////    override fun onPause() {
+////        super.onPause()
+////        viewBinding.mlCameraView.removeCameraListener(cameraListener)
+////        viewBinding.mlCameraView.removeFrameProcessor(frameProcessor)
+////        viewBinding.mlCameraView.close()
+////    }
+////
+////    private fun callFaceMatchingApi(photoFile: File?) {
+////        // 没有人脸文件则重新发起
+////        if (photoFile == null || !photoFile.exists()) {
+////            return
+////        }
+////        val disposable = ApiRepository.authFace(photoFile, LabApp.sLabConfig!!.labId)
+////            .subscribe({ userVo ->
+////                LogUtils.json("Jayce", userVo)
+////                MediaUtils.deleteFile(photoFile)
+////                if (!isResumed) return@subscribe
+////
+////                val activity = requireActivity()
+////                if (activity is OnAuthResultListener) {
+////                    activity.onAuthSuccess(AuthType.FACE, userVo)
+////                }
+////            }, { throwable ->
+////                LogUtils.e("Jayce", throwable.message)
+////                MediaUtils.deleteFile(photoFile)
+////                if (!isResumed) return@subscribe
+////                if (!pretreatmentError(throwable)) {
+////                    throwable.printStackTrace()
+////                    showNetError(throwable)
+////                }
+////            })
+////        addDisposable(disposable)
+////    }
+////
+////    private fun pretreatmentError(throwable: Throwable): Boolean {
+////        if (throwable is NetException) {
+////            if ("500" == throwable.code &&
+////                true == throwable.message?.startsWith("人脸对比不符")
+////            ) {
+////                // 500- 人脸对比不符!
+////                AlertDialog.Builder(requireActivity())
+////                    .setCancelable(false)
+////                    .setTitle("提示")
+////                    .setMessage(throwable.message)
+////                    .setPositiveButton("确定") { _, _ ->
+////                    }.show()
+////                return true
+////            } else if ("500" == throwable.code
+////                && throwable.message == "识别中"
+////            ) {
+////                return true
+////            }
+////        }
+////        return false
+////    }
+////
+////    override fun onDestroy() {
+////        super.onDestroy()
+////        viewBinding.mlCameraView.clearCameraListeners();
+////        viewBinding.mlCameraView.destroy();
+////    }
+//
+//}

+ 251 - 0
app/src/main/java/com/dlc/eboard/ui/auth/fragment/FaceDetectionFragment.java

@@ -0,0 +1,251 @@
+package com.dlc.eboard.ui.auth.fragment;
+
+import android.Manifest;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.camera.core.CameraSelector;
+import androidx.camera.view.PreviewView;
+import androidx.fragment.app.FragmentActivity;
+
+import com.blankj.utilcode.util.ImageUtils;
+import com.blankj.utilcode.util.LogUtils;
+import com.blankj.utilcode.util.ThreadUtils;
+import com.dlc.eboard.LabApp;
+import com.dlc.eboard.databinding.FragmentFaceDetectionBinding;
+import com.dlc.eboard.ui.auth.AuthType;
+import com.dlc.eboard.ui.auth.OnAuthResultListener;
+import com.dlc.eboard.utils.SharedPreferencesUtil;
+import com.google.mlkit.vision.face.Face;
+import com.king.camera.scan.AnalyzeResult;
+import com.king.camera.scan.BaseCameraScan;
+import com.king.camera.scan.CameraScan;
+import com.king.camera.scan.config.CameraConfigFactory;
+import com.king.camera.scan.util.PermissionUtils;
+import com.king.logx.LogX;
+import com.king.mlkit.vision.face.analyze.FaceDetectionAnalyzer;
+import com.rc.core.ui.fragment.RcBaseFragment;
+import com.rc.core.util.MediaUtils;
+import com.rc.httpcore.client.ApiRepository;
+import com.rc.httpcore.exception.NetException;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * 人脸检测验证
+ */
+public class FaceDetectionFragment extends RcBaseFragment<FragmentFaceDetectionBinding> implements CameraScan.OnScanResultCallback<List<Face>> {
+    public static FaceDetectionFragment newInstance() {
+        return new FaceDetectionFragment();
+    }
+
+    private static final int CAMERA_PERMISSION_REQUEST_CODE = 0x86;
+    private CameraScan mCameraScan;
+
+    @NonNull
+    @Override
+    protected FragmentFaceDetectionBinding createViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) {
+        return FragmentFaceDetectionBinding.inflate(inflater, container, false);
+    }
+
+    @Override
+    protected void initViews(@Nullable Bundle savedInstanceState) {
+        PreviewView previewView = getViewBinding().previewView;
+        mCameraScan = new BaseCameraScan<>(this, previewView);
+        FaceDetectionAnalyzer faceDetectionAnalyzer = new FaceDetectionAnalyzer();
+        mCameraScan.setAnalyzer(faceDetectionAnalyzer).setOnScanResultCallback(this);
+        if ("0".equals(SharedPreferencesUtil.INSTANCE.getString(requireActivity(), "0"))) {
+            mCameraScan.setCameraConfig(CameraConfigFactory.createDefaultCameraConfig(requireActivity(), CameraSelector.LENS_FACING_FRONT));
+        } else {
+            mCameraScan.setCameraConfig(CameraConfigFactory.createDefaultCameraConfig(requireActivity(), CameraSelector.LENS_FACING_BACK));
+        }
+        startCamera();
+    }
+
+    public void startCamera() {
+        if (mCameraScan != null) {
+            if (PermissionUtils.checkPermission(requireContext(), Manifest.permission.CAMERA)) {
+                mCameraScan.startCamera();
+            } else {
+                LogX.d("checkPermissionResult != PERMISSION_GRANTED");
+                PermissionUtils.requestPermission(this, Manifest.permission.CAMERA, CAMERA_PERMISSION_REQUEST_CODE);
+            }
+        }
+    }
+
+    private void releaseCamera() {
+        if (mCameraScan != null) {
+            mCameraScan.release();
+        }
+    }
+
+    @Override
+    public void onDestroyView() {
+        if (null != errDialog && errDialog.isShowing()) {
+            errDialog.dismiss();
+        }
+        ThreadUtils.cancel(bitmapSimpleTask);
+        releaseCamera();
+        super.onDestroyView();
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+        if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) {
+            requestCameraPermissionResult(permissions, grantResults);
+        }
+    }
+
+    public void requestCameraPermissionResult(@NonNull String[] permissions, @NonNull int[] grantResults) {
+        if (PermissionUtils.requestPermissionsResult(Manifest.permission.CAMERA, permissions, grantResults)) {
+            startCamera();
+        } else {
+            requireActivity().finish();
+        }
+    }
+
+    private ThreadUtils.SimpleTask<String> bitmapSimpleTask;
+
+    /**
+     * 检测到人脸回调
+     *
+     * @param result 扫描结果
+     */
+    @Override
+    public void onScanResultCallback(@NonNull AnalyzeResult<List<Face>> result) {
+        mCameraScan.setAnalyzeImage(false);
+        // 子线程处理人脸结果
+        bitmapSimpleTask = new ThreadUtils.SimpleTask<String>() {
+            @Override
+            public String doInBackground() throws Throwable {
+                Bitmap srcBitmap = result.getBitmap();
+                List<Face> faceList = result.getResult();
+                Face face = findMaxFace(faceList);
+                if (null == face) {
+                    mCameraScan.setAnalyzeImage(true);
+                } else {
+                    Rect faceRect = face.getBoundingBox();
+                    Bitmap faceBitmap;
+                    try {
+                        int x = faceRect.left;
+                        int y = faceRect.top;
+                        if (x < 0) x = 0;
+                        if (y < 0) y = 0;
+                        faceBitmap = ImageUtils.clip(srcBitmap, x, y, faceRect.width(), faceRect.height());
+                    } catch (Exception e) {
+                        faceBitmap = srcBitmap;
+                    }
+                    File faceFile = new File(requireActivity().getExternalFilesDir("photos"), System.currentTimeMillis() + ".jpg");
+                    // 保存成功
+                    if (ImageUtils.save(faceBitmap, faceFile, Bitmap.CompressFormat.JPEG)) {
+                        LogUtils.e("Jayce", "保存成功发起人脸识别", faceFile.getAbsolutePath());
+                        addDisposable(ApiRepository.INSTANCE.authFace(faceFile, LabApp.sLabConfig.labId).subscribe(userVo -> {
+                            LogUtils.e("Jayce", "接口调用成功", userVo.toString());
+                            MediaUtils.INSTANCE.deleteFile(faceFile);
+                            FragmentActivity activity = requireActivity();
+                            if (activity instanceof OnAuthResultListener) {
+                                ((OnAuthResultListener) activity).onAuthSuccess(AuthType.FACE, userVo);
+                            }
+                        }, throwable -> {
+                            LogUtils.e("Jayce", "接口调用失败", throwable.getMessage());
+                            MediaUtils.INSTANCE.deleteFile(faceFile);
+                            LogUtils.e("Jayce", throwable.getMessage(), throwable.getLocalizedMessage());
+                            if (!pretreatmentError(throwable)) {
+                                showNetError(throwable);
+                                throwable.printStackTrace();
+                            }
+                        }));
+                    } else {
+                        LogUtils.e("Jayce", "保存失败", faceFile.getAbsolutePath());
+                        mCameraScan.setAnalyzeImage(true);
+                    }
+
+                }
+
+                return "";
+            }
+
+            @Override
+            public void onSuccess(String str) {
+//                getViewBinding().faceIV.setImageBitmap(faceBitmap);
+            }
+        };
+        ThreadUtils.executeByCached(bitmapSimpleTask);
+    }
+
+    private AlertDialog errDialog;
+
+    /**
+     * 服务端异常处理
+     */
+    private boolean pretreatmentError(Throwable throwable) {
+        if (throwable instanceof NetException) {
+            if (throwable.getMessage().startsWith("人脸对比不符")
+                    || throwable.getMessage().startsWith("未获取到对比数据")
+                    || throwable.getMessage().startsWith("未获取实验室")) {
+                if (errDialog == null) {
+                    errDialog = new AlertDialog.Builder(requireActivity())
+                            .setCancelable(false)
+                            .setTitle("提示")
+                            .setMessage(throwable.getMessage())
+                            .setPositiveButton("确定", (dialog, which) -> mCameraScan.setAnalyzeImage(true)).show();
+                } else {
+                    if (errDialog.isShowing()) {
+                        errDialog.dismiss();
+                        errDialog.setMessage(throwable.getMessage());
+                        errDialog.show();
+                    }
+                }
+
+            } else {
+                mCameraScan.setAnalyzeImage(true);
+            }
+            return true;
+        } else {
+            mCameraScan.setAnalyzeImage(true);
+        }
+        return false;
+    }
+
+
+    /**
+     * 是否露脸
+     */
+    private boolean isShowingFace(Face face) {
+        Rect rect = face.getBoundingBox();
+        return rect.top > 0 && rect.bottom > 0 && rect.left > 0 && rect.right > 0;
+    }
+
+    /**
+     * 找出最大人脸
+     */
+    private Face findMaxFace(List<Face> faceList) {
+        Face maxFace = null;
+        if (null != faceList && !faceList.isEmpty()) {
+            int maxSize = 0;
+            Iterator<Face> iterator = faceList.iterator();
+            while (iterator.hasNext()) {
+                Face face = iterator.next();
+                if (isShowingFace(face)) {
+                    int size = face.getBoundingBox().width() + face.getBoundingBox().height();
+                    if (maxSize < size) {
+                        maxSize = size;
+                        maxFace = face;
+                    }
+                } else {
+                    iterator.remove();
+                }
+            }
+        }
+        return maxFace;
+    }
+}

+ 0 - 1
app/src/main/java/com/dlc/eboard/ui/common/TabFragmentPagerAdapter.kt

@@ -3,7 +3,6 @@ package com.dlc.eboard.ui.common
 import androidx.fragment.app.FragmentManager
 import androidx.fragment.app.FragmentPagerAdapter
 import androidx.viewbinding.ViewBinding
-import com.rc.core.log.RcLog
 import com.rc.core.ui.fragment.RcBaseFragment
 
 /**

+ 2 - 3
app/src/main/java/com/dlc/eboard/ui/finger/EnrollAuthActivity.kt

@@ -14,10 +14,9 @@ import com.dlc.eboard.databinding.ActivityFingerEnrollAuthBinding
 import com.dlc.eboard.serial.OnSerialScanListener
 import com.dlc.eboard.serial.SerialPortHelper
 import com.dlc.eboard.ui.auth.AuthType
-import com.dlc.eboard.ui.auth.FourChoiceAuthActivity
 import com.dlc.eboard.ui.auth.OnAuthResultListener
 import com.dlc.eboard.ui.auth.fragment.CardAuthFragment
-import com.dlc.eboard.ui.auth.fragment.FaceAuthFragment
+import com.dlc.eboard.ui.auth.fragment.FaceDetectionFragment
 import com.dlc.eboard.ui.common.BaseCountDownActivity
 import com.dlc.eboard.ui.common.TabFragmentPagerAdapter
 import com.dlc.eboard.ui.widget.NavViewCompat
@@ -49,7 +48,7 @@ class EnrollAuthActivity :
         super.initViews(savedInstanceState)
 
         mCardAuthFragment = CardAuthFragment.newInstance(smaller = true)
-        val fragments = mutableListOf(mCardAuthFragment!!, FaceAuthFragment.newInstance())
+        val fragments = mutableListOf(mCardAuthFragment!!, FaceDetectionFragment.newInstance())
         val adapter = TabFragmentPagerAdapter(supportFragmentManager, fragments)
         viewBinding.viewPager.adapter = adapter
 

+ 2 - 2
app/src/main/java/com/dlc/laboratory/ui/things/SelectAuthMethodFragment.kt

@@ -5,9 +5,9 @@ import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.ViewGroup
 import com.dlc.eboard.databinding.FragmentSelectAuthMethodBinding
-import com.dlc.eboard.ui.auth.fragment.FaceAuthFragment
 import com.dlc.eboard.ui.sign.SignInFragmentCallback
 import com.dlc.eboard.ui.auth.fragment.CardAuthFragment
+import com.dlc.eboard.ui.auth.fragment.FaceDetectionFragment
 import com.rc.core.ui.fragment.RcBaseFragment
 
 /**
@@ -39,7 +39,7 @@ class SelectAuthMethodFragment : RcBaseFragment<FragmentSelectAuthMethodBinding>
     override fun initViews(savedInstanceState: Bundle?) {
         viewBinding.faceDetect.setOnClickListener {
             // 人脸识别
-            mFragmentCallback?.switchFragment(FaceAuthFragment())
+            mFragmentCallback?.switchFragment(FaceDetectionFragment.newInstance())
         }
 
         viewBinding.schoolCard.setOnClickListener {

+ 12 - 12
app/src/main/res/layout/fragment_auth_face_matching.xml

@@ -22,19 +22,19 @@
             app:rectanglesStrokeWidth="2dp" />
     </io.fotoapparat.facedetector.view.CameraOverlayLayout>
 
-    <com.otaliastudios.cameraview.CameraView
-        android:id="@+id/ml_camera_view"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        app:cameraDrawHardwareOverlays="true"
-        app:cameraEngine="camera1">
+<!--    <com.otaliastudios.cameraview.CameraView-->
+<!--        android:id="@+id/ml_camera_view"-->
+<!--        android:layout_width="match_parent"-->
+<!--        android:layout_height="match_parent"-->
+<!--        app:cameraDrawHardwareOverlays="true"-->
+<!--        app:cameraEngine="camera1">-->
 
-        <com.dlc.eboard.ui.widget.DetectView
-            android:id="@+id/detectView"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_gravity="center" />
-    </com.otaliastudios.cameraview.CameraView>
+<!--&lt;!&ndash;        <com.dlc.eboard.ui.widget.DetectView&ndash;&gt;-->
+<!--&lt;!&ndash;            android:id="@+id/detectView"&ndash;&gt;-->
+<!--&lt;!&ndash;            android:layout_width="match_parent"&ndash;&gt;-->
+<!--&lt;!&ndash;            android:layout_height="match_parent"&ndash;&gt;-->
+<!--&lt;!&ndash;            android:layout_gravity="center" />&ndash;&gt;-->
+<!--    </com.otaliastudios.cameraview.CameraView>-->
 
     <androidx.constraintlayout.widget.Guideline
         android:id="@+id/middle_line"

+ 29 - 0
app/src/main/res/layout/fragment_face_detection.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <androidx.camera.view.PreviewView
+        android:id="@+id/previewView"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+
+    <TextView
+        android:id="@+id/cameraTips"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_centerHorizontal="true"
+        android:layout_marginTop="17dp"
+        android:layout_marginBottom="80dp"
+        android:text="请正视摄像头"
+        android:textColor="@android:color/white"
+        android:textSize="17sp" />
+
+    <ImageView
+        android:id="@+id/face_IV"
+        android:layout_width="100dp"
+        android:layout_height="100dp" />
+
+</RelativeLayout>