首页、支付功能

This commit is contained in:
2025-11-26 18:57:53 +08:00
parent 60b712f216
commit 88179e27b7
64 changed files with 3380 additions and 319 deletions
@@ -4,15 +4,13 @@ import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.graphics.Outline
import android.graphics.Typeface
import android.hardware.display.DisplayManager
import android.net.Uri
import android.os.Handler
import android.os.Looper
import android.text.TextUtils
import android.view.View
import android.view.ViewOutlineProvider
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Toast
import androidx.activity.viewModels
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
@@ -21,28 +19,19 @@ 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.GlobalData
import com.sw.dualscreen.GlobalKey
import com.sw.dualscreen.R
import com.sw.dualscreen.adapter.GenericItemAdapter
import com.sw.dualscreen.adapter.GridSpacingItemDecoration
import com.sw.dualscreen.adapter.SearchFoodAdapter
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.ext.format2String
import com.sw.dualscreen.ext.gone
import com.sw.dualscreen.ext.visible
import com.sw.dualscreen.model.response.FoodInfo
import com.sw.dualscreen.objbox.FoodModule
import com.sw.dualscreen.objbox.FoodModule.IdNameScore
import com.sw.dualscreen.presentation.SecondaryScreenPresentation
import com.sw.dualscreen.presentation.MainScreenPresentation
import com.sw.dualscreen.sdk.SensorScaleUtils
import com.sw.dualscreen.utils.AssetsTool
import com.sw.dualscreen.utils.BitmapCropper
import com.sw.dualscreen.utils.BitmapSaver
import com.sw.dualscreen.utils.Debouncer
import com.sw.dualscreen.utils.GlideUtils
@@ -55,7 +44,6 @@ import com.sw.dualscreen.viewmodel.UserViewModel
import com.sw.plate.utils.LightManager
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.time.LocalDateTime
@@ -68,12 +56,20 @@ import kotlin.math.roundToInt
/**
* 服务员显示界面
*/
@SuppressLint("NotifyDataSetChanged")
class MainActivity : BaseActivity<ActivityMainBinding>() {
companion object {
const val TIME_OUT = 5 * 60 * 1000L
}
private val viewModel by viewModels<UserViewModel>()
private var imageCapture: ImageCapture? = null
private var isAnalyzing = true // 控制是否进行图像分析
private var presentation: SecondaryScreenPresentation? = null
// private var presentation: SecondaryScreenPresentation? = null
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 {
SearchFoodAdapter(searchFoodList).apply {
@@ -86,8 +82,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
item.photoUri = null
checkedItem = item
notifyDataSetChanged()
// TODO 暂时注释---------------
//updateCurrentFood(item)
updateCurrentFood(item)
}
}
}
@@ -111,19 +106,27 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
override fun initialize() {
super.initialize()
FoodModule.init(this)
initView()
setupSecondaryDisplay()
setupCamera()
initData()
FoodModule.init(this)
}
private fun initData() {
viewModel.getEquipmentToken()
presentation?.step1()
// presentation?.step1()
presentation?.step1FoodRecognizing()
}
private fun initView() {
// // 在Activity中获取DPI
// val metrics = getResources()?.displayMetrics
// val screenDPI = metrics?.densityDpi
// val density = metrics?.density
//
// Log.d("ScreenInfo", "DPI: $screenDPI, density: $density");
viewModel.activeEngine()
updateDateTime()
binding.ivSetting.setOnClickListener {
@@ -143,8 +146,13 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
binding.previewView.clipToOutline = true
binding.tvToSearch.setOnClickListener {
bottomSheetDialog = CustomBottomSheetDialog.newInstance(viewModel) {
checkedItem = it
it.photoUri = null
updateCurrentFood(it)
searchFoodList.forEachIndexed { index, info ->
searchFoodList[index].isChecked = false
}
adapter.notifyDataSetChanged()
}
bottomSheetDialog!!.show(supportFragmentManager, "CustomBottomSheetDialog")
}
@@ -155,9 +163,9 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
// arrayOf("即放即取", "余量取餐")
//)
//binding.spinner.adapter = spinnerAdapter
val mode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0)
//binding.spinner.setSelection(mode ?: 0)
presentation?.updateMealPickupMode(mode ?: 0)
// val mode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0)
// //binding.spinner.setSelection(mode ?: 0)
// presentation?.updateMealPickupMode(mode ?: 0)
//binding.spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
// override fun onItemSelected(
// parent: AdapterView<*>?,
@@ -196,6 +204,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
debouncer.debounce { recognizeFood() }
}
binding.btnPay.setOnClickListener {
presentation?.dismiss()
startActivity(Intent(this, PayActivity::class.java).apply {
putExtra(PayActivity.FOOD_INFO, checkedItem)
})
@@ -233,38 +242,26 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
override fun registerDataChange() {
super.registerDataChange()
//// lifecycleScope.launch {
//// viewModel.identifiedFoodInfoList2.collect { list ->
//// updateFoodInfo(list)
//// }
//// }
// lifecycleScope.launch {
// viewModel.identifiedFoodInfoList2.collect { list ->
// updateFoodInfo(list)
// viewModel.identifiedFoodInfoList.collect { list ->
// //adapter.updateData(list)
// searchFoodList.clear()
// searchFoodList.addAll(list)
// adapter.notifyDataSetChanged()
//
// if (list.isNotEmpty()) {
// checkedItem = list[0]
// checkedItem!!.photoUri = lastPhotoUri
// updateCurrentFood(checkedItem)
// lastPhotoUri = null
// }
// }
// }
lifecycleScope.launch {
viewModel.identifiedFoodInfoList.collect { list ->
//adapter.updateData(list)
searchFoodList.clear()
searchFoodList.addAll(list)
adapter.notifyDataSetChanged()
//TODO 暂时写死数据测试---------------------------
binding.flPay.visible()
binding.recyclerview.visible()
searchFoodList.clear()
searchFoodList.add(FoodInfo(foodName = "岐山臊子面"))
searchFoodList.add(FoodInfo(foodName = "肉末干拌面"))
searchFoodList.add(FoodInfo(foodName = "雪菜肉丝面"))
searchFoodList.add(FoodInfo(foodName = "康帅博方便面"))
searchFoodList.add(FoodInfo(foodName = "担担面"))
searchFoodList.add(FoodInfo(foodName = "四合一干拌面"))
adapter.notifyDataSetChanged()
if (list.isNotEmpty()) {
checkedItem = list[0]
checkedItem!!.photoUri = lastPhotoUri
updateCurrentFood(checkedItem)
lastPhotoUri = null
}
}
}
SensorScaleUtils.addWeightListener { weight ->
Timber.d("registerDataChange weight = $weight")
@@ -286,7 +283,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
}
}
private fun recognizeFood() {
fun recognizeFood() {
LightManager.openRedLight()
LightManager.openGreenLight()
takePhoto { photoUri ->
@@ -325,7 +322,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
binding.layoutRescan.visibility = View.GONE
viewModel.getFoodInfo(foodName) { list ->
runOnUiThread {
updateFoodInfo(list, scoreList)
updateFoodInfo(list.toMutableList(), scoreList)
}
}
}
@@ -335,8 +332,22 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
}
@SuppressLint("NotifyDataSetChanged")
private fun updateFoodInfo(list: List<FoodInfo>, scoreList: List<IdNameScore>) {
Timber.d("registerDataChange识别后查询接口数据:${GsonUtils.toJson(list)}")
private fun updateFoodInfo(list: MutableList<FoodInfo>, scoreList: List<IdNameScore>) {
Timber.d("registerDataChange识别后查询接口数据:${GsonUtils.toJson(list)},识别数据:${GsonUtils.toJson(scoreList)}")
// TODO: 临时测试数据,后续删除-------------------
//if (list.isEmpty()) {
// //binding.flPay.visible()
// //binding.recyclerview.visible()
// //list.add(FoodInfo(foodName = "豆角茄子"))
// //list.add(FoodInfo(foodName = "豆芽炒粉条"))
// //list.add(FoodInfo(foodName = "岐山臊子面"))
// //list.add(FoodInfo(foodName = "肉末干拌面"))
// //list.add(FoodInfo(foodName = "雪菜肉丝面"))
// //list.add(FoodInfo(foodName = "康帅博方便面"))
// //list.add(FoodInfo(foodName = "担担面"))
// //list.add(FoodInfo(foodName = "四合一干拌面"))
//}
// TODO: 临时测试数据,后续删除-------------------
list.forEach { foodInfo ->
val scoreItem = scoreList.firstOrNull { it.name == foodInfo.foodName }
val score = scoreItem?.score ?: 0.0
@@ -349,9 +360,17 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
if (list.isNotEmpty()) {
checkedItem = list[0]
checkedItem!!.photoUri = lastPhotoUri
checkedItem!!.isChecked = true
adapter.notifyItemChanged(0)
updateCurrentFood(checkedItem)
lastPhotoUri = null
LightManager.closeRedLight()
binding.flPay.visible()
binding.tvToSearch.visible()
} else {
binding.flPay.gone()
binding.tvToSearch.gone()
}
}
@@ -532,21 +551,34 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
if (displays.size > 1) {
val secondaryDisplay = displays[1]
// 创建副屏的Presentation
presentation =
SecondaryScreenPresentation(
activity = this,
this,
display = secondaryDisplay,
viewModel = viewModel,
recognizeViewModel = viewModels<RecognizeViewModel>().value
)
presentation!!.setStepChangeCallback { step ->
if (step == 1) {
Timber.d("setStepChangeCallback${step}")
updateCurrentFood(null)
bottomSheetDialog?.dismiss()
presentation = MainScreenPresentation(
activity = this,
display = secondaryDisplay,
userViewModel = viewModel,
recognizeViewModel = viewModels<RecognizeViewModel>().value
).apply {
setStepChangeCallback { step ->
if (step == 1) {
Timber.d("setStepChangeCallback${step}")
updateCurrentFood(null)
bottomSheetDialog?.dismiss()
}
}
}
//SecondaryScreenPresentation(
// activity = this,
// this,
// display = secondaryDisplay,
// viewModel = viewModel,
// recognizeViewModel = viewModels<RecognizeViewModel>().value
//)
// presentation!!.setStepChangeCallback { step ->
// if (step == 1) {
// Timber.d("setStepChangeCallback${step}")
// updateCurrentFood(null)
// bottomSheetDialog?.dismiss()
// }
// }
presentation!!.show()
} else {
ToastUtils.showToast("获取副屏失败")
@@ -568,9 +600,49 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
}
private fun updateDateTime() {
val formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 EEEE", Locale.CHINA)
val formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 E", Locale.CHINA)
val dateTime = formatter.format(LocalDateTime.now(ZoneId.of("Asia/Shanghai")))
binding.tvTitleTime.text = dateTime
}
override fun onResume() {
super.onResume()
// 启动定时器
handler.postDelayed(timeoutRunnable, TIME_OUT)
if (presentation?.isShowing == false) {
setupSecondaryDisplay()
}
val mode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0)
presentation?.updateMealPickupMode(mode ?: 0)
}
override fun onPause() {
super.onPause()
// 取消定时器
handler.removeCallbacks(timeoutRunnable)
}
private var lastTouchTime = 0L
private val timeoutRunnable = Runnable {
//显示待机页面
presentation?.loadStandbyScreen()
}
override fun onUserInteraction() {
super.onUserInteraction()
if (System.currentTimeMillis() - lastTouchTime >= TIME_OUT) {
//隐藏待机页面
presentation?.hideStandbyScreen()
}
// 重置定时器
handler.removeCallbacks(timeoutRunnable)
handler.postDelayed(timeoutRunnable, TIME_OUT)
lastTouchTime = System.currentTimeMillis()
}
fun resetTouchTime() {
handler.removeCallbacks(timeoutRunnable)
handler.postDelayed(timeoutRunnable, TIME_OUT)
lastTouchTime = System.currentTimeMillis()
}
}