精简了称sdk调用

This commit is contained in:
zxj
2025-08-06 14:45:07 +08:00
parent bbf2e605b5
commit 08611349aa
15 changed files with 545 additions and 101 deletions
@@ -19,7 +19,6 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewOutlineProvider
import android.view.ViewTreeObserver
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import androidx.lifecycle.Observer
@@ -32,10 +31,10 @@ import com.sw.dualscreen.databinding.PresentationSecondaryScreenBinding
import com.sw.dualscreen.ext.dp
import com.sw.dualscreen.ext.format2String
import com.sw.dualscreen.ext.maskName
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.sdk.SensorScaleUtils
import com.sw.dualscreen.utils.Debouncer
import com.sw.dualscreen.utils.GlideUtils
import com.sw.dualscreen.viewmodel.UserViewModel
@@ -49,9 +48,11 @@ 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
@@ -67,9 +68,6 @@ class SecondaryScreenPresentation(
val recognizeViewModel: RecognizeViewModel
) : Presentation(context, display), ViewTreeObserver.OnGlobalLayoutListener {
private lateinit var binding: PresentationSecondaryScreenBinding
private var cameraDevice: CameraDevice? = null
// private var cameraCaptureSession: CameraCaptureSession? = null
private lateinit var cameraManager: CameraManager
private var cameraIdList: Array<String> = arrayOf()
@@ -77,13 +75,17 @@ class SecondaryScreenPresentation(
private var currentFood: FoodInfo? = null
private var lastFood: FoodInfo? = null
private var userNutritionData: UserNutritionData? = null
private var dinnerType: DinnerTypeInfo? = null
private var dinnerTypeInfo: DinnerTypeInfo? = null
private lateinit var itemUserNutritionBinding: ItemUserNutritionBinding
private lateinit var stepChangeCallback: (Int) -> Unit
// 上一次的计算热量结果
private var lastCalcResultInfo: UserNutritionUtils.CalcResultInfo? = null
private var startSensorScale: Boolean = false
private var lastWeight = 0.0
private val debouncer = Debouncer(1000)
private val debouncer = Debouncer(2000)
private var faceTime: Long = 0L // 人脸识别时的时间
private var recognitionWeight: Double = 0.0 // 人脸识别时的重量
private var lastFaceTrackId: Int = 0 // 上一次的人脸信息
// 当前步骤
var currentStep: Int = 0
@@ -104,17 +106,20 @@ class SecondaryScreenPresentation(
activity.lifecycleScope.launch {
viewModel.nutritionData.collect {
Timber.d("registerDataChange nutritionData = $it")
if (it == null) return@collect
userNutritionData = it
if (it == null) return@collect
step3()
itemUserNutritionBinding.tvUserName.text = it.userName.maskName()
itemUserNutritionBinding.tvRecommendHeat.text =
"推荐热量:${it.recommendMin}-${it.recommendMax}"
updateWeight(lastWeight)
}
}
activity.lifecycleScope.launch {
viewModel.dinnerTypeInfo.collect {
dinnerType = it
viewModel.dinnerTypeInfo.drop(1).collect {
Timber.d("registerDataChange dinnerTypeInfo = $it")
dinnerTypeInfo = it
}
}
}
@@ -155,8 +160,14 @@ class SecondaryScreenPresentation(
fun step1() {
Timber.d("step1")
currentStep = 1
//回复上一次的设置
lastWeight = 0.0
lastFood = null
lastCalcResultInfo = null
recognitionWeight = 0.0
stepChangeCallback(currentStep)
binding.tvFoodInfo.text = "菜品识别中"
binding.tvFoodName.text = "菜品识别中"
binding.ivFaceBg.visibility = View.GONE
binding.flPreview.visibility = View.VISIBLE
binding.ivImg.visibility = View.VISIBLE
@@ -164,8 +175,6 @@ class SecondaryScreenPresentation(
pauseCamera()
binding.tvBottomTip.visibility = View.VISIBLE
itemUserNutritionBinding.clNutritionData.visibility = View.GONE
stopSensorScale()
viewModel.getDinnerType()
}
/**
@@ -176,14 +185,15 @@ class SecondaryScreenPresentation(
currentStep = 2
stepChangeCallback(currentStep)
currentFood = foodInfo
viewModel.getDinnerType()
resumeCamera()
binding.tvFoodInfo.text = foodInfo.foodName
binding.tvFoodName.text = foodInfo.foodName
binding.ivImg.visibility = View.GONE
binding.flPreview.visibility = View.GONE
binding.flFace.visibility = View.VISIBLE
binding.ivFaceBg.visibility = View.VISIBLE
binding.ivFaceBg.visibility = View.GONE
binding.tvFaceTip.visibility = View.VISIBLE
binding.tvBottomTip.visibility = View.VISIBLE
itemUserNutritionBinding.clNutritionData.visibility = View.GONE
@@ -196,7 +206,7 @@ class SecondaryScreenPresentation(
if (currentStep == 1) {
step2(foodInfo)
} else {
binding.tvFoodInfo.text = foodInfo.foodName
binding.tvFoodName.text = foodInfo.foodName
GlideUtils.loadRoundCornerImage(
context,
url = currentFood!!.imgUrl,
@@ -218,10 +228,7 @@ class SecondaryScreenPresentation(
ToastUtils.showToast("选中物品为空3")
return
}
binding.tvFoodInfo.text = currentFood!!.foodName
pauseCamera()
startSensorScale()
binding.tvFoodName.text = currentFood!!.foodName
GlideUtils.loadRoundCornerImage(
context,
url = currentFood!!.imgUrl,
@@ -238,36 +245,40 @@ class SecondaryScreenPresentation(
itemUserNutritionBinding.clNutritionData.visibility = View.VISIBLE
}
private fun startSensorScale() {
Timber.d("startSensorScale startSensorScale = $startSensorScale")
if (startSensorScale) return
SensorScaleUtils.startScale { weight ->
fun updateWeight(weight: Double) {
Timber.d("updateWeight weight = $weight")
lastWeight = weight
Timber.d("updateWeight lastWeight = $weight")
if (currentFood == null) {
Timber.e("startSensorScale currentFood is null")
return@startScale
Timber.e("updateWeight currentFood is null")
return
}
if (userNutritionData == null) {
Timber.e("startSensorScale userNutritionData is null")
return@startScale
Timber.e("updateWeight userNutritionData is null")
return
}
if (dinnerType == null) {
Timber.e("startSensorScale dinnerType is null")
return@startScale
if (dinnerTypeInfo == null) {
Timber.e("updateWeight dinnerType is null")
return
}
// 重量和 当前食物都没变,则不再计算
if (weight == lastWeight && lastFood == currentFood) return@startScale
Timber.d("registerDataChange weight = $weight")
if (lastFood == currentFood) {
// Timber.e("updateWeight 物品未改变")
return
}
lastFood = currentFood
lastWeight = weight
debouncer.debounce {
val dinnerType = dinnerTypeInfo!!.dinnerType!!.dinnerType!!
Timber.d("updateWeight foodName = ${currentFood!!.foodName}, dinnerType = $dinnerType, userId = ${userNutritionData!!.userId}")
val calcResultInfo = UserNutritionUtils.calculateNutrition(
currentFood!!,
userNutritionData!!,
weight,
dinnerType = dinnerType!!.dinnerType!!.dinnerType!!
dinnerType = dinnerType
)
Timber.d("calcResultInfo = $calcResultInfo")
Timber.d("updateWeight calcResultInfo = $calcResultInfo")
if (lastCalcResultInfo == calcResultInfo) {
Timber.d("updateWeight 与上次计算结果无变化")
return@debounce
}
lastCalcResultInfo = calcResultInfo
@@ -318,11 +329,6 @@ class SecondaryScreenPresentation(
)
}
}
}
}
private fun stopSensorScale() {
SensorScaleUtils.closeScale()
}
private fun openCamera() {
@@ -338,7 +344,11 @@ class SecondaryScreenPresentation(
// 获取摄像头列表
cameraIdList = cameraManager.cameraIdList
if (cameraIdList.isEmpty()) {
Toast.makeText(context, "没有可用的摄像头", Toast.LENGTH_SHORT).show()
ToastUtils.showToast("没有可用的摄像头")
return
}
if (cameraIdList.size < 3) {
ToastUtils.showToast("摄像头数量异常")
return
}
@@ -347,27 +357,29 @@ class SecondaryScreenPresentation(
}
val cameraId = cameraIdList.last()
Timber.d("openCamera cameraId = $cameraId")
cameraManager.openCamera(cameraId, object : CameraDevice.StateCallback() {
val c1 = cameraIdList[0]
val c2 = cameraIdList[1]
val c3 = cameraIdList[2]
cameraManager.openCamera(c3, object : CameraDevice.StateCallback() {
override fun onOpened(device: CameraDevice) {
cameraDevice = device
// createCameraPreviewSession()
recognizeViewModel.init(
PreviewConfig(
2, 4, 0, 0
c3.toInt(), c2.toInt(), 90, 90
)
)
initRgbCamera()
resumeCamera()
// resumeCamera()
if (DualCameraHelper.hasDualCamera() && livenessType === LivenessType.IR) {
initIrCamera()
}
}
override fun onDisconnected(device: CameraDevice) {
device.close()
cameraDevice = null
}
override fun onError(device: CameraDevice, error: Int) {
device.close()
cameraDevice = null
}
}, null)
} catch (e: Exception) {
@@ -383,9 +395,12 @@ class SecondaryScreenPresentation(
private var isRecognition = false
private var rgbCameraHelper: DualCameraHelper? = null
private var rgbFaceRectTransformer: FaceRectTransformer? = null
private val livenessType = LivenessType.RGB
private val livenessType = LivenessType.IR
private var openRectInfoDraw = false
private var irCameraHelper: DualCameraHelper? = null
private var irFaceRectTransformer: FaceRectTransformer? = null
private fun initArcViewModel() {
recognizeViewModel.setLiveType(livenessType)
@@ -427,15 +442,18 @@ class SecondaryScreenPresentation(
})
recognizeViewModel.recognizeNotice.observe(activity, Observer { notice: String? ->
Timber.i("recognizeNotice observe notice = $notice")
if (currentStep == 3 && notice == "leave") {
// step1()
}
})
recognizeViewModel.recognizeUserId.observe(activity, Observer { userId: String? ->
Timber.i("recognizeUserId observe userId = $userId")
recognizeViewModel.recognizeUserId.observe(
activity,
Observer { compareResult: CompareResult ->
Timber.i("recognizeUserId observe compareResult = $compareResult")
faceTime = System.currentTimeMillis()
recognitionWeight = lastWeight
lastFaceTrackId = compareResult.trackId
val faceEntity = compareResult.faceEntity
val userId = faceEntity.userName
if (userId == null) return@Observer
viewModel.getUserNutritionData(userId = userId, foodId = currentFood!!.id!!)
})
@@ -519,12 +537,13 @@ class SecondaryScreenPresentation(
displayOrientation: Int,
isMirror: Boolean
) {
Timber.d("Rgb onCameraOpened: cameraId = $cameraId, displayOrientation = $displayOrientation")
activity.runOnUiThread({
val previewSizeRgb = camera.getParameters().getPreviewSize()
val layoutParams = adjustPreviewViewSize(
binding.dualCameraTexturePreviewRgb,
binding.dualCameraTexturePreviewRgb, binding.dualCameraFaceRectView,
previewSizeRgb, 90, 1f
previewSizeRgb, displayOrientation, 1f
)
Timber.d("initRgbCamera previewSizeRgb = ${previewSizeRgb.width}-${previewSizeRgb.height}")
Timber.d("initRgbCamera layoutParams = ${layoutParams.width}-${layoutParams.height}")
@@ -586,7 +605,7 @@ class SecondaryScreenPresentation(
)
)
.rotation(activity.windowManager.defaultDisplay.rotation)
.additionalRotation(90) // 角度
.additionalRotation(previewConfig.rgbAdditionalDisplayOrientation) // 角度
.previewSize(recognizeViewModel.loadPreviewSize())
.specificCameraId(previewConfig.rgbCameraId)
.isMirror(true)
@@ -597,12 +616,96 @@ class SecondaryScreenPresentation(
rgbCameraHelper!!.start()
}
/**
* 初始化红外相机,若活体检测类型是可见光活体检测或不启用活体,则不需要启用
*/
private fun initIrCamera() {
Timber.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.d("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, 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.i("onCameraClosed: ")
}
override fun onCameraError(e: java.lang.Exception) {
Timber.i("onCameraError: ${e.message}")
e.printStackTrace()
}
override fun onCameraConfigurationChanged(
cameraID: Int,
displayOrientation: Int
) {
if (irFaceRectTransformer != null) {
irFaceRectTransformer!!.cameraDisplayOrientation = displayOrientation
}
Timber.i("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.d("drawPreviewInfo facePreviewInfoList = ${facePreviewInfoList.size}, rgbFaceRectTransformer = ${rgbFaceRectTransformer != null}")
if (rgbFaceRectTransformer != null) {
val rgbDrawInfoList: MutableList<DrawInfo?>? = recognizeViewModel.getDrawInfo(
facePreviewInfoList,
@@ -612,6 +715,27 @@ class SecondaryScreenPresentation(
// 识别成功
binding.dualCameraFaceRectView.drawRealtimeFaceInfo(rgbDrawInfoList)
}
if (facePreviewInfoList.isEmpty() || (lastFaceTrackId != facePreviewInfoList[0]!!.trackId)) {
if (lastFaceTrackId != 0) {
Timber.i("$lastFaceTrackId 用户离开")
lastFaceTrackId = 0
postUserData()
step1()
}
}
}
fun postUserData() {
val userNutritionParam = UserNutritionParam(
userId = userNutritionData?.userId!!,
foodId = currentFood?.id!!,
faceTime = faceTime,
faceEndTime = System.currentTimeMillis(),
eatWeight = recognitionWeight - lastWeight,
foodWeight = lastWeight
)
viewModel.postUserNutritionData(listOf<UserNutritionParam>(userNutritionParam))
}
fun openRectInfoDraw(view: View?) {
@@ -643,7 +767,10 @@ class SecondaryScreenPresentation(
rgbCameraHelper!!.release()
rgbCameraHelper = null
}
SensorScaleUtils.closeScale()
if (irCameraHelper != null) {
irCameraHelper!!.release()
irCameraHelper = null
}
recognizeViewModel.destroy()
super.onStop()
}