package xn.hxp.ui.login import android.os.Bundle import android.os.Handler import android.os.Looper import android.util.Log import android.view.LayoutInflater import android.view.MotionEvent import android.view.View import android.widget.TextView import androidx.viewbinding.ViewBinding import com.blankj.utilcode.util.LogUtils import com.bumptech.glide.Glide import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.request.RequestOptions import com.rc.core.ui.activity.BaseActivity import com.rc.httpcore.HttpClient import com.rc.httpcore.HttpConfig import com.rc.httpcore.client.ApiRepository import com.rc.httpcore.exception.NetException import com.rc.httpcore.vo.request.FaceCompareReq import io.fotoapparat.Fotoapparat import io.fotoapparat.facedetector.processor.FaceDetectorProcessor import io.fotoapparat.log.fileLogger import io.fotoapparat.log.logcat import io.fotoapparat.log.loggers import io.fotoapparat.selector.back import io.fotoapparat.selector.front import retrofit2.HttpException import xn.hxp.R import xn.hxp.app.ChemicalApp import xn.hxp.comm.Constants import xn.hxp.databinding.ActivityFacialLoginBinding import xn.hxp.utils.AudioPlayer import xn.hxp.utils.UiManager import xn.hxp.weidith.AuthenticationDialog import xn.hxp.weidith.CustomDialog import java.io.File import java.lang.Boolean import java.net.ConnectException import java.net.SocketTimeoutException import java.text.SimpleDateFormat import java.util.Calendar import java.util.Locale /** * 人脸登录 */ class FacialLoginActivity : BaseActivity() { private lateinit var mFotoapparat: Fotoapparat // private lateinit var mCountDownTimer: CountDownTimer private lateinit var faceList: String private val handlerBack = Handler(Looper.getMainLooper()) private var timeLeftInSeconds = 2 private var mTvView: TextView? = null private var mDialogsAut: AuthenticationDialog? = null lateinit var viewBinding: ActivityFacialLoginBinding override fun setViewBinding(): ViewBinding { viewBinding = ActivityFacialLoginBinding.inflate(layoutInflater) return viewBinding } override fun onInit() { AudioPlayer.getInstance().play(R.raw.face_detect_hint) // 使用 Glide 加载网络图片 viewBinding.deptName.text = "${ChemicalApp.confs!!.deptName}-${ChemicalApp.confs!!.roomNum}" Glide.with(this) .load("${HttpConfig.API_BASE_IMG_URL}${ChemicalApp.confs!!.circularLogo}") .apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.AUTOMATIC)) .into(viewBinding.image) var str = "" try { str = intent.getStringExtra("faceList")!! } catch (e: Exception) { } val mtypes = intent.getStringExtra("mtypes") try { when (mtypes) { "1" -> { viewBinding.linType.visibility = View.GONE } "4" -> { viewBinding.tvScan.visibility = View.GONE } "5" -> { viewBinding.tvSwipe.visibility = View.GONE } } } catch (e: Exception) { } val map = mutableMapOf() if (mtypes != null) { map["mtypes"] = mtypes } if (str != null && str.length > 0) { // 去除字符串首尾的中括号 faceList = str.removeSurrounding("[", "]") map["faceList"] = str } //跳转刷卡登录 viewBinding.tvSwipe.setOnClickListener { UiManager.switcher(this, map, SwipeActivity::class.java) finish() } //跳转扫码登录 viewBinding.tvScan.setOnClickListener { UiManager.switcher(this, map, ScanLoginActivity::class.java) finish() } viewBinding.tvReturn.text = "返回${ChemicalApp.confs!!.backTime}s" viewBinding.tvReturn.setOnClickListener { finish() } mFotoapparat = Fotoapparat.with(this) .into(viewBinding.cameraView) .lensPosition( if (Constants.FACE_TAG == 0) { front() } else { back() } )//front()前置 back 后置 .frameProcessor( FaceDetectorProcessor.with(this) .listener { faces -> viewBinding.rectanglesView.setRectangles(faces) }.build() ) .logger(loggers(logcat(), fileLogger(this))) .build() mFotoapparat.stop() mFotoapparat.start() handlerBack.post(countdownRunnableTwo) } override fun onBackPressed() { super.onBackPressed() finish() } override fun cdTime(cd: Int) { viewBinding.tvReturn.text = "返回${cd}s" } override fun onDestroy() { super.onDestroy() if (null != mDialogsAut && mDialogsAut!!.isShowing) { mDialogsAut!!.dismiss() } // cancelTime()//手动关闭 LogUtils.i("=============onDestroy") // handlerUtil.removeCallbacks(task) // handlerUtil.stopAllTasks() // 移除回调,以防止内存泄漏 try { mFotoapparat.stop() handlerBack.removeCallbacks(countdownRunnable) handlerBack.removeCallbacks(countdownRunnableTwo) handlerBack.removeCallbacksAndMessages(null) dismissLoading() } catch (e: Exception) { } } override fun cdFinish() { finish() } private fun takePicture() { LogUtils.i("=======111人脸登录任务进行中") val format = SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.getDefault()) val fileName = "${format.format(Calendar.getInstance().time)}.jpg" val photoFile = File(this.getExternalFilesDir("photos"), fileName) try { mFotoapparat.takePicture() .saveToFile(photoFile) .whenAvailable { callFaceMatchingApi(photoFile) } } catch (e: Exception) { } } private fun callFaceMatchingApi( featureData: File, ) { showLoading("比对中....") val param = FaceCompareReq().apply { data = null userIds = faceList } val disposable = ApiRepository.faceCompare(featureData, param) .subscribe({ success -> dismissLoading() ChemicalApp.userData = success LogUtils.d(success.userId, ChemicalApp.subjectId) authenticationInfo(success.userId, ChemicalApp.subjectId!!) }, { throwable -> dismissLoading() throwableView(throwable) }) addDisposable(disposable) } //验证当前人员身份 private fun authenticationInfo(userId: String, subId: String) { LogUtils.d(userId, subId) showLoading("验证中...") val disposable = ApiRepository.userCardValidation(userId, subId) .subscribe({ data -> dismissLoading() val allFalse = Boolean.TRUE == data.cabinetAdmin || Boolean.TRUE == data.belongUser || Boolean.TRUE == data.toipcUser || Boolean.TRUE == data.safeUser || Boolean.TRUE == data.collegeAdmin || Boolean.TRUE == data.schoolLevelAdmin || Boolean.TRUE == data.adminUser || Boolean.TRUE == data.apply || Boolean.TRUE == data.white if (!allFalse) { HttpClient.token = null ChemicalApp.userData = null customDialogView(2, "身份认证不通过") } else { //校级管理员 schoolLevelAdmin //院级管理员 collegeAdmin //实验室负责人 adminUser //安全负责人 safeUser //柜锁管理员 cabinetAdmin //是否化学品归属人 belongUser //是否化学品归属课题组下成员 toipcUser if (data.schoolLevelAdmin == true || data.collegeAdmin == true) { //院级管理员 or 校级管理员 authenticationDialog(data.faceImg, data.userName) } else if (data.adminUser == true || data.safeUser == true || data.cabinetAdmin == true) { //实验室负责人 or 安全负责人 or 柜锁管理员 authenticationDialog(data.faceImg, data.userName) } else if (data.belongUser == true || data.toipcUser == true) { //当前身份 归属人or课题组 authenticationDialog(data.faceImg, data.userName) } else if (data.white == true || data.apply == true) {// 白名单和实验室准入 authenticationDialog(data.faceImg, data.userName) } else { HttpClient.token = null ChemicalApp.userData = null customDialogView(2, "身份认证不通过") } } }, { throwable -> dismissLoading() //暂时注释掉 防止异步请求后 在认证时 token 丢失 // startCountdownAndExecuteMethod() throwableView(throwable) }) addDisposable(disposable) } //身份认证成功 private fun authenticationDialog(faceImg: String?, userName: String) { mFotoapparat.stop() handlerBack.removeCallbacks(countdownRunnableTwo) AudioPlayer.getInstance().play(R.raw.verify_success) mDialogsAut = AuthenticationDialog( this, faceImg, ChemicalApp.confs!!.subName, ChemicalApp.confs!!.deptName, "${ChemicalApp.confs!!.buildName}${ChemicalApp.confs!!.floorName}", userName, object : AuthenticationDialog.IClickLit { override fun onUpView(tvView: TextView) { mTvView = tvView } }) mDialogsAut!!.show() // 开始倒计时 handlerBack.post(countdownRunnable) // 获取对话框的 Window 对象 mDialogsAut!!.window?.decorView?.setOnTouchListener { _, event -> // 判断是否点击了对话框外部空白区域 if (event.action == MotionEvent.ACTION_DOWN) { val x = event.x val y = event.y val dialogView = mDialogsAut!!.window?.decorView if (dialogView != null && (x < 0 || x > dialogView.width || y < 0 || y > dialogView.height)) { // 在此处执行点击对话框外部空白区域时的操作 // 例如关闭对话框 // 移除回调,以防止内存泄漏 mDialogsAut!!.dismiss() finish() return@setOnTouchListener true } } return@setOnTouchListener false } } private val countdownRunnable = object : Runnable { override fun run() { if (timeLeftInSeconds > 0) { mTvView!!.text = "${timeLeftInSeconds}秒后自动返回首页" timeLeftInSeconds-- handlerBack.postDelayed(this, 1000) } else { mDialogsAut!!.dismiss() finish() } } } private val countdownRunnableTwo = object : Runnable { override fun run() { takePicture() handlerBack.postDelayed(this, 4000) } } /** * 0 没有图标 1 绿色(成功) 2红色(失败) * 失败或者成功的弹框 */ private fun customDialogView(types: Int, msg: String) { val customDialog = CustomDialog(this, types, msg) if (!this.isFinishing && !this.isDestroyed) { customDialog.show() } } /** * 异常处理 */ private fun throwableView(throwable: Throwable) { LogUtils.e(Log.getStackTraceString(throwable)) when (throwable) { is NetException -> { if (throwable.message.isNullOrEmpty()) { "接口请求失败(${throwable.code})" } else { throwable.message!! } } is SocketTimeoutException -> "请求超时,请稍后重试" is ConnectException -> "无法连接服务器,请检查网络" is HttpException -> "服务器繁忙,请稍后重试" else -> "服务器异常" }?.let { customDialogView(2, "$it") } } }