FacialLoginActivity.kt 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. package com.example.chemical.ui.login
  2. import android.os.*
  3. import android.util.Log
  4. import android.view.LayoutInflater
  5. import android.view.MotionEvent
  6. import android.view.View
  7. import android.widget.TextView
  8. import com.blankj.utilcode.util.LogUtils
  9. import com.bumptech.glide.Glide
  10. import com.bumptech.glide.load.engine.DiskCacheStrategy
  11. import com.bumptech.glide.request.RequestOptions
  12. import com.example.chemical.ChemicalApp
  13. import com.example.chemical.R
  14. import com.example.chemical.comm.Constants
  15. import com.example.chemical.databinding.ActivityFacialLoginBinding
  16. import com.example.chemical.ui.common.BaseCountDownActivity
  17. import com.example.chemical.utils.HandlerUtil
  18. import com.example.chemical.utils.MediaPlayerHelper
  19. import com.example.chemical.utils.UiManager
  20. import com.example.chemical.weidith.AuthenticationDialog
  21. import com.example.chemical.weidith.CustomDialog
  22. import com.rc.core.log.RcLog
  23. import com.rc.httpcore.HttpClient
  24. import com.rc.httpcore.HttpConfig
  25. import com.rc.httpcore.client.ApiRepository
  26. import com.rc.httpcore.exception.NetException
  27. import com.rc.httpcore.vo.request.FaceCompareReq
  28. import io.fotoapparat.Fotoapparat
  29. import io.fotoapparat.facedetector.processor.FaceDetectorProcessor
  30. import io.fotoapparat.log.fileLogger
  31. import io.fotoapparat.log.logcat
  32. import io.fotoapparat.log.loggers
  33. import io.fotoapparat.selector.back
  34. import io.fotoapparat.selector.front
  35. import retrofit2.HttpException
  36. import java.io.File
  37. import java.net.ConnectException
  38. import java.net.SocketTimeoutException
  39. import java.text.SimpleDateFormat
  40. import java.util.*
  41. /**
  42. * 人脸登录
  43. */
  44. class FacialLoginActivity : BaseCountDownActivity<ActivityFacialLoginBinding>() {
  45. private lateinit var mFotoapparat: Fotoapparat
  46. // private lateinit var mCountDownTimer: CountDownTimer
  47. //人脸验证时间
  48. private val totalTimeInMillis: Long = 30 * 1000 // 总时常
  49. private lateinit var faceList: String
  50. private val handlerBack = Handler(Looper.getMainLooper())
  51. private var timeLeftInSeconds = 2
  52. private var mTvView: TextView? = null
  53. private var mDialogsAut: AuthenticationDialog? = null
  54. private val countHandler = Handler(Looper.getMainLooper()) //整个页面 60秒后 直接关闭
  55. override fun createViewBinding() = ActivityFacialLoginBinding.inflate(LayoutInflater.from(this))
  56. override fun initViews(savedInstanceState: Bundle?) {
  57. super.initViews(savedInstanceState)
  58. MediaPlayerHelper.playRawMp3(this, R.raw.login_ren_lian)
  59. // 使用 Glide 加载网络图片
  60. viewBinding.deptName.text = "${ChemicalApp.confs!!.deptName}-${ChemicalApp.confs!!.roomNum}"
  61. Glide.with(this)
  62. .load("${HttpConfig.API_BASE_IMG_URL}${ChemicalApp.confs!!.circularLogo}")
  63. .apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.AUTOMATIC))
  64. .into(viewBinding.image)
  65. var str = ""
  66. try {
  67. str = intent.getStringExtra("faceList")!!
  68. } catch (e: Exception) {
  69. }
  70. val mtypes = intent.getStringExtra("mtypes")
  71. try {
  72. when (mtypes) {
  73. "1" -> {
  74. viewBinding.linType.visibility = View.GONE
  75. }
  76. "4" -> {
  77. viewBinding.tvScan.visibility = View.GONE
  78. }
  79. "5" -> {
  80. viewBinding.tvSwipe.visibility = View.GONE
  81. }
  82. }
  83. } catch (e: Exception) {
  84. }
  85. val map = mutableMapOf<String, String>()
  86. if (mtypes != null) {
  87. map["mtypes"] = mtypes
  88. }
  89. if (str != null && str.length > 0) {
  90. // 去除字符串首尾的中括号
  91. faceList = str.removeSurrounding("[", "]")
  92. map["faceList"] = str
  93. }
  94. //跳转刷卡登录
  95. viewBinding.tvSwipe.setOnClickListener {
  96. UiManager.switcher(this, map, SwipeActivity::class.java)
  97. finish()
  98. }
  99. //跳转扫码登录
  100. viewBinding.tvScan.setOnClickListener {
  101. UiManager.switcher(this, map, ScanLoginActivity::class.java)
  102. finish()
  103. }
  104. viewBinding.tvReturn.text = "返回${ChemicalApp.confs!!.backTime}s"
  105. viewBinding.tvReturn.setOnClickListener {
  106. finish()
  107. }
  108. mFotoapparat = Fotoapparat.with(this)
  109. .into(viewBinding.cameraView)
  110. .lensPosition(
  111. if (Constants.FACE_TAG == 0) {
  112. front()
  113. } else {
  114. back()
  115. }
  116. )//front()前置 back 后置
  117. .frameProcessor(
  118. FaceDetectorProcessor.with(this)
  119. .listener { faces ->
  120. viewBinding.rectanglesView.setRectangles(faces)
  121. }.build()
  122. )
  123. .logger(loggers(logcat(), fileLogger(this)))
  124. .build()
  125. mFotoapparat.stop()
  126. mFotoapparat.start()
  127. // mCountDownTimer = object : CountDownTimer(totalTimeInMillis, 1000) {
  128. // override fun onTick(millisUntilFinished: Long) {
  129. // val secondsRemaining = millisUntilFinished / 1000
  130. // // 在最后六秒进行提示
  131. // if (secondsRemaining.toInt() == 6) {
  132. // val message = "多次识别失败 $secondsRemaining 秒后关闭当前页面,请注意!"
  133. // showToast(message)
  134. // }
  135. // }
  136. //
  137. // override fun onFinish() {
  138. // mFotoapparat.stop()
  139. // cancelTime()
  140. // finish()
  141. // }
  142. // }
  143. // mCountDownTimer.start()
  144. handlerBack.post(countdownRunnableTwo)
  145. countHandler.postDelayed({
  146. finish()
  147. }, totalTimeInMillis) // 15秒后关闭对话框
  148. }
  149. override fun onStart() {
  150. super.onStart()
  151. // mFotoapparat.start()
  152. }
  153. override fun onStop() {
  154. super.onStop()
  155. // mFotoapparat.stop()
  156. }
  157. override fun onBackPressed() {
  158. super.onBackPressed()
  159. finish()
  160. }
  161. override fun onDestroy() {
  162. super.onDestroy()
  163. // cancelTime()//手动关闭
  164. RcLog.info("=============onDestroy")
  165. // handlerUtil.removeCallbacks(task)
  166. // handlerUtil.stopAllTasks()
  167. // 移除回调,以防止内存泄漏
  168. try {
  169. mFotoapparat.stop()
  170. handlerBack.removeCallbacks(countdownRunnable)
  171. handlerBack.removeCallbacks(countdownRunnableTwo)
  172. handlerBack.removeCallbacksAndMessages(null)
  173. dismissLoading()
  174. } catch (e: Exception) {
  175. }
  176. try {
  177. countHandler.removeCallbacksAndMessages(null)
  178. } catch (e: Exception) {
  179. }
  180. }
  181. private fun takePicture() {
  182. RcLog.info("=======111人脸登录任务进行中")
  183. val format = SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.getDefault())
  184. val fileName = "${format.format(Calendar.getInstance().time)}.jpg"
  185. val photoFile = File(this.getExternalFilesDir("photos"), fileName)
  186. try {
  187. mFotoapparat.takePicture()
  188. .saveToFile(photoFile)
  189. .whenAvailable {
  190. callFaceMatchingApi(photoFile)
  191. }
  192. } catch (e: Exception) {
  193. }
  194. }
  195. private fun callFaceMatchingApi(
  196. featureData: File,
  197. ) {
  198. showLoading("比对中....")
  199. val param = FaceCompareReq().apply {
  200. data = null
  201. userIds = faceList
  202. }
  203. val disposable = ApiRepository.faceCompare(featureData, param)
  204. .subscribe({ success ->
  205. dismissLoading()
  206. LogUtils.json(success)
  207. ChemicalApp.userData = success
  208. LogUtils.d(success.userId, ChemicalApp.subjectId)
  209. authenticationInfo(success.userId, ChemicalApp.subjectId!!)
  210. }, { throwable ->
  211. dismissLoading()
  212. throwableView(throwable)
  213. })
  214. addDisposable(disposable)
  215. }
  216. //验证当前人员身份
  217. private fun authenticationInfo(userId: String, subId: String) {
  218. LogUtils.d(userId, subId)
  219. showLoading("验证中...")
  220. val disposable = ApiRepository.userCardValidation(userId, subId)
  221. .subscribe({ data ->
  222. LogUtils.json(data)
  223. dismissLoading()
  224. val allFalse = with(data) {
  225. cabinetAdmin == false &&
  226. belongUser == false &&
  227. toipcUser == false &&
  228. safeUser == false &&
  229. collegeAdmin == false &&
  230. schoolLevelAdmin == false &&
  231. adminUser == false
  232. }
  233. ChemicalApp.responsibles = false
  234. ChemicalApp.administrators = false
  235. if (allFalse) {
  236. HttpClient.token = null
  237. ChemicalApp.userData = null
  238. customDialogView(2, "身份认证不通过")
  239. } else {
  240. //校级管理员 schoolLevelAdmin
  241. //院级管理员 collegeAdmin
  242. //实验室负责人 adminUser
  243. //安全负责人 safeUser
  244. //柜锁管理员 cabinetAdmin
  245. //是否化学品归属人 belongUser
  246. //是否化学品归属课题组下成员 toipcUser
  247. if (data.schoolLevelAdmin == true || data.collegeAdmin == true) {
  248. //院级管理员 or 校级管理员
  249. ChemicalApp.administrators = true
  250. authenticationDialog(data.faceImg, data.userName)
  251. } else if (data.adminUser == true || data.safeUser == true || data.cabinetAdmin == true) {
  252. //实验室负责人 or 安全负责人 or 柜锁管理员
  253. ChemicalApp.responsibles = true
  254. authenticationDialog(data.faceImg, data.userName)
  255. } else if (data.belongUser == true || data.toipcUser == true) { //当前身份 归属人or课题组
  256. ChemicalApp.responsibles = false
  257. ChemicalApp.administrators = false
  258. authenticationDialog(data.faceImg, data.userName)
  259. } else {
  260. HttpClient.token = null
  261. ChemicalApp.userData = null
  262. customDialogView(2, "身份认证不通过")
  263. }
  264. }
  265. }, { throwable ->
  266. dismissLoading()
  267. //暂时注释掉 防止异步请求后 在认证时 token 丢失
  268. // startCountdownAndExecuteMethod()
  269. throwableView(throwable)
  270. })
  271. addDisposable(disposable)
  272. }
  273. //身份认证成功
  274. private fun authenticationDialog(faceImg: String?, userName: String) {
  275. mFotoapparat.stop()
  276. handlerBack.removeCallbacks(countdownRunnableTwo)
  277. MediaPlayerHelper.playRawMp3(this, R.raw.login_ren_zheng_tong_hua)
  278. mDialogsAut = AuthenticationDialog(
  279. this,
  280. faceImg,
  281. ChemicalApp.confs!!.subName,
  282. ChemicalApp.confs!!.deptName,
  283. "${ChemicalApp.confs!!.buildName}${ChemicalApp.confs!!.floorName}",
  284. userName, object : AuthenticationDialog.IClickLit {
  285. override fun onUpView(tvView: TextView) {
  286. mTvView = tvView
  287. }
  288. })
  289. mDialogsAut!!.show()
  290. // 开始倒计时
  291. handlerBack.post(countdownRunnable)
  292. // 获取对话框的 Window 对象
  293. mDialogsAut!!.window?.decorView?.setOnTouchListener { _, event ->
  294. // 判断是否点击了对话框外部空白区域
  295. if (event.action == MotionEvent.ACTION_DOWN) {
  296. val x = event.x
  297. val y = event.y
  298. val dialogView = mDialogsAut!!.window?.decorView
  299. if (dialogView != null && (x < 0 || x > dialogView.width || y < 0 || y > dialogView.height)) {
  300. // 在此处执行点击对话框外部空白区域时的操作
  301. // 例如关闭对话框
  302. // 移除回调,以防止内存泄漏
  303. mDialogsAut!!.dismiss()
  304. finish()
  305. return@setOnTouchListener true
  306. }
  307. }
  308. return@setOnTouchListener false
  309. }
  310. }
  311. private val countdownRunnable = object : Runnable {
  312. override fun run() {
  313. if (timeLeftInSeconds > 0) {
  314. mTvView!!.text = "${timeLeftInSeconds}秒后自动返回首页"
  315. timeLeftInSeconds--
  316. handlerBack.postDelayed(this, 1000)
  317. } else {
  318. mDialogsAut!!.dismiss()
  319. finish()
  320. }
  321. }
  322. }
  323. private val countdownRunnableTwo = object : Runnable {
  324. override fun run() {
  325. takePicture()
  326. handlerBack.postDelayed(this, 4000)
  327. }
  328. }
  329. /**
  330. * 0 没有图标 1 绿色(成功) 2红色(失败)
  331. * 失败或者成功的弹框
  332. */
  333. private fun customDialogView(types: Int, msg: String) {
  334. val customDialog = CustomDialog(this, types, msg)
  335. if (!this.isFinishing && !this.isDestroyed) {
  336. customDialog.show()
  337. }
  338. }
  339. /**
  340. * 异常处理
  341. */
  342. private fun throwableView(throwable: Throwable) {
  343. LogUtils.e(Log.getStackTraceString(throwable))
  344. when (throwable) {
  345. is NetException -> {
  346. if (throwable.message.isNullOrEmpty()) {
  347. "接口请求失败(${throwable.code})"
  348. } else {
  349. throwable.message!!
  350. }
  351. }
  352. is SocketTimeoutException -> "请求超时,请稍后重试"
  353. is ConnectException -> "无法连接服务器,请检查网络"
  354. is HttpException -> "服务器繁忙,请稍后重试"
  355. else -> "服务器异常"
  356. }?.let { customDialogView(2, "$it") }
  357. }
  358. }