添加了副屏显示逻辑控制
This commit is contained in:
@@ -2,11 +2,13 @@ package com.sw.dualscreen.activity
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Outline
|
||||
import android.graphics.Typeface
|
||||
import android.hardware.display.DisplayManager
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewOutlineProvider
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
@@ -17,27 +19,41 @@ import androidx.camera.core.ImageCaptureException
|
||||
import androidx.camera.core.Preview
|
||||
import androidx.camera.lifecycle.ProcessCameraProvider
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import com.sw.dualscreen.R
|
||||
import com.sw.dualscreen.adapter.GenericItemAdapter
|
||||
import com.sw.dualscreen.adapter.GridSpacingItemDecoration
|
||||
import com.sw.dualscreen.adapter.dpToPx
|
||||
import com.sw.dualscreen.databinding.ActivityMainBinding
|
||||
import com.sw.dualscreen.databinding.ItemFoodInfoBinding
|
||||
import com.sw.dualscreen.ext.dp
|
||||
import com.sw.dualscreen.model.response.FoodInfo
|
||||
import com.sw.dualscreen.presentation.SecondaryScreenPresentation
|
||||
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
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
private lateinit var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>
|
||||
private val viewModel by viewModels<UserViewModel>()
|
||||
private lateinit var imageCapture: ImageCapture
|
||||
private var imageCapture: ImageCapture? = null
|
||||
private var isAnalyzing = true // 控制是否进行图像分析
|
||||
private lateinit var presentation: SecondaryScreenPresentation
|
||||
private val executor = Executors.newSingleThreadExecutor()
|
||||
private lateinit var adapter: GenericItemAdapter<FoodInfo, ItemFoodInfoBinding>
|
||||
private var checkedItem: FoodInfo? = null
|
||||
private var imageAnalysis: ImageAnalysis? = null
|
||||
private var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -54,6 +70,7 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
private fun initData() {
|
||||
viewModel.getEquipmentToken()
|
||||
presentation.step1()
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
@@ -64,12 +81,20 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
binding.previewView.clipToOutline = true
|
||||
|
||||
binding.tvToSearch.setOnClickListener {
|
||||
val bottomSheetDialog = CustomBottomSheetDialog.newInstance()
|
||||
val bottomSheetDialog = CustomBottomSheetDialog.newInstance(viewModel) {
|
||||
updateCurrentFood(it)
|
||||
}
|
||||
bottomSheetDialog.show(supportFragmentManager, "CustomBottomSheetDialog")
|
||||
}
|
||||
binding.spinner.setItems(arrayOf("即放即取", "持续取餐"))
|
||||
|
||||
val spinnerAdapter = ArrayAdapter<String>(
|
||||
this,
|
||||
R.layout.item_for_custom_spinner,
|
||||
arrayOf("即放即取", "持续取餐")
|
||||
)
|
||||
binding.spinner.adapter = spinnerAdapter
|
||||
binding.spinner.setSelection(0)
|
||||
binding.spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(
|
||||
parent: AdapterView<*>?,
|
||||
@@ -84,13 +109,90 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
}
|
||||
}
|
||||
adapter = createAdapter()
|
||||
binding.recyclerview.layoutManager = GridLayoutManager(this, 2)
|
||||
// 添加间距装饰(12dp)
|
||||
binding.recyclerview.addItemDecoration(
|
||||
GridSpacingItemDecoration(
|
||||
spanCount = 2,
|
||||
spacing = dpToPx(30),
|
||||
includeEdge = false // 包含边缘间距
|
||||
)
|
||||
)
|
||||
binding.recyclerview.adapter = adapter
|
||||
binding.tvFoodName.setOnClickListener {
|
||||
presentation.step3()
|
||||
}
|
||||
registerDataChange()
|
||||
}
|
||||
|
||||
fun updateCurrentFood(foodInfo: FoodInfo?) {
|
||||
presentation.updateFood(foodInfo)
|
||||
if (foodInfo != null) {
|
||||
binding.tvFoodName.text = foodInfo.foodName
|
||||
binding.previewView.visibility = View.GONE
|
||||
binding.ivImg.visibility = View.VISIBLE
|
||||
GlideUtils.loadRoundCornerImage(this, foodInfo.imgUrl, binding.ivImg, 12)
|
||||
pauseAnalysis()
|
||||
} else {
|
||||
binding.tvFoodName.text = "-"
|
||||
binding.previewView.visibility = View.VISIBLE
|
||||
binding.ivImg.visibility = View.GONE
|
||||
resumeAnalysis()
|
||||
viewModel.cleanIdentifiedFoodInfoList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerDataChange() {
|
||||
lifecycleScope.launch {
|
||||
viewModel.identifiedFoodInfoList.collect { list ->
|
||||
binding.recyclerview
|
||||
adapter.updateData(list)
|
||||
// presentation.step2()
|
||||
}
|
||||
}
|
||||
viewModel.getIdentifiedFoodList()
|
||||
// SensorScaleUtils.startScale { weight ->
|
||||
// Timber.d("weight = $weight")
|
||||
// if (weight > 0.0) {
|
||||
// if (canIndentify) {
|
||||
// viewModel.getIdentifiedFoodList()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
private fun createAdapter(): GenericItemAdapter<FoodInfo, ItemFoodInfoBinding> {
|
||||
return GenericItemAdapter(
|
||||
items = emptyList(),
|
||||
bindingInflater = ItemFoodInfoBinding::inflate,
|
||||
bindCallback = { item, position ->
|
||||
this.tvName.text = item.foodName
|
||||
if (item.id != checkedItem?.id) {
|
||||
this.tvName.typeface = Typeface.defaultFromStyle(Typeface.NORMAL)
|
||||
this.tvName.setTextColor(resources.getColor(R.color.identify_normal))
|
||||
this.llRoot.setBackgroundResource(R.drawable.grid_item_normal)
|
||||
} else {
|
||||
this.tvName.typeface = Typeface.defaultFromStyle(Typeface.BOLD)
|
||||
this.tvName.setTextColor(resources.getColor(R.color.identify_checked))
|
||||
this.llRoot.setBackgroundResource(R.drawable.grid_item_checked)
|
||||
}
|
||||
//
|
||||
this.llRoot.setOnClickListener {
|
||||
Timber.d("itemClick ${item.foodName}, position = $position")
|
||||
checkedItem = item
|
||||
adapter.notifyDataSetChanged()
|
||||
updateCurrentFood(item)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun setupCamera() {
|
||||
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
|
||||
cameraProviderFuture.addListener({
|
||||
val cameraProvider = cameraProviderFuture.get()
|
||||
Timber.d("setupCamera")
|
||||
cameraProviderFuture = ProcessCameraProvider.getInstance(this)
|
||||
cameraProviderFuture!!.addListener({
|
||||
val cameraProvider = cameraProviderFuture!!.get()
|
||||
|
||||
// 1. 创建预览用例
|
||||
val preview = Preview.Builder()
|
||||
@@ -100,17 +202,16 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
// 2. 创建图像分析用例(用于副屏显示)
|
||||
val imageAnalysis = ImageAnalysis.Builder()
|
||||
imageAnalysis = ImageAnalysis.Builder()
|
||||
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
||||
// .setTargetResolution(Size(640, 480)) // 降低分辨率减轻负担
|
||||
.build()
|
||||
.also { analysis ->
|
||||
analysis.setAnalyzer(executor) { imageProxy ->
|
||||
// 仅在步骤1时传递
|
||||
if (isAnalyzing) {
|
||||
val bitmap = imageProxy.toBitmap()
|
||||
runOnUiThread {
|
||||
presentation.updateImage(bitmap) // 更新副屏
|
||||
}
|
||||
presentation.updateImage(bitmap) // 更新副屏
|
||||
}
|
||||
imageProxy.close() // 必须关闭以释放资源
|
||||
}
|
||||
@@ -139,6 +240,48 @@ class MainActivity : AppCompatActivity() {
|
||||
}, ContextCompat.getMainExecutor(this))
|
||||
}
|
||||
|
||||
fun pauseAnalysis() {
|
||||
Timber.d("pauseAnalysis")
|
||||
isAnalyzing = false
|
||||
imageAnalysis?.clearAnalyzer()
|
||||
}
|
||||
|
||||
fun resumeAnalysis() {
|
||||
Timber.d("resumeAnalysis isAnalyzing = $isAnalyzing")
|
||||
if (isAnalyzing) return
|
||||
isAnalyzing = true
|
||||
imageAnalysis?.setAnalyzer(executor) { imageProxy ->
|
||||
if (presentation.currentStep == 1) {
|
||||
val bitmap = imageProxy.toBitmap()
|
||||
presentation.updateImage(bitmap)
|
||||
}
|
||||
imageProxy.close()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止相机识别
|
||||
*/
|
||||
private fun shutdownCamera() {
|
||||
Timber.d("shutdownCamera")
|
||||
try {
|
||||
// 1. 首先停止图像分析
|
||||
imageAnalysis?.clearAnalyzer()
|
||||
|
||||
// 2. 解除所有绑定
|
||||
cameraProviderFuture?.get()?.unbindAll()
|
||||
|
||||
// 3. 关闭相关资源
|
||||
imageCapture = null
|
||||
imageAnalysis = null
|
||||
|
||||
// 4. 停止副屏更新
|
||||
isAnalyzing = false
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Error shutting down camera")
|
||||
}
|
||||
}
|
||||
|
||||
// 拍照功能
|
||||
private fun takePhoto() {
|
||||
// 临时停止分析以避免干扰
|
||||
@@ -151,7 +294,7 @@ class MainActivity : AppCompatActivity() {
|
||||
)
|
||||
).build()
|
||||
// 执行拍照
|
||||
imageCapture.takePicture(
|
||||
imageCapture?.takePicture(
|
||||
outputFileOptions,
|
||||
executor,
|
||||
object : ImageCapture.OnImageSavedCallback {
|
||||
@@ -191,8 +334,18 @@ class MainActivity : AppCompatActivity() {
|
||||
viewModel = viewModel,
|
||||
recognizeViewModel = viewModels<RecognizeViewModel>().value
|
||||
)
|
||||
|
||||
presentation.setStepChangeCallback { step ->
|
||||
if (step == 1) {
|
||||
updateCurrentFood(null)
|
||||
}
|
||||
}
|
||||
presentation.show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
SensorScaleUtils.closeScale()
|
||||
presentation.dismiss()
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ val Float.sp: Float
|
||||
)
|
||||
|
||||
// 添加扩展函数
|
||||
fun ImageProxy.toBitmap(): Bitmap {
|
||||
fun ImageProxy.toSafeBitmap(): Bitmap {
|
||||
val yBuffer = planes[0].buffer // Y
|
||||
val uBuffer = planes[1].buffer // U
|
||||
val vBuffer = planes[2].buffer // V
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.sw.dualscreen.ext
|
||||
|
||||
/**
|
||||
* 姓名脱敏处理
|
||||
* 2个字:张*
|
||||
* 多于2个字:张*某
|
||||
*/
|
||||
fun String?.maskName(): String {
|
||||
if (this == null || this.isEmpty()) return ""
|
||||
|
||||
return when {
|
||||
length == 2 -> "${this[0]}*"
|
||||
length > 2 -> "${this[0]}*${this[length - 1]}"
|
||||
else -> this // 1个字的情况原样返回
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机号脱敏处理
|
||||
* 将第4-7位替换为*
|
||||
* 例如:138****1234
|
||||
*/
|
||||
fun String?.maskPhone(): String {
|
||||
if (this == null || this.isEmpty()) return ""
|
||||
if (this.length < 11) {
|
||||
return this
|
||||
}
|
||||
|
||||
val start = 3 // 第4位(索引从0开始)
|
||||
val end = 6 // 第7位
|
||||
|
||||
val sb = StringBuilder(this)
|
||||
for (i in start..end) {
|
||||
sb.setCharAt(i, '*')
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
@@ -5,11 +5,11 @@ import android.app.Presentation
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Outline
|
||||
import android.graphics.Point
|
||||
import android.hardware.Camera
|
||||
import android.hardware.camera2.CameraDevice
|
||||
import android.hardware.camera2.CameraManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.DisplayMetrics
|
||||
@@ -17,16 +17,22 @@ import android.util.Log
|
||||
import android.view.Display
|
||||
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.camera.view.PreviewView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.arcsoft.face.ErrorInfo
|
||||
import com.sw.dualscreen.R
|
||||
import com.sw.dualscreen.activity.MainActivity
|
||||
import com.sw.dualscreen.databinding.ItemUserNutritionBinding
|
||||
import com.sw.dualscreen.databinding.PresentationSecondaryScreenBinding
|
||||
import com.sw.dualscreen.ext.dp
|
||||
import com.sw.dualscreen.ext.maskName
|
||||
import com.sw.dualscreen.model.response.FoodInfo
|
||||
import com.sw.dualscreen.utils.GlideUtils
|
||||
import com.sw.dualscreen.viewmodel.UserViewModel
|
||||
import com.sw.plate.utils.ToastUtils
|
||||
import com.sw.plate.utils.arcface.ConfigUtil
|
||||
@@ -41,6 +47,7 @@ import com.sw.plate.utils.arcface.face.constants.LivenessType
|
||||
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.launch
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
@@ -50,7 +57,7 @@ class SecondaryScreenPresentation(
|
||||
val activity: MainActivity,
|
||||
context: Context,
|
||||
display: Display,
|
||||
viewModel: UserViewModel,
|
||||
val viewModel: UserViewModel,
|
||||
private val onDismissListener: () -> Unit = {},
|
||||
val recognizeViewModel: RecognizeViewModel
|
||||
) : Presentation(context, display), ViewTreeObserver.OnGlobalLayoutListener {
|
||||
@@ -60,7 +67,14 @@ class SecondaryScreenPresentation(
|
||||
// private var cameraCaptureSession: CameraCaptureSession? = null
|
||||
private lateinit var cameraManager: CameraManager
|
||||
private var cameraIdList: Array<String> = arrayOf()
|
||||
lateinit var previewView: PreviewView
|
||||
|
||||
// 当前食物信息
|
||||
private var currentFood: FoodInfo? = null
|
||||
private lateinit var itemUserNutritionBinding: ItemUserNutritionBinding
|
||||
private lateinit var stepChangeCallback: (Int) -> Unit
|
||||
|
||||
// 当前步骤
|
||||
var currentStep: Int = 0
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -68,9 +82,48 @@ class SecondaryScreenPresentation(
|
||||
binding = PresentationSecondaryScreenBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
window?.setBackgroundDrawableResource(android.R.color.transparent)
|
||||
initView()
|
||||
// binding.includeUserHeat.ivHeat.showText("1070千卡")
|
||||
registerDataChange()
|
||||
}
|
||||
|
||||
previewView = binding.previewView
|
||||
private fun registerDataChange() {
|
||||
activity.lifecycleScope.launch {
|
||||
viewModel.nutritionData.collect {
|
||||
Timber.d("registerDataChange nutritionData = $it")
|
||||
if (it == null) return@collect
|
||||
itemUserNutritionBinding.tvUserName.text = it.userName.maskName()
|
||||
itemUserNutritionBinding.tvRecommendHeat.text =
|
||||
"推荐热量:${it.recommendMin}-${it.recommendMax}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
binding.ivImg.outlineProvider = object : ViewOutlineProvider() {
|
||||
override fun getOutline(view: View, outline: Outline) {
|
||||
outline.setRoundRect(0, 0, view.width, view.height, 12f.dp)
|
||||
}
|
||||
}
|
||||
binding.ivImg.clipToOutline = true
|
||||
itemUserNutritionBinding = binding.includeUserNutrition
|
||||
}
|
||||
|
||||
override fun onDisplayRemoved() {
|
||||
super.onDisplayRemoved()
|
||||
onDismissListener()
|
||||
}
|
||||
|
||||
fun updateImage(bitmap: Bitmap) {
|
||||
activity.runOnUiThread {
|
||||
binding.ivImg.setImageBitmap(bitmap)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用虹软人脸失败
|
||||
*/
|
||||
private fun setupArcCamera() {
|
||||
cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
|
||||
// 获取摄像头列表
|
||||
cameraIdList = cameraManager.cameraIdList
|
||||
@@ -87,42 +140,95 @@ class SecondaryScreenPresentation(
|
||||
initArcView()
|
||||
openRectInfoDraw = true
|
||||
openCamera(cameraIdList.last())
|
||||
// binding.includeUserHeat.ivHeat.showText("1070千卡")
|
||||
}
|
||||
|
||||
override fun onDisplayRemoved() {
|
||||
super.onDisplayRemoved()
|
||||
onDismissListener()
|
||||
}
|
||||
|
||||
fun updateImage(bitmap: Bitmap) {
|
||||
activity.runOnUiThread {
|
||||
binding.ivImg.setImageBitmap(bitmap)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户1 显示相机预览
|
||||
*/
|
||||
fun step1(foodImg: Uri) {
|
||||
fun step1() {
|
||||
Timber.d("step1")
|
||||
currentStep = 1
|
||||
stepChangeCallback(currentStep)
|
||||
binding.tvFoodInfo.text = "菜品识别中"
|
||||
binding.flPreview.visibility = View.VISIBLE
|
||||
// binding.previewView.visibility = View.VISIBLE
|
||||
binding.ivImg.visibility = View.VISIBLE
|
||||
binding.flFace.visibility = View.GONE
|
||||
pauseCamera()
|
||||
|
||||
binding.tvBottomTip.visibility = View.VISIBLE
|
||||
itemUserNutritionBinding.clNutritionData.visibility = View.GONE
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示人脸识别
|
||||
*/
|
||||
fun step2() {
|
||||
fun step2(foodInfo: FoodInfo) {
|
||||
Timber.d("step2")
|
||||
currentStep = 2
|
||||
stepChangeCallback(currentStep)
|
||||
currentFood = foodInfo
|
||||
resumeCamera()
|
||||
binding.tvFoodInfo.text = foodInfo.foodName
|
||||
|
||||
binding.flPreview.visibility = View.GONE
|
||||
|
||||
binding.flFace.visibility = View.VISIBLE
|
||||
binding.ivFaceBg.visibility = View.VISIBLE
|
||||
binding.tvFaceTip.visibility = View.VISIBLE
|
||||
|
||||
setupArcCamera()
|
||||
}
|
||||
|
||||
fun updateFood(foodInfo: FoodInfo?) {
|
||||
Timber.d("step2 updateFood")
|
||||
currentFood = foodInfo
|
||||
if (foodInfo != null) {
|
||||
if (currentStep != 2) {
|
||||
step2(foodInfo)
|
||||
} else {
|
||||
binding.tvFoodInfo.text = foodInfo.foodName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示识别出的菜品信息
|
||||
*/
|
||||
fun step3() {
|
||||
Timber.d("step3")
|
||||
currentStep = 3
|
||||
stepChangeCallback(currentStep)
|
||||
if (currentFood == null) {
|
||||
ToastUtils.showToast("选中物品为空3")
|
||||
return
|
||||
}
|
||||
binding.tvFoodInfo.text = currentFood!!.foodName
|
||||
|
||||
binding.flPreview.visibility = View.VISIBLE
|
||||
// binding.previewView.visibility = View.GONE
|
||||
binding.ivImg.visibility = View.VISIBLE
|
||||
|
||||
GlideUtils.loadRoundCornerImage(
|
||||
context,
|
||||
url = currentFood!!.imgUrl,
|
||||
imageView = binding.ivImg,
|
||||
radius = 12
|
||||
)
|
||||
|
||||
binding.flFace.visibility = View.GONE
|
||||
binding.ivFaceBg.visibility = View.GONE
|
||||
binding.tvFaceTip.visibility = View.GONE
|
||||
|
||||
binding.tvBottomTip.visibility = View.GONE
|
||||
itemUserNutritionBinding.clNutritionData.visibility = View.VISIBLE
|
||||
|
||||
viewModel.getUserNutritionData(
|
||||
userId = "187c96f7bf294c08bc1d1e57b90a7210",
|
||||
foodId = currentFood!!.id!!
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
private fun openCamera(cameraId: String) {
|
||||
if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) !=
|
||||
PackageManager.PERMISSION_GRANTED
|
||||
@@ -158,6 +264,10 @@ class SecondaryScreenPresentation(
|
||||
}
|
||||
}
|
||||
|
||||
fun setStepChangeCallback(callback: (Int) -> Unit) {
|
||||
stepChangeCallback = callback
|
||||
}
|
||||
|
||||
// 虹软人脸配置 ⬇
|
||||
private var isRecognition = false
|
||||
private var rgbCameraHelper: DualCameraHelper? = null
|
||||
@@ -206,6 +316,9 @@ 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? ->
|
||||
|
||||
@@ -65,7 +65,7 @@ class RemoteRepository constructor(
|
||||
*/
|
||||
suspend fun getRestInfoFoodsByType(
|
||||
restId: String = GlobalData.restId,
|
||||
type: Int = 1,
|
||||
type: Int = 0,
|
||||
foodName: String,
|
||||
): ApiResponse<List<FoodInfo>> {
|
||||
return safeApiCall {
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
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
|
||||
import timber.log.Timber
|
||||
|
||||
typealias Callback = (Double) -> Unit
|
||||
|
||||
private const val TAG = "SensorScaleUtils"
|
||||
|
||||
/**
|
||||
* 称 传感器计算
|
||||
*/
|
||||
object SensorScaleUtils {
|
||||
|
||||
const val serialPort = "/dev/ttyS7"
|
||||
const val baudRate = 115200
|
||||
private var mSensorScale: SensorScale? = null
|
||||
private var isOpened: Boolean = false
|
||||
private var callback: Callback? = {}
|
||||
|
||||
private fun init() {
|
||||
mSensorScale = SensorScale(object : OnScaleResult {
|
||||
/**
|
||||
* 读取重量
|
||||
*/
|
||||
override fun readWeight(state: Int, value: Double) {
|
||||
val stateStr = when (state) {
|
||||
SensorScale.STATE_STABLE -> "稳定"
|
||||
SensorScale.STATE_UNSTABLE -> "不稳定"
|
||||
SensorScale.STATE_OVER_WEIGHT -> "量程溢出"
|
||||
else -> "未知"
|
||||
}
|
||||
// Log.d(TAG, "readWeight state = ${stateStr}, weight = $value")
|
||||
// 只使用稳定值
|
||||
if (state == SensorScale.STATE_STABLE) {
|
||||
callback?.invoke(value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取鉴别率
|
||||
*/
|
||||
override fun readIdentify(rate: Int) {
|
||||
Log.e(TAG, "readIdentify rate = $rate")
|
||||
}
|
||||
|
||||
override fun fail(errCode: Int) {
|
||||
Log.e(TAG, "fail code = $errCode")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启称重
|
||||
* @param autoScale 是否开启自动读取
|
||||
*/
|
||||
fun startScale(autoScale: Boolean = true, callback: Callback?) {
|
||||
if (isOpened) {
|
||||
startContinuousRead(callback = callback)
|
||||
return
|
||||
}
|
||||
this.callback = callback
|
||||
init()
|
||||
mSensorScale?.zero {
|
||||
Timber.d("startScale: zero")
|
||||
}
|
||||
mSensorScale?.openScale(serialPort, baudRate) { open ->
|
||||
isOpened = open
|
||||
Timber.d("isOpened = $isOpened")
|
||||
if (open) {
|
||||
if (autoScale) {
|
||||
Thread({
|
||||
Thread.sleep(1000)
|
||||
// 打开后需要等待后才能调用,否则会 1001 SDK未初始化
|
||||
mSensorScale?.startContinuousRead()
|
||||
}).start()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启自动读取重量
|
||||
*/
|
||||
fun startContinuousRead(callback: Callback?) {
|
||||
this.callback = callback
|
||||
Log.d(TAG, "开启自动读取 = $isOpened")
|
||||
if (!isOpened) {
|
||||
return
|
||||
}
|
||||
mSensorScale?.startContinuousRead()
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动读取重量
|
||||
*/
|
||||
fun readWeight(callback: Callback?) {
|
||||
this.callback = callback
|
||||
Log.d(TAG, "isOpened = $isOpened")
|
||||
if (!isOpened) {
|
||||
return
|
||||
}
|
||||
mSensorScale?.readWeight()
|
||||
}
|
||||
|
||||
/**
|
||||
* 零位标定
|
||||
*/
|
||||
fun zero() {
|
||||
if (!isOpened) return
|
||||
mSensorScale?.zero {
|
||||
Log.d(TAG, "零位标定操作成功")
|
||||
ToastUtils.showToast("零位标定操作成功")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 去皮置零
|
||||
*/
|
||||
fun tare() {
|
||||
if (!isOpened) return
|
||||
mSensorScale?.tare {
|
||||
Log.d(TAG, "去皮置零操作成功")
|
||||
ToastUtils.showToast("去皮置零操作成功")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止自动读取重量
|
||||
*/
|
||||
fun stopContinuousRead() {
|
||||
Log.d(TAG, "isOpened = $isOpened")
|
||||
if (!isOpened) {
|
||||
return
|
||||
}
|
||||
mSensorScale?.stopContinuousRead()
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭称重
|
||||
*/
|
||||
fun closeScale() {
|
||||
isOpened = false
|
||||
mSensorScale?.closeScale()
|
||||
mSensorScale == null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.sw.dualscreen.utils
|
||||
|
||||
class Debouncer(private val delayMillis: Long) {
|
||||
private var lastActionTime = 0L
|
||||
|
||||
/**
|
||||
* 执行防抖操作
|
||||
* @param action 要执行的操作
|
||||
* @return Boolean 是否执行了操作 (true=已执行, false=被防抖)
|
||||
*/
|
||||
fun debounce(action: () -> Unit): Boolean {
|
||||
val currentTime = System.currentTimeMillis()
|
||||
if (currentTime - lastActionTime >= delayMillis) {
|
||||
lastActionTime = currentTime
|
||||
action()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 重置防抖计时
|
||||
fun reset() {
|
||||
lastActionTime = 0L
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
package com.sw.dualscreen.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.widget.ImageView
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.RequestBuilder
|
||||
import com.bumptech.glide.load.DataSource
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.load.engine.GlideException
|
||||
import com.bumptech.glide.load.resource.bitmap.CircleCrop
|
||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
|
||||
import com.bumptech.glide.request.RequestListener
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Glide图片加载工具类
|
||||
*/
|
||||
object GlideUtils {
|
||||
|
||||
// 默认配置
|
||||
private val defaultOptions = RequestOptions()
|
||||
.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
|
||||
.skipMemoryCache(false)
|
||||
|
||||
/**
|
||||
* 普通加载图片
|
||||
* @param context 上下文
|
||||
* @param url 图片地址
|
||||
* @param imageView 目标ImageView
|
||||
* @param placeholderResId 占位图资源ID
|
||||
* @param errorResId 错误图资源ID
|
||||
*/
|
||||
fun loadImage(
|
||||
context: Context,
|
||||
url: Any?,
|
||||
imageView: ImageView,
|
||||
@DrawableRes placeholderResId: Int = 0,
|
||||
@DrawableRes errorResId: Int = 0
|
||||
) {
|
||||
val requestBuilder = Glide.with(context)
|
||||
.load(url)
|
||||
.apply(defaultOptions)
|
||||
|
||||
applyPlaceholder(requestBuilder, placeholderResId, errorResId)
|
||||
.transition(DrawableTransitionOptions.withCrossFade())
|
||||
.into(imageView)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载圆形图片
|
||||
* @param context 上下文
|
||||
* @param url 图片地址
|
||||
* @param imageView 目标ImageView
|
||||
* @param placeholderResId 占位图资源ID
|
||||
* @param errorResId 错误图资源ID
|
||||
*/
|
||||
fun loadCircleImage(
|
||||
context: Context,
|
||||
url: Any?,
|
||||
imageView: ImageView,
|
||||
@DrawableRes placeholderResId: Int = 0,
|
||||
@DrawableRes errorResId: Int = 0
|
||||
) {
|
||||
val requestBuilder = Glide.with(context)
|
||||
.load(url)
|
||||
.apply(defaultOptions)
|
||||
.transform(CircleCrop())
|
||||
|
||||
applyPlaceholder(requestBuilder, placeholderResId, errorResId)
|
||||
.into(imageView)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载圆角图片
|
||||
* @param context 上下文
|
||||
* @param url 图片地址
|
||||
* @param imageView 目标ImageView
|
||||
* @param radius 圆角半径(px)
|
||||
* @param placeholderResId 占位图资源ID
|
||||
* @param errorResId 错误图资源ID
|
||||
*/
|
||||
fun loadRoundCornerImage(
|
||||
context: Context,
|
||||
url: Any?,
|
||||
imageView: ImageView,
|
||||
radius: Int,
|
||||
@DrawableRes placeholderResId: Int = 0,
|
||||
@DrawableRes errorResId: Int = 0
|
||||
) {
|
||||
val requestBuilder = Glide.with(context)
|
||||
.load(url)
|
||||
.apply(defaultOptions)
|
||||
.transform(RoundedCorners(radius))
|
||||
|
||||
applyPlaceholder(requestBuilder, placeholderResId, errorResId)
|
||||
.into(imageView)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载图片并获取Bitmap
|
||||
* @param context 上下文
|
||||
* @param url 图片地址
|
||||
* @param callback 加载回调
|
||||
*/
|
||||
fun loadImageAsBitmap(
|
||||
context: Context,
|
||||
url: Any?,
|
||||
callback: (Bitmap?) -> Unit
|
||||
) {
|
||||
Glide.with(context)
|
||||
.asBitmap()
|
||||
.load(url)
|
||||
.apply(defaultOptions)
|
||||
.addListener(object : RequestListener<Bitmap> {
|
||||
|
||||
override fun onLoadFailed(
|
||||
e: GlideException?,
|
||||
model: Any?,
|
||||
target: Target<Bitmap?>,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
callback(null)
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: Bitmap,
|
||||
model: Any,
|
||||
target: Target<Bitmap?>?,
|
||||
dataSource: DataSource,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
callback(resource)
|
||||
return false
|
||||
}
|
||||
})
|
||||
.submit()
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除内存缓存
|
||||
* @param context 上下文
|
||||
*/
|
||||
fun clearMemoryCache(context: Context) {
|
||||
Glide.get(context).clearMemory()
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除磁盘缓存
|
||||
* @param context 上下文
|
||||
*/
|
||||
fun clearDiskCache(context: Context) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
Glide.get(context).clearDiskCache()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存文件
|
||||
* @param context 上下文
|
||||
* @param url 图片地址
|
||||
* @param callback 回调
|
||||
*/
|
||||
fun getImageCacheFile(
|
||||
context: Context,
|
||||
url: String,
|
||||
callback: (File?) -> Unit
|
||||
) {
|
||||
Glide.with(context)
|
||||
.downloadOnly()
|
||||
.load(url)
|
||||
.addListener(object : RequestListener<File> {
|
||||
override fun onLoadFailed(
|
||||
e: GlideException?,
|
||||
model: Any?,
|
||||
target: Target<File?>,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
callback(null)
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: File,
|
||||
model: Any,
|
||||
target: Target<File?>?,
|
||||
dataSource: DataSource,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
callback(resource)
|
||||
return false
|
||||
}
|
||||
})
|
||||
.submit()
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停请求
|
||||
* @param context 上下文
|
||||
*/
|
||||
fun pauseRequests(context: Context) {
|
||||
Glide.with(context).pauseRequests()
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复请求
|
||||
* @param context 上下文
|
||||
*/
|
||||
fun resumeRequests(context: Context) {
|
||||
Glide.with(context).resumeRequests()
|
||||
}
|
||||
|
||||
// 私有方法:应用占位图和错误图
|
||||
private fun <T> applyPlaceholder(
|
||||
requestBuilder: RequestBuilder<T>,
|
||||
@DrawableRes placeholderResId: Int,
|
||||
@DrawableRes errorResId: Int
|
||||
): RequestBuilder<T> {
|
||||
return requestBuilder.apply {
|
||||
if (placeholderResId != 0) {
|
||||
placeholder(placeholderResId)
|
||||
}
|
||||
if (errorResId != 0) {
|
||||
error(errorResId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
package com.sw.dualscreen.view
|
||||
|
||||
import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import com.sw.dualscreen.R
|
||||
import com.sw.dualscreen.adapter.GenericItemAdapter
|
||||
@@ -17,8 +17,15 @@ import com.sw.dualscreen.adapter.dpToPx
|
||||
import com.sw.dualscreen.databinding.BottomSheetDialogBinding
|
||||
import com.sw.dualscreen.databinding.ItemSearchFoodInfoBinding
|
||||
import com.sw.dualscreen.model.response.FoodInfo
|
||||
import com.sw.dualscreen.utils.Debouncer
|
||||
import com.sw.dualscreen.viewmodel.UserViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
class CustomBottomSheetDialog : BottomSheetDialogFragment() {
|
||||
class CustomBottomSheetDialog(
|
||||
val viewModel: UserViewModel,
|
||||
val itemClickCallback: (FoodInfo) -> Unit
|
||||
) : BottomSheetDialogFragment() {
|
||||
private lateinit var binding: BottomSheetDialogBinding
|
||||
private lateinit var adapter: GenericItemAdapter<FoodInfo, ItemSearchFoodInfoBinding>
|
||||
private var checkedItem: FoodInfo? = null
|
||||
@@ -34,9 +41,12 @@ class CustomBottomSheetDialog : BottomSheetDialogFragment() {
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val debouncer = Debouncer(3000)
|
||||
initView()
|
||||
binding.ivSearch.setOnClickListener {
|
||||
|
||||
debouncer.debounce {
|
||||
viewModel.searchByFoodName(binding.etSearch.text.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,8 +73,11 @@ class CustomBottomSheetDialog : BottomSheetDialogFragment() {
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun newInstance(): CustomBottomSheetDialog {
|
||||
return CustomBottomSheetDialog()
|
||||
fun newInstance(
|
||||
viewModel: UserViewModel,
|
||||
itemClickCallback: (FoodInfo) -> Unit
|
||||
): CustomBottomSheetDialog {
|
||||
return CustomBottomSheetDialog(viewModel, itemClickCallback)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,28 +88,20 @@ class CustomBottomSheetDialog : BottomSheetDialogFragment() {
|
||||
binding.recyclerview.addItemDecoration(
|
||||
GridSpacingItemDecoration(
|
||||
spanCount = 2,
|
||||
spacing = requireContext().dpToPx(6),
|
||||
spacing = requireContext().dpToPx(30),
|
||||
includeEdge = false // 包含边缘间距
|
||||
)
|
||||
)
|
||||
binding.recyclerview.adapter = adapter
|
||||
binding.recyclerview.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
|
||||
val lastVisibleItem = layoutManager.findLastVisibleItemPosition()
|
||||
val totalItems = layoutManager.itemCount
|
||||
registerDataChange()
|
||||
}
|
||||
|
||||
// if (!viewModel.isLoading && viewModel.canLoadMore
|
||||
// && lastVisibleItem >= totalItems - 3
|
||||
// ) {
|
||||
// viewModel.getSearchMemberList(
|
||||
// param = lastText,
|
||||
// pageNum = viewModel.currentPage
|
||||
// )
|
||||
// }
|
||||
private fun registerDataChange() {
|
||||
lifecycleScope.launch {
|
||||
viewModel.searchFoodInfoList.collect {
|
||||
adapter.updateData(it)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun createAdapter(): GenericItemAdapter<FoodInfo, ItemSearchFoodInfoBinding> {
|
||||
@@ -104,20 +109,30 @@ class CustomBottomSheetDialog : BottomSheetDialogFragment() {
|
||||
items = emptyList(),
|
||||
bindingInflater = ItemSearchFoodInfoBinding::inflate,
|
||||
bindCallback = { item, position ->
|
||||
// this.tvName.text = item.name.maskName()
|
||||
// this.tvPhone.text = item.phone.maskPhone()
|
||||
// if (item.id != checkedItem?.id) {
|
||||
// this.llRoot.setBackgroundResource(R.drawable.grid_item_bind)
|
||||
// } else {
|
||||
// this.llRoot.setBackgroundResource(R.drawable.grid_item_unbind)
|
||||
// }
|
||||
//
|
||||
// this.llRoot.setOnClickListener {
|
||||
// Timber.d("itemClick ${item.name}, position = $position")
|
||||
// checkedItem = item
|
||||
// adapter.notifyDataSetChanged()
|
||||
// }
|
||||
this.tvName.text = item.foodName
|
||||
if (item.id != checkedItem?.id) {
|
||||
this.tvName.typeface = Typeface.defaultFromStyle(Typeface.NORMAL)
|
||||
this.tvName.setTextColor(resources.getColor(R.color.search_normal))
|
||||
this.llRoot.setBackgroundResource(R.drawable.grid_search_item_normal)
|
||||
} else {
|
||||
this.tvName.typeface = Typeface.defaultFromStyle(Typeface.BOLD)
|
||||
this.tvName.setTextColor(resources.getColor(R.color.search_checked))
|
||||
this.llRoot.setBackgroundResource(R.drawable.grid_search_item_checked)
|
||||
}
|
||||
|
||||
this.llRoot.setOnClickListener {
|
||||
Timber.d("itemClick ${item.foodName}, position = $position")
|
||||
checkedItem = item
|
||||
// viewModel.updateCurrentItem(item)
|
||||
itemClickCallback(item)
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun dismiss() {
|
||||
viewModel.cleanSearchFoodInfoList()
|
||||
super.dismiss()
|
||||
}
|
||||
}
|
||||
@@ -27,16 +27,12 @@ class UserViewModel : BaseViewModel() {
|
||||
private val faceApi: FaceApi = FaceApi()
|
||||
|
||||
// 识别出的列表
|
||||
private val _identifiedFoodInfoList = MutableStateFlow<List<FoodInfo>?>(null)
|
||||
val identifiedFoodInfoList: StateFlow<List<FoodInfo>?> = _identifiedFoodInfoList
|
||||
private val _identifiedFoodInfoList = MutableStateFlow<List<FoodInfo>>(emptyList())
|
||||
val identifiedFoodInfoList: StateFlow<List<FoodInfo>> = _identifiedFoodInfoList
|
||||
|
||||
// 搜索出的食物列表
|
||||
private val _foodInfoList = MutableStateFlow<List<FoodInfo>?>(null)
|
||||
val foodInfoList: StateFlow<List<FoodInfo>?> = _foodInfoList
|
||||
|
||||
// 当前食物信息
|
||||
private val _currentFoodInfo = MutableStateFlow<FoodInfo?>(null)
|
||||
val currentFoodInfo: StateFlow<FoodInfo?> = _currentFoodInfo
|
||||
private val _searchFoodInfoList = MutableStateFlow<List<FoodInfo>>(emptyList())
|
||||
val searchFoodInfoList: StateFlow<List<FoodInfo>> = _searchFoodInfoList
|
||||
|
||||
// 就餐数据
|
||||
private val _nutritionData = MutableStateFlow<NutritionData?>(null)
|
||||
@@ -46,6 +42,7 @@ class UserViewModel : BaseViewModel() {
|
||||
* 获取token
|
||||
*/
|
||||
fun getEquipmentToken(qrcodeId: String = "3ea47dc0-3cf0-3c2f-909c-265a9a65572e") {
|
||||
Timber.d("getEquipmentToken")
|
||||
launchWithLoading {
|
||||
val response = repository.getEquipmentToken(qrcodeId)
|
||||
if (parseResponse(response)) {
|
||||
@@ -131,35 +128,49 @@ class UserViewModel : BaseViewModel() {
|
||||
Timber.d("resetUserInfo")
|
||||
}
|
||||
|
||||
fun identifiedFoodList() {
|
||||
fun getIdentifiedFoodList() {
|
||||
Timber.d("identifiedFoodList")
|
||||
launchWithLoading {
|
||||
delay(5000)
|
||||
_identifiedFoodInfoList.value = List(6) {
|
||||
FoodInfo(
|
||||
id = "185225109358109491${it}",
|
||||
foodName = "豆芽炒粉条$it",
|
||||
imgUrl = "http://101.201.149.156:9002/temp/20221119/62dc2d17-6853-4277-96ed-a4ef4c8654ea_1668840050258.png"
|
||||
imgUrl = "http://101.201.149.156:9002/temp/20230912/f54b2d76-45e1-4c6b-a7a0-5fd1eace99d1_1694484601879.png"
|
||||
)
|
||||
}
|
||||
// _currentFoodInfo.value = _identifiedFoodInfoList.value[0]
|
||||
}
|
||||
}
|
||||
|
||||
fun cleanIdentifiedFoodInfoList() {
|
||||
Timber.d("cleanIdentifiedFoodInfoList")
|
||||
_identifiedFoodInfoList.value = emptyList<FoodInfo>()
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索食物
|
||||
*/
|
||||
fun searchByFoodName(foodName: String) {
|
||||
Timber.d("searchByFoodName foodName = $foodName")
|
||||
launchWithLoading {
|
||||
val response = repository.getRestInfoFoodsByType(foodName = foodName)
|
||||
if (parseResponse(response)) {
|
||||
_foodInfoList.value = response.result
|
||||
_searchFoodInfoList.value = response.result ?: emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun cleanSearchFoodInfoList() {
|
||||
Timber.d("cleanSearchFoodInfoList")
|
||||
_searchFoodInfoList.value = emptyList<FoodInfo>()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户就餐数据
|
||||
*/
|
||||
fun getUserNutritionData(userId: String, foodId: String) {
|
||||
Timber.d("getUserNutritionData userId = ${userId}, foodId = $foodId")
|
||||
launchWithLoading {
|
||||
val response = repository.getUserNutritionData(userId = userId, foodId = foodId)
|
||||
if (parseResponse(response)) {
|
||||
@@ -172,6 +183,7 @@ class UserViewModel : BaseViewModel() {
|
||||
* 提交就餐数据
|
||||
*/
|
||||
fun postUserNutritionData(param: UserNutritionParam) {
|
||||
Timber.d("postUserNutritionData param = $param")
|
||||
launchWithLoading {
|
||||
val response = repository.postUserNutritionData(param)
|
||||
if (parseResponse(response)) {
|
||||
|
||||
Reference in New Issue
Block a user