首页、支付功能

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
@@ -2,6 +2,8 @@ package com.sw.dualscreen.activity
import android.app.Dialog
import android.content.Context
import android.content.res.Configuration
import android.content.res.Resources
import android.os.Bundle
import android.text.TextUtils
import android.view.View
@@ -19,6 +21,7 @@ import com.sw.dualscreen.viewmodel.BaseViewModel
import kotlinx.coroutines.launch
import timber.log.Timber
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
protected lateinit var binding: VB
protected lateinit var context: Context
@@ -85,12 +85,21 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
box = ObjectBox.boxStore.boxFor(Food::class)
}
list.clear()
var queryList = box?.all?.distinctBy { it.name }
val totalList = box?.all
?.filter { it.name!=null }
?.groupBy { it.name!! }
?.map { CollectedFoodBean(foodName = it.key, count = it.value.size) }
//var queryMap:Map<String, List<Food>> ?= null
if (searchName.isNullOrBlank().not()) {
queryList = queryList?.filter { it.name?.contains(searchName) == true }
}
queryList?.forEach {
list.add(CollectedFoodBean(foodName = it.name))
//queryMap = totalMap?.filter { it.key.contains(searchName) }
val temp = totalList?.filter { it.foodName?.contains(searchName) == true}
if (temp.isNullOrEmpty().not()) {
list.addAll(temp)
}
} else {
if (totalList.isNullOrEmpty().not()) {
list.addAll(totalList)
}
}
adapter.notifyDataSetChanged()
if (list.isEmpty()) {
@@ -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()
}
}
@@ -4,15 +4,16 @@ import android.view.View
import androidx.activity.viewModels
import androidx.fragment.app.Fragment
import com.sw.dualscreen.R
import com.sw.dualscreen.activity.fragment.CashPayFragment
import com.sw.dualscreen.activity.fragment.FacePayFragment
import com.sw.dualscreen.activity.fragment.NumberPayFragment
import com.sw.dualscreen.activity.fragment.PayResultFragment
import com.sw.dualscreen.activity.fragment.QrCodePayFragment
import com.sw.dualscreen.activity.fragment.pay.CashPayFragment
import com.sw.dualscreen.activity.fragment.pay.FacePayFragment
import com.sw.dualscreen.activity.fragment.pay.NumberPayFragment
import com.sw.dualscreen.activity.fragment.pay.PayResultFragment
import com.sw.dualscreen.activity.fragment.pay.QrCodePayFragment
import com.sw.dualscreen.databinding.ActivityPayBinding
import com.sw.dualscreen.model.response.FoodInfo
import com.sw.dualscreen.viewmodel.BaseViewModel
import com.sw.dualscreen.viewmodel.UserViewModel
import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel
class PayActivity : BaseActivity<ActivityPayBinding>() {
@@ -26,10 +27,11 @@ class PayActivity : BaseActivity<ActivityPayBinding>() {
const val TAG_PAY_RESULT = "tagPayResult"
}
private val viewModel by viewModels<UserViewModel>()
val userViewModel by viewModels<UserViewModel>()
val recognizeViewModel by viewModels<RecognizeViewModel>()
override fun getViewModel(): BaseViewModel {
return viewModel
return userViewModel
}
override fun inflateViewBinding(): ActivityPayBinding {
@@ -125,4 +127,12 @@ class PayActivity : BaseActivity<ActivityPayBinding>() {
addFragment(payResultFragment!!, TAG_PAY_RESULT)
}
override fun onDestroy() {
qrCodePayFragment?.presentation?.dismiss()
cashPayFragment?.presentation?.dismiss()
numberPayFragment?.presentation?.dismiss()
facePayFragment?.presentation?.dismiss()
super.onDestroy()
}
}
@@ -1,6 +1,9 @@
package com.sw.dualscreen.activity.fragment
import android.content.Context
import android.hardware.display.DisplayManager
import android.os.Bundle
import android.view.Display
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -9,6 +12,10 @@ import androidx.viewbinding.ViewBinding
abstract class BaseFragment<VB : ViewBinding>: Fragment() {
val displays: Array<Display> by lazy {
val displayManager = requireActivity().getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
displayManager.displays
}
protected lateinit var binding: VB
protected abstract fun inflateViewBinding(): VB
@@ -6,6 +6,7 @@ import com.sw.dualscreen.R
import com.sw.dualscreen.databinding.FragmentBusinessBinding
import com.sw.dualscreen.sdk.SensorScaleUtils
import com.sw.dualscreen.utils.SPUtil
import com.sw.plate.utils.ToastUtils
import kotlin.math.roundToInt
class BusinessFragment: BaseFragment<FragmentBusinessBinding>() {
@@ -34,6 +35,21 @@ class BusinessFragment: BaseFragment<FragmentBusinessBinding>() {
binding.btnClearZero.setOnClickListener {
SensorScaleUtils.tare()
}
binding.btnConfirm.setOnClickListener {
if (binding.rgCharge.checkedRadioButtonId == -1) {
ToastUtils.showToast("请设置计费模式")
return@setOnClickListener
}
if (binding.rgTakeFood.checkedRadioButtonId == -1) {
ToastUtils.showToast("请设置取餐模式")
return@setOnClickListener
}
var mode = if (binding.rgCharge.checkedRadioButtonId == R.id.rbChargeYes) 0 else 1
SPUtil.getInstance().put(GlobalKey.KEY_CHARGE_MODE, mode)
mode = if (binding.rgTakeFood.checkedRadioButtonId == R.id.rbPickAndPlace) 0 else 1
SPUtil.getInstance().put(GlobalKey.KEY_PICKUP_MODE, mode)
}
SensorScaleUtils.addWeightListener { value ->
val realWeight = (value * 1000).roundToInt()
binding.tvFoodWeight.text = "$realWeight"
@@ -1,25 +0,0 @@
package com.sw.dualscreen.activity.fragment
import android.text.Spanned
import android.text.SpannedString
import android.text.style.AbsoluteSizeSpan
import androidx.core.text.buildSpannedString
import com.sw.dualscreen.databinding.FragmentCashPayBinding
class CashPayFragment: BaseFragment<FragmentCashPayBinding>() {
override fun inflateViewBinding(): FragmentCashPayBinding {
return FragmentCashPayBinding.inflate(layoutInflater)
}
override fun initialize() {
binding.tvRealAmount.text = getAmountText("36.8")
}
private fun getAmountText(amount:String): SpannedString {
return buildSpannedString {
append("¥", AbsoluteSizeSpan(36, true), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
append(amount, AbsoluteSizeSpan(60, true), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
}
}
@@ -60,6 +60,7 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
setOnItemClickListener { adapter, view, position ->
checkedItem = searchFoodList[position]
selectedFoodId = checkedItem!!.id
selectedFoodName = checkedItem!!.foodName
val isChecked = checkedItem!!.isChecked
searchFoodList[position].isChecked = isChecked.not()
notifyItemChanged(position)
@@ -1,19 +0,0 @@
package com.sw.dualscreen.activity.fragment
import com.sw.dualscreen.activity.PayActivity
import com.sw.dualscreen.databinding.FragmentFacePayBinding
class FacePayFragment : BaseFragment<FragmentFacePayBinding>() {
override fun inflateViewBinding(): FragmentFacePayBinding {
return FragmentFacePayBinding.inflate(layoutInflater)
}
private var payActivity: PayActivity? = null
override fun initialize() {
payActivity = activity as PayActivity
binding.tvFaceState.setOnClickListener {
payActivity?.showPayResult()
}
}
}
@@ -1,15 +0,0 @@
package com.sw.dualscreen.activity.fragment
import com.sw.dualscreen.databinding.FragmentNumberPayBinding
import com.sw.dualscreen.ext.hideKeyboard
class NumberPayFragment: BaseFragment<FragmentNumberPayBinding>() {
override fun inflateViewBinding(): FragmentNumberPayBinding {
return FragmentNumberPayBinding.inflate(layoutInflater)
}
override fun initialize() {
binding.root.hideKeyboard()
}
}
@@ -1,29 +0,0 @@
package com.sw.dualscreen.activity.fragment
import android.text.Spanned
import android.text.SpannedString
import android.text.style.AbsoluteSizeSpan
import androidx.core.text.buildSpannedString
import com.sw.dualscreen.R
import com.sw.dualscreen.databinding.FragmentQrcodePayBinding
class QrCodePayFragment: BaseFragment<FragmentQrcodePayBinding>() {
override fun inflateViewBinding(): FragmentQrcodePayBinding {
return FragmentQrcodePayBinding.inflate(layoutInflater)
}
override fun initialize() {
binding.tvRealAmount.text = getAmountText("36.8")
binding.ivPayQrCode.setImageResource(R.drawable.ic_qrcode)
binding.tvPayResult.text = "待支付(60s)..."
}
private fun getAmountText(amount:String): SpannedString {
return buildSpannedString {
append("¥", AbsoluteSizeSpan(36, true), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
append(amount, AbsoluteSizeSpan(60, true), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
}
}
@@ -0,0 +1,63 @@
package com.sw.dualscreen.activity.fragment.pay
import android.text.Spanned
import android.text.SpannedString
import android.text.style.AbsoluteSizeSpan
import androidx.core.text.buildSpannedString
import com.sw.dualscreen.activity.PayActivity
import com.sw.dualscreen.activity.fragment.BaseFragment
import com.sw.dualscreen.databinding.FragmentCashPayBinding
import com.sw.dualscreen.presentation.pay.CashPayPresentation
class CashPayFragment : BaseFragment<FragmentCashPayBinding>() {
private lateinit var payActivity: PayActivity
private var foodName: String? = null
private var payAmount: String? = null
var presentation: CashPayPresentation? = null
override fun inflateViewBinding(): FragmentCashPayBinding {
return FragmentCashPayBinding.inflate(layoutInflater)
}
override fun initialize() {
payActivity = requireActivity() as PayActivity
foodName = payActivity.foodInfo?.foodName
payAmount = "36.80"
binding.tvRealAmount.text = getAmountText(payAmount?:"")
showSubScreen()
}
override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden)
if (hidden) {
binding.root.postDelayed({
presentation?.dismiss()
}, 500)
return
}
showSubScreen()
}
private fun showSubScreen() {
// 查找副屏(通常索引为1
if (displays.size > 1) {
presentation = CashPayPresentation(activity = payActivity, display = displays[1]) {
presentation?.dismiss()
}.also {
it.foodName = foodName
it.payAmount = payAmount
}
presentation?.show()
}
}
private fun getAmountText(amount: String): SpannedString {
return buildSpannedString {
append("¥", AbsoluteSizeSpan(36, true), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
append(amount, AbsoluteSizeSpan(60, true), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
}
}
@@ -0,0 +1,57 @@
package com.sw.dualscreen.activity.fragment.pay
import com.sw.dualscreen.activity.PayActivity
import com.sw.dualscreen.activity.fragment.BaseFragment
import com.sw.dualscreen.databinding.FragmentFacePayBinding
import com.sw.dualscreen.presentation.pay.CashPayPresentation
import com.sw.dualscreen.presentation.pay.FacePayPresentation
class FacePayFragment : BaseFragment<FragmentFacePayBinding>() {
private var payActivity: PayActivity? = null
private var foodName: String? = null
private var payAmount: String? = null
var presentation: FacePayPresentation? = null
override fun inflateViewBinding(): FragmentFacePayBinding {
return FragmentFacePayBinding.inflate(layoutInflater)
}
override fun initialize() {
payActivity = activity as PayActivity
binding.tvFaceState.setOnClickListener {
payActivity?.showPayResult()
}
foodName = payActivity?.foodInfo?.foodName
payAmount = "36.80"
showSubScreen()
}
override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden)
if (hidden) {
binding.root.postDelayed({
presentation?.dismiss()
}, 500)
return
}
showSubScreen()
}
private fun showSubScreen() {
// 查找副屏(通常索引为1
if (displays.size > 1) {
presentation = FacePayPresentation(
activity = payActivity!!,
display = displays[1],
userViewModel = payActivity!!.userViewModel,
recognizeViewModel = payActivity!!.recognizeViewModel,
) {
presentation?.dismiss()
}.also {
it.foodName = foodName
it.payAmount = payAmount
}
presentation?.show()
}
}
}
@@ -0,0 +1,52 @@
package com.sw.dualscreen.activity.fragment.pay
import com.sw.dualscreen.activity.PayActivity
import com.sw.dualscreen.activity.fragment.BaseFragment
import com.sw.dualscreen.databinding.FragmentNumberPayBinding
import com.sw.dualscreen.ext.hideKeyboard
import com.sw.dualscreen.presentation.pay.CashPayPresentation
class NumberPayFragment: BaseFragment<FragmentNumberPayBinding>() {
private lateinit var payActivity: PayActivity
private var foodName: String? = null
private var payAmount: String? = null
var presentation: CashPayPresentation? = null
override fun inflateViewBinding(): FragmentNumberPayBinding {
return FragmentNumberPayBinding.inflate(layoutInflater)
}
override fun initialize() {
binding.root.hideKeyboard()
payActivity = requireActivity() as PayActivity
foodName = payActivity.foodInfo?.foodName
payAmount = "36.80"
showSubScreen()
}
override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden)
if (hidden) {
binding.root.postDelayed({
presentation?.dismiss()
}, 500)
return
}
showSubScreen()
}
private fun showSubScreen() {
// 查找副屏(通常索引为1
if (displays.size > 1) {
presentation = CashPayPresentation(activity = payActivity, display = displays[1]) {
presentation?.dismiss()
}.also {
it.foodName = foodName
it.payAmount = payAmount
}
presentation?.show()
}
}
}
@@ -1,6 +1,7 @@
package com.sw.dualscreen.activity.fragment
package com.sw.dualscreen.activity.fragment.pay
import com.sw.dualscreen.activity.PayActivity
import com.sw.dualscreen.activity.fragment.BaseFragment
import com.sw.dualscreen.databinding.FragmentPayResultBinding
class PayResultFragment: BaseFragment<FragmentPayResultBinding>() {
@@ -0,0 +1,95 @@
package com.sw.dualscreen.activity.fragment.pay
import android.text.Spanned
import android.text.SpannedString
import android.text.style.AbsoluteSizeSpan
import androidx.core.text.buildSpannedString
import androidx.lifecycle.lifecycleScope
import com.sw.dualscreen.R
import com.sw.dualscreen.activity.PayActivity
import com.sw.dualscreen.activity.fragment.BaseFragment
import com.sw.dualscreen.databinding.FragmentQrcodePayBinding
import com.sw.dualscreen.ext.load
import com.sw.dualscreen.presentation.pay.QrCodePayPresentation
import com.sw.dualscreen.utils.countDownByFlow
import kotlinx.coroutines.Job
class QrCodePayFragment: BaseFragment<FragmentQrcodePayBinding>() {
private lateinit var payActivity: PayActivity
private var foodName: String? = null
private var payAmount: String? = null
private var payQrCodePic: Any? = null
var presentation: QrCodePayPresentation?=null
override fun inflateViewBinding(): FragmentQrcodePayBinding {
return FragmentQrcodePayBinding.inflate(layoutInflater)
}
override fun initialize() {
payActivity = requireActivity() as PayActivity
foodName = payActivity.foodInfo?.foodName
payAmount = "36.80"
payQrCodePic = R.drawable.ic_qrcode
binding.tvRealAmount.text = getAmountText(payAmount?:"")
binding.ivPayQrCode.load(payQrCodePic)
binding.tvPayResult.text = "待支付(60s)..."
startCountDown()
showSubScreen()
}
override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden)
if (hidden) {
binding.root.postDelayed({
presentation?.dismiss()
}, 500)
return
}
showSubScreen()
}
private fun showSubScreen() {
// 查找副屏(通常索引为1
if (displays.size > 1) {
presentation = QrCodePayPresentation(activity = payActivity, display = displays[1]) {
presentation?.dismiss()
}.also {
it.foodName = foodName
it.payAmount = payAmount
it.payQrCodePic = payQrCodePic
}
presentation?.show()
}
}
private fun getAmountText(amount:String): SpannedString {
return buildSpannedString {
append("¥", AbsoluteSizeSpan(36, true), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
append(amount, AbsoluteSizeSpan(60, true), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
}
private fun startCountDown() {
countDownJob = countDownByFlow(
total = 60,
scope = lifecycleScope,
onStart = {
binding.tvPayResult.text = "待支付(60s)..."
},
onTick = { seconds ->
binding.tvPayResult.text = "待支付(${seconds}s)..."
},
onFinish = {
binding.tvPayResult.text = "支付倒计时结束"
}
)
}
private var countDownJob: Job? = null
override fun onDestroy() {
super.onDestroy()
countDownJob?.cancel() // 自动取消订阅,防止内存泄漏
}
}