添加了模拟识别菜品功能

This commit is contained in:
zxj
2025-08-13 11:29:08 +08:00
parent e93550a24b
commit f055441340
5 changed files with 63 additions and 39 deletions
@@ -31,6 +31,7 @@ import com.sw.dualscreen.ext.dp
import com.sw.dualscreen.model.response.FoodInfo import com.sw.dualscreen.model.response.FoodInfo
import com.sw.dualscreen.presentation.SecondaryScreenPresentation import com.sw.dualscreen.presentation.SecondaryScreenPresentation
import com.sw.dualscreen.sdk.SensorScaleUtils import com.sw.dualscreen.sdk.SensorScaleUtils
import com.sw.dualscreen.utils.Debouncer
import com.sw.dualscreen.utils.GlideUtils import com.sw.dualscreen.utils.GlideUtils
import com.sw.dualscreen.view.CustomBottomSheetDialog import com.sw.dualscreen.view.CustomBottomSheetDialog
import com.sw.dualscreen.viewmodel.BaseViewModel import com.sw.dualscreen.viewmodel.BaseViewModel
@@ -55,6 +56,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
private var canIdentify: Boolean = false private var canIdentify: Boolean = false
private var bottomSheetDialog: CustomBottomSheetDialog? = null private var bottomSheetDialog: CustomBottomSheetDialog? = null
private var lastWeight: Double = 0.0 private var lastWeight: Double = 0.0
private var lastPhotoUri: Uri? = null // 最后拍照的图片
private var debouncer = Debouncer(2000)
override fun getViewModel(): BaseViewModel { override fun getViewModel(): BaseViewModel {
return viewModel return viewModel
@@ -87,6 +90,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
binding.previewView.clipToOutline = true binding.previewView.clipToOutline = true
binding.tvToSearch.setOnClickListener { binding.tvToSearch.setOnClickListener {
bottomSheetDialog = CustomBottomSheetDialog.newInstance(viewModel) { bottomSheetDialog = CustomBottomSheetDialog.newInstance(viewModel) {
it.photoUri = null
updateCurrentFood(it) updateCurrentFood(it)
} }
bottomSheetDialog!!.show(supportFragmentManager, "CustomBottomSheetDialog") bottomSheetDialog!!.show(supportFragmentManager, "CustomBottomSheetDialog")
@@ -139,9 +143,13 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
GlideUtils.loadRoundCornerImage(this, imgUri, binding.ivImg, 12) GlideUtils.loadRoundCornerImage(this, imgUri, binding.ivImg, 12)
presentation?.updateFood(foodInfo.copy(imgUrl = imgUri.toString())) presentation?.updateFood(foodInfo.copy(imgUrl = imgUri.toString()))
} }
} else {
if (foodInfo.photoUri != null) {
GlideUtils.loadRoundCornerImage(this, foodInfo.photoUri, binding.ivImg, 12)
} else { } else {
GlideUtils.loadRoundCornerImage(this, foodInfo.imgUrl, binding.ivImg, 12) GlideUtils.loadRoundCornerImage(this, foodInfo.imgUrl, binding.ivImg, 12)
} }
}
pauseAnalysis() pauseAnalysis()
} else { } else {
binding.tvFoodName.text = "-" binding.tvFoodName.text = "-"
@@ -156,21 +164,29 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
super.registerDataChange() super.registerDataChange()
lifecycleScope.launch { lifecycleScope.launch {
viewModel.identifiedFoodInfoList.collect { list -> viewModel.identifiedFoodInfoList.collect { list ->
binding.recyclerview
adapter.updateData(list) adapter.updateData(list)
// presentation.step2() if (list.isNotEmpty()) {
checkedItem = list[0]
checkedItem!!.photoUri = lastPhotoUri
updateCurrentFood(checkedItem)
lastPhotoUri = null
}
} }
} }
SensorScaleUtils.startScale { weight -> SensorScaleUtils.startScale { weight ->
Timber.d("registerDataChange weight = $weight") Timber.d("registerDataChange weight = $weight")
presentation?.updateWeight(weight) presentation?.updateWeight(weight)
if (weight > 0.0) {
if (canIdentify) { if (weight - lastWeight > 0.1 && presentation?.mealPickupMode == 0) { // 大于100g
canIdentify = false debouncer.debounce {
takePhoto { photoUri ->
lastPhotoUri = photoUri
viewModel.getIdentifiedFoodList() viewModel.getIdentifiedFoodList()
} }
} }
} }
lastWeight = weight
}
} }
private fun createAdapter(): GenericItemAdapter<FoodInfo, ItemFoodInfoBinding> { private fun createAdapter(): GenericItemAdapter<FoodInfo, ItemFoodInfoBinding> {
@@ -191,6 +207,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
// //
this.llRoot.setOnClickListener { this.llRoot.setOnClickListener {
Timber.d("itemClick ${item.foodName}, position = $position") Timber.d("itemClick ${item.foodName}, position = $position")
item.photoUri = null
checkedItem = item checkedItem = item
adapter.notifyDataSetChanged() adapter.notifyDataSetChanged()
updateCurrentFood(item) updateCurrentFood(item)
@@ -319,7 +336,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
runOnUiThread { runOnUiThread {
val savedUri = outputFileResults.savedUri val savedUri = outputFileResults.savedUri
Timber.d("takePhoto savedUri = $savedUri") Timber.d("takePhoto savedUri = $savedUri")
ToastUtils.showToast("照片保存成功: $savedUri") // ToastUtils.showToast("照片保存成功: $savedUri")
if (savedUri != null) { if (savedUri != null) {
callback(savedUri) callback(savedUri)
} }
@@ -1,6 +1,7 @@
package com.sw.dualscreen.model.response package com.sw.dualscreen.model.response
import android.net.Uri
import android.os.Parcelable import android.os.Parcelable
import android.text.TextUtils import android.text.TextUtils
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
@@ -23,7 +24,11 @@ data class FoodInfo(
@SerializedName("id") @SerializedName("id")
val id: String? = "", val id: String? = "",
@SerializedName("imgUrl") @SerializedName("imgUrl")
val imgUrl: String? = "", var imgUrl: String? = "",
/**
* 拍照结果
*/
var photoUri: Uri? = null,
@SerializedName("stFoodInfoMaterial") @SerializedName("stFoodInfoMaterial")
val stFoodInfoMaterial: StFoodInfoMaterial? = StFoodInfoMaterial(), val stFoodInfoMaterial: StFoodInfoMaterial? = StFoodInfoMaterial(),
@SerializedName("stFoodInfoPagoda") @SerializedName("stFoodInfoPagoda")
@@ -80,7 +80,7 @@ class SecondaryScreenPresentation(
private var recognitionTime: Long = 0L // 人脸识别时的时间 private var recognitionTime: Long = 0L // 人脸识别时的时间
private var recognitionWeight: Double = 0.0 // 人脸识别时的重量 private var recognitionWeight: Double = 0.0 // 人脸识别时的重量
private var lastFaceTrackId: Int = -1 // 上一次的人脸信息 private var lastFaceTrackId: Int = -1 // 上一次的人脸信息
private var mealPickupMode: Int = 0 // 取餐模式 0 即放即取 1 余量取餐 var mealPickupMode: Int = 0 // 取餐模式 0 即放即取 1 余量取餐
// 当前步骤 // 当前步骤
var currentStep: Int = 0 var currentStep: Int = 0
@@ -220,13 +220,7 @@ class SecondaryScreenPresentation(
if (currentStep == 1) { if (currentStep == 1) {
step2(foodInfo) step2(foodInfo)
} else { } else {
binding.tvFoodName.text = foodInfo.foodName updateFoodInfo(foodInfo)
GlideUtils.loadRoundCornerImage(
context,
url = currentFood!!.imgUrl,
imageView = binding.ivImg,
radius = 12
)
} }
} }
} }
@@ -243,13 +237,7 @@ class SecondaryScreenPresentation(
ToastUtils.showToast("选中物品为空3") ToastUtils.showToast("选中物品为空3")
return return
} }
binding.tvFoodName.text = currentFood!!.foodName updateFoodInfo(currentFood!!)
GlideUtils.loadRoundCornerImage(
context,
url = currentFood!!.imgUrl,
imageView = binding.ivImg,
radius = 12
)
binding.flPreview.visibility = View.VISIBLE binding.flPreview.visibility = View.VISIBLE
binding.ivImg.visibility = View.VISIBLE binding.ivImg.visibility = View.VISIBLE
binding.flFace.visibility = View.INVISIBLE binding.flFace.visibility = View.INVISIBLE
@@ -260,6 +248,25 @@ class SecondaryScreenPresentation(
itemUserNutritionBinding.clNutritionData.visibility = View.VISIBLE itemUserNutritionBinding.clNutritionData.visibility = View.VISIBLE
} }
private fun updateFoodInfo(foodInfo: FoodInfo) {
binding.tvFoodName.text = foodInfo.foodName
if (foodInfo.photoUri != null) {
GlideUtils.loadRoundCornerImage(
context,
url = foodInfo.photoUri,
imageView = binding.ivImg,
radius = 12
)
} else {
GlideUtils.loadRoundCornerImage(
context,
url = foodInfo.imgUrl,
imageView = binding.ivImg,
radius = 12
)
}
}
fun updateWeight(weight: Double) { fun updateWeight(weight: Double) {
lastWeight = weight * 1000 // 将千克转成克 lastWeight = weight * 1000 // 将千克转成克
Timber.d("updateWeight lastWeight = $weight") Timber.d("updateWeight lastWeight = $weight")
@@ -769,7 +776,7 @@ class SecondaryScreenPresentation(
postUserData() postUserData()
if (mealPickupMode == 0) { if (mealPickupMode == 0) {
step1() step1()
}else{ } else {
step2(currentFood!!) step2(currentFood!!)
} }
} }
@@ -74,12 +74,14 @@ object SensorScaleUtils {
if (open) { if (open) {
// zero() // zero()
if (autoScale) { if (autoScale) {
Thread({ Thread {
Thread.sleep(1000) Thread.sleep(1000)
// 打开后需要等待后才能调用,否则会 1001 SDK未初始化 // 打开后需要等待后才能调用,否则会 1001 SDK未初始化
mSensorScale?.startContinuousRead() mSensorScale?.startContinuousRead()
// Thread.sleep(1000)
zero()
}).start() }.start()
} }
} }
} }
@@ -114,11 +116,11 @@ object SensorScaleUtils {
* 零位标定 * 零位标定
*/ */
fun zero() { fun zero() {
Timber.d("zero isOpened = $isOpened") Timber.d("zero isOpened = $isOpened, mSensorScale = $mSensorScale")
if (!isOpened) return if (!isOpened) return
// isZero = true // isZero = true
mSensorScale?.zero { mSensorScale?.zero {
Timber.d("零位标定操作成功") Timber.d("zero 零位标定操作成功")
ToastUtils.showToast("零位标定操作成功") ToastUtils.showToast("零位标定操作成功")
} }
} }
@@ -16,7 +16,6 @@ import com.sw.plate.utils.ToastUtils
import com.sw.plate.utils.arcface.FaceApi import com.sw.plate.utils.arcface.FaceApi
import com.sw.plate.utils.arcface.facedb.entity.FaceEntity import com.sw.plate.utils.arcface.facedb.entity.FaceEntity
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -51,7 +50,6 @@ class UserViewModel : BaseViewModel() {
private val _loadFaceResult = MutableStateFlow<Boolean>(false) private val _loadFaceResult = MutableStateFlow<Boolean>(false)
val loadFaceResult : StateFlow<Boolean> = _loadFaceResult val loadFaceResult : StateFlow<Boolean> = _loadFaceResult
/** /**
* 获取token * 获取token
*/ */
@@ -140,15 +138,10 @@ class UserViewModel : BaseViewModel() {
fun getIdentifiedFoodList() { fun getIdentifiedFoodList() {
Timber.d("identifiedFoodList") Timber.d("identifiedFoodList")
launchWithLoading { launchWithLoading {
delay(5000) val response = repository.getRestInfoFoodsByType(foodName = "")
_identifiedFoodInfoList.value = List(6) { if (parseResponse(response)) {
FoodInfo( _identifiedFoodInfoList.value = response.result ?: emptyList()
id = "185225109358109491${it}",
foodName = "豆芽炒粉条$it",
imgUrl = "http://101.201.149.156:9002/temp/20230912/f54b2d76-45e1-4c6b-a7a0-5fd1eace99d1_1694484601879.png"
)
} }
// _currentFoodInfo.value = _identifiedFoodInfoList.value[0]
} }
} }