接口调试、增加人脸识别后主副屏支付金额逻辑、其它优化

This commit is contained in:
2025-12-03 18:40:47 +08:00
parent 29188c5944
commit 8899cae323
34 changed files with 1202 additions and 402 deletions
@@ -2,13 +2,12 @@ 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.hardware.display.DisplayManager
import android.os.Bundle
import android.text.TextUtils
import android.view.Display
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.TextView
import androidx.activity.enableEdgeToEdge
@@ -93,6 +92,19 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
mDialogWaiting = null
}
fun showWaitingDialog2(tip: String?) {
if (mDialogWaiting == null) {
hideWaitingDialog()
val view = View.inflate(this, R.layout.dialog_waiting, null)
mDialogWaiting = CustomDialog(this, view, R.style.MyDialog)
mDialogWaiting!!.show()
mDialogWaiting!!.setCancelable(true)
}
val contentView = mDialogWaiting?.findViewById<ViewGroup>(android.R.id.content)
val tvTip = contentView?.findViewById<TextView>(R.id.tvTip)
tvTip?.text = tip
}
private var launchPermissionCallback: IActivityResult.RequestPermissionCallback? = null
fun requestSinglePermissionResult(
@@ -10,7 +10,9 @@ import com.sw.dualscreen.databinding.ActivityCollectedFoodBinding
import com.sw.dualscreen.databinding.LayoutEmptySearchBinding
import com.sw.dualscreen.dialog.WarnDialog
import com.sw.dualscreen.ext.addOnActionSearchListener
import com.sw.dualscreen.ext.gone
import com.sw.dualscreen.ext.hideKeyboard
import com.sw.dualscreen.ext.visible
import com.sw.dualscreen.objbox.CollectedFoodInfo
import com.sw.dualscreen.objbox.Food
import com.sw.dualscreen.objbox.ObjectBox
@@ -21,6 +23,11 @@ import io.objectbox.Box
import io.objectbox.kotlin.boxFor
class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
companion object {
private const val PAGE_SIZE = 100
}
private val viewModel by viewModels<UserViewModel>()
override fun getViewModel(): BaseViewModel {
return viewModel
@@ -33,7 +40,7 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
private val list: MutableList<CollectedFoodInfo> = mutableListOf()
private val adapter by lazy {
CollectedFoodNewAdapter(list).apply {
isStateViewEnable = true
// isStateViewEnable = true
addOnItemChildClickListener(R.id.ivDeleteFood) { _, _, position ->
deleteGoods(position)
}
@@ -42,6 +49,7 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
override fun initialize() {
super.initialize()
//binding.root.setOnClickListener { it.hideKeyboard() }
binding.rvFoodList.let {
it.layoutManager = LinearLayoutManager(this)
it.adapter = adapter
@@ -65,14 +73,30 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
ToastUtils.showToast("请输入物品名称")
return@addOnActionSearchListener
}
pageNo = 1
getCollectGoods(searchName)
}
v.addTextChangedListener {
if (it.isNullOrBlank()) {
pageNo = 1
getCollectGoods()
}
}
}
binding.refreshLayout.let {
it.setEnableRefresh(true)
it.setEnableLoadMore(false)
it.setOnRefreshListener {
pageNo = 1
getCollectGoods()
}
it.setOnLoadMoreListener {
getCollectGoods()
}
}
binding.emptyInclude.root.setOnClickListener {
it.hideKeyboard()
}
getCollectGoods()
}
@@ -81,54 +105,81 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
@SuppressLint("NotifyDataSetChanged")
private fun getCollectGoods(searchName: String? = null) {
if (box == null) {
box = ObjectBox.boxStore.boxFor(Food::class)
}
list.clear()
val totalList = box?.all
?.filter { it.name!=null }
?.groupBy { it.name!! }
?.map { CollectedFoodInfo(foodName = it.key, foodCount = it.value.size) }
//var queryMap:Map<String, List<Food>> ?= null
if (searchName.isNullOrBlank().not()) {
//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()) {
loadEmptyView()
}
binding.root.hideKeyboard()
// if (box == null) {
// box = ObjectBox.boxStore.boxFor(Food::class)
// }
// list.clear()
// val totalList = box?.all
// ?.filter { it.name!=null }
// ?.groupBy { it.name!! }
// ?.map { CollectedFoodInfo(foodName = it.key, foodCount = it.value.size) }
// //var queryMap:Map<String, List<Food>> ?= null
// if (searchName.isNullOrBlank().not()) {
// //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()) {
// loadEmptyView()
// }
viewModel.getCollectedFoodList(pageNo = pageNo) {
if (it.isNotEmpty()) {
list.addAll(it)
runOnUiThread {
adapter.notifyDataSetChanged()
}
viewModel.getCollectedFoodList(
pageNo = pageNo,
pageSize = PAGE_SIZE,
foodName = searchName ?: ""
) { items ->
runOnUiThread {
loadFoodList(items)
}
}
}
@SuppressLint("NotifyDataSetChanged")
private fun loadFoodList(items: List<CollectedFoodInfo>) {
finishRefresh()
if (pageNo == 1 && items.isEmpty()) {
list.clear()
adapter.notifyDataSetChanged()
loadEmptyView()
binding.refreshLayout.setEnableLoadMore(false)
return
}
binding.refreshLayout.visible()
binding.emptyInclude.root.gone()
if (pageNo == 1) {
list.clear()
}
list.addAll(items)
val isEnableLoadMore = items.size >= PAGE_SIZE
if (isEnableLoadMore) {
pageNo++
}
binding.refreshLayout.setEnableLoadMore(isEnableLoadMore)
adapter.notifyDataSetChanged()
binding.root.hideKeyboard()
}
private var pageNo = 1
private var emptyBinding: LayoutEmptySearchBinding? = null
// private var emptyBinding: LayoutEmptySearchBinding? = null
private fun loadEmptyView() {
if (emptyBinding == null) {
emptyBinding =
LayoutEmptySearchBinding.inflate(layoutInflater, binding.rvFoodList, false)
}
emptyBinding?.root?.let { layout ->
layout.setOnClickListener { layout.hideKeyboard() }
adapter.stateView = layout
}
binding.refreshLayout.gone()
binding.emptyInclude.root.visible()
//if (emptyBinding == null) {
// emptyBinding =
// LayoutEmptySearchBinding.inflate(layoutInflater, binding.rvFoodList, false)
//}
//emptyBinding?.root?.let { layout ->
// layout.setOnClickListener { layout.hideKeyboard() }
// adapter.stateView = layout
//}
}
private fun deleteGoods(position: Int) {
@@ -154,4 +205,16 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
}.start()
}).show()
}
private fun finishRefresh() {
binding.refreshLayout.let {
if (pageNo == 1) {
it.finishRefresh(500)
} else {
it.finishLoadMore(500)
}
}
}
}
@@ -11,6 +11,7 @@ import android.os.Looper
import android.text.TextUtils
import android.view.View
import android.view.ViewOutlineProvider
import androidx.activity.OnBackPressedCallback
import androidx.activity.viewModels
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
@@ -21,20 +22,22 @@ import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
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.adapter.SearchFoodAdapter
import com.sw.dualscreen.databinding.ActivityMainBinding
import com.sw.dualscreen.ext.dp
import com.sw.dualscreen.ext.gone
import com.sw.dualscreen.ext.load
import com.sw.dualscreen.ext.visible
import com.sw.dualscreen.model.response.FoodInfo
import com.sw.dualscreen.model.response.FoodOrder
import com.sw.dualscreen.objbox.FoodModule
import com.sw.dualscreen.objbox.FoodModule.IdNameScore
import com.sw.dualscreen.presentation.MainScreenPresentation
import com.sw.dualscreen.sdk.SensorScaleUtils
import com.sw.dualscreen.utils.BitmapSaver
import com.sw.dualscreen.utils.Debouncer
import com.sw.dualscreen.utils.GlideUtils
import com.sw.dualscreen.utils.GsonUtils
import com.sw.dualscreen.utils.ImageUtil
import com.sw.dualscreen.utils.SPUtil
@@ -65,8 +68,9 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
private val viewModel by viewModels<UserViewModel>()
private var imageCapture: ImageCapture? = null
private var isAnalyzing = true // 控制是否进行图像分析
// private var presentation: SecondaryScreenPresentation? = null
var isAnalyzing = true // 控制是否进行图像分析
// private var presentation: SecondaryScreenPresentation? = null
private var presentation: MainScreenPresentation? = null
private val executor = Executors.newSingleThreadExecutor()
private val handler by lazy { Handler(Looper.getMainLooper()) }
@@ -86,7 +90,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
}
}
}
private var checkedItem: FoodInfo? = null
var checkedItem: FoodInfo? = null
private var imageAnalysis: ImageAnalysis? = null
private var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>? = null
private var canIdentify: Boolean = false
@@ -106,6 +110,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
override fun initialize() {
super.initialize()
addBackEventListener()
FoodModule.init(this)
initView()
setupSecondaryDisplay()
@@ -204,45 +209,44 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
// binding.tvRescan.setOnClickListener {
// debouncer.debounce { recognizeFood() }
// }
binding.btnPay.setOnClickListener {
if (checkedItem == null) {
ToastUtils.showToast("暂无识别数据,请搜索选择")
return@setOnClickListener
binding.btnPay.let {
it.run {
val mode = SPUtil.getInstance().get(GlobalKey.KEY_CHARGE_MODE, 0)
if (mode == 0) visible() else gone()
}
it.setOnClickListener { v ->
if (checkedItem == null) {
ToastUtils.showToast("暂无识别数据,请搜索选择")
return@setOnClickListener
}
createOrder(checkedItem!!)
}
presentation?.dismiss()
startActivity(Intent(this, PayActivity::class.java).apply {
putExtra(PayActivity.FOOD_INFO, checkedItem)
})
}
}
fun updateCurrentFood(foodInfo: FoodInfo?) {
if (foodInfo != null) {
presentation?.updateFood(foodInfo)
binding.tvFoodName.text = foodInfo.foodName
binding.previewView.visibility = View.GONE
binding.ivImg.visibility = View.VISIBLE
val imgUrl = foodInfo.imgUrl
if (TextUtils.isEmpty(imgUrl)) {
takePhoto { imgUri ->
GlideUtils.loadRoundCornerImage(this, imgUri, binding.ivImg, 12)
presentation?.updateFood(foodInfo.copy(imgUrl = imgUri.toString()))
}
} else {
if (foodInfo.photoUri != null) {
GlideUtils.loadRoundCornerImage(this, foodInfo.photoUri, binding.ivImg, 12)
} else {
GlideUtils.loadRoundCornerImage(this, foodInfo.imgUrl, binding.ivImg, 12)
}
}
pauseAnalysis()
} else {
if (foodInfo == null) {
binding.tvFoodName.text = "-"
binding.previewView.visibility = View.VISIBLE
binding.ivImg.visibility = View.GONE
resumeAnalysis()
viewModel.cleanIdentifiedFoodInfoList()
return
}
pauseAnalysis()
presentation?.updateFood(foodInfo)
binding.tvFoodName.text = foodInfo.foodName
binding.previewView.visibility = View.GONE
binding.ivImg.visibility = View.VISIBLE
if (foodInfo.foodImg.isNullOrBlank()) {
takePhoto { imgUri ->
binding.ivImg.load(imgUri)
presentation?.updateFood(foodInfo.copy(foodImg = imgUri.toString()))
}
} else {
binding.ivImg.load(foodInfo.photoUri ?: foodInfo.foodImg)
}
//pauseAnalysis()
}
override fun registerDataChange() {
@@ -343,21 +347,13 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
@SuppressLint("NotifyDataSetChanged")
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: 临时测试数据,后续删除-------------------
Timber.d(
"registerDataChange识别后查询接口数据:${GsonUtils.toJson(list)},识别数据:${
GsonUtils.toJson(
scoreList
)
}"
)
list.forEach { foodInfo ->
val scoreItem = scoreList.firstOrNull { it.name == foodInfo.foodName }
val score = scoreItem?.score ?: 0.0
@@ -476,7 +472,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
fun resumeAnalysis() {
Timber.d("resumeAnalysis isAnalyzing = $isAnalyzing")
if (isAnalyzing) return
isAnalyzing = true
// isAnalyzing = true
imageAnalysis?.setAnalyzer(executor) { imageProxy ->
if (presentation?.currentStep == 1) {
val bitmap = imageProxy.toBitmap()
@@ -578,13 +574,13 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
}
}
}
//SecondaryScreenPresentation(
// activity = this,
// this,
// display = secondaryDisplay,
// viewModel = viewModel,
// recognizeViewModel = viewModels<RecognizeViewModel>().value
//)
//SecondaryScreenPresentation(
// activity = this,
// this,
// display = secondaryDisplay,
// viewModel = viewModel,
// recognizeViewModel = viewModels<RecognizeViewModel>().value
//)
// presentation!!.setStepChangeCallback { step ->
// if (step == 1) {
// Timber.d("setStepChangeCallback${step}")
@@ -629,6 +625,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
val mode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0)
presentation?.updateMealPickupMode(mode ?: 0)
}
override fun onPause() {
super.onPause()
// 取消定时器
@@ -658,4 +655,61 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
handler.postDelayed(timeoutRunnable, TIME_OUT)
lastTouchTime = System.currentTimeMillis()
}
private fun createOrder(foodInfo: FoodInfo) {
val mode = SPUtil.getInstance().get(GlobalKey.KEY_CHARGE_MODE, 0)
val realWeight = (lastWeight * 1000).roundToInt()
val order = FoodOrder(
deviceId = GlobalData.deviceId,
foodId = foodInfo.foodId,
foodName = foodInfo.foodName ?: "",
foodMaterialId = foodInfo.foodMaterialId ?: "",
specId = foodInfo.specId ?: "",
foodWeight = realWeight,
eatWeight = realWeight,
//根据specId对应规格重量计算
eatNum = getEatNum(realWeight, foodInfo.specWeight ?: 0.0),
userId = null,
notPay = mode != 0,
)
viewModel.createOrder(order) { orderId ->
runOnUiThread {
if (orderId.isBlank()) {
ToastUtils.showToast("订单id为空")
return@runOnUiThread
}
foodOrderId = orderId
presentation?.dismiss()
startActivity(Intent(this, PayActivity::class.java).apply {
putExtra(PayActivity.FOOD_INFO, foodInfo)
putExtra(PayActivity.FOOD_ORDER_ID, orderId)
})
// ToastUtils.showToast("订单已生成")
}
}
}
private fun getEatNum(realWeight: Int, specWeight: Double): Int {
if (realWeight == 0 || specWeight == 0.0) return 0
return (realWeight / specWeight).roundToInt()
}
private var foodOrderId: String = ""
private var isExist = false
fun addBackEventListener() {
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (isExist.not()) {
ToastUtils.showToast("再按一次退出")
isExist = true
binding.root.postDelayed({
isExist = false
}, 2000)
return
}
finish()
}
})
}
}
@@ -1,6 +1,5 @@
package com.sw.dualscreen.activity
import android.view.View
import androidx.activity.viewModels
import androidx.fragment.app.Fragment
import com.sw.dualscreen.R
@@ -11,7 +10,7 @@ import com.sw.dualscreen.activity.fragment.pay.PayResultFragment
import com.sw.dualscreen.activity.fragment.pay.ScanQrCodePayFragment
import com.sw.dualscreen.databinding.ActivityPayBinding
import com.sw.dualscreen.model.response.FoodInfo
import com.sw.dualscreen.presentation.pay.FacePayPresentation
import com.sw.dualscreen.model.response.MemberInfo
import com.sw.dualscreen.presentation.pay.ScanQrCodePayPresentation
import com.sw.dualscreen.viewmodel.BaseViewModel
import com.sw.dualscreen.viewmodel.UserViewModel
@@ -22,6 +21,10 @@ class PayActivity : BaseActivity<ActivityPayBinding>() {
companion object {
const val FOOD_INFO = "foodInfo"
const val TOTAL_AMOUNT = "totalAmount"
const val FOOD_ORDER_ID = "foodOrderId"
// const val MEMBER_INFO = "memberInfo"
const val TAG_PAY_QR_CODE = "tagPayQrCode"
const val TAG_PAY_CASH = "tagPayCash"
const val TAG_PAY_NUMBER = "tagPayNumber"
@@ -42,16 +45,22 @@ class PayActivity : BaseActivity<ActivityPayBinding>() {
var foodInfo: FoodInfo? = null
// var totalAmount:Double = 0.0
private var qrCodePayFragment: ScanQrCodePayFragment? = null
private var cashPayFragment: CashPayFragment? = null
private var numberPayFragment: NumberPayFragment? = null
var facePayFragment: FacePayFragment? = null
private var payResultFragment: PayResultFragment? = null
var foodOrderId:String = ""
@Suppress("DEPRECATION")
override fun initialize() {
super.initialize()
foodInfo = intent.getParcelableExtra(FOOD_INFO)
foodOrderId = intent.getStringExtra(FOOD_ORDER_ID)?:""
// totalAmount = intent.getDoubleExtra(TOTAL_AMOUNT,0.0)
// memberInfo = intent.getParcelableExtra(MEMBER_INFO)
binding.tvFoodName.text = foodInfo?.foodName
binding.include.ivPageBack.setOnClickListener { finish() }
binding.include.tvPageTitle.text = "下单结算"
@@ -140,10 +149,10 @@ class PayActivity : BaseActivity<ActivityPayBinding>() {
// }
// }
fun showPayResult() {
payResultFragment = PayResultFragment()
showFragment(payResultFragment!!, TAG_PAY_RESULT)
}
// fun showPayResult() {
// payResultFragment = PayResultFragment.instance(1, memberInfo)
// showFragment(payResultFragment!!, TAG_PAY_RESULT)
// }
override fun onDestroy() {
qrCodePayFragment?.presentation?.dismiss()
@@ -154,8 +163,29 @@ class PayActivity : BaseActivity<ActivityPayBinding>() {
}
private var scanQrCodePayPresentation: ScanQrCodePayPresentation? = null
fun paySuccess() {
showFragment(PayResultFragment(), "")
var memberInfo: MemberInfo? = null
fun showPayInfo(memberInfo: MemberInfo) {
this.memberInfo = memberInfo
payResultFragment = PayResultFragment.instance(1, memberInfo)
showFragment(payResultFragment!!, TAG_PAY_RESULT)
if (displays.size > 1) {
scanQrCodePayPresentation = ScanQrCodePayPresentation(
activity = this,
display = displays[1],
type = 1
) {
scanQrCodePayPresentation?.dismiss()
}.also {
it.foodName = foodInfo?.foodName
it.totalPrice = foodInfo?.vipPrice?:0.0
}
scanQrCodePayPresentation?.show()
}
}
fun showPaySuccess() {
payResultFragment = PayResultFragment.instance(2, memberInfo!!)
showFragment(payResultFragment!!, TAG_PAY_RESULT)
if (displays.size > 1) {
scanQrCodePayPresentation = ScanQrCodePayPresentation(
activity = this,
@@ -164,8 +194,8 @@ class PayActivity : BaseActivity<ActivityPayBinding>() {
) {
scanQrCodePayPresentation?.dismiss()
}.also {
// it.foodName = foodName
// it.payAmount = payAmount
it.foodName = foodInfo?.foodName
it.totalPrice = foodInfo?.vipPrice?:0.0
}
scanQrCodePayPresentation?.show()
}
@@ -17,18 +17,18 @@ class BusinessFragment: BaseFragment<FragmentBusinessBinding>() {
override fun initialize() {
binding.rgCharge.let {
it.setOnCheckedChangeListener { group, checkedId ->
val mode = if (checkedId == R.id.rbChargeYes) 0 else 1
SPUtil.getInstance().put(GlobalKey.KEY_CHARGE_MODE, mode)
}
// it.setOnCheckedChangeListener { group, checkedId ->
// val mode = if (checkedId == R.id.rbChargeYes) 0 else 1
// SPUtil.getInstance().put(GlobalKey.KEY_CHARGE_MODE, mode)
// }
val mode = SPUtil.getInstance().get(GlobalKey.KEY_CHARGE_MODE, 0)
it.check(if (mode == 0) R.id.rbChargeYes else R.id.rbChargeNot)
}
binding.rgTakeFood.let {
it.setOnCheckedChangeListener { group, checkedId ->
val mode = if (checkedId == R.id.rbPickAndPlace) 0 else 1
SPUtil.getInstance().put(GlobalKey.KEY_PICKUP_MODE, mode)
}
// it.setOnCheckedChangeListener { group, checkedId ->
// val mode = if (checkedId == R.id.rbPickAndPlace) 0 else 1
// SPUtil.getInstance().put(GlobalKey.KEY_PICKUP_MODE, mode)
// }
val mode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0)
it.check(if (mode == 0) R.id.rbPickAndPlace else R.id.rbSurplusCalculate)
}
@@ -49,6 +49,8 @@ class BusinessFragment: BaseFragment<FragmentBusinessBinding>() {
mode = if (binding.rgTakeFood.checkedRadioButtonId == R.id.rbPickAndPlace) 0 else 1
SPUtil.getInstance().put(GlobalKey.KEY_PICKUP_MODE, mode)
ToastUtils.showToast("设置已保存")
}
SensorScaleUtils.addWeightListener { value ->
val realWeight = (value * 1000).roundToInt()
@@ -12,6 +12,7 @@ import android.widget.Toast
import androidx.camera.view.PreviewView
import androidx.core.net.toUri
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import com.sw.dualscreen.R
import com.sw.dualscreen.activity.CollectedFoodActivity
@@ -30,10 +31,12 @@ import com.sw.dualscreen.objbox.ObjectBox
import com.sw.dualscreen.utils.BitmapSaver
import com.sw.dualscreen.utils.CameraUtils
import com.sw.dualscreen.utils.Debouncer
import com.sw.dualscreen.utils.ImageUploader
import com.sw.dualscreen.utils.ImageUtil
import com.sw.plate.utils.ToastUtils
import io.objectbox.Box
import io.objectbox.kotlin.boxFor
import kotlinx.coroutines.launch
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import timber.log.Timber
@@ -44,7 +47,7 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
companion object {
private const val TAG = "CollectFragment"
const val MAX_COUNT = 5
const val MAX_COUNT = 100
}
private var selectedFoodId: String? = ""
private var selectedFoodName: String? = ""
@@ -181,9 +184,7 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
Toast.makeText(requireActivity(), "请拍摄菜品照片", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
vectorThread(){
upload()
}
upload()
}
binding.editFoodName.setOnEditorActionListener { v, actionId, event ->
@@ -216,42 +217,71 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
}
}
private fun uploadCollectFoodPics(files: List<File?>, params: HashMap<String, RequestBody>, callback: (Boolean) -> Unit) {
(requireActivity() as SettingActivity).viewModel.uploadCollectFoodPics(files,params, callback)
}
private fun upload() {
val params = HashMap<String, RequestBody>()
//params["placeId"] = restId.toRequestBody()
params["foodId"] = checkedItem!!.foodId.toRequestBody()
params["foodName"] = checkedItem!!.foodName!!.toRequestBody()
//params["foodVector"] = foodVector.toRequestBody()
for (index in foodCollectionList.indices step 5) {
val end = if(index + 5 < foodCollectionList.size - 1) index + 5 else foodCollectionList.size - 1
val subList = foodCollectionList.subList(index, end)
val subFiles = subList.map { it.imageFile }
uploadCollectFoodPics(subFiles, params) { isSuccess->
Timber.tag(TAG).d("uploadMultipleImages: ${isSuccess}")
subList.filter { it.imageFile!=null }.forEach { it.uploadSuccess = isSuccess }
// val count = collectList.count { it.imageFile!=null && it.uploadSuccess.not() }
// runOnUiThread {
// binding.btnUploadImage.text = "待上传图片${count}张"
// if (count == 0) {
// Loading.dismiss()
// }
// }
}
lifecycleScope.launch {
val totalFileCount = foodCollectionList.count { it.imageFile != null }
settingActivity?.showWaitingDialog("图片上传中0/$totalFileCount")
val params = HashMap<String, RequestBody>()
//params["placeId"] = restId.toRequestBody()
params["foodId"] = checkedItem!!.foodId.toRequestBody()
params["foodName"] = checkedItem!!.foodName!!.toRequestBody()
//params["foodVector"] = foodVector.toRequestBody()
ImageUploader(
totalList = foodCollectionList,
uploadImage = { batch->
val files = batch.map { it.imageFile }
(requireActivity() as SettingActivity).viewModel.uploadCollectFoodPics(files,params)
},
onProgress = { count, batch ->
activity?.runOnUiThread {
settingActivity?.showWaitingDialog2("图片上传中$count/$totalFileCount")
batch.forEach {
it.uploadSuccess = true
}
}
},
onError = {
activity?.runOnUiThread {
ToastUtils.showToast("上传失败,请稍后重试")
}
},
onComplete = {
vectorThread()
}
).processUploads()
}
// for (index in foodCollectionList.indices step 5) {
// val end = if(index + 5 < foodCollectionList.size - 1) index + 5 else foodCollectionList.size - 1
// val subList = foodCollectionList.subList(index, end)
// val subFiles = subList.map { it.imageFile }
// uploadCollectFoodPics(subFiles, params) { isSuccess->
// Timber.tag(TAG).d("uploadMultipleImages: ${isSuccess}")
// subList.filter { it.imageFile!=null }.forEach { it.uploadSuccess = isSuccess }
//
//// val count = collectList.count { it.imageFile!=null && it.uploadSuccess.not() }
//// runOnUiThread {
//// binding.btnUploadImage.text = "待上传图片${count}张"
//// if (count == 0) {
//// Loading.dismiss()
//// }
//// }
// }
// }
}
private fun vectorThread(block:()-> Unit) {
private fun vectorThread() {
settingActivity?.showWaitingDialog("加载中……")
Thread {
foodCollectionList
// .filter { it.bitmap != null }
.filter { it.imageVector != null }
// .filter { it.imageVector != null }
.forEachIndexed { index, it ->
if (it.imageVector == null) {
return@forEachIndexed
}
image2VectorTask(imageVector = it.imageVector!!, index)
}
activity?.runOnUiThread {
@@ -18,9 +18,9 @@ class FacePayFragment : BaseFragment<FragmentFacePayBinding>() {
override fun initialize() {
payActivity = activity as PayActivity
binding.tvFaceState.setOnClickListener {
payActivity?.showPayResult()
}
// binding.tvFaceState.setOnClickListener {
// payActivity?.showPayResult()
// }
foodName = payActivity?.foodInfo?.foodName
payAmount = "36.80"
showSubScreen()
@@ -1,16 +1,47 @@
package com.sw.dualscreen.activity.fragment.pay
import android.os.Bundle
import android.text.Spanned
import android.text.SpannedString
import android.text.style.AbsoluteSizeSpan
import android.text.style.ForegroundColorSpan
import androidx.core.graphics.toColorInt
import androidx.core.text.buildSpannedString
import com.sw.dualscreen.R
import com.sw.dualscreen.activity.PayActivity
import com.sw.dualscreen.activity.fragment.BaseFragment
import com.sw.dualscreen.databinding.FragmentPayResultBinding
import com.sw.dualscreen.ext.format2String
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.FoodInfo
import com.sw.dualscreen.model.response.MemberInfo
import com.sw.dualscreen.model.response.TextBean
import com.sw.dualscreen.utils.SpannedUtils
class PayResultFragment: BaseFragment<FragmentPayResultBinding>() {
class PayResultFragment : BaseFragment<FragmentPayResultBinding>() {
//type = 0,扫码支付默认显示二维码
//type = 1,会员结算显示二维码,是会员则显示名字、头像、手机号、可用余额
//type = 2,支付成功,是会员则显示名字、头像、手机号、可用余额
companion object {
const val PAGE_TYPE = "pageType"
const val MEMBER_INFO = "memberInfo"
fun instance(pageType: Int, memberInfo: MemberInfo): PayResultFragment {
return PayResultFragment().apply {
arguments = Bundle().also {
it.putInt(PAGE_TYPE, pageType)
it.putParcelable(MEMBER_INFO, memberInfo)
}
}
}
}
private var pageType: Int = 1
private var memberInfo: MemberInfo? = null
override fun inflateViewBinding(): FragmentPayResultBinding {
return FragmentPayResultBinding.inflate(layoutInflater)
@@ -22,19 +53,121 @@ class PayResultFragment: BaseFragment<FragmentPayResultBinding>() {
binding.btnBack.setOnClickListener {
//payActivity?.showFacePay()
}
arguments?.let {
pageType = it.getInt(PAGE_TYPE, 0)
memberInfo = it.getParcelable(MEMBER_INFO)
}
binding.ivHeadPic.load(R.mipmap.ic_launcher)
binding.tvUserName.text = "张三"
binding.tvUserPhone.text = "132****5678"
binding.tvUserBalance.text = getBalanceText("0.00")
binding.tvPayAmount.text = "收款金额 20.00 元"
binding.tvPayInfo.text = "应收 38.00 元,余额扣除 16.80 元"
}
totalPrice = payActivity?.foodInfo?.vipPrice ?: 0.0
balance = (memberInfo?.topUpBalance ?: 0.0) + (memberInfo?.rewardBalance ?: 0.0)
realPayPrice = if (balance >= totalPrice) 0.0 else totalPrice - balance
expensesBalance = if (balance >= totalPrice) totalPrice else balance
memberInfo?.let { loadUserInfo(it) }
private fun getBalanceText(amount: String): SpannedString {
return buildSpannedString {
append("¥", AbsoluteSizeSpan(20, true), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
append(amount, AbsoluteSizeSpan(32, true), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
when (pageType) {
0 -> {
}
1 -> {
binding.layoutPayInfo.visible()
binding.layoutPaySuccess.gone()
binding.tvRealAmount.text = SpannedUtils.getAmountText(
listOf(
TextBean(text = "¥", textSize = 36),
TextBean(text = realPayPrice.format2String(2), textSize = 60),
)
)
//余额扣除 -36.80 元,扣除后可用余额 10.00 元
//余额扣除 -16.80 元,还需支付 20.00 元
binding.tvUserBalance.text = SpannedUtils.getAmountText(
listOf(
TextBean(text = "¥", textSize = 24),
TextBean(text = balance.format2String(2), textSize = 36),
)
)
binding.tvAccountInfo.text = SpannedUtils.getAmountText(
getAmountList()
)
if (balance >= totalPrice) {
binding.ivPayQrCode.invisible()
binding.btnConfirmPay.isEnabled = true
} else {
binding.ivPayQrCode.visible()
// binding.ivPayQrCode.load(payQrCodePic)
binding.btnConfirmPay.isEnabled = false
}
//点击支付接口成功打开成功页面,或者扫码手动成功回调打开成功页面
binding.btnConfirmPay.setOnClickListener {
payActivity?.showWaitingDialog("支付中,请稍后……")
binding.root.postDelayed({
payActivity?.hideWaitingDialog()
payActivity?.showPaySuccess()
},1000)
}
}
2 -> {
binding.layoutPayInfo.gone()
binding.layoutPaySuccess.visible()
binding.tvPayInfo.text =
"应收 ${totalPrice.format2String(2)} 元,余额扣除 ${expensesBalance.format2String(2)}"
binding.tvPayAmount.text = "收款金额 ${realPayPrice.format2String(2)}"
}
else -> {}
}
}
//总价格
private var totalPrice = 0.0
//余额
private var balance = 0.0
//实际支付金额
private var realPayPrice = 0.0
//扣除余额
private var expensesBalance = 0.0
private fun getAmountList(): List<TextBean> {
val list: MutableList<TextBean> = mutableListOf()
list.add(TextBean(text = "余额扣除 ", textSize = 30, textColor = "#FF889AC2"))
list.add(TextBean(text = "-${expensesBalance.format2String(2)}", textSize = 30, textColor = "#FF0A1428"))
if (balance >= totalPrice) {
//余额大于等于总价格,使用余额支付
TextBean(text = " 元,扣除后可用余额 ", textSize = 30, textColor = "#FF889AC2")
val remainingBalance = balance - totalPrice
TextBean(text = remainingBalance.format2String(2), textSize = 30, textColor = "#FF0A1428")
} else {
//余额小于总价格,使用余额+扫码支付
//实际支付金额
TextBean(text = " 元,还需支付 ", textSize = 30, textColor = "#FF889AC2")
TextBean(text = realPayPrice.format2String(2), textSize = 30, textColor = "#FF0A1428")
TextBean(text = "", textSize = 30, textColor = "#FF889AC2")
}
return list
}
private fun loadUserInfo(item: MemberInfo) {
binding.ivHeadPic.load(R.mipmap.ic_launcher)
binding.tvUserName.text = item.name
val phone = item.phone ?: ""
binding.tvUserPhone.text =
if (phone.length == 11)
phone.replace(phone.substring(3, 7), "****")
else
phone
}
}
@@ -13,6 +13,7 @@ import com.sw.dualscreen.presentation.pay.ScanQrCodePayPresentation
import com.sw.dualscreen.utils.countDownByFlow
import kotlinx.coroutines.Job
import com.sw.dualscreen.databinding.FragmentScanQrcodePayBinding
import com.sw.dualscreen.ext.format2String
class ScanQrCodePayFragment: BaseFragment<FragmentScanQrcodePayBinding>() {
@@ -35,7 +36,7 @@ class ScanQrCodePayFragment: BaseFragment<FragmentScanQrcodePayBinding>() {
payActivity = requireActivity() as PayActivity
foodName = payActivity.foodInfo?.foodName
payAmount = "36.80"
payAmount = payActivity.foodInfo?.specPrice.format2String(2)
payQrCodePic = R.drawable.ic_qrcode
binding.tvRealAmount.text = getAmountText(payAmount?:"")
binding.ivPayQrCode.load(payQrCodePic)
@@ -75,7 +76,7 @@ class ScanQrCodePayFragment: BaseFragment<FragmentScanQrcodePayBinding>() {
presentation?.dismiss()
}.also {
it.foodName = foodName
it.payAmount = payAmount
it.totalPrice = payActivity.foodInfo?.specPrice?:0.0
it.payQrCodePic = payQrCodePic
}
presentation?.show()
@@ -10,6 +10,7 @@ import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.sw.dualscreen.R
import com.sw.dualscreen.databinding.ListItemFoodCollectionBinding
import com.sw.dualscreen.ext.dp
import com.sw.dualscreen.ext.load
import com.sw.dualscreen.objbox.FoodCollectionBean
import com.sw.dualscreen.utils.GlideUtils
@@ -38,14 +39,15 @@ class FoodCollectionAdapter (var list: MutableList<FoodCollectionBean>) :
scaleType = ImageView.ScaleType.CENTER_CROP
//setImageURI(it.imageUri)
//setImageBitmap(it.bitmap)
it.bitmap?.let { bitmap ->
GlideUtils.loadRoundCornerWitBitmap(
context,
url = bitmap,
imageView = this,
radius = 8.dp
)
}
load(it.imageFile)
// it.bitmap?.let { bitmap ->
// GlideUtils.loadRoundCornerWitBitmap(
// context,
// url = bitmap,
// imageView = this,
// radius = 8.dp
// )
// }
}
}
}
@@ -65,8 +65,8 @@ val Float.sp: Float
Resources.getSystem().displayMetrics
)
fun Double.format2String(): String = "%.1f".format(this)
fun Double.format2String(num:Int): String = "%.${num}f".format(this)
fun Double?.format2String(): String = this.format2String(1)
fun Double?.format2String(num:Int): String = "%.${num}f".format(this?:0.0)
// 添加扩展函数
fun ImageProxy.toSafeBitmap(): Bitmap {
@@ -1,5 +1,55 @@
package com.sw.dualscreen.model.response
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
data class FoodSearchReq(
var nameList:List<String>
var nameList: List<String>
)
data class FoodOrder(
//设备id
var deviceId: String,
//菜品id
var foodId: String,
//菜品名称
var foodName: String,
//菜品营养表id
var foodMaterialId: String,
//餐品规格ID
var specId: String,
//用户id(档口机,内部员工直接取餐:获取的用户ID/memberId
var userId: String? = null,
//食物重量
var foodWeight: Int,
//食用重量
var eatWeight: Int,
//份数
var eatNum: Int,
//是否需要付款
var notPay: Boolean,
//备注
var remark: String? = "",
)
@Parcelize
data class MemberInfo(
//会员id
val id: String?,
//手机号
val phone: String?,
//姓名
val name: String?,
//充值余额
val topUpBalance: Double?,
//赠送余额
val rewardBalance: Double?,
//积分余额
val integralBalance: Int?
) : Parcelable
data class TextBean(
var text: String,
var textSize: Int,
var textColor: String? = null
)
@@ -24,13 +24,21 @@ data class FoodInfo(
//碳水化合物
val carbohydrate:String? = null,
//规格售卖价格(元)
val specPrice:String? = null,
val specPrice: Double? = null,
//VIP售卖价(元)
val vipPrice:String? = null,
val vipPrice:Double? = null,
//餐品营养id
val foodMaterialId:String? = null,
//菜品规格id
val specId:String? = null,
//规格重量(g)
val specWeight:Double? = null,
//-----------------------------------
var score: Int = 0,
var isChecked: Boolean = false,
var photoUri: Uri? = null,
var imgUrl: String? = "",
var foodImg: String? = "",
// @SerializedName("foodTypeAndRealIntakeVoList")
// val foodTypeAndRealIntakeVoList: List<FoodTypeAndRealIntakeVo>? = listOf(),
@@ -6,6 +6,8 @@ import com.sw.dualscreen.model.response.ApiResponse
import com.sw.dualscreen.model.response.DinnerType
import com.sw.dualscreen.model.response.FoodInfo
import com.sw.dualscreen.model.response.FoodSearchReq
import com.sw.dualscreen.model.response.FoodOrder
import com.sw.dualscreen.model.response.MemberInfo
import com.sw.dualscreen.model.response.UserFaceModel
import com.sw.dualscreen.model.response.UserNutritionData
import com.sw.dualscreen.objbox.CollectedFoodInfo
@@ -71,7 +73,7 @@ interface ApiService {
@POST
suspend fun getCollectedFoodList(
@Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/boothMachine/app/dishPage",
@Body param: Map<String, Int>
@Body param: Map<String, String>
): ApiResponse<List<CollectedFoodInfo>>
@@ -108,7 +110,7 @@ interface ApiService {
@GET
suspend fun getDinnerType(
// @Url url: String = "${GlobalData.appBaseUrl}/zhstapi/zhst/getCanteenDinnerType",
@Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/boothMachine/app/getRegionRule",
@Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/common/app/getRegionRule",
// @Query("appVersion") appVersion: String = GlobalData.appVersion,
// @Query("canteenId") restId: String,
): ApiResponse<DinnerType>
@@ -134,6 +136,34 @@ interface ApiService {
@Body req: FoodSearchReq
): ApiResponse<List<FoodInfo>>
/**
* 开餐-生成订单
*/
@POST
suspend fun createOrder(
@Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/boothMachine/app/userEatFood/doubleScale",
@Body order: FoodOrder
): ApiResponse<Any?>
/**
* APP根据id获取会员或员工信息及余额
*/
@GET
suspend fun getMemberInfo(
@Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/common/app/getUserInfoBalanceById",
@Query("id") memberId: String,
): ApiResponse<MemberInfo?>
/**
* 绑定用户与订单号
*/
@GET
suspend fun bindOrder(
@Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/boothMachine/app/bingOrder",
@Query("userId") userId: String,
@Query("orderId") orderId: String
): ApiResponse<Any?>
/**
* 提交采集图片数据
@@ -16,6 +16,7 @@ import android.view.ViewOutlineProvider
import android.view.ViewTreeObserver
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import com.arcsoft.face.ErrorInfo
@@ -26,6 +27,7 @@ import com.sw.dualscreen.databinding.PresentationMainScreenBinding
import com.sw.dualscreen.ext.dp
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.visible
import com.sw.dualscreen.model.request.UserNutritionParam
@@ -33,8 +35,6 @@ import com.sw.dualscreen.model.response.DinnerType
import com.sw.dualscreen.model.response.FoodInfo
import com.sw.dualscreen.model.response.UserNutritionData
import com.sw.dualscreen.utils.Debouncer
import com.sw.dualscreen.utils.GlideUtils
import com.sw.dualscreen.utils.GsonUtils
import com.sw.dualscreen.utils.SPUtil
import com.sw.dualscreen.viewmodel.UserViewModel
import com.sw.plate.utils.LightManager
@@ -55,6 +55,7 @@ import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.launch
import timber.log.Timber
import kotlin.math.max
import kotlin.math.roundToInt
class MainScreenPresentation(
@@ -63,7 +64,7 @@ class MainScreenPresentation(
val userViewModel: UserViewModel,
val recognizeViewModel: RecognizeViewModel,
private val onDismissListener: () -> Unit = {}
) : Presentation(activity, display), ViewTreeObserver.OnGlobalLayoutListener {
) : Presentation(activity, display), ViewTreeObserver.OnGlobalLayoutListener {
companion object {
const val TAG = "SubScreenPresentation"
@@ -71,6 +72,7 @@ class MainScreenPresentation(
private lateinit var binding: PresentationMainScreenBinding
private lateinit var stepChangeCallback: (Int) -> Unit
// 当前步骤
var currentStep: Int = 0
@@ -110,11 +112,19 @@ class MainScreenPresentation(
fun updateWeight(weight: Double) {
lastWeight = weight * 1000 // 将千克转成克
Timber.tag(TAG).d("updateWeight lastWeight = $weight, currentStep = $currentStep, isGoStep1 = $isGoStep1")
if ((currentStep == 2) &&
lastWeight <= 5 &&
isGoStep1
) {//人脸识别过程中,秤上物品拿走
Timber.tag(TAG)
.d("updateWeight lastWeight = $weight, currentStep = $currentStep, isGoStep1 = $isGoStep1")
// if (lastWeight <= 5) {
// activity.isAnalyzing = false
// }
if (currentStep == 2 && lastWeight <= 5 && isGoStep1) {
//人脸识别过程中,秤上物品拿走
isGoStep1 = false
activity.runOnUiThread {
Timber.tag(TAG).e("切回step1")
step1FoodRecognizing()
}
} else if (lastWeight <= 5) {
isGoStep1 = false
activity.runOnUiThread {
Timber.tag(TAG).e("切回step1")
@@ -140,6 +150,7 @@ class MainScreenPresentation(
}
calculateNutrition(recognitionWeight - lastWeight)
}
fun setStepChangeCallback(callback: (Int) -> Unit) {
stepChangeCallback = callback
}
@@ -200,10 +211,10 @@ class MainScreenPresentation(
Timber.tag(TAG).d("registerDataChange nutritionData = $it")
userNutritionData = it
if (it == null) return@collect
step3ShowRecognizeResult()
//binding.nutritionInclude.tvUserName.text = it.userName.maskName()
//binding.nutritionInclude.tvRecommendHeat.text = "推荐热量:${it.recommendMin}-${it.recommendMax}"
//updateWeight(lastWeight / 1000)
// step3ShowRecognizeResult()
// //binding.nutritionInclude.tvUserName.text = it.userName.maskName()
// //binding.nutritionInclude.tvRecommendHeat.text = "推荐热量:${it.recommendMin}-${it.recommendMax}"
// //updateWeight(lastWeight / 1000)
}
}
activity.lifecycleScope.launch {
@@ -227,20 +238,22 @@ class MainScreenPresentation(
ToastUtils.showToast("选中物品为空")
return
}
updateFoodInfo(currentFood!!)
// updateFoodInfo(currentFood!!)
foodRecSuccess(currentFood!!)
//foodRecSuccess(currentFood!!)
}
private fun initView() {
// //binding.ivRecImage.let {
// it.outlineProvider = object : ViewOutlineProvider() {
// override fun getOutline(view: View, outline: Outline) {
// outline.setRoundRect(0, 0, view.width, view.height, 12f.dp)
// }
// }
// it.clipToOutline = true
// }
binding.ivRecImage.let {
it.outlineProvider = object : ViewOutlineProvider() {
override fun getOutline(view: View, outline: Outline) {
outline.setRoundRect(0, 0, view.width, view.height, 12f.dp)
}
}
it.clipToOutline = true
}
}
override fun onDisplayRemoved() {
super.onDisplayRemoved()
onDismissListener()
@@ -274,11 +287,46 @@ class MainScreenPresentation(
//updateFoodInfo(foodInfo)
userNutritionData?.let {
Timber.tag(TAG).d("loadBilledMode${GsonUtils.toJson(it)}")
// userNutritionData?.let {
// Timber.tag(TAG).d("loadBilledMode${GsonUtils.toJson(it)}")
// }
currentFood?.let {
val specPrice = it.specPrice?:0.0
val vipPrice = it.vipPrice?:0.0
binding.tvNormalPrice.text = "${specPrice.format2String(2)} 元/份"
binding.tvVipPrice.text = "${vipPrice.format2String(2)} 元/份"
var calorie = getIntNutritionValue(it.calorie)
calorie = max(calorie, 0)
val fat = getIntNutritionValue(it.fat)
val protein = getIntNutritionValue(it.protein)
val carbohydrate = getIntNutritionValue(it.carbohydrate)
val fatRate = getNutritionRate(fat, calorie)
val proteinRate = getNutritionRate(protein, calorie)
val carbohydrateRate = getNutritionRate(carbohydrate, calorie)
binding.calorieInclude.run {
tvCalorie.text = "$calorie"
tvFatRate.text = "$fatRate%"
tvProteinRate.text = "$proteinRate%"
tvCarbohydrateRate.text = "$carbohydrateRate%"
viewFatLine.updateLayoutParams { height = (fatRate/100.0*84).roundToInt()*1.dp }
viewProteinLine.updateLayoutParams { height = (proteinRate/100.0*84).roundToInt()*1.dp }
viewCarbohydrateLine.updateLayoutParams { height = (carbohydrateRate/100.0*84).roundToInt()*1.dp }
}
}
}
private fun getNutritionRate(value: Int, total: Int): Int{
if (value == 0 || total == 0) return 0
return (100.0 * value / total).roundToInt()
}
private fun getIntNutritionValue(text: String?): Int {
return if (text.isNullOrBlank()) 0 else text.toFloat().roundToInt() }
/**
* 餐品识别成功,不计费模式
*/
@@ -296,7 +344,8 @@ class MainScreenPresentation(
//updateFoodInfo(foodInfo)
userNutritionData?.let {
binding.nutritionInclude.tvUserName.text = it.userName.maskName()
binding.nutritionInclude.tvRecommendHeat.text = "推荐热量:${it.recommendMin}-${it.recommendMax}kcal"
binding.nutritionInclude.tvRecommendHeat.text =
"推荐热量:${it.recommendMin}-${it.recommendMax}kcal"
updateWeight(lastWeight / 1000)
}
}
@@ -307,7 +356,7 @@ class MainScreenPresentation(
fun step1FoodRecognizing() {
hideStandbyScreen()
activity.resetTouchTime()
activity.resumeAnalysis()
Timber.tag(TAG).d("step1")
//if (abs(lastWeight.toInt()) <= 5) {
// activity.runOnUiThread {
@@ -332,8 +381,8 @@ class MainScreenPresentation(
binding.tvFoodName.text = "餐品识别中..."
binding.tvFoodRecPrompt.visible()
binding.flCameraView.visible()
binding.ivRecImage.gone()
binding.ivPreviewImage.visible()
//binding.ivRecImage.gone()
binding.ivFaceRecMask.gone()
binding.tvFaceTip.gone()
binding.llPriceInfo.gone()
@@ -344,17 +393,21 @@ class MainScreenPresentation(
activity.clearFoodList()
}
fun foodRecSuccess(foodInfo: FoodInfo) {
//识别逻辑完成根据是否计费显示不同页面
val mode = SPUtil.getInstance().get(GlobalKey.KEY_CHARGE_MODE, 0)
if (mode == 0) {
//计费
loadBilledMode()
} else {
//不计费
loadUnbilledMode()
}
}
// fun foodRecSuccess(foodInfo: FoodInfo) {
// //识别逻辑完成根据是否计费显示不同页面
// val mode = SPUtil.getInstance().get(GlobalKey.KEY_CHARGE_MODE, 0)
// if (mode == 0) {
// //计费
// loadBilledMode()
// } else {
// //不计费
// //userViewModel.getUserNutritionData(
// // userId = userId,
// // foodId = currentFood!!.foodId!!
// //)
// loadUnbilledMode()
// }
// }
/**
* 用户人脸识别中
@@ -377,7 +430,7 @@ class MainScreenPresentation(
binding.tvFoodRecPrompt.visible()
binding.llPriceInfo.gone()
//binding.ivRecImage.gone()
binding.ivRecImage.gone()
binding.ivPreviewImage.gone()
dinnerTypeInfo = null
@@ -386,7 +439,9 @@ class MainScreenPresentation(
stepChangeCallback(currentStep)
currentFood = foodInfo
userViewModel.getDinnerType()
resumeCamera()
}
@@ -395,18 +450,37 @@ class MainScreenPresentation(
fun updateFood(foodInfo: FoodInfo?) {
Timber.tag(TAG).d("updateFood")
currentFood = foodInfo
if (foodInfo != null) {
if (foodInfo == null) {
return
}
binding.tvFoodName.text = foodInfo.foodName
binding.ivPreviewImage.gone()
binding.ivRecImage.let {
it.visible()
val imageUrl = if (foodInfo.foodImg.isNullOrBlank()) foodInfo.photoUri else foodInfo.foodImg
it.load(imageUrl)
}
val mode = SPUtil.getInstance().get(GlobalKey.KEY_CHARGE_MODE, 0)
if (mode == 0) {
//计费,查询营养信息、价格信息------------------------
loadBilledMode()
} else {
//不计费
//开启人脸识别
if (currentStep == 1) {
step2FaceRecognizing(foodInfo)
} else {
updateFoodInfo(foodInfo)
}
}
//if (currentStep == 1) {
// step2FaceRecognizing(foodInfo)
//} else {
// updateFoodInfo(foodInfo)
//}
}
private fun updateFoodInfo(foodInfo: FoodInfo) {
binding.tvFoodName.text = foodInfo.foodName
binding.ivPreviewImage.gone()
// binding.tvFoodName.text = foodInfo.foodName
// binding.ivPreviewImage.gone()
//binding.ivRecImage.visible()
// if (foodInfo.photoUri != null) {
// GlideUtils.loadRoundCornerImage(
@@ -429,9 +503,11 @@ class MainScreenPresentation(
var weight = weight1
if (weight < 0) weight = 0.0
debouncer.debounce {
Timber.tag(TAG).d("calculateNutrition weight = $weight, recognitionWeight = $recognitionWeight")
Timber.tag(TAG)
.d("calculateNutrition weight = $weight, recognitionWeight = $recognitionWeight")
val dinnerType = dinnerTypeInfo!!.dinnerType!!
Timber.tag(TAG).d("calculateNutrition foodName = ${currentFood!!.foodName}, dinnerType = $dinnerType, userId = ${userNutritionData!!.userId}")
Timber.tag(TAG)
.d("calculateNutrition foodName = ${currentFood!!.foodName}, dinnerType = $dinnerType, userId = ${userNutritionData!!.userId}")
val calcResultInfo = UserNutritionUtils.calculateNutrition(
currentFood!!,
userNutritionData!!,
@@ -528,6 +604,7 @@ class MainScreenPresentation(
}
}
}
fun updateImage(bitmap: Bitmap) {
activity.runOnUiThread {
binding.ivPreviewImage.setImageBitmap(bitmap)
@@ -538,10 +615,11 @@ class MainScreenPresentation(
* 余量取餐
*/
fun updateMealPickupMode(mode: Int) {
Timber.tag(TAG).d("updateMealPickupMode mode = $mode")
mealPickupMode = mode
step1FoodRecognizing()
Timber.tag(TAG).d("updateMealPickupMode mode = $mode")
mealPickupMode = mode
step1FoodRecognizing()
}
override fun onStop() {
if (rgbCameraHelper != null) {
rgbCameraHelper!!.release()
@@ -605,7 +683,8 @@ class MainScreenPresentation(
recognizeViewModel.recognizeConfiguration
.observe(activity, Observer { recognizeConfiguration: RecognizeConfiguration? ->
Timber.tag(TAG).i("recognizeConfiguration: observe = ${recognizeConfiguration.toString()}")
Timber.tag(TAG)
.i("recognizeConfiguration: observe = ${recognizeConfiguration.toString()}")
})
recognizeViewModel.recognizeNotice.observe(activity, Observer { notice: String? ->
Timber.tag(TAG).i("recognizeNotice observe notice = $notice")
@@ -614,26 +693,34 @@ class MainScreenPresentation(
recognizeViewModel.recognizeUserId.observe(
activity,
Observer { compareResult: CompareResult ->
Timber.tag(TAG).i("recognizeUserId observe compareResult = ${compareResult.trackId}, userId = ${compareResult.faceEntity.userName}")
Timber.tag(TAG)
.i("recognizeUserId observe compareResult = ${compareResult.trackId}, userId = ${compareResult.faceEntity.userName}")
recognitionTime = System.currentTimeMillis()
recognitionWeight = lastWeight
lastFaceTrackId = compareResult.trackId
val faceEntity = compareResult.faceEntity
val userId = faceEntity.userName
if (userId == null ) return@Observer
if (userId == null) return@Observer
if (currentFood == null) {
//重新识别
activity.runOnUiThread {
ToastUtils.showToast("currentFood == null")
}
//activity.recognizeFood()
//step1FoodRecognizing()
return@Observer
currentFood = activity.checkedItem
}
userViewModel.getUserNutritionData(
userId = userId,
foodId = currentFood!!.foodId!!
)
if (currentFood == null) {
activity.runOnUiThread {
ToastUtils.showToast("请选择菜品")
return@runOnUiThread
}
}
// TODO: ------------------人脸识别成功
//-------------------------------------------------
// TODO: ------------------人脸识别成功
step3ShowRecognizeResult()
//根据接口数据更新热量数据--------------------------
loadUnbilledMode()
// userViewModel.getUserNutritionData(
// userId = userId,
// foodId = currentFood!!.foodId!!
// )
})
recognizeViewModel.drawRectInfoText.observe(activity, Observer { info ->
@@ -724,7 +811,8 @@ class MainScreenPresentation(
displayOrientation: Int,
isMirror: Boolean
) {
Timber.tag(TAG).d("initRgbCamera Rgb onCameraOpened: cameraId = $cameraId, displayOrientation = $displayOrientation")
Timber.tag(TAG)
.d("initRgbCamera Rgb onCameraOpened: cameraId = $cameraId, displayOrientation = $displayOrientation")
activity.runOnUiThread({
val previewSizeRgb = camera.getParameters().getPreviewSize()
val layoutParams = adjustPreviewViewSize(
@@ -733,8 +821,10 @@ class MainScreenPresentation(
previewSizeRgb, displayOrientation, 0.6F
)
Timber.tag(TAG).d("initRgbCamera previewSizeRgb = ${previewSizeRgb.width}-${previewSizeRgb.height}")
Timber.tag(TAG).d("initRgbCamera layoutParams = ${layoutParams.width}-${layoutParams.height}")
Timber.tag(TAG)
.d("initRgbCamera previewSizeRgb = ${previewSizeRgb.width}-${previewSizeRgb.height}")
Timber.tag(TAG)
.d("initRgbCamera layoutParams = ${layoutParams.width}-${layoutParams.height}")
Timber.tag(TAG).d(
"initRgbCamera isMirror = ${isMirror}, isDrawRgbRectHorizontalMirror = ${
ConfigUtil.isDrawRgbRectHorizontalMirror(
@@ -790,16 +880,19 @@ class MainScreenPresentation(
}
override fun onCameraConfigurationChanged(cameraID: Int, displayOrientation: Int) {
Timber.tag(TAG).i("initRgbCamera onCameraConfigurationChanged: threadName = ${Thread.currentThread().name}")
Timber.tag(TAG)
.i("initRgbCamera onCameraConfigurationChanged: threadName = ${Thread.currentThread().name}")
if (rgbFaceRectTransformer != null) {
rgbFaceRectTransformer!!.cameraDisplayOrientation = displayOrientation
}
Timber.tag(TAG).i("initRgbCamera onCameraConfigurationChanged: $cameraID $displayOrientation")
Timber.tag(TAG)
.i("initRgbCamera onCameraConfigurationChanged: $cameraID $displayOrientation")
}
}
val measuredWidth = binding.dualCameraTexturePreviewRgb.measuredWidth
val measuredHeight = binding.dualCameraTexturePreviewRgb.measuredHeight
Timber.tag(TAG).i("initRgbCamera measuredWidth=$measuredWidthmeasuredHeight=$measuredHeight")
val measuredHeight = binding.dualCameraTexturePreviewRgb.measuredHeight
Timber.tag(TAG)
.i("initRgbCamera measuredWidth=$measuredWidthmeasuredHeight=$measuredHeight")
val previewConfig: PreviewConfig = recognizeViewModel.previewConfig
rgbCameraHelper = DualCameraHelper.Builder()
@@ -831,7 +924,8 @@ class MainScreenPresentation(
displayOrientation: Int,
isMirror: Boolean
) {
Timber.tag(TAG).d("initIrCamera IR onCameraOpened: cameraId = $cameraId, displayOrientation = $displayOrientation")
Timber.tag(TAG)
.d("initIrCamera IR onCameraOpened: cameraId = $cameraId, displayOrientation = $displayOrientation")
val previewSizeIr = camera.getParameters().getPreviewSize()
val layoutParams = adjustPreviewViewSize(
binding.dualCameraTexturePreviewRgb,
@@ -872,7 +966,8 @@ class MainScreenPresentation(
if (irFaceRectTransformer != null) {
irFaceRectTransformer!!.cameraDisplayOrientation = displayOrientation
}
Timber.tag(TAG).i("initIrCamera onCameraConfigurationChanged: cameraID = $cameraID, displayOrientation = $displayOrientation")
Timber.tag(TAG)
.i("initIrCamera onCameraConfigurationChanged: cameraID = $cameraID, displayOrientation = $displayOrientation")
}
}
@@ -916,14 +1011,17 @@ class MainScreenPresentation(
// 识别成功
binding.dualCameraFaceRectView.drawRealtimeFaceInfo(rgbDrawInfoList)
}
if (facePreviewInfoList.isEmpty() || (lastFaceTrackId != facePreviewInfoList[0]!!.trackId)) {
val listIsEmpty = facePreviewInfoList.isEmpty()
val listFirstTrackId = if (listIsEmpty.not()) facePreviewInfoList[0]!!.trackId else null
Timber.tag(TAG).d("listIsEmpty=$listIsEmpty,lastFaceTrackId=$lastFaceTrackId,listFirstTrackId=$listFirstTrackId")
if (listIsEmpty || (lastFaceTrackId != listFirstTrackId)) {
if (lastFaceTrackId != -1) {
mealPickupMode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0) ?: 0
Timber.tag(TAG).i("$lastFaceTrackId 用户离开")
lastFaceTrackId = -1
postUserData()
if (mealPickupMode == 0) {
activity.isAnalyzing = false
step1FoodRecognizing()
} else {
step2FaceRecognizing(currentFood!!)
@@ -935,7 +1033,8 @@ class MainScreenPresentation(
fun postUserData() {
Timber.tag(TAG).d("postUserData")
if (userNutritionData == null || currentFood == null) {
Timber.tag(TAG).d("postUserData userNutritionData = ${userNutritionData == null}, currentFood = ${currentFood == null}")
Timber.tag(TAG)
.d("postUserData userNutritionData = ${userNutritionData == null}, currentFood = ${currentFood == null}")
return
}
var eatWeight = 0.0
@@ -947,7 +1046,7 @@ class MainScreenPresentation(
}
val userNutritionParam = UserNutritionParam(
userId = userNutritionData?.userId!!,
foodId = currentFood?.foodId !!,
foodId = currentFood?.foodId!!,
faceTime = recognitionTime,
faceEndTime = System.currentTimeMillis(),
eatWeight = eatWeight,//lastWeight
@@ -30,6 +30,7 @@ import com.sw.dualscreen.R
import com.sw.dualscreen.activity.PayActivity
import com.sw.dualscreen.databinding.PresentationFacePayBinding
import com.sw.dualscreen.ext.load
import com.sw.dualscreen.utils.GsonUtils
import com.sw.dualscreen.view.CustomDialog
import com.sw.dualscreen.viewmodel.UserViewModel
import com.sw.plate.utils.ToastUtils
@@ -267,13 +268,30 @@ class FacePayPresentation(
activity.runOnUiThread {
//ToastUtils.showToast("用户人脸识别成功,挑战支付页面")
showWaitingDialog("刷脸支付中,请稍后……")
binding.root.postDelayed({
hideWaitingDialog()
activity.paySuccess()
binding.root.postDelayed({
dismiss()
},500)
},1500)
userViewModel.getMemberInfo(memberId = userId) { memberInfo ->
activity.runOnUiThread {
if (memberInfo == null) {
ToastUtils.showToast("查询会员信息失败,请稍后重试")
return@runOnUiThread
}
userViewModel.bindOrder(userId, activity.foodOrderId) { bindResult ->
activity.runOnUiThread {
if (bindResult.not()) {
hideWaitingDialog()
ToastUtils.showToast("订单绑定失败")
return@runOnUiThread
}
binding.root.postDelayed({
hideWaitingDialog()
activity.showPayInfo(memberInfo)
binding.root.postDelayed({
dismiss()
},500)
},1500)
}
}
}
}
}
// userViewModel.getUserNutritionData(
// userId = userId,
@@ -577,6 +595,8 @@ class FacePayPresentation(
// } else {
// step2FaceRecognizing(currentFood!!)
// }
resumeCamera()
}
}
}
@@ -2,24 +2,23 @@ package com.sw.dualscreen.presentation.pay
import android.app.Presentation
import android.os.Bundle
import android.text.Spanned
import android.text.SpannedString
import android.text.style.AbsoluteSizeSpan
import android.text.style.ForegroundColorSpan
import android.view.Display
import androidx.core.graphics.toColorInt
import androidx.core.text.buildSpannedString
import com.sw.dualscreen.R
import com.sw.dualscreen.activity.PayActivity
import com.sw.dualscreen.databinding.PresentationScanQrcodePayBinding
import com.sw.dualscreen.ext.format2String
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.MemberInfo
import com.sw.dualscreen.model.response.TextBean
import com.sw.dualscreen.utils.SpannedUtils
class ScanQrCodePayPresentation(
val activity: PayActivity,
display: Display,
val type:Int,
val type: Int,
private val onDismissListener: () -> Unit = {}
) : Presentation(activity, display) {
@@ -38,74 +37,139 @@ class ScanQrCodePayPresentation(
}
var foodName: String? = null
var payAmount: String? = null
var payQrCodePic: Any? = null
//总价格
var totalPrice = 0.0
//余额
private var balance = 0.0
//实际支付金额
private var realPayPrice = 0.0
//扣除余额
private var expensesBalance = 0.0
private fun initView() {
when(type) {
0->{
when (type) {
0 -> {
binding.layoutVip.gone()
binding.layoutPaySuccess.gone()
binding.layoutScanQrCode.visible()
binding.tvPayTip.text = "请扫码支付或出示付款码"
binding.tvFoodName.text = foodName
binding.tvRealAmount.text = getAmountText(payAmount ?: "")
binding.tvRealAmount.text = SpannedUtils.getAmountText(
listOf(
TextBean(text = "¥", textSize = 32),
TextBean(text = totalPrice.format2String(2), textSize = 48),
)
)
binding.ivPayQrCode.load(payQrCodePic)
}
1->{
1 -> {
binding.layoutVip.visible()
binding.layoutPaySuccess.gone()
binding.layoutScanQrCode.visible()
val memberInfo = activity.memberInfo
totalPrice = activity.foodInfo?.vipPrice ?: 0.0
balance = (memberInfo?.topUpBalance ?: 0.0) + (memberInfo?.rewardBalance ?: 0.0)
realPayPrice = if (balance >= totalPrice) 0.0 else totalPrice - balance
expensesBalance = if (balance >= totalPrice) totalPrice else balance
memberInfo?.let { loadUserInfo(it) }
binding.tvUserBalance.text = SpannedUtils.getAmountText(
listOf(
TextBean(text = "¥", textSize = 20),
TextBean(text = balance.format2String(2), textSize = 32),
)
)
binding.tvFoodName.text = foodName
binding.tvRealAmount.text = getAmountText(payAmount ?: "")
binding.ivPayQrCode.load(payQrCodePic)
binding.tvRealAmount.text = SpannedUtils.getAmountText(
listOf(
TextBean(text = "¥", textSize = 32),
TextBean(text = totalPrice.format2String(2), textSize = 48),
)
)
binding.ivPayQrCode.run {
// load(payQrCodePic)
if (balance >= totalPrice) invisible() else visible()
}
binding.ivHeadPic.load(R.mipmap.ic_launcher)
binding.tvUserName.text = "张三"
binding.tvUserPhone.text = "132****5678"
binding.tvUserBalance.text = getBalanceText("0.00")
binding.tvPayTip.text = getPayTip("-16.80", "22.00")
binding.tvPayTip.text = SpannedUtils.getAmountText(getAmountList())
}
2->{
2 -> {
binding.layoutVip.visible()
binding.layoutPaySuccess.visible()
binding.layoutScanQrCode.gone()
binding.ivHeadPic.load(R.mipmap.ic_launcher)
binding.tvUserName.text = "张三"
binding.tvUserPhone.text = "132****5678"
binding.tvUserBalance.text = getBalanceText("0.00")
val memberInfo = activity.memberInfo
totalPrice = activity.foodInfo?.vipPrice ?: 0.0
balance = (memberInfo?.topUpBalance ?: 0.0) + (memberInfo?.rewardBalance ?: 0.0)
realPayPrice = if (balance >= totalPrice) 0.0 else totalPrice - balance
expensesBalance = if (balance >= totalPrice) totalPrice else balance
binding.tvPayInfo.text = "余额扣除 16.80 元,在线支付 20.00 元"
memberInfo?.let { loadUserInfo(it) }
val remainBalance = if (balance >= totalPrice) balance - totalPrice else 0.0
binding.tvUserBalance.text = SpannedUtils.getAmountText(
listOf(
TextBean(text = "¥", textSize = 24),
TextBean(text = remainBalance.format2String(2), textSize = 36),
)
)
binding.tvPayInfo.text =
"余额扣除 ${expensesBalance.format2String(2)} 元,在线支付 ${totalPrice.format2String(2)}"
}
}
}
private fun getPayTip(startAmount:String, endAmount: String): SpannedString {
return buildSpannedString {
append("余额扣除 ", ForegroundColorSpan("#FF5E7585".toColorInt()), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
append(startAmount, ForegroundColorSpan("#FF0A1428".toColorInt()), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
append(" 元,还需支付 ", ForegroundColorSpan("#FF5E7585".toColorInt()), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
append(endAmount, ForegroundColorSpan("#FF0A1428".toColorInt()), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
append("", ForegroundColorSpan("#FF5E7585".toColorInt()), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
private fun loadUserInfo(item: MemberInfo) {
binding.ivHeadPic.load(R.mipmap.ic_launcher)
binding.tvUserName.text = item.name
val phone = item.phone ?: ""
binding.tvUserPhone.text =
if (phone.length == 11)
phone.replace(phone.substring(3, 7), "****")
else
phone
}
private fun getBalanceText(amount: String): SpannedString {
return buildSpannedString {
append("¥", AbsoluteSizeSpan(20, true), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
append(amount, AbsoluteSizeSpan(32, true), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
}
private fun getAmountList(): List<TextBean> {
val list: MutableList<TextBean> = mutableListOf()
private fun getAmountText(amount: String): SpannedString {
return buildSpannedString {
append("¥", AbsoluteSizeSpan(32, true), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
append(amount, AbsoluteSizeSpan(48, true), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
list.add(TextBean(text = "余额扣除 ", textSize = 30, textColor = "#FF5E7585"))
list.add(
TextBean(
text = "-${expensesBalance.format2String(2)}",
textSize = 30,
textColor = "#FF0A1428"
)
)
if (balance >= totalPrice) {
//余额大于等于总价格,使用余额支付
TextBean(text = " 元,扣除后可用余额 ", textSize = 30, textColor = "#FF5E7585")
val remainingBalance = balance - totalPrice
TextBean(
text = remainingBalance.format2String(2),
textSize = 30,
textColor = "#FF0A1428"
)
} else {
//余额小于总价格,使用余额+扫码支付
//实际支付金额
TextBean(text = " 元,还需支付 ", textSize = 30, textColor = "#FF5E7585")
TextBean(text = realPayPrice.format2String(2), textSize = 30, textColor = "#FF0A1428")
TextBean(text = "", textSize = 30, textColor = "#FF5E7585")
}
return list
}
override fun onDisplayRemoved() {
@@ -0,0 +1,12 @@
package com.sw.dualscreen.presentation.pay
import android.view.TextureView
import androidx.camera.lifecycle.ProcessCameraProvider
import java.util.concurrent.ExecutorService
class TransmitScreen {
}
@@ -1,23 +1,21 @@
package com.sw.dualscreen.repository
import android.content.Context
import android.net.Uri
import com.sw.dualscreen.GlobalData
import com.sw.dualscreen.model.request.UserNutritionParam
import com.sw.dualscreen.model.response.ApiResponse
import com.sw.dualscreen.model.response.DinnerType
import com.sw.dualscreen.model.response.FoodInfo
import com.sw.dualscreen.model.response.FoodSearchReq
import com.sw.dualscreen.model.response.FoodOrder
import com.sw.dualscreen.model.response.MemberInfo
import com.sw.dualscreen.model.response.UserFaceModel
import com.sw.dualscreen.model.response.UserNutritionData
import com.sw.dualscreen.network.api.ApiService
import com.sw.dualscreen.objbox.CollectedFoodInfo
import com.sw.dualscreen.utils.ImageUtil
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.File
/**
@@ -74,18 +72,21 @@ class RemoteRepository constructor(
)
}
}
/**
* 获取人脸数据
*/
suspend fun getCollectedFoodList(
pageNum: Int,
pageSize: Int = 50,
pageSize: Int = 100,
foodName: String,
): ApiResponse<List<CollectedFoodInfo>> {
return safeApiCall {
apiService.getCollectedFoodList(
param = mapOf(
"pageNum" to pageNum,
"pageSize" to pageSize
"pageNum" to "$pageNum",
"pageSize" to "$pageSize",
"foodName" to foodName,
)
)
}
@@ -141,10 +142,7 @@ class RemoteRepository constructor(
* @param restId 从device服务获取的canteenId字段
* @param foodName 菜品名称,多个使用逗号拼接
*/
suspend fun getFoodInfo(
restId: String = GlobalData.restId,
foodName: String,
): ApiResponse<List<FoodInfo>> {
suspend fun getFoodInfo(foodName: String): ApiResponse<List<FoodInfo>> {
return safeApiCall {
// val map = mutableMapOf<String, List<String>>()
// map["nameList"] = foodName.split(",")
@@ -152,10 +150,31 @@ class RemoteRepository constructor(
}
}
suspend fun createOrder(order: FoodOrder): ApiResponse<Any?> {
return safeApiCall {
apiService.createOrder(order = order)
}
}
suspend fun bindOrder(userId: String, orderId: String): ApiResponse<Any?> {
return safeApiCall {
apiService.bindOrder(userId = userId, orderId = orderId)
}
}
suspend fun getMemberInfo(memberId: String): ApiResponse<MemberInfo?> {
return safeApiCall {
apiService.getMemberInfo(memberId = memberId)
}
}
/**
* 上传采集菜品信息
*/
suspend fun uploadCollectFoodPics(fileList: List<File>, params: HashMap<String, RequestBody>): ApiResponse<String> {
suspend fun uploadCollectFoodPics(
fileList: List<File>,
params: HashMap<String, RequestBody>
): ApiResponse<String> {
// 准备文件参数
val fileParts = mutableListOf<MultipartBody.Part>()
fileList.forEachIndexed { index, file ->
@@ -0,0 +1,38 @@
package com.sw.dualscreen.utils
import com.sw.dualscreen.objbox.FoodCollectionBean
import java.util.concurrent.atomic.AtomicInteger
class ImageUploader(
private val totalList: List<FoodCollectionBean>,
private val uploadImage: suspend (List<FoodCollectionBean>)-> Boolean,
private val onProgress: (Int, List<FoodCollectionBean>) -> Unit,
private val onError:(List<FoodCollectionBean>) -> Unit,
private val onComplete:() -> Unit
) {
companion object {
private const val BATCH_SIZE = 5
}
private val uploadedCount = AtomicInteger(0)
suspend fun processUploads() {
val batches = totalList.chunked(BATCH_SIZE)
for (batch in batches) {
val success = uploadImage(batch)
if (!success) {
//println("上传失败,终止流程")
onError(batch)
return
}
val fileCount = batch.count { it.imageFile!=null }
uploadedCount.addAndGet(fileCount)
onProgress(uploadedCount.get(), batch)
//println("已上传 ${uploadedCount.get()}/$totalImages")
}
onComplete()
//println("流程完成,总计上传 ${uploadedCount.get()} 张图片")
}
}
@@ -0,0 +1,32 @@
package com.sw.dualscreen.utils
import android.text.Spanned
import android.text.SpannedString
import android.text.style.AbsoluteSizeSpan
import android.text.style.ForegroundColorSpan
import androidx.core.graphics.toColorInt
import androidx.core.text.buildSpannedString
import com.sw.dualscreen.model.response.TextBean
object SpannedUtils {
public fun getAmountText(list: List<TextBean>): SpannedString {
return buildSpannedString {
list.forEach {
append(
it.text,
AbsoluteSizeSpan(it.textSize, true),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
if (it.textColor.isNullOrBlank().not()) {
append(
it.text,
ForegroundColorSpan(it.textColor!!.toColorInt()),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
}
}
}
}
@@ -128,7 +128,11 @@ class CustomBottomSheetDialog(
private fun searchInfo(){
debouncer.debounce {
viewModel.searchByFoodName(binding.etSearch.text.toString())
viewModel.searchByFoodName(binding.etSearch.text.toString()) {
searchFoodList.clear()
searchFoodList.addAll(it)
adapter.notifyDataSetChanged()
}
}
}
@@ -9,6 +9,9 @@ import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
import com.sw.dualscreen.R;
/**
* 加载进度条弹窗
@@ -88,4 +91,5 @@ public class CustomDialog extends Dialog {
// fullScreenImmersive(getWindow().getDecorView());
// this.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
}
}
@@ -5,11 +5,15 @@ import com.arcsoft.face.ErrorInfo
import com.sw.dualscreen.GlobalData
import com.sw.dualscreen.GlobalKey
import com.sw.dualscreen.model.request.UserNutritionParam
import com.sw.dualscreen.model.response.ApiResponse
import com.sw.dualscreen.model.response.DinnerType
import com.sw.dualscreen.model.response.FoodInfo
import com.sw.dualscreen.model.response.FoodOrder
import com.sw.dualscreen.model.response.MemberInfo
import com.sw.dualscreen.model.response.UserFaceModel
import com.sw.dualscreen.model.response.UserNutritionData
import com.sw.dualscreen.objbox.CollectedFoodInfo
import com.sw.dualscreen.utils.GsonUtils
import com.sw.dualscreen.utils.SPUtil
import com.sw.plate.App
import com.sw.plate.utils.Base64
@@ -115,10 +119,15 @@ class UserViewModel : BaseViewModel() {
}
}
fun getCollectedFoodList(pageNo: Int = 1, pageSize: Int = 100, block:(List<CollectedFoodInfo>)-> Unit) {
fun getCollectedFoodList(
pageNo: Int = 1,
pageSize: Int = 100,
foodName: String,
block: (List<CollectedFoodInfo>) -> Unit
) {
Timber.tag(TAG).d("getCollectedFoodList index = $pageNo")
launch {
val response = repository.getCollectedFoodList(pageNo)
val response = repository.getCollectedFoodList(pageNum = pageNo, pageSize = pageSize, foodName = foodName)
if (parseResponse(response)) {
withContext(Dispatchers.Default) {
val list: List<CollectedFoodInfo> = response.result ?: emptyList()
@@ -254,22 +263,73 @@ class UserViewModel : BaseViewModel() {
}
}
fun uploadCollectFoodPics(files: List<File?>, params: HashMap<String, RequestBody>, callback: (Boolean) -> Unit) {
Timber.tag(TAG).d("uploadCollectFoodPics")
fun createOrder(order: FoodOrder, block:(String)-> Unit) {
Timber.tag(TAG).d("createOrder")
launchWithLoading {
val fileList = files.filterNotNull()
if (fileList.isEmpty()) {
callback(false)
return@launchWithLoading
val response = repository.createOrder(order)
if (parseResponse(response)) {
block(response.data.toString())
} else {
block("")
}
val resp = repository.uploadCollectFoodPics(fileList, params)
if (!parseResponse(resp)) {
callback(false)
return@launchWithLoading
}
callback(resp.code == "00000")
}
}
fun bindOrder(userId: String, orderId: String, block:(Boolean)-> Unit){
Timber.tag(TAG).d("bindOrder")
launchWithLoading {
val response = repository.bindOrder(userId = userId, orderId = orderId)
if (parseResponse(response)) {
block(true)
} else {
block(false)
}
}
}
fun getMemberInfo(memberId: String, block:(MemberInfo?)-> Unit) {
Timber.tag(TAG).d("getMemberInfo")
launchWithLoading {
val response = repository.getMemberInfo(memberId)
if (parseResponse(response)) {
block(response.data)
} else {
block(null)
}
}
}
// fun uploadCollectFoodPics(files: List<File?>, params: HashMap<String, RequestBody>, callback: (Boolean) -> Unit) {
// Timber.tag(TAG).d("uploadCollectFoodPics")
// launchWithLoading {
// val fileList = files.filterNotNull()
// if (fileList.isEmpty()) {
// callback(false)
// return@launchWithLoading
// }
// val resp = repository.uploadCollectFoodPics(fileList, params)
// if (!parseResponse(resp)) {
// callback(false)
// return@launchWithLoading
// }
// callback(resp.code == "00000")
// }
// }
suspend fun uploadCollectFoodPics(
files: List<File?>,
params: HashMap<String, RequestBody>
): Boolean {
Timber.tag(TAG).d("uploadCollectFoodPics")
val fileList = files.filterNotNull()
if (fileList.isEmpty()) {
return true
}
val resp = repository.uploadCollectFoodPics(fileList, params)
// if (!parseResponse(resp)) {
// return false
// }
return resp.code == "00000"
}
}