首页、支付功能
This commit is contained in:
@@ -0,0 +1,962 @@
|
||||
package com.sw.dualscreen.presentation
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Presentation
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Outline
|
||||
import android.graphics.Point
|
||||
import android.hardware.Camera
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.Display
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewOutlineProvider
|
||||
import android.view.ViewTreeObserver
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.arcsoft.face.ErrorInfo
|
||||
import com.sw.dualscreen.GlobalKey
|
||||
import com.sw.dualscreen.R
|
||||
import com.sw.dualscreen.activity.MainActivity
|
||||
import com.sw.dualscreen.databinding.PresentationMainScreenBinding
|
||||
import com.sw.dualscreen.ext.dp
|
||||
import com.sw.dualscreen.ext.format2String
|
||||
import com.sw.dualscreen.ext.gone
|
||||
import com.sw.dualscreen.ext.maskName
|
||||
import com.sw.dualscreen.ext.visible
|
||||
import com.sw.dualscreen.model.request.UserNutritionParam
|
||||
import com.sw.dualscreen.model.response.DinnerTypeInfo
|
||||
import com.sw.dualscreen.model.response.FoodInfo
|
||||
import com.sw.dualscreen.model.response.UserNutritionData
|
||||
import com.sw.dualscreen.utils.Debouncer
|
||||
import com.sw.dualscreen.utils.GlideUtils
|
||||
import com.sw.dualscreen.utils.GsonUtils
|
||||
import com.sw.dualscreen.utils.SPUtil
|
||||
import com.sw.dualscreen.viewmodel.UserViewModel
|
||||
import com.sw.plate.utils.LightManager
|
||||
import com.sw.plate.utils.ToastUtils
|
||||
import com.sw.plate.utils.arcface.ConfigUtil
|
||||
import com.sw.plate.utils.arcface.ErrorCodeUtil
|
||||
import com.sw.plate.utils.arcface.FaceRectTransformer
|
||||
import com.sw.plate.utils.arcface.FaceRectView
|
||||
import com.sw.plate.utils.arcface.FaceRectView.DrawInfo
|
||||
import com.sw.plate.utils.arcface.PreviewConfig
|
||||
import com.sw.plate.utils.arcface.camera.CameraListener
|
||||
import com.sw.plate.utils.arcface.camera.DualCameraHelper
|
||||
import com.sw.plate.utils.arcface.face.constants.LivenessType
|
||||
import com.sw.plate.utils.arcface.face.model.CompareResult
|
||||
import com.sw.plate.utils.arcface.face.model.FacePreviewInfo
|
||||
import com.sw.plate.utils.arcface.face.model.RecognizeConfiguration
|
||||
import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class MainScreenPresentation(
|
||||
val activity: MainActivity,
|
||||
display: Display,
|
||||
val userViewModel: UserViewModel,
|
||||
val recognizeViewModel: RecognizeViewModel,
|
||||
private val onDismissListener: () -> Unit = {}
|
||||
) : Presentation(activity, display), ViewTreeObserver.OnGlobalLayoutListener {
|
||||
|
||||
companion object {
|
||||
const val TAG = "SubScreenPresentation"
|
||||
}
|
||||
|
||||
private lateinit var binding: PresentationMainScreenBinding
|
||||
private lateinit var stepChangeCallback: (Int) -> Unit
|
||||
// 当前步骤
|
||||
var currentStep: Int = 0
|
||||
|
||||
private var isGoStep1 = false;//防止持续调用
|
||||
|
||||
// 虹软人脸配置 ⬇
|
||||
private var isRecognition = false
|
||||
private var rgbCameraHelper: DualCameraHelper? = null
|
||||
private var rgbFaceRectTransformer: FaceRectTransformer? = null
|
||||
private val livenessType = LivenessType.IR
|
||||
private var openRectInfoDraw = false
|
||||
|
||||
private var irCameraHelper: DualCameraHelper? = null
|
||||
private var irFaceRectTransformer: FaceRectTransformer? = null
|
||||
|
||||
private var detectWeight = 0.0 //识别菜品时的重量
|
||||
private var lastWeight = 0.0 // 上一次的计算热量结果
|
||||
private var userNutritionData: UserNutritionData? = null
|
||||
private var dinnerTypeInfo: DinnerTypeInfo? = null
|
||||
private val debouncer = Debouncer(500)
|
||||
private var recognitionTime: Long = 0L // 人脸识别时的时间
|
||||
private var recognitionWeight: Double = 0.0 // 人脸识别时的重量
|
||||
|
||||
private var lastFaceTrackId: Int = -1 // 上一次的人脸信息
|
||||
var mealPickupMode: Int = 0 // 取餐模式 0 即放即取 1 余量取餐
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = PresentationMainScreenBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
mealPickupMode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0) ?: 0
|
||||
window?.setBackgroundDrawableResource(android.R.color.transparent)
|
||||
initView()
|
||||
setupArcCamera()
|
||||
registerDataChange()
|
||||
}
|
||||
|
||||
fun updateWeight(weight: Double) {
|
||||
lastWeight = weight * 1000 // 将千克转成克
|
||||
Timber.tag(TAG).d("updateWeight lastWeight = $weight, currentStep = $currentStep, isGoStep1 = $isGoStep1")
|
||||
if ((currentStep == 2) &&
|
||||
lastWeight <= 5 &&
|
||||
isGoStep1
|
||||
) {//人脸识别过程中,秤上物品拿走
|
||||
isGoStep1 = false
|
||||
activity.runOnUiThread {
|
||||
Timber.tag(TAG).e("切回step1")
|
||||
step1FoodRecognizing()
|
||||
}
|
||||
}
|
||||
|
||||
// if (currentStep != 3) {
|
||||
// return
|
||||
// }
|
||||
if (currentFood == null) {
|
||||
Timber.tag(TAG).e("updateWeight currentFood is null")
|
||||
return
|
||||
}
|
||||
|
||||
if (userNutritionData == null) {
|
||||
Timber.tag(TAG).e("updateWeight userNutritionData is null")
|
||||
return
|
||||
}
|
||||
if (dinnerTypeInfo == null) {
|
||||
Timber.tag(TAG).e("updateWeight dinnerType is null")
|
||||
return
|
||||
}
|
||||
calculateNutrition(recognitionWeight - lastWeight)
|
||||
}
|
||||
fun setStepChangeCallback(callback: (Int) -> Unit) {
|
||||
stepChangeCallback = callback
|
||||
}
|
||||
|
||||
override fun onGlobalLayout() {
|
||||
Timber.tag(TAG).d("onGlobalLayout")
|
||||
binding.dualCameraTexturePreviewRgb.getViewTreeObserver().removeOnGlobalLayoutListener(this)
|
||||
openCamera()
|
||||
}
|
||||
|
||||
private fun openCamera() {
|
||||
Timber.tag(TAG).d("openCamera")
|
||||
if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) !=
|
||||
PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
ToastUtils.showToast("无摄像头权限")
|
||||
return
|
||||
}
|
||||
try {
|
||||
val cameraCount = Camera.getNumberOfCameras()
|
||||
if (cameraCount < 3) {
|
||||
ToastUtils.showToast("摄像头数量异常")
|
||||
return
|
||||
}
|
||||
recognizeViewModel.init(
|
||||
PreviewConfig(
|
||||
2, 1, 90, 90
|
||||
)
|
||||
)
|
||||
initRgbCamera()
|
||||
if (DualCameraHelper.hasDualCamera() && livenessType === LivenessType.IR) {
|
||||
initIrCamera()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.tag(TAG).e(e, "打开摄像头失败")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用虹软人脸失败
|
||||
*/
|
||||
private fun setupArcCamera() {
|
||||
initArcViewModel()
|
||||
initArcView()
|
||||
openRectInfoDraw = true
|
||||
}
|
||||
|
||||
private fun registerDataChange() {
|
||||
activity.lifecycleScope.launch {
|
||||
userViewModel.loadFaceResult.collect { needUpdate ->
|
||||
if (needUpdate) {
|
||||
recognizeViewModel.refreshFaceList()
|
||||
}
|
||||
}
|
||||
}
|
||||
activity.lifecycleScope.launch {
|
||||
userViewModel.nutritionData.collect {
|
||||
Timber.tag(TAG).d("registerDataChange nutritionData = $it")
|
||||
userNutritionData = it
|
||||
if (it == null) return@collect
|
||||
step3ShowRecognizeResult()
|
||||
//binding.nutritionInclude.tvUserName.text = it.userName.maskName()
|
||||
//binding.nutritionInclude.tvRecommendHeat.text = "推荐热量:${it.recommendMin}-${it.recommendMax}"
|
||||
//updateWeight(lastWeight / 1000)
|
||||
}
|
||||
}
|
||||
activity.lifecycleScope.launch {
|
||||
userViewModel.dinnerTypeInfo.drop(1).collect {
|
||||
Timber.tag(TAG).d("registerDataChange dinnerTypeInfo = $it")
|
||||
dinnerTypeInfo = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示识别出的菜品信息
|
||||
*/
|
||||
fun step3ShowRecognizeResult() {
|
||||
Timber.tag(TAG).d("step3")
|
||||
LightManager.closeRedLight()
|
||||
LightManager.openGreenLight()
|
||||
currentStep = 3
|
||||
stepChangeCallback(currentStep)
|
||||
if (currentFood == null) {
|
||||
ToastUtils.showToast("选中物品为空")
|
||||
return
|
||||
}
|
||||
updateFoodInfo(currentFood!!)
|
||||
// binding.flPreview.visibility = View.VISIBLE
|
||||
// binding.ivRecImage.visibility = View.VISIBLE
|
||||
// binding.flFace.visibility = View.INVISIBLE
|
||||
//// binding.ivFaceBg.visibility = View.GONE
|
||||
// binding.tvFaceTip.visibility = View.GONE
|
||||
//
|
||||
//// binding.tvBottomTip.visibility = View.GONE
|
||||
// binding.nutritionInclude.clNutritionData.visibility = View.VISIBLE
|
||||
|
||||
|
||||
foodRecSuccess(currentFood!!)
|
||||
}
|
||||
private fun initView() {
|
||||
binding.ivRecImage.let {
|
||||
it.outlineProvider = object : ViewOutlineProvider() {
|
||||
override fun getOutline(view: View, outline: Outline) {
|
||||
outline.setRoundRect(0, 0, view.width, view.height, 12f.dp)
|
||||
}
|
||||
}
|
||||
it.clipToOutline = true
|
||||
}
|
||||
}
|
||||
override fun onDisplayRemoved() {
|
||||
super.onDisplayRemoved()
|
||||
onDismissListener()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 加载待机页面
|
||||
*/
|
||||
fun loadStandbyScreen() {
|
||||
binding.llStandbyPage.visible()
|
||||
}
|
||||
|
||||
fun hideStandbyScreen() {
|
||||
binding.llStandbyPage.gone()
|
||||
}
|
||||
|
||||
/**
|
||||
* 餐品识别成功,计费模式
|
||||
*/
|
||||
fun loadBilledMode(foodInfo: FoodInfo) {
|
||||
binding.calorieInclude.root.visible()
|
||||
binding.nutritionInclude.root.gone()
|
||||
|
||||
binding.flCameraView.visible()
|
||||
binding.ivFaceRecMask.gone()
|
||||
binding.tvFaceTip.gone()
|
||||
binding.tvFoodRecPrompt.gone()
|
||||
binding.llPriceInfo.visible()
|
||||
binding.ivRecImage.gone()
|
||||
|
||||
//updateFoodInfo(foodInfo)
|
||||
|
||||
userNutritionData?.let {
|
||||
Timber.tag(TAG).d("loadBilledMode:${GsonUtils.toJson(it)}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 餐品识别成功,不计费模式
|
||||
*/
|
||||
fun loadUnbilledMode(foodInfo: FoodInfo) {
|
||||
binding.calorieInclude.root.gone()
|
||||
binding.nutritionInclude.root.visible()
|
||||
|
||||
binding.flCameraView.visible()
|
||||
binding.ivFaceRecMask.gone()
|
||||
binding.tvFaceTip.gone()
|
||||
binding.tvFoodRecPrompt.gone()
|
||||
binding.llPriceInfo.gone()
|
||||
binding.ivRecImage.gone()
|
||||
|
||||
//updateFoodInfo(foodInfo)
|
||||
userNutritionData?.let {
|
||||
binding.nutritionInclude.tvUserName.text = it.userName.maskName()
|
||||
binding.nutritionInclude.tvRecommendHeat.text = "推荐热量:${it.recommendMin}-${it.recommendMax}kcal"
|
||||
updateWeight(lastWeight / 1000)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 餐品识别中
|
||||
*/
|
||||
fun step1FoodRecognizing() {
|
||||
hideStandbyScreen()
|
||||
activity.resetTouchTime()
|
||||
|
||||
Timber.tag(TAG).d("step1")
|
||||
//if (abs(lastWeight.toInt()) <= 5) {
|
||||
// activity.runOnUiThread {
|
||||
// ToastUtils.showToast("秤上重量几乎为0,请确认")
|
||||
// }
|
||||
// return
|
||||
//}
|
||||
currentStep = 1
|
||||
LightManager.closeGreenLight()
|
||||
LightManager.closeRedLight()
|
||||
|
||||
dinnerTypeInfo = null
|
||||
currentFood = null
|
||||
userNutritionData = null
|
||||
recognitionWeight = 0.0
|
||||
|
||||
stepChangeCallback(currentStep)
|
||||
|
||||
binding.calorieInclude.root.gone()
|
||||
binding.nutritionInclude.root.gone()
|
||||
|
||||
binding.tvFoodName.text = "餐品识别中..."
|
||||
binding.tvFoodRecPrompt.visible()
|
||||
binding.flCameraView.visible()
|
||||
binding.ivPreviewImage.visible()
|
||||
binding.ivRecImage.gone()
|
||||
binding.ivFaceRecMask.gone()
|
||||
binding.tvFaceTip.gone()
|
||||
binding.llPriceInfo.gone()
|
||||
|
||||
pauseCamera()
|
||||
activity.clearFoodList()
|
||||
}
|
||||
|
||||
fun foodRecSuccess(foodInfo: FoodInfo) {
|
||||
//识别逻辑完成根据是否计费显示不同页面
|
||||
val mode = SPUtil.getInstance().get(GlobalKey.KEY_CHARGE_MODE, 0)
|
||||
if (mode == 0) {
|
||||
//计费
|
||||
loadBilledMode(foodInfo)
|
||||
} else {
|
||||
//不计费
|
||||
loadUnbilledMode(foodInfo)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户人脸识别中
|
||||
*/
|
||||
fun step2FaceRecognizing(foodInfo: FoodInfo) {
|
||||
isGoStep1 = true
|
||||
Timber.tag(TAG).d("step2")
|
||||
currentStep = 2
|
||||
LightManager.closeRedLight()
|
||||
|
||||
binding.calorieInclude.root.gone()
|
||||
binding.nutritionInclude.root.gone()
|
||||
|
||||
binding.tvFoodName.text = foodInfo.foodName
|
||||
binding.flFace.visible()
|
||||
binding.flCameraView.visible()
|
||||
binding.ivFaceRecMask.visible()
|
||||
binding.tvFaceTip.visible()
|
||||
binding.tvFoodRecPrompt.visible()
|
||||
binding.llPriceInfo.gone()
|
||||
|
||||
binding.ivRecImage.gone()
|
||||
binding.ivPreviewImage.gone()
|
||||
|
||||
dinnerTypeInfo = null
|
||||
userNutritionData = null
|
||||
recognitionWeight = 0.0
|
||||
|
||||
stepChangeCallback(currentStep)
|
||||
currentFood = foodInfo
|
||||
userViewModel.getDinnerType()
|
||||
resumeCamera()
|
||||
}
|
||||
|
||||
// 当前食物信息
|
||||
private var currentFood: FoodInfo? = null
|
||||
fun updateFood(foodInfo: FoodInfo?) {
|
||||
Timber.tag(TAG).d("updateFood")
|
||||
currentFood = foodInfo
|
||||
if (foodInfo != null) {
|
||||
if (currentStep == 1) {
|
||||
step2FaceRecognizing(foodInfo)
|
||||
} else {
|
||||
updateFoodInfo(foodInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateFoodInfo(foodInfo: FoodInfo) {
|
||||
binding.tvFoodName.text = foodInfo.foodName
|
||||
binding.ivPreviewImage.gone()
|
||||
binding.ivRecImage.visible()
|
||||
if (foodInfo.photoUri != null) {
|
||||
GlideUtils.loadRoundCornerImage(
|
||||
context,
|
||||
url = foodInfo.photoUri,
|
||||
imageView = binding.ivRecImage,
|
||||
radius = 12
|
||||
)
|
||||
} else {
|
||||
GlideUtils.loadRoundCornerImage(
|
||||
context,
|
||||
url = foodInfo.imgUrl,
|
||||
imageView = binding.ivRecImage,
|
||||
radius = 12
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateNutrition(weight1: Double) {
|
||||
var weight = weight1
|
||||
if (weight < 0) weight = 0.0
|
||||
debouncer.debounce {
|
||||
Timber.tag(TAG).d("calculateNutrition weight = $weight, recognitionWeight = $recognitionWeight")
|
||||
val dinnerType = dinnerTypeInfo!!.dinnerType!!.dinnerType!!
|
||||
Timber.tag(TAG).d("calculateNutrition foodName = ${currentFood!!.foodName}, dinnerType = $dinnerType, userId = ${userNutritionData!!.userId}")
|
||||
val calcResultInfo = UserNutritionUtils.calculateNutrition(
|
||||
currentFood!!,
|
||||
userNutritionData!!,
|
||||
weight,
|
||||
dinnerType = dinnerType
|
||||
)
|
||||
Timber.tag(TAG).d("calculateNutrition calcResultInfo = $calcResultInfo")
|
||||
val columnMax = UserNutritionUtils.getMaxInt(calcResultInfo)
|
||||
|
||||
activity.runOnUiThread {
|
||||
val totalKcal = calcResultInfo.totalKcal
|
||||
val grain = calcResultInfo.grain
|
||||
val fruits = calcResultInfo.fruits
|
||||
val meat = calcResultInfo.meat
|
||||
// 设置热量
|
||||
binding.nutritionInclude.customKcalColumn.setImageDrawable(
|
||||
true,
|
||||
totalKcal.max < totalKcal.current
|
||||
)
|
||||
var maxKcal = totalKcal.max
|
||||
Timber.tag(TAG).d("maxKcal = $maxKcal")
|
||||
// 避免0作为被除数
|
||||
if (maxKcal <= 0.0) {
|
||||
maxKcal = 1.0
|
||||
}
|
||||
Timber.tag(TAG).d("maxKcal 1 = $maxKcal")
|
||||
binding.nutritionInclude.totalKcalTv.text = totalKcal.current.format2String()
|
||||
var totalKcalHeight = (totalKcal.current / maxKcal).toFloat()
|
||||
if (totalKcalHeight == 0F) {
|
||||
binding.nutritionInclude.divKcalView.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.nutritionInclude.divKcalView.visibility = View.GONE
|
||||
totalKcalHeight += 0.1F
|
||||
}
|
||||
binding.nutritionInclude.customKcalColumn.setCustomHeightPercent(
|
||||
totalKcalHeight,
|
||||
true
|
||||
)
|
||||
|
||||
// 设置主食
|
||||
binding.nutritionInclude.customFoodColumn.setImageDrawable(
|
||||
false,
|
||||
grain.max < grain.current
|
||||
)
|
||||
binding.nutritionInclude.totalFoodTv.text = grain.current.format2String()
|
||||
var grainHeight = (grain.current / columnMax).toFloat()
|
||||
if (grainHeight == 0F) {
|
||||
binding.nutritionInclude.divTotalFoodView.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.nutritionInclude.divTotalFoodView.visibility = View.GONE
|
||||
grainHeight += 0.1F
|
||||
}
|
||||
binding.nutritionInclude.customFoodColumn.setCustomHeightPercent(
|
||||
grainHeight,
|
||||
true
|
||||
)
|
||||
|
||||
// 设置果蔬
|
||||
binding.nutritionInclude.customVegetableColumn.setImageDrawable(
|
||||
false,
|
||||
fruits.max < fruits.current
|
||||
)
|
||||
binding.nutritionInclude.totalVegetableTv.text = fruits.current.format2String()
|
||||
var fruitsHeight = (fruits.current / columnMax).toFloat()
|
||||
|
||||
if (fruitsHeight == 0F) {
|
||||
binding.nutritionInclude.divVegetableView.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.nutritionInclude.divVegetableView.visibility = View.GONE
|
||||
fruitsHeight += 0.1F
|
||||
}
|
||||
binding.nutritionInclude.customVegetableColumn.setCustomHeightPercent(
|
||||
fruitsHeight,
|
||||
true
|
||||
)
|
||||
|
||||
// 设置肉蛋
|
||||
binding.nutritionInclude.customMeatColumn.setImageDrawable(
|
||||
false,
|
||||
meat.max < meat.current
|
||||
)
|
||||
binding.nutritionInclude.totalMeatTv.text = meat.current.format2String()
|
||||
var meatHeight = (meat.current / columnMax).toFloat()
|
||||
if (meatHeight == 0F) {
|
||||
binding.nutritionInclude.divMeatView.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.nutritionInclude.divMeatView.visibility = View.GONE
|
||||
meatHeight += 0.1F
|
||||
}
|
||||
binding.nutritionInclude.customMeatColumn.setCustomHeightPercent(
|
||||
meatHeight,
|
||||
true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
fun updateImage(bitmap: Bitmap) {
|
||||
activity.runOnUiThread {
|
||||
binding.ivPreviewImage.setImageBitmap(bitmap)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 余量取餐
|
||||
*/
|
||||
fun updateMealPickupMode(mode: Int) {
|
||||
Timber.tag(TAG).d("updateMealPickupMode mode = $mode")
|
||||
mealPickupMode = mode
|
||||
step1FoodRecognizing()
|
||||
}
|
||||
override fun onStop() {
|
||||
if (rgbCameraHelper != null) {
|
||||
rgbCameraHelper!!.release()
|
||||
rgbCameraHelper = null
|
||||
}
|
||||
if (irCameraHelper != null) {
|
||||
irCameraHelper!!.release()
|
||||
irCameraHelper = null
|
||||
}
|
||||
recognizeViewModel.destroy()
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
fun resumeCamera() {
|
||||
Timber.tag(TAG).d("resumeCamera isRecognition = $isRecognition")
|
||||
isRecognition = true
|
||||
if (rgbCameraHelper != null && rgbCameraHelper!!.isStopped) {
|
||||
rgbCameraHelper!!.start()
|
||||
}
|
||||
}
|
||||
|
||||
fun pauseCamera() {
|
||||
Timber.tag(TAG).d("pauseCamera isRecognition = $isRecognition")
|
||||
isRecognition = false
|
||||
|
||||
recognizeViewModel.onPreviewFrame(ByteArray(1382400), true)
|
||||
}
|
||||
|
||||
private fun initArcViewModel() {
|
||||
recognizeViewModel.setLiveType(livenessType)
|
||||
recognizeViewModel.ftInitCode.observe(activity, Observer { ftInitCode: Int? ->
|
||||
if (ftInitCode != ErrorInfo.MOK) {
|
||||
val error: String? = context.getString(
|
||||
R.string.specific_engine_init_failed, "ftEngine",
|
||||
ftInitCode, ErrorCodeUtil.arcFaceErrorCodeToFieldName(ftInitCode!!)
|
||||
)
|
||||
Timber.tag(TAG).e("ftInitCode observe = $error")
|
||||
ToastUtils.showToast(error)
|
||||
}
|
||||
})
|
||||
recognizeViewModel.frInitCode.observe(activity, Observer { frInitCode: Int? ->
|
||||
if (frInitCode != ErrorInfo.MOK) {
|
||||
val error: String? = context.getString(
|
||||
R.string.specific_engine_init_failed, "frEngine",
|
||||
frInitCode, ErrorCodeUtil.arcFaceErrorCodeToFieldName(frInitCode!!)
|
||||
)
|
||||
Timber.tag(TAG).e("frInitCode observe = $error")
|
||||
ToastUtils.showToast(error)
|
||||
}
|
||||
})
|
||||
recognizeViewModel.flInitCode.observe(activity, Observer { flInitCode: Int? ->
|
||||
if (flInitCode != ErrorInfo.MOK) {
|
||||
val error: String? = context.getString(
|
||||
R.string.specific_engine_init_failed, "flEngine",
|
||||
flInitCode, ErrorCodeUtil.arcFaceErrorCodeToFieldName(flInitCode!!)
|
||||
)
|
||||
Timber.tag(TAG).e("flInitCode observe = $error")
|
||||
ToastUtils.showToast(error)
|
||||
}
|
||||
})
|
||||
|
||||
recognizeViewModel.recognizeConfiguration
|
||||
.observe(activity, Observer { recognizeConfiguration: RecognizeConfiguration? ->
|
||||
Timber.tag(TAG).i("recognizeConfiguration: observe = ${recognizeConfiguration.toString()}")
|
||||
})
|
||||
recognizeViewModel.recognizeNotice.observe(activity, Observer { notice: String? ->
|
||||
Timber.tag(TAG).i("recognizeNotice observe notice = $notice")
|
||||
})
|
||||
|
||||
recognizeViewModel.recognizeUserId.observe(
|
||||
activity,
|
||||
Observer { compareResult: CompareResult ->
|
||||
Timber.tag(TAG).i("recognizeUserId observe compareResult = ${compareResult.trackId}, userId = ${compareResult.faceEntity.userName}")
|
||||
recognitionTime = System.currentTimeMillis()
|
||||
recognitionWeight = lastWeight
|
||||
lastFaceTrackId = compareResult.trackId
|
||||
val faceEntity = compareResult.faceEntity
|
||||
val userId = faceEntity.userName
|
||||
if (userId == null ) return@Observer
|
||||
if (currentFood == null) {
|
||||
//重新识别
|
||||
activity.runOnUiThread {
|
||||
ToastUtils.showToast("currentFood == null")
|
||||
}
|
||||
//activity.recognizeFood()
|
||||
//step1FoodRecognizing()
|
||||
return@Observer
|
||||
}
|
||||
userViewModel.getUserNutritionData(
|
||||
userId = userId,
|
||||
foodId = currentFood!!.id!!
|
||||
)
|
||||
})
|
||||
|
||||
recognizeViewModel.drawRectInfoText.observe(activity, Observer { info ->
|
||||
Timber.tag(TAG).i("drawRectInfoText observe info = $info")
|
||||
})
|
||||
}
|
||||
|
||||
private fun initArcView() {
|
||||
//在布局结束后才做初始化操作
|
||||
binding.dualCameraTexturePreviewRgb.getViewTreeObserver().addOnGlobalLayoutListener(this)
|
||||
recognizeViewModel.getCompareResultList().getValue()
|
||||
}
|
||||
|
||||
/**
|
||||
* 调整View的宽高,使预览显示正常且采集框固定为
|
||||
*
|
||||
* @param rgbPreview RGB预览View
|
||||
* @param previewView 显示预览数据的view
|
||||
* @param faceRectView 画框的view
|
||||
* @param previewSize 预览大小
|
||||
* @param displayOrientation 相机旋转角度
|
||||
* @param scale 缩放比例
|
||||
* @return 调整后的LayoutParams
|
||||
*/
|
||||
private fun adjustPreviewViewSize(
|
||||
rgbPreview: View,
|
||||
previewView: View,
|
||||
faceRectView: FaceRectView,
|
||||
previewSize: Camera.Size,
|
||||
displayOrientation: Int,
|
||||
scale: Float
|
||||
): ViewGroup.LayoutParams {
|
||||
// val layoutParams = previewView.layoutParams
|
||||
// val measuredWidth = previewView.measuredWidth
|
||||
// val measuredHeight = previewView.measuredHeight
|
||||
// var ratio = (previewSize.height.toFloat()) / previewSize.width.toFloat()
|
||||
// if (ratio > 1) {
|
||||
// ratio = 1 / ratio
|
||||
// }
|
||||
// if (displayOrientation % 180 == 0) {
|
||||
// layoutParams.width = measuredWidth
|
||||
// layoutParams.height = (measuredWidth * ratio).toInt()
|
||||
// } else {
|
||||
// layoutParams.height = measuredHeight
|
||||
// layoutParams.width = (measuredHeight * ratio).toInt()
|
||||
// }
|
||||
// if (scale < 1f) {
|
||||
// val rgbParam = rgbPreview.getLayoutParams()
|
||||
// layoutParams.width = (rgbParam.width * scale).toInt()
|
||||
// layoutParams.height = (rgbParam.height * scale).toInt()
|
||||
// } else {
|
||||
// layoutParams.width = (layoutParams.width * scale).toInt()
|
||||
// layoutParams.height = (layoutParams.height * scale).toInt()
|
||||
// }
|
||||
//
|
||||
// val metrics = DisplayMetrics()
|
||||
// activity.windowManager.defaultDisplay.getMetrics(metrics)
|
||||
//
|
||||
// if (layoutParams.width >= metrics.widthPixels) {
|
||||
// val viewRatio = layoutParams.width / (metrics.widthPixels.toFloat())
|
||||
// layoutParams.width = (layoutParams.width / viewRatio).toInt()
|
||||
// layoutParams.height = (layoutParams.height / viewRatio).toInt()
|
||||
// }
|
||||
// if (layoutParams.height >= metrics.heightPixels) {
|
||||
// val viewRatio = layoutParams.height / (metrics.heightPixels.toFloat())
|
||||
// layoutParams.width = (layoutParams.width / viewRatio).toInt()
|
||||
// layoutParams.height = (layoutParams.height / viewRatio).toInt()
|
||||
// }
|
||||
|
||||
|
||||
val layoutParams = previewView.layoutParams
|
||||
// layoutParams.width = (608.dp * 1.5).roundToInt()
|
||||
// layoutParams.height = (456.dp * 1.5).roundToInt()
|
||||
layoutParams.width = (600.dp * 1.5).roundToInt()
|
||||
layoutParams.height = (400.dp * 1.5).roundToInt()
|
||||
previewView.setLayoutParams(layoutParams)
|
||||
faceRectView.setLayoutParams(layoutParams)
|
||||
return layoutParams
|
||||
}
|
||||
|
||||
private fun initRgbCamera() {
|
||||
val cameraListener: CameraListener = object : CameraListener {
|
||||
override fun onCameraOpened(
|
||||
camera: Camera,
|
||||
cameraId: Int,
|
||||
displayOrientation: Int,
|
||||
isMirror: Boolean
|
||||
) {
|
||||
Timber.tag(TAG).d("initRgbCamera Rgb onCameraOpened: cameraId = $cameraId, displayOrientation = $displayOrientation")
|
||||
activity.runOnUiThread({
|
||||
val previewSizeRgb = camera.getParameters().getPreviewSize()
|
||||
val layoutParams = adjustPreviewViewSize(
|
||||
binding.dualCameraTexturePreviewRgb,
|
||||
binding.dualCameraTexturePreviewRgb, binding.dualCameraFaceRectView,
|
||||
previewSizeRgb, displayOrientation, 0.6F
|
||||
)
|
||||
|
||||
Timber.tag(TAG).d("initRgbCamera previewSizeRgb = ${previewSizeRgb.width}-${previewSizeRgb.height}")
|
||||
Timber.tag(TAG).d("initRgbCamera layoutParams = ${layoutParams.width}-${layoutParams.height}")
|
||||
Timber.tag(TAG).d(
|
||||
"initRgbCamera isMirror = ${isMirror}, isDrawRgbRectHorizontalMirror = ${
|
||||
ConfigUtil.isDrawRgbRectHorizontalMirror(
|
||||
context
|
||||
)
|
||||
}, isDrawRgbRectVerticalMirror = ${
|
||||
ConfigUtil.isDrawRgbRectVerticalMirror(
|
||||
context
|
||||
)
|
||||
}"
|
||||
)
|
||||
// 调整识别窗口位置
|
||||
rgbFaceRectTransformer = FaceRectTransformer(
|
||||
// previewSizeRgb.width,
|
||||
// previewSizeRgb.height,
|
||||
layoutParams.width,
|
||||
layoutParams.height,
|
||||
layoutParams.width,
|
||||
layoutParams.height,
|
||||
90,
|
||||
cameraId,
|
||||
isMirror,
|
||||
true,
|
||||
true
|
||||
)
|
||||
|
||||
recognizeViewModel.onRgbCameraOpened(camera)
|
||||
recognizeViewModel.setRgbFaceRectTransformer(rgbFaceRectTransformer)
|
||||
})
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||
override fun onPreview(nv21: ByteArray?, camera: Camera?) {
|
||||
if (!isRecognition) {
|
||||
return
|
||||
}
|
||||
binding.dualCameraFaceRectView.clearFaceInfo()
|
||||
val facePreviewInfoList: MutableList<FacePreviewInfo?>? =
|
||||
recognizeViewModel.onPreviewFrame(nv21, true)
|
||||
if (facePreviewInfoList != null && rgbFaceRectTransformer != null) {
|
||||
drawPreviewInfo(facePreviewInfoList)
|
||||
}
|
||||
recognizeViewModel.clearLeftFace(facePreviewInfoList)
|
||||
}
|
||||
|
||||
override fun onCameraClosed() {
|
||||
Timber.tag(TAG).i("initRgbCamera onCameraClosed: ")
|
||||
}
|
||||
|
||||
override fun onCameraError(e: java.lang.Exception) {
|
||||
Timber.tag(TAG).i("initRgbCamera onCameraError: %s", e.message)
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
override fun onCameraConfigurationChanged(cameraID: Int, displayOrientation: Int) {
|
||||
Timber.tag(TAG).i("initRgbCamera onCameraConfigurationChanged: threadName = ${Thread.currentThread().name}")
|
||||
if (rgbFaceRectTransformer != null) {
|
||||
rgbFaceRectTransformer!!.cameraDisplayOrientation = displayOrientation
|
||||
}
|
||||
Timber.tag(TAG).i("initRgbCamera onCameraConfigurationChanged: $cameraID $displayOrientation")
|
||||
}
|
||||
}
|
||||
val measuredWidth = binding.dualCameraTexturePreviewRgb.measuredWidth
|
||||
val measuredHeight = binding.dualCameraTexturePreviewRgb.measuredHeight
|
||||
Timber.tag(TAG).i("initRgbCamera measuredWidth=$measuredWidth,measuredHeight=$measuredHeight")
|
||||
|
||||
val previewConfig: PreviewConfig = recognizeViewModel.previewConfig
|
||||
rgbCameraHelper = DualCameraHelper.Builder()
|
||||
.previewViewSize(Point(measuredWidth, measuredHeight))
|
||||
.rotation(activity.windowManager.defaultDisplay.rotation)
|
||||
.additionalRotation(previewConfig.rgbAdditionalDisplayOrientation) // 角度
|
||||
.previewSize(recognizeViewModel.loadPreviewSize())
|
||||
.specificCameraId(previewConfig.rgbCameraId)
|
||||
.isMirror(true)
|
||||
.previewOn(binding.dualCameraTexturePreviewRgb)
|
||||
.cameraListener(cameraListener)
|
||||
.build()
|
||||
rgbCameraHelper!!.init()
|
||||
// rgbCameraHelper!!.start()
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化红外相机,若活体检测类型是可见光活体检测或不启用活体,则不需要启用
|
||||
*/
|
||||
private fun initIrCamera() {
|
||||
Timber.tag(TAG).d("initIrCamera: livenessType = $livenessType")
|
||||
if (livenessType === LivenessType.RGB) {
|
||||
return
|
||||
}
|
||||
val irCameraListener: CameraListener = object : CameraListener {
|
||||
override fun onCameraOpened(
|
||||
camera: Camera,
|
||||
cameraId: Int,
|
||||
displayOrientation: Int,
|
||||
isMirror: Boolean
|
||||
) {
|
||||
Timber.tag(TAG).d("initIrCamera IR onCameraOpened: cameraId = $cameraId, displayOrientation = $displayOrientation")
|
||||
val previewSizeIr = camera.getParameters().getPreviewSize()
|
||||
val layoutParams = adjustPreviewViewSize(
|
||||
binding.dualCameraTexturePreviewRgb,
|
||||
binding.dualCameraTexturePreviewIr, binding.dualCameraFaceRectViewIr,
|
||||
previewSizeIr, displayOrientation, 0.25f
|
||||
)
|
||||
|
||||
irFaceRectTransformer = FaceRectTransformer(
|
||||
// previewSizeIr.width, previewSizeIr.height,
|
||||
layoutParams.width, layoutParams.height,
|
||||
layoutParams.width, layoutParams.height, displayOrientation, cameraId, isMirror,
|
||||
ConfigUtil.isDrawIrRectHorizontalMirror(context),
|
||||
ConfigUtil.isDrawIrRectVerticalMirror(context)
|
||||
)
|
||||
|
||||
recognizeViewModel.onIrCameraOpened(camera)
|
||||
recognizeViewModel.setIrFaceRectTransformer(irFaceRectTransformer)
|
||||
}
|
||||
|
||||
|
||||
override fun onPreview(nv21: ByteArray?, camera: Camera?) {
|
||||
recognizeViewModel.refreshIrPreviewData(nv21)
|
||||
}
|
||||
|
||||
override fun onCameraClosed() {
|
||||
Timber.tag(TAG).i("initIrCamera onCameraClosed: ")
|
||||
}
|
||||
|
||||
override fun onCameraError(e: java.lang.Exception) {
|
||||
Timber.tag(TAG).i("initIrCamera onCameraError: ${e.message}")
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
override fun onCameraConfigurationChanged(
|
||||
cameraID: Int,
|
||||
displayOrientation: Int
|
||||
) {
|
||||
if (irFaceRectTransformer != null) {
|
||||
irFaceRectTransformer!!.cameraDisplayOrientation = displayOrientation
|
||||
}
|
||||
Timber.tag(TAG).i("initIrCamera onCameraConfigurationChanged: cameraID = $cameraID, displayOrientation = $displayOrientation")
|
||||
}
|
||||
}
|
||||
|
||||
val previewConfig = recognizeViewModel.previewConfig
|
||||
irCameraHelper = DualCameraHelper.Builder()
|
||||
.previewViewSize(
|
||||
Point(
|
||||
binding.dualCameraTexturePreviewIr.measuredWidth,
|
||||
binding.dualCameraTexturePreviewIr.measuredHeight
|
||||
)
|
||||
)
|
||||
.rotation(activity.windowManager.defaultDisplay.rotation)
|
||||
.specificCameraId(previewConfig.irCameraId)
|
||||
.previewOn(binding.dualCameraTexturePreviewIr)
|
||||
.cameraListener(irCameraListener)
|
||||
.isMirror(true)
|
||||
.previewSize(recognizeViewModel.loadPreviewSize()) //相机预览大小设置,RGB与IR需使用相同大小
|
||||
.additionalRotation(previewConfig.irAdditionalDisplayOrientation) //额外旋转角度
|
||||
.build()
|
||||
irCameraHelper!!.init()
|
||||
try {
|
||||
irCameraHelper!!.start()
|
||||
} catch (e: RuntimeException) {
|
||||
ToastUtils.showToast(e.message + context.getString(R.string.camera_error_notice))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制RGB、IR画面的实时人脸信息
|
||||
*
|
||||
* @param facePreviewInfoList RGB画面的实时人脸信息
|
||||
*/
|
||||
private fun drawPreviewInfo(facePreviewInfoList: MutableList<FacePreviewInfo?>) {
|
||||
// Timber.tag(TAG).d("drawPreviewInfo facePreviewInfoList = ${facePreviewInfoList.size}, rgbFaceRectTransformer = ${rgbFaceRectTransformer != null}")
|
||||
if (rgbFaceRectTransformer != null) {
|
||||
val rgbDrawInfoList: MutableList<DrawInfo?>? = recognizeViewModel.getDrawInfo(
|
||||
facePreviewInfoList,
|
||||
LivenessType.RGB,
|
||||
openRectInfoDraw
|
||||
)
|
||||
// 识别成功
|
||||
binding.dualCameraFaceRectView.drawRealtimeFaceInfo(rgbDrawInfoList)
|
||||
}
|
||||
|
||||
if (facePreviewInfoList.isEmpty() || (lastFaceTrackId != facePreviewInfoList[0]!!.trackId)) {
|
||||
if (lastFaceTrackId != -1) {
|
||||
mealPickupMode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0) ?: 0
|
||||
Timber.tag(TAG).i("$lastFaceTrackId 用户离开")
|
||||
lastFaceTrackId = -1
|
||||
postUserData()
|
||||
if (mealPickupMode == 0) {
|
||||
step1FoodRecognizing()
|
||||
} else {
|
||||
step2FaceRecognizing(currentFood!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun postUserData() {
|
||||
Timber.tag(TAG).d("postUserData")
|
||||
if (userNutritionData == null || currentFood == null) {
|
||||
Timber.tag(TAG).d("postUserData userNutritionData = ${userNutritionData == null}, currentFood = ${currentFood == null}")
|
||||
return
|
||||
}
|
||||
var eatWeight = 0.0
|
||||
eatWeight = if (mealPickupMode == 0) {
|
||||
//即放即取
|
||||
recognitionWeight - 0
|
||||
} else {
|
||||
recognitionWeight - lastWeight
|
||||
}
|
||||
val userNutritionParam = UserNutritionParam(
|
||||
userId = userNutritionData?.userId!!,
|
||||
foodId = currentFood?.id!!,
|
||||
faceTime = recognitionTime,
|
||||
faceEndTime = System.currentTimeMillis(),
|
||||
eatWeight = eatWeight,//lastWeight
|
||||
foodWeight = lastWeight
|
||||
)
|
||||
userViewModel.postUserNutritionData(listOf(userNutritionParam))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user