精简了称sdk调用
This commit is contained in:
@@ -7,6 +7,10 @@ object GlobalData {
|
||||
// var globalEquipmentCode: String = "202501171634"
|
||||
var restId: String = ""
|
||||
|
||||
/**
|
||||
* 设备id
|
||||
*/
|
||||
var deviceId: String = ""
|
||||
/**
|
||||
* app版本号
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.sw.dualscreen
|
||||
|
||||
import com.sw.plate.App
|
||||
import com.sw.plate.utils.AppUtil
|
||||
import timber.log.Timber
|
||||
|
||||
class MyApp : App() {
|
||||
@@ -11,5 +12,8 @@ class MyApp : App() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
Timber.plant(Timber.DebugTree())
|
||||
val deviceId = AppUtil.getUDID(this)
|
||||
Timber.d("UDID = ${AppUtil.getUDID(this)}")
|
||||
GlobalData.deviceId = deviceId
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import android.graphics.Typeface
|
||||
import android.hardware.display.DisplayManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
import android.view.ViewOutlineProvider
|
||||
import android.widget.AdapterView
|
||||
@@ -36,7 +37,6 @@ import com.sw.dualscreen.sdk.SensorScaleUtils
|
||||
import com.sw.dualscreen.utils.GlideUtils
|
||||
import com.sw.dualscreen.view.CustomBottomSheetDialog
|
||||
import com.sw.dualscreen.viewmodel.UserViewModel
|
||||
import com.sw.plate.utils.AppUtil
|
||||
import com.sw.plate.utils.ToastUtils
|
||||
import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -56,6 +56,8 @@ class MainActivity : AppCompatActivity() {
|
||||
private var imageAnalysis: ImageAnalysis? = null
|
||||
private var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>? = null
|
||||
private var canIdentify: Boolean = false
|
||||
private var bottomSheetDialog: CustomBottomSheetDialog? = null
|
||||
private var lastWeight: Double = 0.0
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -63,7 +65,6 @@ class MainActivity : AppCompatActivity() {
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.main)
|
||||
|
||||
Timber.d("UDID = ${AppUtil.getUDID(this)}")
|
||||
initView()
|
||||
setupSecondaryDisplay()
|
||||
setupCamera()
|
||||
@@ -84,10 +85,10 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
binding.previewView.clipToOutline = true
|
||||
binding.tvToSearch.setOnClickListener {
|
||||
val bottomSheetDialog = CustomBottomSheetDialog.newInstance(viewModel) {
|
||||
bottomSheetDialog = CustomBottomSheetDialog.newInstance(viewModel) {
|
||||
updateCurrentFood(it)
|
||||
}
|
||||
bottomSheetDialog.show(supportFragmentManager, "CustomBottomSheetDialog")
|
||||
bottomSheetDialog!!.show(supportFragmentManager, "CustomBottomSheetDialog")
|
||||
}
|
||||
|
||||
val spinnerAdapter = ArrayAdapter<String>(
|
||||
@@ -138,7 +139,15 @@ class MainActivity : AppCompatActivity() {
|
||||
binding.tvFoodName.text = foodInfo.foodName
|
||||
binding.previewView.visibility = View.GONE
|
||||
binding.ivImg.visibility = View.VISIBLE
|
||||
GlideUtils.loadRoundCornerImage(this, foodInfo.imgUrl, binding.ivImg, 12)
|
||||
val imgUrl = foodInfo.imgUrl
|
||||
if (TextUtils.isEmpty(imgUrl)) {
|
||||
takePhoto { imgUri ->
|
||||
GlideUtils.loadRoundCornerImage(this, imgUri, binding.ivImg, 12)
|
||||
presentation.updateFood(foodInfo.copy(imgUrl = imgUri.toString()))
|
||||
}
|
||||
} else {
|
||||
GlideUtils.loadRoundCornerImage(this, foodInfo.imgUrl, binding.ivImg, 12)
|
||||
}
|
||||
pauseAnalysis()
|
||||
} else {
|
||||
binding.tvFoodName.text = "-"
|
||||
@@ -158,7 +167,8 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
SensorScaleUtils.startScale { weight ->
|
||||
// Timber.d("weight = $weight")
|
||||
Timber.d("registerDataChange weight = $weight")
|
||||
presentation.updateWeight(weight)
|
||||
if (weight > 0.0) {
|
||||
if (canIdentify) {
|
||||
canIdentify = false
|
||||
@@ -290,23 +300,30 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
// 拍照功能
|
||||
private fun takePhoto(callback: (Uri) -> Unit) {
|
||||
Timber.d("takePhoto")
|
||||
// 临时停止分析以避免干扰
|
||||
isAnalyzing = false
|
||||
|
||||
// 创建输出选项
|
||||
val outputFileOptions = ImageCapture.OutputFileOptions.Builder(
|
||||
File(
|
||||
cacheDir.path, "IMG_${System.currentTimeMillis()}.jpg"
|
||||
)
|
||||
).build()
|
||||
val executor = ContextCompat.getMainExecutor(this)
|
||||
val cacheDir = cacheDir
|
||||
val photoFile = File.createTempFile(
|
||||
"IMG_${System.currentTimeMillis()}",
|
||||
".jpg",
|
||||
cacheDir
|
||||
)
|
||||
|
||||
val cacheOutputOptions =
|
||||
ImageCapture.OutputFileOptions.Builder(photoFile).build()
|
||||
// 执行拍照
|
||||
imageCapture?.takePicture(
|
||||
outputFileOptions,
|
||||
cacheOutputOptions,
|
||||
executor,
|
||||
object : ImageCapture.OnImageSavedCallback {
|
||||
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
|
||||
runOnUiThread {
|
||||
val savedUri = outputFileResults.savedUri
|
||||
Timber.d("takePhoto savedUri = $savedUri")
|
||||
ToastUtils.showToast("照片保存成功: $savedUri")
|
||||
if (savedUri != null) {
|
||||
callback(savedUri)
|
||||
@@ -347,6 +364,7 @@ class MainActivity : AppCompatActivity() {
|
||||
presentation.setStepChangeCallback { step ->
|
||||
if (step == 1) {
|
||||
updateCurrentFood(null)
|
||||
bottomSheetDialog?.dismiss()
|
||||
}
|
||||
}
|
||||
presentation.show()
|
||||
|
||||
@@ -14,5 +14,5 @@ data class UserNutritionParam(
|
||||
val faceEndTime: Long,
|
||||
val eatWeight: Double,
|
||||
val foodWeight: Double,
|
||||
val deviceId: String = ""
|
||||
val deviceId: String = GlobalData.deviceId
|
||||
) : Parcelable
|
||||
|
||||
@@ -97,6 +97,6 @@ interface ApiService {
|
||||
@POST
|
||||
suspend fun postUserNutritionData(
|
||||
@Url url: String = "${GlobalData.appBaseUrl}/zhstapi/zhst/userEatFood/doubleBalance/face/stall",
|
||||
@Body param: UserNutritionParam
|
||||
@Body param: List<UserNutritionParam>
|
||||
): ApiResponse<String>
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ class RemoteRepository constructor(
|
||||
* 提交就餐数据
|
||||
*/
|
||||
suspend fun postUserNutritionData(
|
||||
param: UserNutritionParam
|
||||
param: List<UserNutritionParam>
|
||||
): ApiResponse<String> {
|
||||
return safeApiCall { apiService.postUserNutritionData(param = param) }
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.sw.dualscreen.sdk
|
||||
|
||||
import android.util.Log
|
||||
import com.sw.plate.utils.ToastUtils
|
||||
import com.wabon.wbintelligenthardwaresdk.api.SensorScale
|
||||
import com.wabon.wbintelligenthardwaresdk.api.SensorScale.OnScaleResult
|
||||
@@ -20,7 +19,9 @@ object SensorScaleUtils {
|
||||
private var mSensorScale: SensorScale? = null
|
||||
private var isOpened: Boolean = false
|
||||
private var isRead: Boolean = false
|
||||
private var isZero = false
|
||||
private var callback: Callback? = {}
|
||||
private var lastWeight: Double? = null
|
||||
|
||||
private fun init() {
|
||||
mSensorScale = SensorScale(object : OnScaleResult {
|
||||
@@ -36,7 +37,8 @@ object SensorScaleUtils {
|
||||
}
|
||||
// Log.d(TAG, "readWeight state = ${stateStr}, weight = $value")
|
||||
// 只使用稳定值
|
||||
if (state == SensorScale.STATE_STABLE) {
|
||||
if (state == SensorScale.STATE_STABLE && lastWeight != value) {
|
||||
lastWeight = value
|
||||
callback?.invoke(value)
|
||||
}
|
||||
}
|
||||
@@ -45,11 +47,11 @@ object SensorScaleUtils {
|
||||
* 读取鉴别率
|
||||
*/
|
||||
override fun readIdentify(rate: Int) {
|
||||
Log.e(TAG, "readIdentify rate = $rate")
|
||||
Timber.e("readIdentify rate = $rate")
|
||||
}
|
||||
|
||||
override fun fail(errCode: Int) {
|
||||
Log.e(TAG, "fail code = $errCode")
|
||||
Timber.e("fail code = $errCode")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -66,9 +68,6 @@ object SensorScaleUtils {
|
||||
}
|
||||
this.callback = callback
|
||||
init()
|
||||
mSensorScale?.zero {
|
||||
Timber.d("startScale: zero")
|
||||
}
|
||||
mSensorScale?.openScale(serialPort, baudRate) { open ->
|
||||
isOpened = open
|
||||
Timber.d("isOpened = $isOpened")
|
||||
@@ -78,6 +77,7 @@ object SensorScaleUtils {
|
||||
Thread.sleep(1000)
|
||||
// 打开后需要等待后才能调用,否则会 1001 SDK未初始化
|
||||
mSensorScale?.startContinuousRead()
|
||||
zero()
|
||||
}).start()
|
||||
}
|
||||
}
|
||||
@@ -88,7 +88,7 @@ object SensorScaleUtils {
|
||||
* 开启自动读取重量
|
||||
*/
|
||||
fun startContinuousRead(callback: Callback?) {
|
||||
Log.d(TAG, "开启自动读取 = $isRead")
|
||||
Timber.d("开启自动读取 = $isRead")
|
||||
if (isRead) {
|
||||
return
|
||||
}
|
||||
@@ -101,7 +101,7 @@ object SensorScaleUtils {
|
||||
* 手动读取重量
|
||||
*/
|
||||
fun readWeight(callback: Callback?) {
|
||||
Log.d(TAG, "isOpened = $isOpened")
|
||||
Timber.d("isOpened = $isOpened")
|
||||
if (!isOpened) {
|
||||
return
|
||||
}
|
||||
@@ -113,9 +113,11 @@ object SensorScaleUtils {
|
||||
* 零位标定
|
||||
*/
|
||||
fun zero() {
|
||||
if (!isOpened) return
|
||||
Timber.d("zero isZero = $isZero")
|
||||
if (isZero) return
|
||||
isZero = true
|
||||
mSensorScale?.zero {
|
||||
Log.d(TAG, "零位标定操作成功")
|
||||
Timber.d("零位标定操作成功")
|
||||
ToastUtils.showToast("零位标定操作成功")
|
||||
}
|
||||
}
|
||||
@@ -126,7 +128,7 @@ object SensorScaleUtils {
|
||||
fun tare() {
|
||||
if (!isOpened) return
|
||||
mSensorScale?.tare {
|
||||
Log.d(TAG, "去皮置零操作成功")
|
||||
Timber.d("去皮置零操作成功")
|
||||
ToastUtils.showToast("去皮置零操作成功")
|
||||
}
|
||||
}
|
||||
@@ -135,7 +137,7 @@ object SensorScaleUtils {
|
||||
* 停止自动读取重量
|
||||
*/
|
||||
fun stopContinuousRead() {
|
||||
Log.d(TAG, "isOpened = $isOpened")
|
||||
Timber.d("isOpened = $isOpened")
|
||||
if (!isOpened) {
|
||||
return
|
||||
}
|
||||
@@ -147,7 +149,7 @@ object SensorScaleUtils {
|
||||
* 关闭称重
|
||||
*/
|
||||
fun closeScale() {
|
||||
Log.d(TAG, "closeScale")
|
||||
Timber.d("closeScale")
|
||||
isOpened = false
|
||||
mSensorScale?.closeScale()
|
||||
mSensorScale == null
|
||||
|
||||
@@ -68,7 +68,6 @@ abstract class BaseViewModel() : ViewModel() {
|
||||
GlobalData.restId = equipmentInfo.canteenId!!
|
||||
}
|
||||
|
||||
|
||||
fun handleError(exception: Exception) {
|
||||
Timber.d("handleError ${exception.message}")
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ class UserViewModel : BaseViewModel() {
|
||||
val nutritionData: StateFlow<UserNutritionData?> = _nutritionData
|
||||
|
||||
/**
|
||||
* 饭点类型
|
||||
* 饭点类型 早餐/午餐/晚餐
|
||||
*/
|
||||
private val _dinnerTypeInfo = MutableStateFlow<DinnerTypeInfo?>(null)
|
||||
val dinnerTypeInfo: StateFlow<DinnerTypeInfo?> = _dinnerTypeInfo
|
||||
@@ -201,9 +201,9 @@ class UserViewModel : BaseViewModel() {
|
||||
/**
|
||||
* 提交就餐数据
|
||||
*/
|
||||
fun postUserNutritionData(param: UserNutritionParam) {
|
||||
fun postUserNutritionData(param: List<UserNutritionParam>) {
|
||||
Timber.d("postUserNutritionData param = $param")
|
||||
launchWithLoading {
|
||||
launch {
|
||||
val response = repository.postUserNutritionData(param)
|
||||
if (parseResponse(response)) {
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:paddingTop="1dp"
|
||||
android:visibility="gone">
|
||||
android:visibility="visible">
|
||||
|
||||
<TextureView
|
||||
android:id="@+id/dual_camera_texture_preview_rgb"
|
||||
@@ -29,8 +29,25 @@
|
||||
android:id="@+id/dual_camera_face_rect_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/fl_recognize_ir"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="200dp"
|
||||
android:layout_gravity="bottom">
|
||||
|
||||
<TextureView
|
||||
android:id="@+id/dual_camera_texture_preview_ir"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<com.sw.plate.utils.arcface.FaceRectView
|
||||
android:id="@+id/dual_camera_face_rect_view_ir"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</FrameLayout>
|
||||
</FrameLayout>
|
||||
<ImageView
|
||||
android:id="@+id/iv_face_bg"
|
||||
android:layout_width="match_parent"
|
||||
@@ -38,7 +55,7 @@
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/bg_face"
|
||||
android:contentDescription="相机遮盖层"
|
||||
android:visibility="visible" />
|
||||
android:visibility="gone" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
@@ -55,7 +72,7 @@
|
||||
android:visibility="visible">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_food_info"
|
||||
android:id="@+id/tv_food_name"
|
||||
style="@style/text_title_blob_2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<string name="app_name">档口双屏</string>
|
||||
<string name="specific_engine_init_failed">%s 初始化失败,错误码:%d\n错误码常量名:%s</string>
|
||||
<string name="permission_denied">权限被拒绝!</string>
|
||||
<string name="camera_error_notice">\n可能的原因:该设备不支持同时打开两个摄像头</string>
|
||||
|
||||
<array name="detection_method">
|
||||
<item>即放即取</item>
|
||||
|
||||
Reference in New Issue
Block a user