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

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
+3
View File
@@ -108,6 +108,9 @@ dependencies {
implementation(libs.pytorch.android) implementation(libs.pytorch.android)
implementation(libs.pytorch.android.torchvision) implementation(libs.pytorch.android.torchvision)
implementation("io.github.scwang90:refresh-layout-kernel:3.0.0-alpha")
implementation("io.github.scwang90:refresh-header-classics:3.0.0-alpha")
val objectboxVersion = "5.0.1" val objectboxVersion = "5.0.1"
debugImplementation("io.objectbox:objectbox-android-objectbrowser:$objectboxVersion") debugImplementation("io.objectbox:objectbox-android-objectbrowser:$objectboxVersion")
// releaseImplementation("io.objectbox:objectbox-android:$objectboxVersion") // releaseImplementation("io.objectbox:objectbox-android:$objectboxVersion")
@@ -2,13 +2,12 @@ package com.sw.dualscreen.activity
import android.app.Dialog import android.app.Dialog
import android.content.Context import android.content.Context
import android.content.res.Configuration
import android.content.res.Resources
import android.hardware.display.DisplayManager import android.hardware.display.DisplayManager
import android.os.Bundle import android.os.Bundle
import android.text.TextUtils import android.text.TextUtils
import android.view.Display import android.view.Display
import android.view.View import android.view.View
import android.view.ViewGroup
import android.view.WindowManager import android.view.WindowManager
import android.widget.TextView import android.widget.TextView
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
@@ -93,6 +92,19 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
mDialogWaiting = null 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 private var launchPermissionCallback: IActivityResult.RequestPermissionCallback? = null
fun requestSinglePermissionResult( fun requestSinglePermissionResult(
@@ -10,7 +10,9 @@ import com.sw.dualscreen.databinding.ActivityCollectedFoodBinding
import com.sw.dualscreen.databinding.LayoutEmptySearchBinding import com.sw.dualscreen.databinding.LayoutEmptySearchBinding
import com.sw.dualscreen.dialog.WarnDialog import com.sw.dualscreen.dialog.WarnDialog
import com.sw.dualscreen.ext.addOnActionSearchListener import com.sw.dualscreen.ext.addOnActionSearchListener
import com.sw.dualscreen.ext.gone
import com.sw.dualscreen.ext.hideKeyboard import com.sw.dualscreen.ext.hideKeyboard
import com.sw.dualscreen.ext.visible
import com.sw.dualscreen.objbox.CollectedFoodInfo import com.sw.dualscreen.objbox.CollectedFoodInfo
import com.sw.dualscreen.objbox.Food import com.sw.dualscreen.objbox.Food
import com.sw.dualscreen.objbox.ObjectBox import com.sw.dualscreen.objbox.ObjectBox
@@ -21,6 +23,11 @@ import io.objectbox.Box
import io.objectbox.kotlin.boxFor import io.objectbox.kotlin.boxFor
class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() { class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
companion object {
private const val PAGE_SIZE = 100
}
private val viewModel by viewModels<UserViewModel>() private val viewModel by viewModels<UserViewModel>()
override fun getViewModel(): BaseViewModel { override fun getViewModel(): BaseViewModel {
return viewModel return viewModel
@@ -33,7 +40,7 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
private val list: MutableList<CollectedFoodInfo> = mutableListOf() private val list: MutableList<CollectedFoodInfo> = mutableListOf()
private val adapter by lazy { private val adapter by lazy {
CollectedFoodNewAdapter(list).apply { CollectedFoodNewAdapter(list).apply {
isStateViewEnable = true // isStateViewEnable = true
addOnItemChildClickListener(R.id.ivDeleteFood) { _, _, position -> addOnItemChildClickListener(R.id.ivDeleteFood) { _, _, position ->
deleteGoods(position) deleteGoods(position)
} }
@@ -42,6 +49,7 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
override fun initialize() { override fun initialize() {
super.initialize() super.initialize()
//binding.root.setOnClickListener { it.hideKeyboard() }
binding.rvFoodList.let { binding.rvFoodList.let {
it.layoutManager = LinearLayoutManager(this) it.layoutManager = LinearLayoutManager(this)
it.adapter = adapter it.adapter = adapter
@@ -65,14 +73,30 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
ToastUtils.showToast("请输入物品名称") ToastUtils.showToast("请输入物品名称")
return@addOnActionSearchListener return@addOnActionSearchListener
} }
pageNo = 1
getCollectGoods(searchName) getCollectGoods(searchName)
} }
v.addTextChangedListener { v.addTextChangedListener {
if (it.isNullOrBlank()) { if (it.isNullOrBlank()) {
pageNo = 1
getCollectGoods() 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() getCollectGoods()
} }
@@ -81,54 +105,81 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
private fun getCollectGoods(searchName: String? = null) { private fun getCollectGoods(searchName: String? = null) {
if (box == null) { // if (box == null) {
box = ObjectBox.boxStore.boxFor(Food::class) // box = ObjectBox.boxStore.boxFor(Food::class)
} // }
list.clear() // list.clear()
val totalList = box?.all // val totalList = box?.all
?.filter { it.name!=null } // ?.filter { it.name!=null }
?.groupBy { it.name!! } // ?.groupBy { it.name!! }
?.map { CollectedFoodInfo(foodName = it.key, foodCount = it.value.size) } // ?.map { CollectedFoodInfo(foodName = it.key, foodCount = it.value.size) }
//var queryMap:Map<String, List<Food>> ?= null // //var queryMap:Map<String, List<Food>> ?= null
if (searchName.isNullOrBlank().not()) { // if (searchName.isNullOrBlank().not()) {
//queryMap = totalMap?.filter { it.key.contains(searchName) } // //queryMap = totalMap?.filter { it.key.contains(searchName) }
val temp = totalList?.filter { it.foodName?.contains(searchName) == true} // val temp = totalList?.filter { it.foodName?.contains(searchName) == true}
if (temp.isNullOrEmpty().not()) { // if (temp.isNullOrEmpty().not()) {
list.addAll(temp) // list.addAll(temp)
} // }
} else { // } else {
if (totalList.isNullOrEmpty().not()) { // if (totalList.isNullOrEmpty().not()) {
list.addAll(totalList) // list.addAll(totalList)
} // }
} // }
adapter.notifyDataSetChanged() // adapter.notifyDataSetChanged()
if (list.isEmpty()) { // if (list.isEmpty()) {
loadEmptyView() // loadEmptyView()
} // }
binding.root.hideKeyboard()
viewModel.getCollectedFoodList(pageNo = pageNo) { viewModel.getCollectedFoodList(
if (it.isNotEmpty()) { pageNo = pageNo,
list.addAll(it) pageSize = PAGE_SIZE,
foodName = searchName ?: ""
) { items ->
runOnUiThread { runOnUiThread {
loadFoodList(items)
}
}
}
@SuppressLint("NotifyDataSetChanged")
private fun loadFoodList(items: List<CollectedFoodInfo>) {
finishRefresh()
if (pageNo == 1 && items.isEmpty()) {
list.clear()
adapter.notifyDataSetChanged() 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 pageNo = 1
private var emptyBinding: LayoutEmptySearchBinding? = null // private var emptyBinding: LayoutEmptySearchBinding? = null
private fun loadEmptyView() { private fun loadEmptyView() {
if (emptyBinding == null) { binding.refreshLayout.gone()
emptyBinding = binding.emptyInclude.root.visible()
LayoutEmptySearchBinding.inflate(layoutInflater, binding.rvFoodList, false) //if (emptyBinding == null) {
} // emptyBinding =
emptyBinding?.root?.let { layout -> // LayoutEmptySearchBinding.inflate(layoutInflater, binding.rvFoodList, false)
layout.setOnClickListener { layout.hideKeyboard() } //}
adapter.stateView = layout //emptyBinding?.root?.let { layout ->
} // layout.setOnClickListener { layout.hideKeyboard() }
// adapter.stateView = layout
//}
} }
private fun deleteGoods(position: Int) { private fun deleteGoods(position: Int) {
@@ -154,4 +205,16 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
}.start() }.start()
}).show() }).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.text.TextUtils
import android.view.View import android.view.View
import android.view.ViewOutlineProvider import android.view.ViewOutlineProvider
import androidx.activity.OnBackPressedCallback
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.camera.core.CameraSelector import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis import androidx.camera.core.ImageAnalysis
@@ -21,20 +22,22 @@ import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import com.sw.dualscreen.GlobalData
import com.sw.dualscreen.GlobalKey import com.sw.dualscreen.GlobalKey
import com.sw.dualscreen.adapter.SearchFoodAdapter import com.sw.dualscreen.adapter.SearchFoodAdapter
import com.sw.dualscreen.databinding.ActivityMainBinding import com.sw.dualscreen.databinding.ActivityMainBinding
import com.sw.dualscreen.ext.dp import com.sw.dualscreen.ext.dp
import com.sw.dualscreen.ext.gone import com.sw.dualscreen.ext.gone
import com.sw.dualscreen.ext.load
import com.sw.dualscreen.ext.visible import com.sw.dualscreen.ext.visible
import com.sw.dualscreen.model.response.FoodInfo 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
import com.sw.dualscreen.objbox.FoodModule.IdNameScore import com.sw.dualscreen.objbox.FoodModule.IdNameScore
import com.sw.dualscreen.presentation.MainScreenPresentation import com.sw.dualscreen.presentation.MainScreenPresentation
import com.sw.dualscreen.sdk.SensorScaleUtils import com.sw.dualscreen.sdk.SensorScaleUtils
import com.sw.dualscreen.utils.BitmapSaver import com.sw.dualscreen.utils.BitmapSaver
import com.sw.dualscreen.utils.Debouncer import com.sw.dualscreen.utils.Debouncer
import com.sw.dualscreen.utils.GlideUtils
import com.sw.dualscreen.utils.GsonUtils import com.sw.dualscreen.utils.GsonUtils
import com.sw.dualscreen.utils.ImageUtil import com.sw.dualscreen.utils.ImageUtil
import com.sw.dualscreen.utils.SPUtil import com.sw.dualscreen.utils.SPUtil
@@ -65,7 +68,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
private val viewModel by viewModels<UserViewModel>() private val viewModel by viewModels<UserViewModel>()
private var imageCapture: ImageCapture? = null private var imageCapture: ImageCapture? = null
private var isAnalyzing = true // 控制是否进行图像分析 var isAnalyzing = true // 控制是否进行图像分析
// private var presentation: SecondaryScreenPresentation? = null // private var presentation: SecondaryScreenPresentation? = null
private var presentation: MainScreenPresentation? = null private var presentation: MainScreenPresentation? = null
private val executor = Executors.newSingleThreadExecutor() private val executor = Executors.newSingleThreadExecutor()
@@ -86,7 +90,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
} }
} }
} }
private var checkedItem: FoodInfo? = null var checkedItem: FoodInfo? = null
private var imageAnalysis: ImageAnalysis? = null private var imageAnalysis: ImageAnalysis? = null
private var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>? = null private var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>? = null
private var canIdentify: Boolean = false private var canIdentify: Boolean = false
@@ -106,6 +110,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
override fun initialize() { override fun initialize() {
super.initialize() super.initialize()
addBackEventListener()
FoodModule.init(this) FoodModule.init(this)
initView() initView()
setupSecondaryDisplay() setupSecondaryDisplay()
@@ -204,45 +209,44 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
// binding.tvRescan.setOnClickListener { // binding.tvRescan.setOnClickListener {
// debouncer.debounce { recognizeFood() } // debouncer.debounce { recognizeFood() }
// } // }
binding.btnPay.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) { if (checkedItem == null) {
ToastUtils.showToast("暂无识别数据,请搜索选择") ToastUtils.showToast("暂无识别数据,请搜索选择")
return@setOnClickListener return@setOnClickListener
} }
presentation?.dismiss() createOrder(checkedItem!!)
startActivity(Intent(this, PayActivity::class.java).apply { }
putExtra(PayActivity.FOOD_INFO, checkedItem)
})
} }
} }
fun updateCurrentFood(foodInfo: FoodInfo?) { fun updateCurrentFood(foodInfo: FoodInfo?) {
if (foodInfo != null) { 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 {
binding.tvFoodName.text = "-" binding.tvFoodName.text = "-"
binding.previewView.visibility = View.VISIBLE binding.previewView.visibility = View.VISIBLE
binding.ivImg.visibility = View.GONE binding.ivImg.visibility = View.GONE
resumeAnalysis() resumeAnalysis()
viewModel.cleanIdentifiedFoodInfoList() 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() { override fun registerDataChange() {
@@ -343,21 +347,13 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
private fun updateFoodInfo(list: MutableList<FoodInfo>, scoreList: List<IdNameScore>) { private fun updateFoodInfo(list: MutableList<FoodInfo>, scoreList: List<IdNameScore>) {
Timber.d("registerDataChange识别后查询接口数据:${GsonUtils.toJson(list)},识别数据:${GsonUtils.toJson(scoreList)}") Timber.d(
// TODO: 临时测试数据,后续删除------------------- "registerDataChange识别后查询接口数据:${GsonUtils.toJson(list)},识别数据:${
//if (list.isEmpty()) { GsonUtils.toJson(
// //binding.flPay.visible() scoreList
// //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 -> list.forEach { foodInfo ->
val scoreItem = scoreList.firstOrNull { it.name == foodInfo.foodName } val scoreItem = scoreList.firstOrNull { it.name == foodInfo.foodName }
val score = scoreItem?.score ?: 0.0 val score = scoreItem?.score ?: 0.0
@@ -476,7 +472,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
fun resumeAnalysis() { fun resumeAnalysis() {
Timber.d("resumeAnalysis isAnalyzing = $isAnalyzing") Timber.d("resumeAnalysis isAnalyzing = $isAnalyzing")
if (isAnalyzing) return if (isAnalyzing) return
isAnalyzing = true // isAnalyzing = true
imageAnalysis?.setAnalyzer(executor) { imageProxy -> imageAnalysis?.setAnalyzer(executor) { imageProxy ->
if (presentation?.currentStep == 1) { if (presentation?.currentStep == 1) {
val bitmap = imageProxy.toBitmap() val bitmap = imageProxy.toBitmap()
@@ -629,6 +625,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
val mode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0) val mode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0)
presentation?.updateMealPickupMode(mode ?: 0) presentation?.updateMealPickupMode(mode ?: 0)
} }
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
// 取消定时器 // 取消定时器
@@ -658,4 +655,61 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
handler.postDelayed(timeoutRunnable, TIME_OUT) handler.postDelayed(timeoutRunnable, TIME_OUT)
lastTouchTime = System.currentTimeMillis() 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 package com.sw.dualscreen.activity
import android.view.View
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.sw.dualscreen.R 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.activity.fragment.pay.ScanQrCodePayFragment
import com.sw.dualscreen.databinding.ActivityPayBinding import com.sw.dualscreen.databinding.ActivityPayBinding
import com.sw.dualscreen.model.response.FoodInfo 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.presentation.pay.ScanQrCodePayPresentation
import com.sw.dualscreen.viewmodel.BaseViewModel import com.sw.dualscreen.viewmodel.BaseViewModel
import com.sw.dualscreen.viewmodel.UserViewModel import com.sw.dualscreen.viewmodel.UserViewModel
@@ -22,6 +21,10 @@ class PayActivity : BaseActivity<ActivityPayBinding>() {
companion object { companion object {
const val FOOD_INFO = "foodInfo" 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_QR_CODE = "tagPayQrCode"
const val TAG_PAY_CASH = "tagPayCash" const val TAG_PAY_CASH = "tagPayCash"
const val TAG_PAY_NUMBER = "tagPayNumber" const val TAG_PAY_NUMBER = "tagPayNumber"
@@ -42,16 +45,22 @@ class PayActivity : BaseActivity<ActivityPayBinding>() {
var foodInfo: FoodInfo? = null var foodInfo: FoodInfo? = null
// var totalAmount:Double = 0.0
private var qrCodePayFragment: ScanQrCodePayFragment? = null private var qrCodePayFragment: ScanQrCodePayFragment? = null
private var cashPayFragment: CashPayFragment? = null private var cashPayFragment: CashPayFragment? = null
private var numberPayFragment: NumberPayFragment? = null private var numberPayFragment: NumberPayFragment? = null
var facePayFragment: FacePayFragment? = null var facePayFragment: FacePayFragment? = null
private var payResultFragment: PayResultFragment? = null private var payResultFragment: PayResultFragment? = null
var foodOrderId:String = ""
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
override fun initialize() { override fun initialize() {
super.initialize() super.initialize()
foodInfo = intent.getParcelableExtra(FOOD_INFO) 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.tvFoodName.text = foodInfo?.foodName
binding.include.ivPageBack.setOnClickListener { finish() } binding.include.ivPageBack.setOnClickListener { finish() }
binding.include.tvPageTitle.text = "下单结算" binding.include.tvPageTitle.text = "下单结算"
@@ -140,10 +149,10 @@ class PayActivity : BaseActivity<ActivityPayBinding>() {
// } // }
// } // }
fun showPayResult() { // fun showPayResult() {
payResultFragment = PayResultFragment() // payResultFragment = PayResultFragment.instance(1, memberInfo)
showFragment(payResultFragment!!, TAG_PAY_RESULT) // showFragment(payResultFragment!!, TAG_PAY_RESULT)
} // }
override fun onDestroy() { override fun onDestroy() {
qrCodePayFragment?.presentation?.dismiss() qrCodePayFragment?.presentation?.dismiss()
@@ -154,8 +163,29 @@ class PayActivity : BaseActivity<ActivityPayBinding>() {
} }
private var scanQrCodePayPresentation: ScanQrCodePayPresentation? = null private var scanQrCodePayPresentation: ScanQrCodePayPresentation? = null
fun paySuccess() { var memberInfo: MemberInfo? = null
showFragment(PayResultFragment(), "") 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) { if (displays.size > 1) {
scanQrCodePayPresentation = ScanQrCodePayPresentation( scanQrCodePayPresentation = ScanQrCodePayPresentation(
activity = this, activity = this,
@@ -164,8 +194,8 @@ class PayActivity : BaseActivity<ActivityPayBinding>() {
) { ) {
scanQrCodePayPresentation?.dismiss() scanQrCodePayPresentation?.dismiss()
}.also { }.also {
// it.foodName = foodName it.foodName = foodInfo?.foodName
// it.payAmount = payAmount it.totalPrice = foodInfo?.vipPrice?:0.0
} }
scanQrCodePayPresentation?.show() scanQrCodePayPresentation?.show()
} }
@@ -17,18 +17,18 @@ class BusinessFragment: BaseFragment<FragmentBusinessBinding>() {
override fun initialize() { override fun initialize() {
binding.rgCharge.let { binding.rgCharge.let {
it.setOnCheckedChangeListener { group, checkedId -> // it.setOnCheckedChangeListener { group, checkedId ->
val mode = if (checkedId == R.id.rbChargeYes) 0 else 1 // val mode = if (checkedId == R.id.rbChargeYes) 0 else 1
SPUtil.getInstance().put(GlobalKey.KEY_CHARGE_MODE, mode) // SPUtil.getInstance().put(GlobalKey.KEY_CHARGE_MODE, mode)
} // }
val mode = SPUtil.getInstance().get(GlobalKey.KEY_CHARGE_MODE, 0) val mode = SPUtil.getInstance().get(GlobalKey.KEY_CHARGE_MODE, 0)
it.check(if (mode == 0) R.id.rbChargeYes else R.id.rbChargeNot) it.check(if (mode == 0) R.id.rbChargeYes else R.id.rbChargeNot)
} }
binding.rgTakeFood.let { binding.rgTakeFood.let {
it.setOnCheckedChangeListener { group, checkedId -> // it.setOnCheckedChangeListener { group, checkedId ->
val mode = if (checkedId == R.id.rbPickAndPlace) 0 else 1 // val mode = if (checkedId == R.id.rbPickAndPlace) 0 else 1
SPUtil.getInstance().put(GlobalKey.KEY_PICKUP_MODE, mode) // SPUtil.getInstance().put(GlobalKey.KEY_PICKUP_MODE, mode)
} // }
val mode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0) val mode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0)
it.check(if (mode == 0) R.id.rbPickAndPlace else R.id.rbSurplusCalculate) 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 mode = if (binding.rgTakeFood.checkedRadioButtonId == R.id.rbPickAndPlace) 0 else 1
SPUtil.getInstance().put(GlobalKey.KEY_PICKUP_MODE, mode) SPUtil.getInstance().put(GlobalKey.KEY_PICKUP_MODE, mode)
ToastUtils.showToast("设置已保存")
} }
SensorScaleUtils.addWeightListener { value -> SensorScaleUtils.addWeightListener { value ->
val realWeight = (value * 1000).roundToInt() val realWeight = (value * 1000).roundToInt()
@@ -12,6 +12,7 @@ import android.widget.Toast
import androidx.camera.view.PreviewView import androidx.camera.view.PreviewView
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import com.sw.dualscreen.R import com.sw.dualscreen.R
import com.sw.dualscreen.activity.CollectedFoodActivity 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.BitmapSaver
import com.sw.dualscreen.utils.CameraUtils import com.sw.dualscreen.utils.CameraUtils
import com.sw.dualscreen.utils.Debouncer import com.sw.dualscreen.utils.Debouncer
import com.sw.dualscreen.utils.ImageUploader
import com.sw.dualscreen.utils.ImageUtil import com.sw.dualscreen.utils.ImageUtil
import com.sw.plate.utils.ToastUtils import com.sw.plate.utils.ToastUtils
import io.objectbox.Box import io.objectbox.Box
import io.objectbox.kotlin.boxFor import io.objectbox.kotlin.boxFor
import kotlinx.coroutines.launch
import okhttp3.RequestBody import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import timber.log.Timber import timber.log.Timber
@@ -44,7 +47,7 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
companion object { companion object {
private const val TAG = "CollectFragment" private const val TAG = "CollectFragment"
const val MAX_COUNT = 5 const val MAX_COUNT = 100
} }
private var selectedFoodId: String? = "" private var selectedFoodId: String? = ""
private var selectedFoodName: String? = "" private var selectedFoodName: String? = ""
@@ -181,10 +184,8 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
Toast.makeText(requireActivity(), "请拍摄菜品照片", Toast.LENGTH_SHORT).show() Toast.makeText(requireActivity(), "请拍摄菜品照片", Toast.LENGTH_SHORT).show()
return@setOnClickListener return@setOnClickListener
} }
vectorThread(){
upload() upload()
} }
}
binding.editFoodName.setOnEditorActionListener { v, actionId, event -> binding.editFoodName.setOnEditorActionListener { v, actionId, event ->
if (actionId == EditorInfo.IME_ACTION_SEARCH) { if (actionId == EditorInfo.IME_ACTION_SEARCH) {
@@ -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() { private fun upload() {
lifecycleScope.launch {
val totalFileCount = foodCollectionList.count { it.imageFile != null }
settingActivity?.showWaitingDialog("图片上传中0/$totalFileCount")
val params = HashMap<String, RequestBody>() val params = HashMap<String, RequestBody>()
//params["placeId"] = restId.toRequestBody() //params["placeId"] = restId.toRequestBody()
params["foodId"] = checkedItem!!.foodId.toRequestBody() params["foodId"] = checkedItem!!.foodId.toRequestBody()
params["foodName"] = checkedItem!!.foodName!!.toRequestBody() params["foodName"] = checkedItem!!.foodName!!.toRequestBody()
//params["foodVector"] = foodVector.toRequestBody() //params["foodVector"] = foodVector.toRequestBody()
for (index in foodCollectionList.indices step 5) { ImageUploader(
val end = if(index + 5 < foodCollectionList.size - 1) index + 5 else foodCollectionList.size - 1 totalList = foodCollectionList,
val subList = foodCollectionList.subList(index, end) uploadImage = { batch->
val subFiles = subList.map { it.imageFile } val files = batch.map { it.imageFile }
uploadCollectFoodPics(subFiles, params) { isSuccess-> (requireActivity() as SettingActivity).viewModel.uploadCollectFoodPics(files,params)
Timber.tag(TAG).d("uploadMultipleImages: ${isSuccess}") },
subList.filter { it.imageFile!=null }.forEach { it.uploadSuccess = isSuccess } onProgress = { count, batch ->
activity?.runOnUiThread {
settingActivity?.showWaitingDialog2("图片上传中$count/$totalFileCount")
batch.forEach {
it.uploadSuccess = true
}
}
},
onError = {
activity?.runOnUiThread {
ToastUtils.showToast("上传失败,请稍后重试")
}
},
onComplete = {
vectorThread()
}
).processUploads()
}
// val count = collectList.count { it.imageFile!=null && it.uploadSuccess.not() }
// runOnUiThread { // for (index in foodCollectionList.indices step 5) {
// binding.btnUploadImage.text = "待上传图片${count}张" // val end = if(index + 5 < foodCollectionList.size - 1) index + 5 else foodCollectionList.size - 1
// if (count == 0) { // val subList = foodCollectionList.subList(index, end)
// Loading.dismiss() // 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("加载中……") settingActivity?.showWaitingDialog("加载中……")
Thread { Thread {
foodCollectionList foodCollectionList
// .filter { it.bitmap != null } // .filter { it.bitmap != null }
.filter { it.imageVector != null } // .filter { it.imageVector != null }
.forEachIndexed { index, it -> .forEachIndexed { index, it ->
if (it.imageVector == null) {
return@forEachIndexed
}
image2VectorTask(imageVector = it.imageVector!!, index) image2VectorTask(imageVector = it.imageVector!!, index)
} }
activity?.runOnUiThread { activity?.runOnUiThread {
@@ -18,9 +18,9 @@ class FacePayFragment : BaseFragment<FragmentFacePayBinding>() {
override fun initialize() { override fun initialize() {
payActivity = activity as PayActivity payActivity = activity as PayActivity
binding.tvFaceState.setOnClickListener { // binding.tvFaceState.setOnClickListener {
payActivity?.showPayResult() // payActivity?.showPayResult()
} // }
foodName = payActivity?.foodInfo?.foodName foodName = payActivity?.foodInfo?.foodName
payAmount = "36.80" payAmount = "36.80"
showSubScreen() showSubScreen()
@@ -1,17 +1,48 @@
package com.sw.dualscreen.activity.fragment.pay package com.sw.dualscreen.activity.fragment.pay
import android.os.Bundle
import android.text.Spanned import android.text.Spanned
import android.text.SpannedString import android.text.SpannedString
import android.text.style.AbsoluteSizeSpan import android.text.style.AbsoluteSizeSpan
import android.text.style.ForegroundColorSpan
import androidx.core.graphics.toColorInt
import androidx.core.text.buildSpannedString import androidx.core.text.buildSpannedString
import com.sw.dualscreen.R import com.sw.dualscreen.R
import com.sw.dualscreen.activity.PayActivity import com.sw.dualscreen.activity.PayActivity
import com.sw.dualscreen.activity.fragment.BaseFragment import com.sw.dualscreen.activity.fragment.BaseFragment
import com.sw.dualscreen.databinding.FragmentPayResultBinding 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.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 { override fun inflateViewBinding(): FragmentPayResultBinding {
return FragmentPayResultBinding.inflate(layoutInflater) return FragmentPayResultBinding.inflate(layoutInflater)
} }
@@ -22,19 +53,121 @@ class PayResultFragment: BaseFragment<FragmentPayResultBinding>() {
binding.btnBack.setOnClickListener { binding.btnBack.setOnClickListener {
//payActivity?.showFacePay() //payActivity?.showFacePay()
} }
arguments?.let {
pageType = it.getInt(PAGE_TYPE, 0)
memberInfo = it.getParcelable(MEMBER_INFO)
}
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) }
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.ivHeadPic.load(R.mipmap.ic_launcher)
binding.tvUserName.text = "张三" binding.tvUserName.text = item.name
binding.tvUserPhone.text = "132****5678"
binding.tvUserBalance.text = getBalanceText("0.00") val phone = item.phone ?: ""
binding.tvPayAmount.text = "收款金额 20.00 元" binding.tvUserPhone.text =
binding.tvPayInfo.text = "应收 38.00 元,余额扣除 16.80 元" 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)
}
}
} }
@@ -13,6 +13,7 @@ import com.sw.dualscreen.presentation.pay.ScanQrCodePayPresentation
import com.sw.dualscreen.utils.countDownByFlow import com.sw.dualscreen.utils.countDownByFlow
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import com.sw.dualscreen.databinding.FragmentScanQrcodePayBinding import com.sw.dualscreen.databinding.FragmentScanQrcodePayBinding
import com.sw.dualscreen.ext.format2String
class ScanQrCodePayFragment: BaseFragment<FragmentScanQrcodePayBinding>() { class ScanQrCodePayFragment: BaseFragment<FragmentScanQrcodePayBinding>() {
@@ -35,7 +36,7 @@ class ScanQrCodePayFragment: BaseFragment<FragmentScanQrcodePayBinding>() {
payActivity = requireActivity() as PayActivity payActivity = requireActivity() as PayActivity
foodName = payActivity.foodInfo?.foodName foodName = payActivity.foodInfo?.foodName
payAmount = "36.80" payAmount = payActivity.foodInfo?.specPrice.format2String(2)
payQrCodePic = R.drawable.ic_qrcode payQrCodePic = R.drawable.ic_qrcode
binding.tvRealAmount.text = getAmountText(payAmount?:"") binding.tvRealAmount.text = getAmountText(payAmount?:"")
binding.ivPayQrCode.load(payQrCodePic) binding.ivPayQrCode.load(payQrCodePic)
@@ -75,7 +76,7 @@ class ScanQrCodePayFragment: BaseFragment<FragmentScanQrcodePayBinding>() {
presentation?.dismiss() presentation?.dismiss()
}.also { }.also {
it.foodName = foodName it.foodName = foodName
it.payAmount = payAmount it.totalPrice = payActivity.foodInfo?.specPrice?:0.0
it.payQrCodePic = payQrCodePic it.payQrCodePic = payQrCodePic
} }
presentation?.show() presentation?.show()
@@ -10,6 +10,7 @@ import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.sw.dualscreen.R import com.sw.dualscreen.R
import com.sw.dualscreen.databinding.ListItemFoodCollectionBinding import com.sw.dualscreen.databinding.ListItemFoodCollectionBinding
import com.sw.dualscreen.ext.dp import com.sw.dualscreen.ext.dp
import com.sw.dualscreen.ext.load
import com.sw.dualscreen.objbox.FoodCollectionBean import com.sw.dualscreen.objbox.FoodCollectionBean
import com.sw.dualscreen.utils.GlideUtils import com.sw.dualscreen.utils.GlideUtils
@@ -38,14 +39,15 @@ class FoodCollectionAdapter (var list: MutableList<FoodCollectionBean>) :
scaleType = ImageView.ScaleType.CENTER_CROP scaleType = ImageView.ScaleType.CENTER_CROP
//setImageURI(it.imageUri) //setImageURI(it.imageUri)
//setImageBitmap(it.bitmap) //setImageBitmap(it.bitmap)
it.bitmap?.let { bitmap -> load(it.imageFile)
GlideUtils.loadRoundCornerWitBitmap( // it.bitmap?.let { bitmap ->
context, // GlideUtils.loadRoundCornerWitBitmap(
url = bitmap, // context,
imageView = this, // url = bitmap,
radius = 8.dp // imageView = this,
) // radius = 8.dp
} // )
// }
} }
} }
} }
@@ -65,8 +65,8 @@ val Float.sp: Float
Resources.getSystem().displayMetrics Resources.getSystem().displayMetrics
) )
fun Double.format2String(): String = "%.1f".format(this) fun Double?.format2String(): String = this.format2String(1)
fun Double.format2String(num:Int): String = "%.${num}f".format(this) fun Double?.format2String(num:Int): String = "%.${num}f".format(this?:0.0)
// 添加扩展函数 // 添加扩展函数
fun ImageProxy.toSafeBitmap(): Bitmap { fun ImageProxy.toSafeBitmap(): Bitmap {
@@ -1,5 +1,55 @@
package com.sw.dualscreen.model.response package com.sw.dualscreen.model.response
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
data class FoodSearchReq( 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 carbohydrate:String? = null,
//规格售卖价格(元) //规格售卖价格(元)
val specPrice:String? = null, val specPrice: Double? = null,
//VIP售卖价(元) //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 score: Int = 0,
var isChecked: Boolean = false, var isChecked: Boolean = false,
var photoUri: Uri? = null, var photoUri: Uri? = null,
var imgUrl: String? = "", var foodImg: String? = "",
// @SerializedName("foodTypeAndRealIntakeVoList") // @SerializedName("foodTypeAndRealIntakeVoList")
// val foodTypeAndRealIntakeVoList: List<FoodTypeAndRealIntakeVo>? = listOf(), // 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.DinnerType
import com.sw.dualscreen.model.response.FoodInfo import com.sw.dualscreen.model.response.FoodInfo
import com.sw.dualscreen.model.response.FoodSearchReq 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.UserFaceModel
import com.sw.dualscreen.model.response.UserNutritionData import com.sw.dualscreen.model.response.UserNutritionData
import com.sw.dualscreen.objbox.CollectedFoodInfo import com.sw.dualscreen.objbox.CollectedFoodInfo
@@ -71,7 +73,7 @@ interface ApiService {
@POST @POST
suspend fun getCollectedFoodList( suspend fun getCollectedFoodList(
@Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/boothMachine/app/dishPage", @Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/boothMachine/app/dishPage",
@Body param: Map<String, Int> @Body param: Map<String, String>
): ApiResponse<List<CollectedFoodInfo>> ): ApiResponse<List<CollectedFoodInfo>>
@@ -108,7 +110,7 @@ interface ApiService {
@GET @GET
suspend fun getDinnerType( suspend fun getDinnerType(
// @Url url: String = "${GlobalData.appBaseUrl}/zhstapi/zhst/getCanteenDinnerType", // @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("appVersion") appVersion: String = GlobalData.appVersion,
// @Query("canteenId") restId: String, // @Query("canteenId") restId: String,
): ApiResponse<DinnerType> ): ApiResponse<DinnerType>
@@ -134,6 +136,34 @@ interface ApiService {
@Body req: FoodSearchReq @Body req: FoodSearchReq
): ApiResponse<List<FoodInfo>> ): 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 android.view.ViewTreeObserver
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.arcsoft.face.ErrorInfo 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.dp
import com.sw.dualscreen.ext.format2String import com.sw.dualscreen.ext.format2String
import com.sw.dualscreen.ext.gone import com.sw.dualscreen.ext.gone
import com.sw.dualscreen.ext.load
import com.sw.dualscreen.ext.maskName import com.sw.dualscreen.ext.maskName
import com.sw.dualscreen.ext.visible import com.sw.dualscreen.ext.visible
import com.sw.dualscreen.model.request.UserNutritionParam 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.FoodInfo
import com.sw.dualscreen.model.response.UserNutritionData import com.sw.dualscreen.model.response.UserNutritionData
import com.sw.dualscreen.utils.Debouncer 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.utils.SPUtil
import com.sw.dualscreen.viewmodel.UserViewModel import com.sw.dualscreen.viewmodel.UserViewModel
import com.sw.plate.utils.LightManager 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.flow.drop
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import kotlin.math.max
import kotlin.math.roundToInt import kotlin.math.roundToInt
class MainScreenPresentation( class MainScreenPresentation(
@@ -71,6 +72,7 @@ class MainScreenPresentation(
private lateinit var binding: PresentationMainScreenBinding private lateinit var binding: PresentationMainScreenBinding
private lateinit var stepChangeCallback: (Int) -> Unit private lateinit var stepChangeCallback: (Int) -> Unit
// 当前步骤 // 当前步骤
var currentStep: Int = 0 var currentStep: Int = 0
@@ -110,11 +112,19 @@ class MainScreenPresentation(
fun updateWeight(weight: Double) { fun updateWeight(weight: Double) {
lastWeight = weight * 1000 // 将千克转成克 lastWeight = weight * 1000 // 将千克转成克
Timber.tag(TAG).d("updateWeight lastWeight = $weight, currentStep = $currentStep, isGoStep1 = $isGoStep1") Timber.tag(TAG)
if ((currentStep == 2) && .d("updateWeight lastWeight = $weight, currentStep = $currentStep, isGoStep1 = $isGoStep1")
lastWeight <= 5 && // if (lastWeight <= 5) {
isGoStep1 // 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 isGoStep1 = false
activity.runOnUiThread { activity.runOnUiThread {
Timber.tag(TAG).e("切回step1") Timber.tag(TAG).e("切回step1")
@@ -140,6 +150,7 @@ class MainScreenPresentation(
} }
calculateNutrition(recognitionWeight - lastWeight) calculateNutrition(recognitionWeight - lastWeight)
} }
fun setStepChangeCallback(callback: (Int) -> Unit) { fun setStepChangeCallback(callback: (Int) -> Unit) {
stepChangeCallback = callback stepChangeCallback = callback
} }
@@ -200,10 +211,10 @@ class MainScreenPresentation(
Timber.tag(TAG).d("registerDataChange nutritionData = $it") Timber.tag(TAG).d("registerDataChange nutritionData = $it")
userNutritionData = it userNutritionData = it
if (it == null) return@collect if (it == null) return@collect
step3ShowRecognizeResult() // step3ShowRecognizeResult()
//binding.nutritionInclude.tvUserName.text = it.userName.maskName() // //binding.nutritionInclude.tvUserName.text = it.userName.maskName()
//binding.nutritionInclude.tvRecommendHeat.text = "推荐热量:${it.recommendMin}-${it.recommendMax}" // //binding.nutritionInclude.tvRecommendHeat.text = "推荐热量:${it.recommendMin}-${it.recommendMax}"
//updateWeight(lastWeight / 1000) // //updateWeight(lastWeight / 1000)
} }
} }
activity.lifecycleScope.launch { activity.lifecycleScope.launch {
@@ -227,20 +238,22 @@ class MainScreenPresentation(
ToastUtils.showToast("选中物品为空") ToastUtils.showToast("选中物品为空")
return return
} }
updateFoodInfo(currentFood!!) // updateFoodInfo(currentFood!!)
foodRecSuccess(currentFood!!) //foodRecSuccess(currentFood!!)
} }
private fun initView() { private fun initView() {
// //binding.ivRecImage.let { binding.ivRecImage.let {
// it.outlineProvider = object : ViewOutlineProvider() { it.outlineProvider = object : ViewOutlineProvider() {
// override fun getOutline(view: View, outline: Outline) { override fun getOutline(view: View, outline: Outline) {
// outline.setRoundRect(0, 0, view.width, view.height, 12f.dp) outline.setRoundRect(0, 0, view.width, view.height, 12f.dp)
// }
// }
// it.clipToOutline = true
// }
} }
}
it.clipToOutline = true
}
}
override fun onDisplayRemoved() { override fun onDisplayRemoved() {
super.onDisplayRemoved() super.onDisplayRemoved()
onDismissListener() onDismissListener()
@@ -274,11 +287,46 @@ class MainScreenPresentation(
//updateFoodInfo(foodInfo) //updateFoodInfo(foodInfo)
userNutritionData?.let { // userNutritionData?.let {
Timber.tag(TAG).d("loadBilledMode${GsonUtils.toJson(it)}") // 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) //updateFoodInfo(foodInfo)
userNutritionData?.let { userNutritionData?.let {
binding.nutritionInclude.tvUserName.text = it.userName.maskName() 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) updateWeight(lastWeight / 1000)
} }
} }
@@ -307,7 +356,7 @@ class MainScreenPresentation(
fun step1FoodRecognizing() { fun step1FoodRecognizing() {
hideStandbyScreen() hideStandbyScreen()
activity.resetTouchTime() activity.resetTouchTime()
activity.resumeAnalysis()
Timber.tag(TAG).d("step1") Timber.tag(TAG).d("step1")
//if (abs(lastWeight.toInt()) <= 5) { //if (abs(lastWeight.toInt()) <= 5) {
// activity.runOnUiThread { // activity.runOnUiThread {
@@ -332,8 +381,8 @@ class MainScreenPresentation(
binding.tvFoodName.text = "餐品识别中..." binding.tvFoodName.text = "餐品识别中..."
binding.tvFoodRecPrompt.visible() binding.tvFoodRecPrompt.visible()
binding.flCameraView.visible() binding.flCameraView.visible()
binding.ivRecImage.gone()
binding.ivPreviewImage.visible() binding.ivPreviewImage.visible()
//binding.ivRecImage.gone()
binding.ivFaceRecMask.gone() binding.ivFaceRecMask.gone()
binding.tvFaceTip.gone() binding.tvFaceTip.gone()
binding.llPriceInfo.gone() binding.llPriceInfo.gone()
@@ -344,17 +393,21 @@ class MainScreenPresentation(
activity.clearFoodList() activity.clearFoodList()
} }
fun foodRecSuccess(foodInfo: FoodInfo) { // fun foodRecSuccess(foodInfo: FoodInfo) {
//识别逻辑完成根据是否计费显示不同页面 // //识别逻辑完成根据是否计费显示不同页面
val mode = SPUtil.getInstance().get(GlobalKey.KEY_CHARGE_MODE, 0) // val mode = SPUtil.getInstance().get(GlobalKey.KEY_CHARGE_MODE, 0)
if (mode == 0) { // if (mode == 0) {
//计费 // //计费
loadBilledMode() // loadBilledMode()
} else { // } else {
//不计费 // //不计费
loadUnbilledMode() // //userViewModel.getUserNutritionData(
} // // userId = userId,
} // // foodId = currentFood!!.foodId!!
// //)
// loadUnbilledMode()
// }
// }
/** /**
* 用户人脸识别中 * 用户人脸识别中
@@ -377,7 +430,7 @@ class MainScreenPresentation(
binding.tvFoodRecPrompt.visible() binding.tvFoodRecPrompt.visible()
binding.llPriceInfo.gone() binding.llPriceInfo.gone()
//binding.ivRecImage.gone() binding.ivRecImage.gone()
binding.ivPreviewImage.gone() binding.ivPreviewImage.gone()
dinnerTypeInfo = null dinnerTypeInfo = null
@@ -386,7 +439,9 @@ class MainScreenPresentation(
stepChangeCallback(currentStep) stepChangeCallback(currentStep)
currentFood = foodInfo currentFood = foodInfo
userViewModel.getDinnerType() userViewModel.getDinnerType()
resumeCamera() resumeCamera()
} }
@@ -395,18 +450,37 @@ class MainScreenPresentation(
fun updateFood(foodInfo: FoodInfo?) { fun updateFood(foodInfo: FoodInfo?) {
Timber.tag(TAG).d("updateFood") Timber.tag(TAG).d("updateFood")
currentFood = foodInfo 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) { if (currentStep == 1) {
step2FaceRecognizing(foodInfo) step2FaceRecognizing(foodInfo)
} else {
updateFoodInfo(foodInfo)
} }
} }
//if (currentStep == 1) {
// step2FaceRecognizing(foodInfo)
//} else {
// updateFoodInfo(foodInfo)
//}
} }
private fun updateFoodInfo(foodInfo: FoodInfo) { private fun updateFoodInfo(foodInfo: FoodInfo) {
binding.tvFoodName.text = foodInfo.foodName // binding.tvFoodName.text = foodInfo.foodName
binding.ivPreviewImage.gone() // binding.ivPreviewImage.gone()
//binding.ivRecImage.visible() //binding.ivRecImage.visible()
// if (foodInfo.photoUri != null) { // if (foodInfo.photoUri != null) {
// GlideUtils.loadRoundCornerImage( // GlideUtils.loadRoundCornerImage(
@@ -429,9 +503,11 @@ class MainScreenPresentation(
var weight = weight1 var weight = weight1
if (weight < 0) weight = 0.0 if (weight < 0) weight = 0.0
debouncer.debounce { debouncer.debounce {
Timber.tag(TAG).d("calculateNutrition weight = $weight, recognitionWeight = $recognitionWeight") Timber.tag(TAG)
.d("calculateNutrition weight = $weight, recognitionWeight = $recognitionWeight")
val dinnerType = dinnerTypeInfo!!.dinnerType!! 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( val calcResultInfo = UserNutritionUtils.calculateNutrition(
currentFood!!, currentFood!!,
userNutritionData!!, userNutritionData!!,
@@ -528,6 +604,7 @@ class MainScreenPresentation(
} }
} }
} }
fun updateImage(bitmap: Bitmap) { fun updateImage(bitmap: Bitmap) {
activity.runOnUiThread { activity.runOnUiThread {
binding.ivPreviewImage.setImageBitmap(bitmap) binding.ivPreviewImage.setImageBitmap(bitmap)
@@ -542,6 +619,7 @@ class MainScreenPresentation(
mealPickupMode = mode mealPickupMode = mode
step1FoodRecognizing() step1FoodRecognizing()
} }
override fun onStop() { override fun onStop() {
if (rgbCameraHelper != null) { if (rgbCameraHelper != null) {
rgbCameraHelper!!.release() rgbCameraHelper!!.release()
@@ -605,7 +683,8 @@ class MainScreenPresentation(
recognizeViewModel.recognizeConfiguration recognizeViewModel.recognizeConfiguration
.observe(activity, Observer { recognizeConfiguration: 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? -> recognizeViewModel.recognizeNotice.observe(activity, Observer { notice: String? ->
Timber.tag(TAG).i("recognizeNotice observe notice = $notice") Timber.tag(TAG).i("recognizeNotice observe notice = $notice")
@@ -614,7 +693,8 @@ class MainScreenPresentation(
recognizeViewModel.recognizeUserId.observe( recognizeViewModel.recognizeUserId.observe(
activity, activity,
Observer { compareResult: CompareResult -> 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() recognitionTime = System.currentTimeMillis()
recognitionWeight = lastWeight recognitionWeight = lastWeight
lastFaceTrackId = compareResult.trackId lastFaceTrackId = compareResult.trackId
@@ -622,18 +702,25 @@ class MainScreenPresentation(
val userId = faceEntity.userName val userId = faceEntity.userName
if (userId == null) return@Observer if (userId == null) return@Observer
if (currentFood == null) { if (currentFood == null) {
//重新识别 currentFood = activity.checkedItem
}
if (currentFood == null) {
activity.runOnUiThread { activity.runOnUiThread {
ToastUtils.showToast("currentFood == null") ToastUtils.showToast("请选择菜品")
return@runOnUiThread
} }
//activity.recognizeFood()
//step1FoodRecognizing()
return@Observer
} }
userViewModel.getUserNutritionData( // TODO: ------------------人脸识别成功
userId = userId, //-------------------------------------------------
foodId = currentFood!!.foodId!! // TODO: ------------------人脸识别成功
)
step3ShowRecognizeResult()
//根据接口数据更新热量数据--------------------------
loadUnbilledMode()
// userViewModel.getUserNutritionData(
// userId = userId,
// foodId = currentFood!!.foodId!!
// )
}) })
recognizeViewModel.drawRectInfoText.observe(activity, Observer { info -> recognizeViewModel.drawRectInfoText.observe(activity, Observer { info ->
@@ -724,7 +811,8 @@ class MainScreenPresentation(
displayOrientation: Int, displayOrientation: Int,
isMirror: Boolean 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({ activity.runOnUiThread({
val previewSizeRgb = camera.getParameters().getPreviewSize() val previewSizeRgb = camera.getParameters().getPreviewSize()
val layoutParams = adjustPreviewViewSize( val layoutParams = adjustPreviewViewSize(
@@ -733,8 +821,10 @@ class MainScreenPresentation(
previewSizeRgb, displayOrientation, 0.6F previewSizeRgb, displayOrientation, 0.6F
) )
Timber.tag(TAG).d("initRgbCamera previewSizeRgb = ${previewSizeRgb.width}-${previewSizeRgb.height}") Timber.tag(TAG)
Timber.tag(TAG).d("initRgbCamera layoutParams = ${layoutParams.width}-${layoutParams.height}") .d("initRgbCamera previewSizeRgb = ${previewSizeRgb.width}-${previewSizeRgb.height}")
Timber.tag(TAG)
.d("initRgbCamera layoutParams = ${layoutParams.width}-${layoutParams.height}")
Timber.tag(TAG).d( Timber.tag(TAG).d(
"initRgbCamera isMirror = ${isMirror}, isDrawRgbRectHorizontalMirror = ${ "initRgbCamera isMirror = ${isMirror}, isDrawRgbRectHorizontalMirror = ${
ConfigUtil.isDrawRgbRectHorizontalMirror( ConfigUtil.isDrawRgbRectHorizontalMirror(
@@ -790,16 +880,19 @@ class MainScreenPresentation(
} }
override fun onCameraConfigurationChanged(cameraID: Int, displayOrientation: Int) { 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) { if (rgbFaceRectTransformer != null) {
rgbFaceRectTransformer!!.cameraDisplayOrientation = displayOrientation 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 measuredWidth = binding.dualCameraTexturePreviewRgb.measuredWidth
val measuredHeight = binding.dualCameraTexturePreviewRgb.measuredHeight val measuredHeight = binding.dualCameraTexturePreviewRgb.measuredHeight
Timber.tag(TAG).i("initRgbCamera measuredWidth=$measuredWidthmeasuredHeight=$measuredHeight") Timber.tag(TAG)
.i("initRgbCamera measuredWidth=$measuredWidthmeasuredHeight=$measuredHeight")
val previewConfig: PreviewConfig = recognizeViewModel.previewConfig val previewConfig: PreviewConfig = recognizeViewModel.previewConfig
rgbCameraHelper = DualCameraHelper.Builder() rgbCameraHelper = DualCameraHelper.Builder()
@@ -831,7 +924,8 @@ class MainScreenPresentation(
displayOrientation: Int, displayOrientation: Int,
isMirror: Boolean 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 previewSizeIr = camera.getParameters().getPreviewSize()
val layoutParams = adjustPreviewViewSize( val layoutParams = adjustPreviewViewSize(
binding.dualCameraTexturePreviewRgb, binding.dualCameraTexturePreviewRgb,
@@ -872,7 +966,8 @@ class MainScreenPresentation(
if (irFaceRectTransformer != null) { if (irFaceRectTransformer != null) {
irFaceRectTransformer!!.cameraDisplayOrientation = displayOrientation 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) binding.dualCameraFaceRectView.drawRealtimeFaceInfo(rgbDrawInfoList)
} }
val listIsEmpty = facePreviewInfoList.isEmpty()
if (facePreviewInfoList.isEmpty() || (lastFaceTrackId != facePreviewInfoList[0]!!.trackId)) { 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) { if (lastFaceTrackId != -1) {
mealPickupMode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0) ?: 0 mealPickupMode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0) ?: 0
Timber.tag(TAG).i("$lastFaceTrackId 用户离开") Timber.tag(TAG).i("$lastFaceTrackId 用户离开")
lastFaceTrackId = -1 lastFaceTrackId = -1
postUserData() postUserData()
if (mealPickupMode == 0) { if (mealPickupMode == 0) {
activity.isAnalyzing = false
step1FoodRecognizing() step1FoodRecognizing()
} else { } else {
step2FaceRecognizing(currentFood!!) step2FaceRecognizing(currentFood!!)
@@ -935,7 +1033,8 @@ class MainScreenPresentation(
fun postUserData() { fun postUserData() {
Timber.tag(TAG).d("postUserData") Timber.tag(TAG).d("postUserData")
if (userNutritionData == null || currentFood == null) { 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 return
} }
var eatWeight = 0.0 var eatWeight = 0.0
@@ -30,6 +30,7 @@ import com.sw.dualscreen.R
import com.sw.dualscreen.activity.PayActivity import com.sw.dualscreen.activity.PayActivity
import com.sw.dualscreen.databinding.PresentationFacePayBinding import com.sw.dualscreen.databinding.PresentationFacePayBinding
import com.sw.dualscreen.ext.load import com.sw.dualscreen.ext.load
import com.sw.dualscreen.utils.GsonUtils
import com.sw.dualscreen.view.CustomDialog import com.sw.dualscreen.view.CustomDialog
import com.sw.dualscreen.viewmodel.UserViewModel import com.sw.dualscreen.viewmodel.UserViewModel
import com.sw.plate.utils.ToastUtils import com.sw.plate.utils.ToastUtils
@@ -267,14 +268,31 @@ class FacePayPresentation(
activity.runOnUiThread { activity.runOnUiThread {
//ToastUtils.showToast("用户人脸识别成功,挑战支付页面") //ToastUtils.showToast("用户人脸识别成功,挑战支付页面")
showWaitingDialog("刷脸支付中,请稍后……") showWaitingDialog("刷脸支付中,请稍后……")
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({ binding.root.postDelayed({
hideWaitingDialog() hideWaitingDialog()
activity.paySuccess() activity.showPayInfo(memberInfo)
binding.root.postDelayed({ binding.root.postDelayed({
dismiss() dismiss()
},500) },500)
},1500) },1500)
} }
}
}
}
}
// userViewModel.getUserNutritionData( // userViewModel.getUserNutritionData(
// userId = userId, // userId = userId,
// foodId = currentFood!!.id!! // foodId = currentFood!!.id!!
@@ -577,6 +595,8 @@ class FacePayPresentation(
// } else { // } else {
// step2FaceRecognizing(currentFood!!) // step2FaceRecognizing(currentFood!!)
// } // }
resumeCamera()
} }
} }
} }
@@ -2,19 +2,18 @@ package com.sw.dualscreen.presentation.pay
import android.app.Presentation import android.app.Presentation
import android.os.Bundle 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 android.view.Display
import androidx.core.graphics.toColorInt
import androidx.core.text.buildSpannedString
import com.sw.dualscreen.R import com.sw.dualscreen.R
import com.sw.dualscreen.activity.PayActivity import com.sw.dualscreen.activity.PayActivity
import com.sw.dualscreen.databinding.PresentationScanQrcodePayBinding import com.sw.dualscreen.databinding.PresentationScanQrcodePayBinding
import com.sw.dualscreen.ext.format2String
import com.sw.dualscreen.ext.gone import com.sw.dualscreen.ext.gone
import com.sw.dualscreen.ext.invisible
import com.sw.dualscreen.ext.load import com.sw.dualscreen.ext.load
import com.sw.dualscreen.ext.visible 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( class ScanQrCodePayPresentation(
val activity: PayActivity, val activity: PayActivity,
@@ -38,8 +37,19 @@ class ScanQrCodePayPresentation(
} }
var foodName: String? = null var foodName: String? = null
var payAmount: String? = null
var payQrCodePic: Any? = 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() { private fun initView() {
when (type) { when (type) {
0 -> { 0 -> {
@@ -49,63 +59,117 @@ class ScanQrCodePayPresentation(
binding.tvPayTip.text = "请扫码支付或出示付款码" binding.tvPayTip.text = "请扫码支付或出示付款码"
binding.tvFoodName.text = foodName 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) binding.ivPayQrCode.load(payQrCodePic)
} }
1 -> { 1 -> {
binding.layoutVip.visible() binding.layoutVip.visible()
binding.layoutPaySuccess.gone() binding.layoutPaySuccess.gone()
binding.layoutScanQrCode.visible() 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.tvFoodName.text = foodName
binding.tvRealAmount.text = getAmountText(payAmount ?: "") binding.tvRealAmount.text = SpannedUtils.getAmountText(
binding.ivPayQrCode.load(payQrCodePic) listOf(
TextBean(text = "¥", textSize = 32),
binding.ivHeadPic.load(R.mipmap.ic_launcher) TextBean(text = totalPrice.format2String(2), textSize = 48),
binding.tvUserName.text = "张三" )
binding.tvUserPhone.text = "132****5678" )
binding.tvUserBalance.text = getBalanceText("0.00") binding.ivPayQrCode.run {
// load(payQrCodePic)
binding.tvPayTip.text = getPayTip("-16.80", "22.00") if (balance >= totalPrice) invisible() else visible()
} }
binding.tvPayTip.text = SpannedUtils.getAmountText(getAmountList())
}
2 -> { 2 -> {
binding.layoutVip.visible() binding.layoutVip.visible()
binding.layoutPaySuccess.visible() binding.layoutPaySuccess.visible()
binding.layoutScanQrCode.gone() binding.layoutScanQrCode.gone()
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) }
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 loadUserInfo(item: MemberInfo) {
binding.ivHeadPic.load(R.mipmap.ic_launcher) binding.ivHeadPic.load(R.mipmap.ic_launcher)
binding.tvUserName.text = "张三" binding.tvUserName.text = item.name
binding.tvUserPhone.text = "132****5678"
binding.tvUserBalance.text = getBalanceText("0.00")
binding.tvPayInfo.text = "余额扣除 16.80 元,在线支付 20.00 元" val phone = item.phone ?: ""
binding.tvUserPhone.text =
} if (phone.length == 11)
} phone.replace(phone.substring(3, 7), "****")
else
phone
} }
private fun getPayTip(startAmount:String, endAmount: String): SpannedString { private fun getAmountList(): List<TextBean> {
return buildSpannedString { val list: MutableList<TextBean> = mutableListOf()
append("余额扣除 ", ForegroundColorSpan("#FF5E7585".toColorInt()), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
append(startAmount, ForegroundColorSpan("#FF0A1428".toColorInt()), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) list.add(TextBean(text = "余额扣除 ", textSize = 30, textColor = "#FF5E7585"))
append(" 元,还需支付 ", ForegroundColorSpan("#FF5E7585".toColorInt()), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) list.add(
append(endAmount, ForegroundColorSpan("#FF0A1428".toColorInt()), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) TextBean(
append("", ForegroundColorSpan("#FF5E7585".toColorInt()), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) 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")
} }
private fun getBalanceText(amount: String): SpannedString { return list
return buildSpannedString {
append("¥", AbsoluteSizeSpan(20, true), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
append(amount, AbsoluteSizeSpan(32, true), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
}
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)
}
} }
override fun onDisplayRemoved() { 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 package com.sw.dualscreen.repository
import android.content.Context
import android.net.Uri
import com.sw.dualscreen.GlobalData import com.sw.dualscreen.GlobalData
import com.sw.dualscreen.model.request.UserNutritionParam import com.sw.dualscreen.model.request.UserNutritionParam
import com.sw.dualscreen.model.response.ApiResponse import com.sw.dualscreen.model.response.ApiResponse
import com.sw.dualscreen.model.response.DinnerType import com.sw.dualscreen.model.response.DinnerType
import com.sw.dualscreen.model.response.FoodInfo import com.sw.dualscreen.model.response.FoodInfo
import com.sw.dualscreen.model.response.FoodSearchReq 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.UserFaceModel
import com.sw.dualscreen.model.response.UserNutritionData import com.sw.dualscreen.model.response.UserNutritionData
import com.sw.dualscreen.network.api.ApiService import com.sw.dualscreen.network.api.ApiService
import com.sw.dualscreen.objbox.CollectedFoodInfo import com.sw.dualscreen.objbox.CollectedFoodInfo
import com.sw.dualscreen.utils.ImageUtil
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody import okhttp3.MultipartBody
import okhttp3.RequestBody import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.asRequestBody import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.File import java.io.File
/** /**
@@ -74,18 +72,21 @@ class RemoteRepository constructor(
) )
} }
} }
/** /**
* 获取人脸数据 * 获取人脸数据
*/ */
suspend fun getCollectedFoodList( suspend fun getCollectedFoodList(
pageNum: Int, pageNum: Int,
pageSize: Int = 50, pageSize: Int = 100,
foodName: String,
): ApiResponse<List<CollectedFoodInfo>> { ): ApiResponse<List<CollectedFoodInfo>> {
return safeApiCall { return safeApiCall {
apiService.getCollectedFoodList( apiService.getCollectedFoodList(
param = mapOf( param = mapOf(
"pageNum" to pageNum, "pageNum" to "$pageNum",
"pageSize" to pageSize "pageSize" to "$pageSize",
"foodName" to foodName,
) )
) )
} }
@@ -141,10 +142,7 @@ class RemoteRepository constructor(
* @param restId 从device服务获取的canteenId字段 * @param restId 从device服务获取的canteenId字段
* @param foodName 菜品名称,多个使用逗号拼接 * @param foodName 菜品名称,多个使用逗号拼接
*/ */
suspend fun getFoodInfo( suspend fun getFoodInfo(foodName: String): ApiResponse<List<FoodInfo>> {
restId: String = GlobalData.restId,
foodName: String,
): ApiResponse<List<FoodInfo>> {
return safeApiCall { return safeApiCall {
// val map = mutableMapOf<String, List<String>>() // val map = mutableMapOf<String, List<String>>()
// map["nameList"] = foodName.split(",") // 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>() val fileParts = mutableListOf<MultipartBody.Part>()
fileList.forEachIndexed { index, file -> 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(){ private fun searchInfo(){
debouncer.debounce { 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.View;
import android.view.Window; import android.view.Window;
import android.view.WindowManager; 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()); // fullScreenImmersive(getWindow().getDecorView());
// this.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); // 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.GlobalData
import com.sw.dualscreen.GlobalKey import com.sw.dualscreen.GlobalKey
import com.sw.dualscreen.model.request.UserNutritionParam 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.DinnerType
import com.sw.dualscreen.model.response.FoodInfo 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.UserFaceModel
import com.sw.dualscreen.model.response.UserNutritionData import com.sw.dualscreen.model.response.UserNutritionData
import com.sw.dualscreen.objbox.CollectedFoodInfo import com.sw.dualscreen.objbox.CollectedFoodInfo
import com.sw.dualscreen.utils.GsonUtils
import com.sw.dualscreen.utils.SPUtil import com.sw.dualscreen.utils.SPUtil
import com.sw.plate.App import com.sw.plate.App
import com.sw.plate.utils.Base64 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") Timber.tag(TAG).d("getCollectedFoodList index = $pageNo")
launch { launch {
val response = repository.getCollectedFoodList(pageNo) val response = repository.getCollectedFoodList(pageNum = pageNo, pageSize = pageSize, foodName = foodName)
if (parseResponse(response)) { if (parseResponse(response)) {
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
val list: List<CollectedFoodInfo> = response.result ?: emptyList() val list: List<CollectedFoodInfo> = response.result ?: emptyList()
@@ -254,22 +263,73 @@ class UserViewModel : BaseViewModel() {
} }
} }
fun createOrder(order: FoodOrder, block:(String)-> Unit) {
Timber.tag(TAG).d("createOrder")
fun uploadCollectFoodPics(files: List<File?>, params: HashMap<String, RequestBody>, callback: (Boolean) -> Unit) {
Timber.tag(TAG).d("uploadCollectFoodPics")
launchWithLoading { launchWithLoading {
val response = repository.createOrder(order)
if (parseResponse(response)) {
block(response.data.toString())
} else {
block("")
}
}
}
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() val fileList = files.filterNotNull()
if (fileList.isEmpty()) { if (fileList.isEmpty()) {
callback(false) return true
return@launchWithLoading
} }
val resp = repository.uploadCollectFoodPics(fileList, params) val resp = repository.uploadCollectFoodPics(fileList, params)
if (!parseResponse(resp)) { // if (!parseResponse(resp)) {
callback(false) // return false
return@launchWithLoading // }
} return resp.code == "00000"
callback(resp.code == "00000")
}
} }
} }
+11 -2
View File
@@ -1,6 +1,15 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" <selector xmlns:android="http://schemas.android.com/apk/res/android">
android:shape="rectangle"> <item android:state_enabled="false">
<shape android:shape="rectangle">
<solid android:color="#80FF3232"/> <solid android:color="#80FF3232"/>
<corners android:radius="6dp"/> <corners android:radius="6dp"/>
</shape> </shape>
</item>
<item android:state_enabled="true">
<shape android:shape="rectangle">
<solid android:color="#FFFF3232"/>
<corners android:radius="6dp"/>
</shape>
</item>
</selector>
@@ -44,33 +44,39 @@
</FrameLayout> </FrameLayout>
<!-- <com.scwang.smart.refresh.layout.SmartRefreshLayout--> <FrameLayout
<!-- android:id="@+id/refreshLayout"--> android:layout_width="match_parent"
<!-- android:layout_width="match_parent"--> android:layout_height="match_parent">
<!-- android:layout_height="0dp"-->
<!-- android:layout_marginHorizontal="130dp"-->
<!-- android:layout_marginTop="40dp"-->
<!-- android:layout_marginBottom="58dp"-->
<!-- app:layout_constraintBottom_toTopOf="@id/dividerLine"-->
<!-- app:layout_constraintTop_toBottomOf="@id/flSearchBlock">-->
<!-- <com.scwang.smart.refresh.header.ClassicsHeader--> <com.scwang.smart.refresh.layout.SmartRefreshLayout
<!-- android:layout_width="match_parent"--> android:id="@+id/refreshLayout"
<!-- android:layout_height="wrap_content" />--> android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp">
<com.scwang.smart.refresh.header.ClassicsHeader
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvFoodList" android:id="@+id/rvFoodList"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:overScrollMode="never" android:overScrollMode="never"
android:layout_margin="10dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:itemCount="8" tools:itemCount="8"
tools:listitem="@layout/list_item_collected_food" /> tools:listitem="@layout/list_item_collected_food" />
<!-- <com.scwang.smart.refresh.footer.ClassicsFooter--> <com.scwang.smart.refresh.footer.ClassicsFooter
<!-- android:layout_width="match_parent"--> android:layout_width="match_parent"
<!-- android:layout_height="wrap_content" />--> android:layout_height="wrap_content" />
<!-- </com.scwang.smart.refresh.layout.SmartRefreshLayout>--> </com.scwang.smart.refresh.layout.SmartRefreshLayout>
<include
android:id="@+id/emptyInclude"
layout="@layout/layout_empty_search"
android:visibility="gone"/>
</FrameLayout>
</LinearLayout> </LinearLayout>
+2 -1
View File
@@ -77,12 +77,13 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent" />
<ImageView <com.google.android.material.imageview.ShapeableImageView
android:id="@+id/iv_img" android:id="@+id/iv_img"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:contentDescription="食物图预览" android:contentDescription="食物图预览"
android:scaleType="centerCrop" android:scaleType="centerCrop"
app:shapeAppearance="@style/round10dpCornerStyle"
android:visibility="gone" /> android:visibility="gone" />
<View <View
+3 -2
View File
@@ -111,7 +111,7 @@
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvFoodList" android:id="@+id/rvFoodList"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="400dp"
android:layout_marginHorizontal="16dp" android:layout_marginHorizontal="16dp"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager" app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
@@ -119,7 +119,8 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/flPreview" app:layout_constraintTop_toBottomOf="@id/flPreview"
app:spanCount="3" app:spanCount="3"
tools:itemCount="5" tools:itemCount="12"
android:overScrollMode="never"
tools:listitem="@layout/list_item_food_collection" /> tools:listitem="@layout/list_item_food_collection" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
@@ -126,15 +126,10 @@
android:id="@+id/ivPayQrCode" android:id="@+id/ivPayQrCode"
android:layout_width="300dp" android:layout_width="300dp"
android:layout_height="300dp" android:layout_height="300dp"
android:layout_marginTop="56dp" android:layout_marginVertical="50dp"
android:src="@drawable/ic_qrcode" android:src="@drawable/ic_qrcode"
android:visibility="visible" /> android:visibility="visible" />
<Space
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<TextView <TextView
android:id="@+id/tvAccountInfo" android:id="@+id/tvAccountInfo"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -143,20 +138,26 @@
android:textColor="#ff889ac2" android:textColor="#ff889ac2"
android:textSize="30sp" /> android:textSize="30sp" />
<Space
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<androidx.appcompat.widget.AppCompatButton <androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnConfirmPay" android:id="@+id/btnConfirmPay"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="120dp" android:layout_height="120dp"
android:layout_marginHorizontal="32dp" android:layout_marginHorizontal="32dp"
android:layout_marginTop="50dp"
android:layout_marginBottom="32dp" android:layout_marginBottom="32dp"
android:background="@drawable/bg_btn_save2" android:background="@drawable/bg_btn_save2"
android:text="确认支付" android:text="确认支付"
android:enabled="true"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="40sp" /> android:textSize="40sp" />
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/layoutPaySuccess"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:gravity="center" android:gravity="center"
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
android:layout_gravity="center"
android:orientation="vertical" android:orientation="vertical"
tools:ignore="HardcodedText"> tools:ignore="HardcodedText">
@@ -18,17 +19,17 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="30dp" android:layout_marginTop="30dp"
android:layout_marginBottom="39dp" android:layout_marginBottom="39dp"
android:text="暂无该物品" android:text="暂无采集记录"
android:textColor="#ff000000" android:textColor="#ff000000"
android:textSize="30sp" android:textSize="36sp"
android:textStyle="bold" /> android:textStyle="bold" />
<TextView <TextView
android:id="@+id/tvContent" android:id="@+id/tvContent"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="请检查物品名称,或稍后重新搜索" android:text="请确认菜品名称,或稍后重"
android:textColor="#ff999999" android:textColor="#ff999999"
android:gravity="center" android:gravity="center"
android:textSize="26sp" /> android:textSize="30sp" />
</LinearLayout> </LinearLayout>
+16 -11
View File
@@ -3,7 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:layout_gravity="center" android:layout_gravity="center"
android:orientation="vertical"> android:orientation="vertical">
@@ -12,15 +12,16 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="热量" android:text="热量"
android:layout_marginTop="65dp" android:layout_marginTop="50dp"
android:textColor="#ff5e7585" android:textColor="#ff5e7585"
android:textSize="24sp" android:textSize="24sp"
android:textStyle="bold" /> android:textStyle="bold" />
<TextView <TextView
android:id="@+id/tvCalorie"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginVertical="15dp" android:layout_marginVertical="13dp"
android:textColor="#ff0a1428" android:textColor="#ff0a1428"
android:textSize="40sp" android:textSize="40sp"
android:textStyle="bold" android:textStyle="bold"
@@ -32,12 +33,12 @@
android:textColor="#ff5e7585" android:textColor="#ff5e7585"
android:textSize="20sp" android:textSize="20sp"
android:textStyle="bold" android:textStyle="bold"
tools:text="kcal/100g" /> android:text="kcal/100g" />
<View <View
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="1dp"
android:layout_marginVertical="28dp" android:layout_marginVertical="22dp"
android:layout_marginHorizontal="28dp" android:layout_marginHorizontal="28dp"
android:background="#FFDBE5FB" /> android:background="#FFDBE5FB" />
@@ -51,6 +52,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:gravity="center_horizontal"
app:layout_constraintEnd_toStartOf="@+id/llProtein" app:layout_constraintEnd_toStartOf="@+id/llProtein"
app:layout_constraintHorizontal_chainStyle="spread" app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintStart_toStartOf="parent"> app:layout_constraintStart_toStartOf="parent">
@@ -59,7 +61,7 @@
android:id="@+id/tvFatRate" android:id="@+id/tvFatRate"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="13%" tools:text="13%"
android:textColor="#ff5e7585" android:textColor="#ff5e7585"
android:textSize="16sp" /> android:textSize="16sp" />
@@ -91,6 +93,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:gravity="center_horizontal"
app:layout_constraintEnd_toStartOf="@+id/llCarbohydrate" app:layout_constraintEnd_toStartOf="@+id/llCarbohydrate"
app:layout_constraintStart_toEndOf="@id/llFat"> app:layout_constraintStart_toEndOf="@id/llFat">
@@ -98,7 +101,7 @@
android:id="@+id/tvProteinRate" android:id="@+id/tvProteinRate"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="47%" tools:text="47%"
android:textColor="#ff5e7585" android:textColor="#ff5e7585"
android:textSize="16sp" /> android:textSize="16sp" />
@@ -130,6 +133,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:gravity="center_horizontal"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/llProtein"> app:layout_constraintStart_toEndOf="@id/llProtein">
@@ -137,7 +141,7 @@
android:id="@+id/tvCarbohydrateRate" android:id="@+id/tvCarbohydrateRate"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="40%" tools:text="40%"
android:textColor="#ff5e7585" android:textColor="#ff5e7585"
android:textSize="16sp" /> android:textSize="16sp" />
@@ -168,11 +172,12 @@
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="0dp"
android:layout_weight="1"
android:text="(所示营养数据为每百克含量)" android:text="(所示营养数据为每百克含量)"
android:textColor="#ff889ac2" android:textColor="#ff889ac2"
android:layout_marginTop="56dp" android:paddingBottom="20dp"
android:layout_marginBottom="27dp" android:gravity="bottom"
android:textSize="16sp" /> android:textSize="16sp" />
</LinearLayout> </LinearLayout>
@@ -2,7 +2,6 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@@ -18,14 +17,14 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/setting_border" android:background="@drawable/setting_border"
android:orientation="vertical" android:orientation="vertical"
android:paddingBottom="28dp"> android:paddingBottom="26dp">
<TextView <TextView
android:id="@+id/tvFoodName" android:id="@+id/tvFoodName"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:layout_marginVertical="60dp" android:layout_marginVertical="50dp"
android:text="餐品识别中..." android:text="餐品识别中..."
android:textColor="#ff0a1428" android:textColor="#ff0a1428"
android:textSize="40sp" android:textSize="40sp"
@@ -76,17 +75,20 @@
</FrameLayout> </FrameLayout>
<ImageView <com.google.android.material.imageview.ShapeableImageView
android:id="@+id/ivPreviewImage" android:id="@+id/ivPreviewImage"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="456dp" android:layout_height="456dp"
app:shapeAppearance="@style/round10dpCornerStyle"
tools:background="@mipmap/ic_launcher"/> tools:background="@mipmap/ic_launcher"/>
<!-- <ImageView--> <com.google.android.material.imageview.ShapeableImageView
<!-- android:id="@+id/ivRecImage"--> android:id="@+id/ivRecImage"
<!-- android:layout_width="match_parent"--> android:layout_width="match_parent"
<!-- android:layout_height="456dp"--> android:layout_height="456dp"
<!-- tools:background="@mipmap/ic_launcher"/>--> tools:background="@mipmap/ic_launcher_round"
app:shapeAppearance="@style/round10dpCornerStyle"
android:visibility="gone"/>
<ImageView <ImageView
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -100,13 +102,13 @@
android:id="@+id/llPriceInfo" android:id="@+id/llPriceInfo"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="28dp" android:layout_marginTop="26dp"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:gravity="center_vertical" android:gravity="center_vertical"
android:visibility="gone"> android:visibility="gone">
<TextView <TextView
android:id="@+id/tvOldPrice" android:id="@+id/tvNormalPrice"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textColor="#ff0a1428" android:textColor="#ff0a1428"
+4
View File
@@ -172,4 +172,8 @@
<item name="cornerSize">50%</item> <item name="cornerSize">50%</item>
</style> </style>
<style name="round10dpCornerStyle">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">10dp</item>
</style>
</resources> </resources>