|
@@ -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();
|
|
|
+//// }
|
|
|
+//
|
|
|
+//}
|