package com.example.chemical.ui.login import android.os.* import android.util.Log import android.view.LayoutInflater import android.view.MotionEvent import android.view.View import android.widget.TextView 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.example.chemical.ChemicalApp import com.example.chemical.R import com.example.chemical.comm.Constants import com.example.chemical.databinding.ActivityFacialLoginBinding import com.example.chemical.ui.common.BaseCountDownActivity import com.example.chemical.utils.HandlerUtil import com.example.chemical.utils.MediaPlayerHelper import com.example.chemical.utils.UiManager import com.example.chemical.weidith.AuthenticationDialog import com.example.chemical.weidith.CustomDialog import com.rc.core.log.RcLog 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 java.io.File import java.net.ConnectException import java.net.SocketTimeoutException import java.text.SimpleDateFormat import java.util.* /** * 人脸登录 */ class FacialLoginActivity : BaseCountDownActivity() { private lateinit var mFotoapparat: Fotoapparat // private lateinit var mCountDownTimer: CountDownTimer //人脸验证时间 private val totalTimeInMillis: Long = 30 * 1000 // 总时常 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 private val countHandler = Handler(Looper.getMainLooper()) //整个页面 60秒后 直接关闭 override fun createViewBinding() = ActivityFacialLoginBinding.inflate(LayoutInflater.from(this)) override fun initViews(savedInstanceState: Bundle?) { super.initViews(savedInstanceState) MediaPlayerHelper.playRawMp3(this, R.raw.login_ren_lian) // 使用 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() // mCountDownTimer = object : CountDownTimer(totalTimeInMillis, 1000) { // override fun onTick(millisUntilFinished: Long) { // val secondsRemaining = millisUntilFinished / 1000 // // 在最后六秒进行提示 // if (secondsRemaining.toInt() == 6) { // val message = "多次识别失败 $secondsRemaining 秒后关闭当前页面,请注意!" // showToast(message) // } // } // // override fun onFinish() { // mFotoapparat.stop() // cancelTime() // finish() // } // } // mCountDownTimer.start() handlerBack.post(countdownRunnableTwo) countHandler.postDelayed({ finish() }, totalTimeInMillis) // 15秒后关闭对话框 } override fun onStart() { super.onStart() // mFotoapparat.start() } override fun onStop() { super.onStop() // mFotoapparat.stop() } override fun onBackPressed() { super.onBackPressed() finish() } override fun onDestroy() { super.onDestroy() // cancelTime()//手动关闭 RcLog.info("=============onDestroy") // handlerUtil.removeCallbacks(task) // handlerUtil.stopAllTasks() // 移除回调,以防止内存泄漏 try { mFotoapparat.stop() handlerBack.removeCallbacks(countdownRunnable) handlerBack.removeCallbacks(countdownRunnableTwo) handlerBack.removeCallbacksAndMessages(null) dismissLoading() } catch (e: Exception) { } try { countHandler.removeCallbacksAndMessages(null) } catch (e: Exception) { } } private fun takePicture() { RcLog.info("=======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() LogUtils.json(success) 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 -> LogUtils.json(data) dismissLoading() val allFalse = with(data) { cabinetAdmin == false && belongUser == false && toipcUser == false && safeUser == false && collegeAdmin == false && schoolLevelAdmin == false && adminUser == false } ChemicalApp.responsibles = false ChemicalApp.administrators = false 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 校级管理员 ChemicalApp.administrators = true authenticationDialog(data.faceImg, data.userName) } else if (data.adminUser == true || data.safeUser == true || data.cabinetAdmin == true) { //实验室负责人 or 安全负责人 or 柜锁管理员 ChemicalApp.responsibles = true authenticationDialog(data.faceImg, data.userName) } else if (data.belongUser == true || data.toipcUser == true) { //当前身份 归属人or课题组 ChemicalApp.responsibles = false ChemicalApp.administrators = false 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) MediaPlayerHelper.playRawMp3(this, R.raw.login_ren_zheng_tong_hua) 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") } } }