feat(activity): 新增菜品识别功能,重构FoodRecognizeDialog并临时跳过调料配置检查
- 新增 FoodRecognizeDialog(继承 BottomSheetDialog),只初始化一次,暴露 loadData() 方法 - 新增 dialog_food_recognize.xml 布局(标题 + 2列 GridLayoutManager RecyclerView) - PrepareCookActivity 集成相机初始化、拍照识别流程(重量稳定 ≥10g 时触发) - PrepareCookActivity 新增 flCameraContainer(1dp 隐藏容器)用于相机预览绑定 - SelectDishActivity 临时注释 judgeDeviceConfig 逻辑,直接调用 block() - 修正 GpioUtils/L/LightManager 包名为 com.shuwei.dish.match.utils
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
package com.shuwei.dish.match.ui
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Typeface
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.viewModels
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.shuwei.dish.match.R
|
||||
@@ -12,23 +13,34 @@ import com.shuwei.dish.match.adapter.DishPartAdapter
|
||||
import com.shuwei.dish.match.base.BaseActivity
|
||||
import com.shuwei.dish.match.databinding.ActivityPrepareCookBinding
|
||||
import com.shuwei.dish.match.databinding.LayoutFoodRemindBinding
|
||||
import com.shuwei.dish.match.dialog.FoodSearchDialog
|
||||
import com.shuwei.dish.match.dialog.CommonDialog
|
||||
import com.shuwei.dish.match.dialog.FoodRecognizeDialog
|
||||
import com.shuwei.dish.match.dialog.FoodSearchDialog
|
||||
import com.shuwei.dish.match.entity.CookFoodEntity
|
||||
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
|
||||
import com.shuwei.dish.match.entity.FoodRecord
|
||||
import com.shuwei.dish.match.net.NetViewModel
|
||||
import android.view.ViewGroup
|
||||
import androidx.camera.view.PreviewView
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.shuwei.dish.match.databinding.LayoutCameraPreviewBinding
|
||||
import com.shuwei.dish.match.objbox.FoodModule
|
||||
import com.shuwei.dish.match.utils.AddressUtil
|
||||
import com.shuwei.dish.match.utils.CameraUtils
|
||||
import com.shuwei.dish.match.utils.ImageUtil
|
||||
import com.shuwei.dish.match.utils.SwipeCallback
|
||||
import com.shuwei.dish.match.utils.WeightUtil
|
||||
import com.shuwei.dish.match.utils.ext.clickWithDebounce
|
||||
import com.shuwei.dish.match.utils.ext.gone
|
||||
import com.shuwei.dish.match.utils.ext.roundedDecimalPlace
|
||||
import com.shuwei.dish.match.utils.ext.startActivity
|
||||
import com.shuwei.dish.match.utils.ext.toJsonString
|
||||
import com.shuwei.dish.match.utils.ext.toast
|
||||
import com.shuwei.dish.match.utils.ext.visible
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.Serializable
|
||||
import kotlin.getValue
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
class PrepareCookActivity : BaseActivity() {
|
||||
@@ -40,6 +52,8 @@ class PrepareCookActivity : BaseActivity() {
|
||||
|
||||
private lateinit var binding: ActivityPrepareCookBinding
|
||||
|
||||
private val cameraUtils: CameraUtils by lazy { CameraUtils(this) }
|
||||
|
||||
private var food: FoodRecord? = null
|
||||
|
||||
private var goodsList: MutableList<CookFoodGoodsEntity>? = null
|
||||
@@ -69,6 +83,7 @@ class PrepareCookActivity : BaseActivity() {
|
||||
binding.tvDishName.text = food?.foodName ?: ""
|
||||
addViewClickListener()
|
||||
|
||||
initCamera(binding.flCameraContainer)
|
||||
initRecyclerView()
|
||||
|
||||
getDishDetail()
|
||||
@@ -81,6 +96,9 @@ class PrepareCookActivity : BaseActivity() {
|
||||
//Log.d(TAG, "addViewClickListener: address=$address,state=$state,weight=$weight")
|
||||
if (address == AddressUtil.ONE) {
|
||||
binding.tvDishPartWeight.text = "${weight}"
|
||||
if (state == WeightUtil.STATE_STABLE) {
|
||||
recognizeFood(weight)
|
||||
}
|
||||
}
|
||||
})
|
||||
binding.ivWeightClear.setOnClickListener {
|
||||
@@ -134,6 +152,25 @@ class PrepareCookActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private val isTakingPhoto = AtomicBoolean(false)
|
||||
|
||||
/**
|
||||
* 重量稳定时触发菜品识别
|
||||
* @param weight 当前稳定重量
|
||||
*/
|
||||
private fun recognizeFood(weight: Double) {
|
||||
if (weight < 10) {
|
||||
if (recognizeDialog.isShowing) recognizeDialog.dismiss()
|
||||
return
|
||||
}
|
||||
if (isTakingPhoto.get() || recognizeDialog.isShowing) return
|
||||
isTakingPhoto.set(true)
|
||||
cameraUtils.takePhoto(
|
||||
succCallback = cameraSuccessCallback,
|
||||
failCallback = cameraFailureCallback
|
||||
)
|
||||
}
|
||||
|
||||
private fun addFood() {
|
||||
FoodSearchDialog().show(this) { item ->
|
||||
val filterResult = list.firstOrNull { it.goodsId == item.goodsId }
|
||||
@@ -251,6 +288,16 @@ class PrepareCookActivity : BaseActivity() {
|
||||
|
||||
private var firstReqSize = 0
|
||||
private val list = mutableListOf<CookFoodGoodsEntity>()
|
||||
private val recognizeDialog by lazy {
|
||||
FoodRecognizeDialog(this) { item ->
|
||||
// TODO: 处理识别结果选中
|
||||
}
|
||||
.apply {
|
||||
setOnDismissListener {
|
||||
isTakingPhoto.set(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
private val dishPartAdapter by lazy {
|
||||
DishPartAdapter(list).apply {
|
||||
setOnItemClickListener { _, _, positon ->
|
||||
@@ -326,6 +373,26 @@ class PrepareCookActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
cameraUtils.bind()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
cameraUtils.unbind()
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化相机并绑定预览容器
|
||||
* @param container 相机预览容器
|
||||
*/
|
||||
fun initCamera(container: ViewGroup) {
|
||||
cameraUtils.initCamera()
|
||||
val previewBinding = LayoutCameraPreviewBinding.inflate(layoutInflater, container)
|
||||
cameraUtils.setPreviewController(previewBinding.previewView)
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
@SuppressLint("GestureBackNavigation")
|
||||
@Deprecated("Deprecated in Java")
|
||||
@@ -367,4 +434,49 @@ class PrepareCookActivity : BaseActivity() {
|
||||
}.show()
|
||||
}
|
||||
|
||||
private val cameraSuccessCallback: (Uri) -> Unit = { uri ->
|
||||
Log.d(TAG, "takePhoto success")
|
||||
lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
val bitmap = ImageUtil.uriToBitmap(this@PrepareCookActivity, uri)
|
||||
if (bitmap == null) {
|
||||
Log.d(TAG, "takePhoto bitmap is null")
|
||||
isTakingPhoto.set(false)
|
||||
return@withContext
|
||||
}
|
||||
val foodScoreList = FoodModule.getFoodScoreList(bitmap)
|
||||
if (foodScoreList.isEmpty()) {
|
||||
Log.d(TAG, "takePhoto foodScoreList is empty")
|
||||
isTakingPhoto.set(false)
|
||||
return@withContext
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
queryFood(foodScoreList)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val cameraFailureCallback: (String) -> Unit = { errMsg ->
|
||||
Log.d(TAG, "takePhoto failure")
|
||||
isTakingPhoto.set(false)
|
||||
}
|
||||
|
||||
private fun queryFood(list: List<FoodModule.IdNameScore>) {
|
||||
Log.d(TAG, "takePhoto queryFood, list=${list.toString()}")
|
||||
//调用接口成功,返回食材列表设置isTakingPhoto.set(false),暂时写死数据
|
||||
val foodList = mutableListOf<CookFoodGoodsEntity>()
|
||||
list.forEachIndexed { index, food ->
|
||||
val foodName = food.name.split("WP").first()
|
||||
val foodScore = ((1-food.score) * 100).roundedDecimalPlace(2)
|
||||
foodList.add(CookFoodGoodsEntity(
|
||||
goodsId = index.toString(),
|
||||
goodsName = "${foodName}-${foodScore}%",
|
||||
foodId = index.toString()
|
||||
))
|
||||
}
|
||||
recognizeDialog.loadData(foodList)
|
||||
isTakingPhoto.set(false)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -82,18 +82,19 @@ class SelectDishActivity : BaseActivity() {
|
||||
private var configFinished = false
|
||||
|
||||
fun judgeDeviceConfig(block: () -> Unit) {
|
||||
if (configFinished) {
|
||||
block()
|
||||
return
|
||||
}
|
||||
appViewModel.loadSeasoning {
|
||||
if (it.isEmpty()) {
|
||||
showDeviceConfigDialog()
|
||||
return@loadSeasoning
|
||||
}
|
||||
configFinished = true
|
||||
block()
|
||||
}
|
||||
// if (configFinished) {
|
||||
// block()
|
||||
// return
|
||||
// }
|
||||
// appViewModel.loadSeasoning {
|
||||
// if (it.isEmpty()) {
|
||||
// showDeviceConfigDialog()
|
||||
// return@loadSeasoning
|
||||
// }
|
||||
// configFinished = true
|
||||
// block()
|
||||
// }
|
||||
block()
|
||||
}
|
||||
|
||||
private fun showDeviceConfigDialog() {
|
||||
|
||||
Reference in New Issue
Block a user