联合支付功能调试

This commit is contained in:
2026-03-23 18:13:55 +08:00
parent bf8444e5d5
commit c4a6101a7b
13 changed files with 350 additions and 143 deletions
+7 -5
View File
@@ -59,7 +59,7 @@ android {
}
dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar", "*.jar"))))
//implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar", "*.jar"))))
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)
@@ -110,17 +110,19 @@ dependencies {
implementation(libs.pytorch.android)
implementation(libs.pytorch.android.torchvision)
implementation("io.github.scwang90:refresh-layout-kernel:3.0.0-alpha")
implementation("io.github.scwang90:refresh-header-classics:3.0.0-alpha")
implementation(libs.baseRecyclerViewAdapterHelper4)
implementation(libs.refresh.layout.kernel)
implementation(libs.refresh.header.classics)
val objectboxVersion = "5.0.1"
debugImplementation("io.objectbox:objectbox-android-objectbrowser:$objectboxVersion")
debugImplementation(libs.objectbox.android.objectbrowser)
// releaseImplementation("io.objectbox:objectbox-android:$objectboxVersion")
// implementation("io.objectbox:objectbox-fulltext:5.0.1")
implementation(libs.androidx.recyclerview)
implementation("org.greenrobot:eventbus:3.2.0")
implementation(libs.eventbus)
}
apply(plugin = "io.objectbox")
Binary file not shown.
@@ -32,10 +32,10 @@ import com.sw.dualscreen.databinding.ActivityMainBinding
import com.sw.dualscreen.dialog.RemindDialog
import com.sw.dualscreen.ext.dp
import com.sw.dualscreen.ext.gone
import com.sw.dualscreen.ext.invisible
import com.sw.dualscreen.ext.load
import com.sw.dualscreen.ext.visible
import com.sw.dualscreen.model.response.ChargeModeEvent
import com.sw.dualscreen.model.response.ClickBackEvent
import com.sw.dualscreen.model.response.FoodInfo
import com.sw.dualscreen.model.response.FoodOrder
import com.sw.dualscreen.model.response.PaySuccessEvent
@@ -79,7 +79,6 @@ import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.util.Locale
import java.util.concurrent.Executors
import kotlin.math.abs
import kotlin.math.roundToInt
/**
@@ -91,6 +90,9 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
companion object {
private const val TAG = "MainActivity"
const val TIME_OUT = 5 * 60 * 1000L
const val WEIGHT_CHANGE_VALUE = 25
const val WEIGHT_RESET_RECOGNIZE = 10
}
private val viewModel by viewModels<UserViewModel>()
@@ -101,28 +103,36 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
private var presentation: MainScreenPresentation? = null
private val executor = Executors.newSingleThreadExecutor()
private val handler by lazy { Handler(Looper.getMainLooper()) }
private val searchFoodList = mutableListOf<FoodInfo>()
private val adapter by lazy {
MainFoodListAdapter(searchFoodList).apply {
MainFoodListAdapter(mutableListOf()).apply {
setOnItemClickListener { adapter, view, position ->
if (searchFoodList[position].isChecked) {
if (items[position].isChecked) {
return@setOnItemClickListener
}
searchFoodList.forEachIndexed { index, info ->
searchFoodList[index].isChecked = index == position
items.forEachIndexed { index, info ->
items[index].isChecked = index == position
}
val item = searchFoodList[position]
val item = items[position]
log("itemClick ${item.foodName}, position = $position")
item.photoUri = null
checkedItem = item
notifyDataSetChanged()
updateCurrentFood(item)
//0-计费,1-不计费
val mode = SPUtil.getInstance().get(GlobalKey.KEY_CHARGE_MODE, 0)
//不计费模式切换菜品,重新加载数据
if (mode == 1) {
presentation?.isAgainLoadData = false
presentation?.loadUnbilledMode()
if (settlementMode == 0) {
//联合支付,切换菜品重新请求接口
presentation?.let {
it.pauseCamera()
it.step2FaceRecognizing(item, true)
}
// presentation?.step3ShowRecognizeResult(presentation!!.currentUserId?:"")
} else {
//0-计费,1-不计费
val mode = SPUtil.getInstance().get(GlobalKey.KEY_CHARGE_MODE, 0)
//不计费模式切换菜品,重新加载数据
if (mode == 1) {
presentation?.isAgainLoadData = false
presentation?.loadUnbilledMode()
}
}
}
}
@@ -132,7 +142,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
private var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>? = null
private var canIdentify: Boolean = false
private var bottomSheetDialog: CustomBottomSheetDialog? = null
private var lastWeight: Double = 0.0
private var lastWeight: Int = 0
private var lastPhotoUri: Uri? = null // 最后拍照的图片
private var debouncer = Debouncer(5000)
private var isRecognitionFood = true
@@ -200,8 +210,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
checkedItem = it
it.photoUri = null
updateCurrentFood(it)
searchFoodList.forEachIndexed { index, info ->
searchFoodList[index].isChecked = false
adapter.items.forEachIndexed { index, info ->
adapter.items[index].isChecked = false
}
adapter.notifyDataSetChanged()
}
@@ -209,7 +219,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
}
binding.recyclerview.let {
it.layoutManager = GridLayoutManager(this, 2)
it.layoutManager = GridLayoutManager(this, 2, GridLayoutManager.VERTICAL, false)
it.adapter = adapter
}
binding.tvFoodName.setOnClickListener {
@@ -220,7 +230,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
}
}
private var recognizeWeight = 0.0
private var recognizeWeight = 0
fun updateCurrentFood(foodInfo: FoodInfo?) {
if (foodInfo == null) {
binding.tvFoodName.text = "-"
@@ -245,52 +255,75 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
binding.ivImg.load(imgUrl)
}
override fun registerDataChange() {
super.registerDataChange()
SensorScaleUtils.addWeightListener { weight ->
log("registerDataChange weight = $weight")
//runOnUiThread { binding.tvShowWeight.text="${weight*1000}克" }
if (presentation?.jointPaymentQueryFinish == true) {
//0-即放即取,1-余量计量
val mealPickupMode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0) ?: 0
if (mealPickupMode == 0 && weight <= 0.005) {
isAnalyzing = false
presentation?.step1FoodRecognizing()
} else {
return@addWeightListener
}
private fun readWeight(weight: Int) {
log("registerDataChange weight = $weight")
//runOnUiThread { binding.tvShowWeight.text="${weight*1000}克" }
if (settlementMode == 0) {
//联合支付-------------------------------------------
//0-即放即取,1-余量计量
val mealPickupMode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0) ?: 0
if (mealPickupMode == 1) {
return
}
//if (presentation?.currentFood == null) return@addWeightListener
if (lastWeight - weight > 0.005) {
//及放及取,从秤上拿走超过5克
if (presentation?.mealPickupMode == 0) {
presentation?.updateWeight(weight)
lastWeight = weight
return@addWeightListener
//即放即取+联合支付人脸查询订单完成
if (presentation!!.jointPaymentQueryFinish) {
if (weight > WEIGHT_RESET_RECOGNIZE) {
//已识别完成人脸并查询到订单数据,增加重量不再重新识别
return
}
//重量小于等于5克时改为识别中页面
isAnalyzing = false
presentation?.step1FoodRecognizing()
return
}
if (isPageVisible.not() || isStartRecognize.not()) {
if (ActivityManager.currentActivity() is MainActivity) {
isPageVisible = true
isStartRecognize = true
}
return@addWeightListener
if (weight <= WEIGHT_RESET_RECOGNIZE) {
//秤上重量过下,显示识别中页面
presentation?.step1FoodRecognizing()
return
}
//0.05
// val isWeightChange = abs(weight - lastWeight) > 0.02
val isWeightChange = weight - lastWeight > 0.02
val isWeightChange = weight - lastWeight > WEIGHT_CHANGE_VALUE
if (weight != lastWeight) {
log("recognizeByWeight---00001,weight=$weight,lastWeight=$lastWeight,isWeightChange=$isWeightChange")
}
recognizeByWeight(weight, isWeightChange)
return
}
//独立支付-------------------------------------------
if (lastWeight - weight > 5) {
//及放及取,从秤上拿走超过5克
if (presentation?.mealPickupMode == 0) {
presentation?.updateWeight(weight)
lastWeight = weight
return
}
}
if (isPageVisible.not() || isStartRecognize.not()) {
if (ActivityManager.currentActivity() is MainActivity) {
isPageVisible = true
isStartRecognize = true
}
return
}
// val isWeightChange = abs(weight - lastWeight) > 0.02
val isWeightChange = weight - lastWeight > WEIGHT_CHANGE_VALUE
if (weight != lastWeight) {
log("recognizeByWeight---00001,weight=$weight,lastWeight=$lastWeight,isWeightChange=$isWeightChange")
}
recognizeByWeight(weight, isWeightChange)
}
override fun registerDataChange() {
super.registerDataChange()
SensorScaleUtils.addWeightListener { value ->
readWeight(value)
}
}
private fun recognizeByWeight(weight: Double, isWeightChange: Boolean, block: () -> Unit = {}) {
private fun recognizeByWeight(weight: Int, isWeightChange: Boolean, block: () -> Unit = {}) {
presentation?.updateWeight(weight)
if (weight <= 0.005) {
if (weight <= WEIGHT_RESET_RECOGNIZE) {
log("recognizeByWeight---00002,秤重未超过5克")
//检测到秤上没有东西,重新启动识别菜品 && presentation?.mealPickupMode == 1
isRecognitionFood = true
@@ -374,9 +407,10 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
// e.printStackTrace()
// list
// }
searchFoodList.clear()
searchFoodList.addAll(list)
adapter.notifyDataSetChanged()
adapter.submitList(list)
binding.recyclerview.visible()
checkedItem = list[0].also {
it.photoUri = lastPhotoUri
@@ -559,10 +593,14 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
}
@SuppressLint("NotifyDataSetChanged")
fun clearFoodList() {
//adapter.updateData(listOf())
searchFoodList.clear()
adapter.notifyDataSetChanged()
fun clearRecognizeFood() {
log("clearRecognizeFood")
binding.recyclerview.invisible()
adapter.submitList(null)
binding.tvToSearch.gone()
binding.previewView.visible()
binding.ivImg.gone()
}
private fun updateDateTime() {
@@ -594,7 +632,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
log("onResume-00001isRefreshPage=$isRefreshPage")
if (presentation == null || !presentation!!.isShowing) {
log("onResume-00002,副屏未显示,加载副屏")
lastWeight = 0.0
lastWeight = 0
setupSecondaryDisplay()
presentation?.step1FoodRecognizing()
} else {
@@ -603,7 +641,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
isRefreshPage = false
//已显示副屏,则进行更新
val weight = lastWeight
lastWeight = 0.0
lastWeight = 0
presentation?.hideRecImage()
presentation?.step1FoodRecognizing()
@@ -662,7 +700,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
eatWeight: Int,
eatNum: Int,
userId: String?,
isMember:Boolean,
isMember: Boolean,
notEnoughOneBlock: () -> Unit = {},
successBlock: () -> Unit
) {
@@ -800,36 +838,36 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
val mealPickupMode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0) ?: 0
//0-计费,1-不计费
val chargeMode = SPUtil.getInstance().get(GlobalKey.KEY_CHARGE_MODE, 0) ?: 0
var foodWeight = 0.0
var eatWeight = 0.0
var foodWeight = 0
var eatWeight = 0
if (chargeMode == 0) {
//计费
if (mealPickupMode == 0) {
//即放即取
eatWeight = lastWeight * 1000
foodWeight = lastWeight * 1000
eatWeight = lastWeight
foodWeight = lastWeight
} else {
//余量计量
eatWeight = (recognizeWeight - lastWeight) * 1000
foodWeight = recognizeWeight * 1000
eatWeight = recognizeWeight - lastWeight
foodWeight = recognizeWeight
log("eatWeight=$eatWeight, recognizeWeight=$recognizeWeight, lastWeight=$lastWeight")
}
} else {
//不计费
foodWeight = recognizeWeight * 1000
foodWeight = recognizeWeight
eatWeight = if (mealPickupMode == 0) {
//即放即取
recognizeWeight * 1000
recognizeWeight
} else {
//余量计量
(recognizeWeight - lastWeight) * 1000
recognizeWeight - lastWeight
}
}
val eatNum = getEatNum(eatWeight, checkedItem!!.specWeight)
val eatNum = getEatNum(eatWeight.toDouble(), checkedItem!!.specWeight)
createOrder(
foodInfo = checkedItem!!,
foodWeight = foodWeight.roundToInt(),
eatWeight = eatWeight.roundToInt(),
foodWeight = foodWeight,
eatWeight = eatWeight,
eatNum = eatNum,
userId = null,
isMember = false
@@ -956,7 +994,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
// private val dealyMillis = 10 * 60 * 1000L
private val initialDelay = 10 * 1000L
private val dealyMillis = 5 * 1000L
// private var taskPageNo = 1
// private var taskPageNo = 1
fun startFaceTask() {
faceTaskJob =
intervalExecutor.startIntervalTaskWithInitialDelay(initialDelay, dealyMillis) {
@@ -1097,13 +1136,6 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
}
}
/**
* 隐藏搜索按钮
*/
fun hideSearchView() {
binding.tvToSearch.gone()
}
// @Subscribe(threadMode = ThreadMode.MAIN)
// public fun onResetBox(event: ResetBoxEvent) {
// ObjectBox.boxStore.close()
@@ -77,8 +77,8 @@ class BusinessFragment: BaseFragment<FragmentBusinessBinding>() {
}
}
SensorScaleUtils.addWeightListener { value ->
val realWeight = (value * 1000).roundToInt()
binding.tvFoodWeight.text = "$realWeight"
//val realWeight = (value * 1000).roundToInt()
binding.tvFoodWeight.text = "$value"
}
}
}
@@ -10,19 +10,21 @@ import androidx.core.view.updateLayoutParams
import androidx.recyclerview.widget.RecyclerView
import com.chad.library.adapter4.BaseQuickAdapter
import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.sw.dualscreen.databinding.LayoutFoodListHeaderBinding
import com.sw.dualscreen.databinding.ListItemFoodOrderBinding
import com.sw.dualscreen.ext.dp
import com.sw.dualscreen.ext.format2String
import com.sw.dualscreen.ext.gone
import com.sw.dualscreen.ext.visible
import com.sw.dualscreen.model.response.FoodItem
class FoodOrderAdapter(var list: MutableList<FoodItem>) :
BaseQuickAdapter<FoodItem, FoodOrderAdapter.VH>(list) {
inner class VH(var binding: LayoutFoodListHeaderBinding) : QuickViewHolder(binding.root)
inner class VH(var binding: ListItemFoodOrderBinding) : QuickViewHolder(binding.root)
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
val inflater = LayoutInflater.from(context)
val binding = LayoutFoodListHeaderBinding.inflate(inflater, parent, false)
val binding = ListItemFoodOrderBinding.inflate(inflater, parent, false)
return VH(binding)
}
@@ -31,9 +33,20 @@ class FoodOrderAdapter(var list: MutableList<FoodItem>) :
val binding = holder.binding
item?.let {
binding.tvFoodName.text = it.foodName
binding.tvPriceNorm.text = it.foodPrice.format2String(2)
val foodPrice = it.foodPrice.format2String(2)
if (it.orderFrom == 2) {
binding.tvPriceNorm.text = "${it.specName}-${it.specWeight}"
} else {
binding.tvPriceNorm.text = "${foodPrice}/${it.num}"
}
//if (it.specName.isNullOrBlank().not()) {
// binding.tvPriceFlag.text = "[${it.specName}]"
//}
binding.tvWeight.text = "${it.num}"
binding.tvAmount.text = it.price.format2String(2)
binding.tvLabel.run {
if (it.orderFrom == 2) visible() else gone()
}
}
setTextColor(binding.tvFoodName, binding.tvPriceNorm, binding.tvWeight, binding.tvAmount)
binding.root.updateLayoutParams<RecyclerView.LayoutParams> {
@@ -177,7 +177,10 @@ data class FoodItem(
val discount: Double = 0.0,
val income: Double = 0.0,
val foodDifferenceId: Long = 0,
val createTime: String? = null
val createTime: String? = null,
val specName: String? = null,
val specWeight: Int = 0,
val orderFrom: Int = 0
)
data class FoodOrderModel(
@@ -5,24 +5,16 @@ import android.annotation.SuppressLint
import android.app.Presentation
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Outline
import android.graphics.Point
import android.hardware.Camera
import android.os.Build
import android.os.Bundle
import android.text.TextUtils
import android.util.DisplayMetrics
import android.view.Display
import android.view.View
import android.view.ViewGroup
import android.view.ViewOutlineProvider
import android.view.ViewTreeObserver
import android.widget.FrameLayout
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.view.postDelayed
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
@@ -30,7 +22,6 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.arcsoft.face.ErrorInfo
import com.sw.dualscreen.BuildConfig
import com.sw.dualscreen.GlobalKey
import com.sw.dualscreen.MyApp
import com.sw.dualscreen.R
import com.sw.dualscreen.activity.MainActivity
import com.sw.dualscreen.adapter.FoodOrderAdapter
@@ -40,7 +31,6 @@ import com.sw.dualscreen.ext.format2String
import com.sw.dualscreen.ext.gone
import com.sw.dualscreen.ext.load
import com.sw.dualscreen.ext.maskName
import com.sw.dualscreen.ext.roundedDecimalPlace
import com.sw.dualscreen.ext.screenSize
import com.sw.dualscreen.ext.visible
import com.sw.dualscreen.model.response.DinnerType
@@ -66,7 +56,6 @@ 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 com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel.REGISTER_STATUS_READY
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.launch
import timber.log.Timber
@@ -105,14 +94,14 @@ class MainScreenPresentation(
private var irFaceRectTransformer: FaceRectTransformer? = null
private var detectWeight = 0.0 //识别菜品时的重量
var lastWeight = 0.0 // 上一次的计算热量结果
var lastWeight = 0 // 上一次的计算热量结果
// private var userNutritionData: UserNutritionData? = null
private var userNutrition: UserNutrition? = null
private var dinnerTypeInfo: DinnerType? = null
private val debouncer = Debouncer(500)
private var recognitionTime: Long = 0L // 人脸识别时的时间
var recognitionWeight: Double = 0.0 // 人脸识别时的重量
var recognitionWeight: Int = 0 // 人脸识别时的重量
private var lastFaceTrackId: Int = -1 // 上一次的人脸信息
var mealPickupMode: Int = 0 // 取餐模式 0 即放即取 1 余量取餐
@@ -144,8 +133,8 @@ class MainScreenPresentation(
private var isFirstGetWeight = true
fun updateWeight(weight: Double) {
lastWeight = weight * 1000 // 将千克转成克
fun updateWeight(weight: Int) {
lastWeight = weight
Timber.tag(TAG)
.d("updateWeight lastWeight = $weight, currentStep = $currentStep, isGoStep1 = $isGoStep1")
//0-即放即取,1-余量计量
@@ -164,14 +153,14 @@ class MainScreenPresentation(
// }
// isFirstGetWeight = true
// }
if (currentStep == 2 && lastWeight <= 5 && isGoStep1) {
if (currentStep == 2 && lastWeight <= MainActivity.WEIGHT_RESET_RECOGNIZE && isGoStep1) {
//人脸识别过程中,秤上物品拿走
isGoStep1 = false
activity.runOnUiThread {
Timber.tag(TAG).e("切回step1")
step1FoodRecognizing()
}
} else if (lastWeight <= 5) {
} else if (lastWeight <= MainActivity.WEIGHT_RESET_RECOGNIZE) {
isGoStep1 = false
activity.runOnUiThread {
Timber.tag(TAG).e("切回step1")
@@ -287,6 +276,14 @@ class MainScreenPresentation(
}
}
private fun setTopInfoVisible() {
binding.llTopInfo.visible()
binding.flFoodDetail.gone()
binding.llTopInfo.updateLayoutParams {
height = activity.screenSize[1] / 2 + 120.dp
}
}
/**
* 显示识别出的菜品信息
*/
@@ -297,8 +294,7 @@ class MainScreenPresentation(
currentStep = 3
stepChangeCallback(currentStep)
binding.llTopInfo.visible()
binding.flFoodDetail.gone()
setTopInfoVisible()
//根据接口数据更新热量数据--------------------------
userViewModel.getUserNutritionData(userId = userId) { nutrition ->
activity.runOnUiThread {
@@ -491,6 +487,9 @@ class MainScreenPresentation(
* 餐品识别中
*/
fun step1FoodRecognizing() {
if (currentStep == 1) {
return
}
Timber.tag(TAG).d("main,step1,耗时:${System.currentTimeMillis() - startTime}")
startTime = System.currentTimeMillis()
jointPaymentQueryFinish = false
@@ -505,7 +504,6 @@ class MainScreenPresentation(
// }
// return
//}
currentStep = 1
LightManager.closeGreenLight()
LightManager.closeRedLight()
@@ -513,12 +511,9 @@ class MainScreenPresentation(
currentFood = null
// userNutritionData = null
userNutrition = null
recognitionWeight = 0.0
recognitionWeight = 0
stepChangeCallback(currentStep)
binding.llTopInfo.visible()
binding.flFoodDetail.gone()
setTopInfoVisible()
binding.calorieInclude.root.gone()
binding.nutritionInclude.root.gone()
@@ -537,8 +532,10 @@ class MainScreenPresentation(
binding.flRecognizeIr.gone()
pauseCamera()
activity.clearFoodList()
activity.hideSearchView()
activity.clearRecognizeFood()
currentStep = 1
stepChangeCallback(currentStep)
}
// fun foodRecSuccess(foodInfo: FoodInfo) {
@@ -560,9 +557,14 @@ class MainScreenPresentation(
/**
* 用户人脸识别中
*/
fun step2FaceRecognizing(foodInfo: FoodInfo) {
fun step2FaceRecognizing(foodInfo: FoodInfo, hideFoodDetail: Boolean = false) {
isGoStep1 = true
Timber.tag(TAG).d("main,step2,耗时:${System.currentTimeMillis() - startTime}")
if (hideFoodDetail) {
setTopInfoVisible()
}
startTime = System.currentTimeMillis()
currentStep = 2
LightManager.closeRedLight()
@@ -586,7 +588,7 @@ class MainScreenPresentation(
dinnerTypeInfo = null
// userNutritionData = null
userNutrition = null
recognitionWeight = 0.0
recognitionWeight = 0
stepChangeCallback(currentStep)
currentFood = foodInfo
@@ -668,12 +670,12 @@ class MainScreenPresentation(
// }
}
private fun calculateNutrition(weight1: Double) {
private fun calculateNutrition(value: Int) {
if (currentFood == null || userNutrition == null) {
return
}
var weight = weight1
if (weight < 0) weight = 0.0
var weight = value
if (weight < 0) weight = 0
debouncer.debounce {
Timber.tag(TAG)
.d("calculateNutrition weight = $weight, recognitionWeight = $recognitionWeight")
@@ -684,7 +686,7 @@ class MainScreenPresentation(
currentFood!!,
// userNutritionData!!,
userNutrition!!,
weight,
weight.toDouble(),
dinnerType = dinnerType ?: ""
)
// Timber.tag(TAG).d("calculateNutrition calcResultInfo = $calcResultInfo")
@@ -830,8 +832,23 @@ class MainScreenPresentation(
isRecognition = false
recognizeViewModel.onPreviewFrame(ByteArray(1382400), true)
//triggerReidentify()
}
/**
* 重置人脸跟踪状态,让已在画面中的人脸重新触发识别
* 适用场景:切换按钮后需要重新识别当前画面中的人脸
*/
fun triggerReidentify() {
// 清除UI上的人脸框
binding.dualCameraFaceRectView.clearFaceInfo()
// 清除本地跟踪数据
clearLeftFaceData()
// 重置上一次的 trackId,避免 drawPreviewInfo 中的去重逻辑拦截
lastFaceTrackId = -1
// 插入空帧,让引擎重置内部跟踪状态
// isRecognition 保持 true,相机继续送帧,下一帧真实人脸会产生新 trackId
recognizeViewModel.onPreviewFrame(ByteArray(1382400), true)
}
private var recognizeTime = 0L
private fun initArcViewModel() {
recognizeViewModel.setLiveType(livenessType)
@@ -1187,8 +1204,7 @@ class MainScreenPresentation(
}
createOrder(notEnoughOneBlock = {
if (activity.settlementMode == 0) {
binding.flFoodDetail.gone()
binding.llTopInfo.visible()
setTopInfoVisible()
}
// if (mealPickupMode == 0) {
currentStep = 2
@@ -1249,11 +1265,11 @@ class MainScreenPresentation(
//userViewModel.postUserNutritionData(listOf(userNutritionParam))
activity.createOrder(
foodInfo = currentFood!!,
foodWeight = lastWeight.roundToInt(),
eatWeight = eatWeight.roundToInt(),
eatNum = activity.getEatNum(eatWeight, currentFood!!.specWeight),
foodWeight = lastWeight,
eatWeight = eatWeight,
eatNum = activity.getEatNum(eatWeight.toDouble(), currentFood!!.specWeight),
userId = currentUserId,
isMember = isMember ,
isMember = isMember,
notEnoughOneBlock = notEnoughOneBlock,
successBlock = {
activity.runOnUiThread {
@@ -1316,11 +1332,12 @@ class MainScreenPresentation(
binding.flFoodDetail.visible()
binding.detailInclude.let {
it.root.updateLayoutParams {
height = activity.screenSize[1]/2
height = activity.screenSize[1] / 2 + 120.dp
}
it.tvFoodList.run {
if (adapter == null) {
layoutManager = LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
layoutManager =
LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
adapter = foodOrderAdapter
}
}
@@ -1332,10 +1349,36 @@ class MainScreenPresentation(
}
foodOrderAdapter.notifyDataSetChanged()
binding.detailInclude.tvTotalWeight.text = "总重量:${model.eatWeightSum}"
binding.detailInclude.tvTotalCalorie.text = "总热量:${model.calorie.roundToInt()}千卡"
binding.detailInclude.tvTotalCalorie.text =
"总热量:${model.calorie.roundToInt()}千卡"
jointPaymentQueryFinish = true
}
}
}
/**
* 构造符合分辨率的 NV21 格式全黑帧
* @param width 图像宽度(如 1920
* @param height 图像高度(如 1080
* @return 合法的全黑帧字节数组
*/
private fun createBlackNV21Frame(width: Int, height: Int): ByteArray {
val frameSize = width * height
val nv21Data = ByteArray(frameSize * 3 / 2) // NV21 格式长度 = 宽×高×1.5
// Y 分量(亮度)设为 0(全黑),UV 分量设为 128(默认值)
// 1. 填充 Y 分量(前 frameSize 个字节)
// Arrays.fill(nv21Data, 0, frameSize, 0.toByte())
nv21Data.fill(0, 0, frameSize)
// 2. 填充 UV 分量(后 frameSize/2 个字节)
// Arrays.fill(nv21Data, frameSize, nv21Data.size, 128.toByte())
nv21Data.fill(128.toByte(), frameSize, nv21Data.size)
return nv21Data
}
// 调用示例:构造 1920×1080 的全黑帧(长度 1382400
// private val emptyFrame = createBlackNV21Frame(1920, 1080)
private val emptyFrame = createBlackNV21Frame(1280, 720)
}
@@ -7,7 +7,7 @@ import timber.log.Timber
import kotlin.math.roundToInt
typealias Callback = (Double) -> Unit
typealias WeightCallback = (Double) -> Unit
typealias WeightCallback = (Int) -> Unit
private const val TAG = "SensorScaleUtils"
@@ -45,7 +45,7 @@ object SensorScaleUtils {
}
weightCallbackList.forEach {
it.invoke(value)
it.invoke((value * 1000).roundToInt())
}
}
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/darker_gray"/>
<stroke android:color="#cdcdcd" android:width="1dp"/>
<corners android:radius="5dp"/>
</shape>
@@ -285,7 +285,7 @@
<TextView
android:id="@+id/meatTv"
style="@style/takeFoodNameTv"
android:text="肉"
android:text="肉蛋豆"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
@@ -10,7 +10,7 @@
android:id="@+id/tvFoodName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2.5"
android:layout_weight="2.1"
android:text="菜品名称"
android:textColor="#FF889AC2"
android:textSize="26sp"
@@ -23,7 +23,7 @@
android:id="@+id/tvPriceNorm"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:layout_weight="1.6"
android:text="规格"
android:textColor="#FF889AC2"
android:textSize="26sp"
@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal"
android:gravity="center_vertical"
xmlns:tools="http://schemas.android.com/tools">
<!-- 菜品名称列,权重2.5 -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2.1"
android:gravity="center_vertical">
<TextView
android:id="@+id/tvFoodName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="菜品名称"
android:textColor="#FF889AC2"
android:textSize="25sp"
android:paddingHorizontal="5dp"
android:singleLine="true"
android:gravity="center_vertical|start" />
<TextView
android:id="@+id/tvLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="档口"
android:textColor="@color/black"
android:textSize="13sp"
android:background="@drawable/bg_order_flag"
android:paddingHorizontal="5dp"
android:paddingVertical="1dp"
android:visibility="gone"/>
</LinearLayout>
<!-- 规格列,权重1.0 -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.6"
android:gravity="center">
<TextView
android:id="@+id/tvPriceNorm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="规格"
android:textColor="#FF889AC2"
android:textSize="25sp"
android:paddingHorizontal="5dp"
android:singleLine="true"
android:gravity="center" />
<TextView
android:id="@+id/tvPriceFlag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="[大份]"
android:textColor="@color/black"
android:textSize="15sp"
android:paddingHorizontal="5dp"
android:singleLine="true"
android:gravity="center"
android:visibility="gone"/>
</LinearLayout>
<!-- 重量列,权重1.0 -->
<TextView
android:id="@+id/tvWeight"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0"
tools:text="重量(克)"
android:textColor="#FF889AC2"
android:textSize="25sp"
android:paddingHorizontal="5dp"
android:singleLine="true"
android:gravity="center" />
<!-- 小计列,权重1.0 -->
<TextView
android:id="@+id/tvAmount"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0"
tools:text="小计(元)"
android:textColor="#FF889AC2"
android:textSize="25sp"
android:paddingHorizontal="5dp"
android:singleLine="true"
android:gravity="center" />
</LinearLayout>
+13 -2
View File
@@ -2,6 +2,7 @@
agp = "8.10.1"
core = "3.4.1"
androidCore = "3.3.0"
eventbus = "3.2.0"
glide = "4.16.0"
kotlin = "2.0.21"
coreKtx = "1.10.1"
@@ -14,6 +15,9 @@ activity = "1.8.0"
constraintlayout = "2.1.4"
accompanistPermissions = "0.37.3"
cameraCore = "1.4.1"
objectboxVersion = "5.0.1"
refreshLayoutKernel = "3.0.0-alpha"
refreshHeaderClassics = "3.0.0-alpha"
retrofit = "3.0.0"
okhttp = "4.12.0"
timber = "5.0.1"
@@ -30,11 +34,13 @@ pytorch = "1.13.0"
recyclerview = "1.3.2"
#baseQuickAdapter = "4.1.2"
baseQuickAdapter = "3.0.4"
baseQuickAdapter4 = "4.1.2"
[libraries]
android-core = { module = "com.google.zxing:android-core", version.ref = "androidCore" }
core = { module = "com.google.zxing:core", version.ref = "core" }
eventbus = { module = "org.greenrobot:eventbus", version.ref = "eventbus" }
glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
@@ -56,7 +62,10 @@ hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", ve
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hiltAndroid" }
logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" }
objectbox-android-objectbrowser = { module = "io.objectbox:objectbox-android-objectbrowser", version.ref = "objectboxVersion" }
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
refresh-header-classics = { module = "io.github.scwang90:refresh-header-classics", version.ref = "refreshHeaderClassics" }
refresh-layout-kernel = { module = "io.github.scwang90:refresh-layout-kernel", version.ref = "refreshLayoutKernel" }
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "retrofit" }
timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" }
@@ -68,10 +77,12 @@ androidx-fragment-ktx = { module = "androidx.fragment:fragment-ktx", version.ref
androidx-lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" }
#noinspection Aligned16KB
pytorch-android = { group = "org.pytorch", name = "pytorch_android", version.ref = "pytorch" }
pytorch-android-torchvision = { group = "org.pytorch", name = "pytorch_android_torchvision", version.ref = "pytorch" }
#base-quick-adapter-ktx = { module = "io.github.cymchad:BaseRecyclerViewAdapterHelper4", version.ref = "baseQuickAdapter" }
#baseRecyclerViewAdapterHelper = { module = "com.github.CymChad:BaseRecyclerViewAdapterHelper", version.ref = "baseQuickAdapter" }
baseRecyclerViewAdapterHelper4 = { module = "io.github.cymchad:BaseRecyclerViewAdapterHelper4", version.ref = "baseQuickAdapter4" }
androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerview" }