首页、支付功能

This commit is contained in:
2025-11-26 18:57:53 +08:00
parent 60b712f216
commit 88179e27b7
64 changed files with 3380 additions and 319 deletions
@@ -39,7 +39,7 @@ object GlobalData {
var appId = "Hkz1rBk6PZXbS8KwKr67K2eZtsz8bRoMHLg64bUdgCZj"
var sdkKey = "AabXs3sHM8UhhE7oCGf4LVMLrdkGb1nJNksbjjTdVt7k"
var activeKey = "085F-118G-Q3LH-HPGZ"//2号楼"085F-118G-Q3AB-1WVJ" 5号楼"085F-118G-Q3LH-HPGZ"
var activeKey = "085F-118G-Q3NU-134V"//2号楼"085F-118G-Q3AB-1WVJ" 5号楼"085F-118G-Q3LH-HPGZ"
}
/**
@@ -2,6 +2,8 @@ package com.sw.dualscreen.activity
import android.app.Dialog
import android.content.Context
import android.content.res.Configuration
import android.content.res.Resources
import android.os.Bundle
import android.text.TextUtils
import android.view.View
@@ -19,6 +21,7 @@ import com.sw.dualscreen.viewmodel.BaseViewModel
import kotlinx.coroutines.launch
import timber.log.Timber
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
protected lateinit var binding: VB
protected lateinit var context: Context
@@ -85,12 +85,21 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
box = ObjectBox.boxStore.boxFor(Food::class)
}
list.clear()
var queryList = box?.all?.distinctBy { it.name }
val totalList = box?.all
?.filter { it.name!=null }
?.groupBy { it.name!! }
?.map { CollectedFoodBean(foodName = it.key, count = it.value.size) }
//var queryMap:Map<String, List<Food>> ?= null
if (searchName.isNullOrBlank().not()) {
queryList = queryList?.filter { it.name?.contains(searchName) == true }
//queryMap = totalMap?.filter { it.key.contains(searchName) }
val temp = totalList?.filter { it.foodName?.contains(searchName) == true}
if (temp.isNullOrEmpty().not()) {
list.addAll(temp)
}
} else {
if (totalList.isNullOrEmpty().not()) {
list.addAll(totalList)
}
queryList?.forEach {
list.add(CollectedFoodBean(foodName = it.name))
}
adapter.notifyDataSetChanged()
if (list.isEmpty()) {
@@ -4,15 +4,13 @@ import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.graphics.Outline
import android.graphics.Typeface
import android.hardware.display.DisplayManager
import android.net.Uri
import android.os.Handler
import android.os.Looper
import android.text.TextUtils
import android.view.View
import android.view.ViewOutlineProvider
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Toast
import androidx.activity.viewModels
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
@@ -21,28 +19,19 @@ import androidx.camera.core.ImageCaptureException
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import com.google.common.util.concurrent.ListenableFuture
import com.sw.dualscreen.GlobalData
import com.sw.dualscreen.GlobalKey
import com.sw.dualscreen.R
import com.sw.dualscreen.adapter.GenericItemAdapter
import com.sw.dualscreen.adapter.GridSpacingItemDecoration
import com.sw.dualscreen.adapter.SearchFoodAdapter
import com.sw.dualscreen.adapter.dpToPx
import com.sw.dualscreen.databinding.ActivityMainBinding
import com.sw.dualscreen.databinding.ItemFoodInfoBinding
import com.sw.dualscreen.ext.dp
import com.sw.dualscreen.ext.format2String
import com.sw.dualscreen.ext.gone
import com.sw.dualscreen.ext.visible
import com.sw.dualscreen.model.response.FoodInfo
import com.sw.dualscreen.objbox.FoodModule
import com.sw.dualscreen.objbox.FoodModule.IdNameScore
import com.sw.dualscreen.presentation.SecondaryScreenPresentation
import com.sw.dualscreen.presentation.MainScreenPresentation
import com.sw.dualscreen.sdk.SensorScaleUtils
import com.sw.dualscreen.utils.AssetsTool
import com.sw.dualscreen.utils.BitmapCropper
import com.sw.dualscreen.utils.BitmapSaver
import com.sw.dualscreen.utils.Debouncer
import com.sw.dualscreen.utils.GlideUtils
@@ -55,7 +44,6 @@ import com.sw.dualscreen.viewmodel.UserViewModel
import com.sw.plate.utils.LightManager
import com.sw.plate.utils.ToastUtils
import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel
import kotlinx.coroutines.launch
import timber.log.Timber
import java.io.File
import java.time.LocalDateTime
@@ -68,12 +56,20 @@ import kotlin.math.roundToInt
/**
* 服务员显示界面
*/
@SuppressLint("NotifyDataSetChanged")
class MainActivity : BaseActivity<ActivityMainBinding>() {
companion object {
const val TIME_OUT = 5 * 60 * 1000L
}
private val viewModel by viewModels<UserViewModel>()
private var imageCapture: ImageCapture? = null
private var isAnalyzing = true // 控制是否进行图像分析
private var presentation: SecondaryScreenPresentation? = null
// private var presentation: SecondaryScreenPresentation? = null
private var presentation: MainScreenPresentation? = null
private val executor = Executors.newSingleThreadExecutor()
private val handler by lazy { Handler(Looper.getMainLooper()) }
private val searchFoodList = mutableListOf<FoodInfo>()
private val adapter by lazy {
SearchFoodAdapter(searchFoodList).apply {
@@ -86,8 +82,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
item.photoUri = null
checkedItem = item
notifyDataSetChanged()
// TODO 暂时注释---------------
//updateCurrentFood(item)
updateCurrentFood(item)
}
}
}
@@ -111,19 +106,27 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
override fun initialize() {
super.initialize()
FoodModule.init(this)
initView()
setupSecondaryDisplay()
setupCamera()
initData()
FoodModule.init(this)
}
private fun initData() {
viewModel.getEquipmentToken()
presentation?.step1()
// presentation?.step1()
presentation?.step1FoodRecognizing()
}
private fun initView() {
// // 在Activity中获取DPI
// val metrics = getResources()?.displayMetrics
// val screenDPI = metrics?.densityDpi
// val density = metrics?.density
//
// Log.d("ScreenInfo", "DPI: $screenDPI, density: $density");
viewModel.activeEngine()
updateDateTime()
binding.ivSetting.setOnClickListener {
@@ -143,8 +146,13 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
binding.previewView.clipToOutline = true
binding.tvToSearch.setOnClickListener {
bottomSheetDialog = CustomBottomSheetDialog.newInstance(viewModel) {
checkedItem = it
it.photoUri = null
updateCurrentFood(it)
searchFoodList.forEachIndexed { index, info ->
searchFoodList[index].isChecked = false
}
adapter.notifyDataSetChanged()
}
bottomSheetDialog!!.show(supportFragmentManager, "CustomBottomSheetDialog")
}
@@ -155,9 +163,9 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
// arrayOf("即放即取", "余量取餐")
//)
//binding.spinner.adapter = spinnerAdapter
val mode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0)
//binding.spinner.setSelection(mode ?: 0)
presentation?.updateMealPickupMode(mode ?: 0)
// val mode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0)
// //binding.spinner.setSelection(mode ?: 0)
// presentation?.updateMealPickupMode(mode ?: 0)
//binding.spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
// override fun onItemSelected(
// parent: AdapterView<*>?,
@@ -196,6 +204,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
debouncer.debounce { recognizeFood() }
}
binding.btnPay.setOnClickListener {
presentation?.dismiss()
startActivity(Intent(this, PayActivity::class.java).apply {
putExtra(PayActivity.FOOD_INFO, checkedItem)
})
@@ -233,38 +242,26 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
override fun registerDataChange() {
super.registerDataChange()
//// lifecycleScope.launch {
//// viewModel.identifiedFoodInfoList2.collect { list ->
//// updateFoodInfo(list)
//// }
//// }
// lifecycleScope.launch {
// viewModel.identifiedFoodInfoList2.collect { list ->
// updateFoodInfo(list)
// viewModel.identifiedFoodInfoList.collect { list ->
// //adapter.updateData(list)
// searchFoodList.clear()
// searchFoodList.addAll(list)
// adapter.notifyDataSetChanged()
//
// if (list.isNotEmpty()) {
// checkedItem = list[0]
// checkedItem!!.photoUri = lastPhotoUri
// updateCurrentFood(checkedItem)
// lastPhotoUri = null
// }
// }
// }
lifecycleScope.launch {
viewModel.identifiedFoodInfoList.collect { list ->
//adapter.updateData(list)
searchFoodList.clear()
searchFoodList.addAll(list)
adapter.notifyDataSetChanged()
//TODO 暂时写死数据测试---------------------------
binding.flPay.visible()
binding.recyclerview.visible()
searchFoodList.clear()
searchFoodList.add(FoodInfo(foodName = "岐山臊子面"))
searchFoodList.add(FoodInfo(foodName = "肉末干拌面"))
searchFoodList.add(FoodInfo(foodName = "雪菜肉丝面"))
searchFoodList.add(FoodInfo(foodName = "康帅博方便面"))
searchFoodList.add(FoodInfo(foodName = "担担面"))
searchFoodList.add(FoodInfo(foodName = "四合一干拌面"))
adapter.notifyDataSetChanged()
if (list.isNotEmpty()) {
checkedItem = list[0]
checkedItem!!.photoUri = lastPhotoUri
updateCurrentFood(checkedItem)
lastPhotoUri = null
}
}
}
SensorScaleUtils.addWeightListener { weight ->
Timber.d("registerDataChange weight = $weight")
@@ -286,7 +283,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
}
}
private fun recognizeFood() {
fun recognizeFood() {
LightManager.openRedLight()
LightManager.openGreenLight()
takePhoto { photoUri ->
@@ -325,7 +322,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
binding.layoutRescan.visibility = View.GONE
viewModel.getFoodInfo(foodName) { list ->
runOnUiThread {
updateFoodInfo(list, scoreList)
updateFoodInfo(list.toMutableList(), scoreList)
}
}
}
@@ -335,8 +332,22 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
}
@SuppressLint("NotifyDataSetChanged")
private fun updateFoodInfo(list: List<FoodInfo>, scoreList: List<IdNameScore>) {
Timber.d("registerDataChange识别后查询接口数据:${GsonUtils.toJson(list)}")
private fun updateFoodInfo(list: MutableList<FoodInfo>, scoreList: List<IdNameScore>) {
Timber.d("registerDataChange识别后查询接口数据:${GsonUtils.toJson(list)},识别数据:${GsonUtils.toJson(scoreList)}")
// TODO: 临时测试数据,后续删除-------------------
//if (list.isEmpty()) {
// //binding.flPay.visible()
// //binding.recyclerview.visible()
// //list.add(FoodInfo(foodName = "豆角茄子"))
// //list.add(FoodInfo(foodName = "豆芽炒粉条"))
// //list.add(FoodInfo(foodName = "岐山臊子面"))
// //list.add(FoodInfo(foodName = "肉末干拌面"))
// //list.add(FoodInfo(foodName = "雪菜肉丝面"))
// //list.add(FoodInfo(foodName = "康帅博方便面"))
// //list.add(FoodInfo(foodName = "担担面"))
// //list.add(FoodInfo(foodName = "四合一干拌面"))
//}
// TODO: 临时测试数据,后续删除-------------------
list.forEach { foodInfo ->
val scoreItem = scoreList.firstOrNull { it.name == foodInfo.foodName }
val score = scoreItem?.score ?: 0.0
@@ -349,9 +360,17 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
if (list.isNotEmpty()) {
checkedItem = list[0]
checkedItem!!.photoUri = lastPhotoUri
checkedItem!!.isChecked = true
adapter.notifyItemChanged(0)
updateCurrentFood(checkedItem)
lastPhotoUri = null
LightManager.closeRedLight()
binding.flPay.visible()
binding.tvToSearch.visible()
} else {
binding.flPay.gone()
binding.tvToSearch.gone()
}
}
@@ -532,21 +551,34 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
if (displays.size > 1) {
val secondaryDisplay = displays[1]
// 创建副屏的Presentation
presentation =
SecondaryScreenPresentation(
presentation = MainScreenPresentation(
activity = this,
this,
display = secondaryDisplay,
viewModel = viewModel,
userViewModel = viewModel,
recognizeViewModel = viewModels<RecognizeViewModel>().value
)
presentation!!.setStepChangeCallback { step ->
).apply {
setStepChangeCallback { step ->
if (step == 1) {
Timber.d("setStepChangeCallback${step}")
updateCurrentFood(null)
bottomSheetDialog?.dismiss()
}
}
}
//SecondaryScreenPresentation(
// activity = this,
// this,
// display = secondaryDisplay,
// viewModel = viewModel,
// recognizeViewModel = viewModels<RecognizeViewModel>().value
//)
// presentation!!.setStepChangeCallback { step ->
// if (step == 1) {
// Timber.d("setStepChangeCallback${step}")
// updateCurrentFood(null)
// bottomSheetDialog?.dismiss()
// }
// }
presentation!!.show()
} else {
ToastUtils.showToast("获取副屏失败")
@@ -568,9 +600,49 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
}
private fun updateDateTime() {
val formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 EEEE", Locale.CHINA)
val formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 E", Locale.CHINA)
val dateTime = formatter.format(LocalDateTime.now(ZoneId.of("Asia/Shanghai")))
binding.tvTitleTime.text = dateTime
}
override fun onResume() {
super.onResume()
// 启动定时器
handler.postDelayed(timeoutRunnable, TIME_OUT)
if (presentation?.isShowing == false) {
setupSecondaryDisplay()
}
val mode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0)
presentation?.updateMealPickupMode(mode ?: 0)
}
override fun onPause() {
super.onPause()
// 取消定时器
handler.removeCallbacks(timeoutRunnable)
}
private var lastTouchTime = 0L
private val timeoutRunnable = Runnable {
//显示待机页面
presentation?.loadStandbyScreen()
}
override fun onUserInteraction() {
super.onUserInteraction()
if (System.currentTimeMillis() - lastTouchTime >= TIME_OUT) {
//隐藏待机页面
presentation?.hideStandbyScreen()
}
// 重置定时器
handler.removeCallbacks(timeoutRunnable)
handler.postDelayed(timeoutRunnable, TIME_OUT)
lastTouchTime = System.currentTimeMillis()
}
fun resetTouchTime() {
handler.removeCallbacks(timeoutRunnable)
handler.postDelayed(timeoutRunnable, TIME_OUT)
lastTouchTime = System.currentTimeMillis()
}
}
@@ -4,15 +4,16 @@ import android.view.View
import androidx.activity.viewModels
import androidx.fragment.app.Fragment
import com.sw.dualscreen.R
import com.sw.dualscreen.activity.fragment.CashPayFragment
import com.sw.dualscreen.activity.fragment.FacePayFragment
import com.sw.dualscreen.activity.fragment.NumberPayFragment
import com.sw.dualscreen.activity.fragment.PayResultFragment
import com.sw.dualscreen.activity.fragment.QrCodePayFragment
import com.sw.dualscreen.activity.fragment.pay.CashPayFragment
import com.sw.dualscreen.activity.fragment.pay.FacePayFragment
import com.sw.dualscreen.activity.fragment.pay.NumberPayFragment
import com.sw.dualscreen.activity.fragment.pay.PayResultFragment
import com.sw.dualscreen.activity.fragment.pay.QrCodePayFragment
import com.sw.dualscreen.databinding.ActivityPayBinding
import com.sw.dualscreen.model.response.FoodInfo
import com.sw.dualscreen.viewmodel.BaseViewModel
import com.sw.dualscreen.viewmodel.UserViewModel
import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel
class PayActivity : BaseActivity<ActivityPayBinding>() {
@@ -26,10 +27,11 @@ class PayActivity : BaseActivity<ActivityPayBinding>() {
const val TAG_PAY_RESULT = "tagPayResult"
}
private val viewModel by viewModels<UserViewModel>()
val userViewModel by viewModels<UserViewModel>()
val recognizeViewModel by viewModels<RecognizeViewModel>()
override fun getViewModel(): BaseViewModel {
return viewModel
return userViewModel
}
override fun inflateViewBinding(): ActivityPayBinding {
@@ -125,4 +127,12 @@ class PayActivity : BaseActivity<ActivityPayBinding>() {
addFragment(payResultFragment!!, TAG_PAY_RESULT)
}
override fun onDestroy() {
qrCodePayFragment?.presentation?.dismiss()
cashPayFragment?.presentation?.dismiss()
numberPayFragment?.presentation?.dismiss()
facePayFragment?.presentation?.dismiss()
super.onDestroy()
}
}
@@ -1,6 +1,9 @@
package com.sw.dualscreen.activity.fragment
import android.content.Context
import android.hardware.display.DisplayManager
import android.os.Bundle
import android.view.Display
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -9,6 +12,10 @@ import androidx.viewbinding.ViewBinding
abstract class BaseFragment<VB : ViewBinding>: Fragment() {
val displays: Array<Display> by lazy {
val displayManager = requireActivity().getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
displayManager.displays
}
protected lateinit var binding: VB
protected abstract fun inflateViewBinding(): VB
@@ -6,6 +6,7 @@ import com.sw.dualscreen.R
import com.sw.dualscreen.databinding.FragmentBusinessBinding
import com.sw.dualscreen.sdk.SensorScaleUtils
import com.sw.dualscreen.utils.SPUtil
import com.sw.plate.utils.ToastUtils
import kotlin.math.roundToInt
class BusinessFragment: BaseFragment<FragmentBusinessBinding>() {
@@ -34,6 +35,21 @@ class BusinessFragment: BaseFragment<FragmentBusinessBinding>() {
binding.btnClearZero.setOnClickListener {
SensorScaleUtils.tare()
}
binding.btnConfirm.setOnClickListener {
if (binding.rgCharge.checkedRadioButtonId == -1) {
ToastUtils.showToast("请设置计费模式")
return@setOnClickListener
}
if (binding.rgTakeFood.checkedRadioButtonId == -1) {
ToastUtils.showToast("请设置取餐模式")
return@setOnClickListener
}
var mode = if (binding.rgCharge.checkedRadioButtonId == R.id.rbChargeYes) 0 else 1
SPUtil.getInstance().put(GlobalKey.KEY_CHARGE_MODE, mode)
mode = if (binding.rgTakeFood.checkedRadioButtonId == R.id.rbPickAndPlace) 0 else 1
SPUtil.getInstance().put(GlobalKey.KEY_PICKUP_MODE, mode)
}
SensorScaleUtils.addWeightListener { value ->
val realWeight = (value * 1000).roundToInt()
binding.tvFoodWeight.text = "$realWeight"
@@ -1,25 +0,0 @@
package com.sw.dualscreen.activity.fragment
import android.text.Spanned
import android.text.SpannedString
import android.text.style.AbsoluteSizeSpan
import androidx.core.text.buildSpannedString
import com.sw.dualscreen.databinding.FragmentCashPayBinding
class CashPayFragment: BaseFragment<FragmentCashPayBinding>() {
override fun inflateViewBinding(): FragmentCashPayBinding {
return FragmentCashPayBinding.inflate(layoutInflater)
}
override fun initialize() {
binding.tvRealAmount.text = getAmountText("36.8")
}
private fun getAmountText(amount:String): SpannedString {
return buildSpannedString {
append("¥", AbsoluteSizeSpan(36, true), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
append(amount, AbsoluteSizeSpan(60, true), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
}
}
@@ -60,6 +60,7 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
setOnItemClickListener { adapter, view, position ->
checkedItem = searchFoodList[position]
selectedFoodId = checkedItem!!.id
selectedFoodName = checkedItem!!.foodName
val isChecked = checkedItem!!.isChecked
searchFoodList[position].isChecked = isChecked.not()
notifyItemChanged(position)
@@ -1,19 +0,0 @@
package com.sw.dualscreen.activity.fragment
import com.sw.dualscreen.activity.PayActivity
import com.sw.dualscreen.databinding.FragmentFacePayBinding
class FacePayFragment : BaseFragment<FragmentFacePayBinding>() {
override fun inflateViewBinding(): FragmentFacePayBinding {
return FragmentFacePayBinding.inflate(layoutInflater)
}
private var payActivity: PayActivity? = null
override fun initialize() {
payActivity = activity as PayActivity
binding.tvFaceState.setOnClickListener {
payActivity?.showPayResult()
}
}
}
@@ -1,15 +0,0 @@
package com.sw.dualscreen.activity.fragment
import com.sw.dualscreen.databinding.FragmentNumberPayBinding
import com.sw.dualscreen.ext.hideKeyboard
class NumberPayFragment: BaseFragment<FragmentNumberPayBinding>() {
override fun inflateViewBinding(): FragmentNumberPayBinding {
return FragmentNumberPayBinding.inflate(layoutInflater)
}
override fun initialize() {
binding.root.hideKeyboard()
}
}
@@ -1,29 +0,0 @@
package com.sw.dualscreen.activity.fragment
import android.text.Spanned
import android.text.SpannedString
import android.text.style.AbsoluteSizeSpan
import androidx.core.text.buildSpannedString
import com.sw.dualscreen.R
import com.sw.dualscreen.databinding.FragmentQrcodePayBinding
class QrCodePayFragment: BaseFragment<FragmentQrcodePayBinding>() {
override fun inflateViewBinding(): FragmentQrcodePayBinding {
return FragmentQrcodePayBinding.inflate(layoutInflater)
}
override fun initialize() {
binding.tvRealAmount.text = getAmountText("36.8")
binding.ivPayQrCode.setImageResource(R.drawable.ic_qrcode)
binding.tvPayResult.text = "待支付(60s)..."
}
private fun getAmountText(amount:String): SpannedString {
return buildSpannedString {
append("¥", AbsoluteSizeSpan(36, true), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
append(amount, AbsoluteSizeSpan(60, true), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
}
}
@@ -0,0 +1,63 @@
package com.sw.dualscreen.activity.fragment.pay
import android.text.Spanned
import android.text.SpannedString
import android.text.style.AbsoluteSizeSpan
import androidx.core.text.buildSpannedString
import com.sw.dualscreen.activity.PayActivity
import com.sw.dualscreen.activity.fragment.BaseFragment
import com.sw.dualscreen.databinding.FragmentCashPayBinding
import com.sw.dualscreen.presentation.pay.CashPayPresentation
class CashPayFragment : BaseFragment<FragmentCashPayBinding>() {
private lateinit var payActivity: PayActivity
private var foodName: String? = null
private var payAmount: String? = null
var presentation: CashPayPresentation? = null
override fun inflateViewBinding(): FragmentCashPayBinding {
return FragmentCashPayBinding.inflate(layoutInflater)
}
override fun initialize() {
payActivity = requireActivity() as PayActivity
foodName = payActivity.foodInfo?.foodName
payAmount = "36.80"
binding.tvRealAmount.text = getAmountText(payAmount?:"")
showSubScreen()
}
override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden)
if (hidden) {
binding.root.postDelayed({
presentation?.dismiss()
}, 500)
return
}
showSubScreen()
}
private fun showSubScreen() {
// 查找副屏(通常索引为1
if (displays.size > 1) {
presentation = CashPayPresentation(activity = payActivity, display = displays[1]) {
presentation?.dismiss()
}.also {
it.foodName = foodName
it.payAmount = payAmount
}
presentation?.show()
}
}
private fun getAmountText(amount: String): SpannedString {
return buildSpannedString {
append("¥", AbsoluteSizeSpan(36, true), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
append(amount, AbsoluteSizeSpan(60, true), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
}
}
@@ -0,0 +1,57 @@
package com.sw.dualscreen.activity.fragment.pay
import com.sw.dualscreen.activity.PayActivity
import com.sw.dualscreen.activity.fragment.BaseFragment
import com.sw.dualscreen.databinding.FragmentFacePayBinding
import com.sw.dualscreen.presentation.pay.CashPayPresentation
import com.sw.dualscreen.presentation.pay.FacePayPresentation
class FacePayFragment : BaseFragment<FragmentFacePayBinding>() {
private var payActivity: PayActivity? = null
private var foodName: String? = null
private var payAmount: String? = null
var presentation: FacePayPresentation? = null
override fun inflateViewBinding(): FragmentFacePayBinding {
return FragmentFacePayBinding.inflate(layoutInflater)
}
override fun initialize() {
payActivity = activity as PayActivity
binding.tvFaceState.setOnClickListener {
payActivity?.showPayResult()
}
foodName = payActivity?.foodInfo?.foodName
payAmount = "36.80"
showSubScreen()
}
override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden)
if (hidden) {
binding.root.postDelayed({
presentation?.dismiss()
}, 500)
return
}
showSubScreen()
}
private fun showSubScreen() {
// 查找副屏(通常索引为1
if (displays.size > 1) {
presentation = FacePayPresentation(
activity = payActivity!!,
display = displays[1],
userViewModel = payActivity!!.userViewModel,
recognizeViewModel = payActivity!!.recognizeViewModel,
) {
presentation?.dismiss()
}.also {
it.foodName = foodName
it.payAmount = payAmount
}
presentation?.show()
}
}
}
@@ -0,0 +1,52 @@
package com.sw.dualscreen.activity.fragment.pay
import com.sw.dualscreen.activity.PayActivity
import com.sw.dualscreen.activity.fragment.BaseFragment
import com.sw.dualscreen.databinding.FragmentNumberPayBinding
import com.sw.dualscreen.ext.hideKeyboard
import com.sw.dualscreen.presentation.pay.CashPayPresentation
class NumberPayFragment: BaseFragment<FragmentNumberPayBinding>() {
private lateinit var payActivity: PayActivity
private var foodName: String? = null
private var payAmount: String? = null
var presentation: CashPayPresentation? = null
override fun inflateViewBinding(): FragmentNumberPayBinding {
return FragmentNumberPayBinding.inflate(layoutInflater)
}
override fun initialize() {
binding.root.hideKeyboard()
payActivity = requireActivity() as PayActivity
foodName = payActivity.foodInfo?.foodName
payAmount = "36.80"
showSubScreen()
}
override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden)
if (hidden) {
binding.root.postDelayed({
presentation?.dismiss()
}, 500)
return
}
showSubScreen()
}
private fun showSubScreen() {
// 查找副屏(通常索引为1
if (displays.size > 1) {
presentation = CashPayPresentation(activity = payActivity, display = displays[1]) {
presentation?.dismiss()
}.also {
it.foodName = foodName
it.payAmount = payAmount
}
presentation?.show()
}
}
}
@@ -1,6 +1,7 @@
package com.sw.dualscreen.activity.fragment
package com.sw.dualscreen.activity.fragment.pay
import com.sw.dualscreen.activity.PayActivity
import com.sw.dualscreen.activity.fragment.BaseFragment
import com.sw.dualscreen.databinding.FragmentPayResultBinding
class PayResultFragment: BaseFragment<FragmentPayResultBinding>() {
@@ -0,0 +1,95 @@
package com.sw.dualscreen.activity.fragment.pay
import android.text.Spanned
import android.text.SpannedString
import android.text.style.AbsoluteSizeSpan
import androidx.core.text.buildSpannedString
import androidx.lifecycle.lifecycleScope
import com.sw.dualscreen.R
import com.sw.dualscreen.activity.PayActivity
import com.sw.dualscreen.activity.fragment.BaseFragment
import com.sw.dualscreen.databinding.FragmentQrcodePayBinding
import com.sw.dualscreen.ext.load
import com.sw.dualscreen.presentation.pay.QrCodePayPresentation
import com.sw.dualscreen.utils.countDownByFlow
import kotlinx.coroutines.Job
class QrCodePayFragment: BaseFragment<FragmentQrcodePayBinding>() {
private lateinit var payActivity: PayActivity
private var foodName: String? = null
private var payAmount: String? = null
private var payQrCodePic: Any? = null
var presentation: QrCodePayPresentation?=null
override fun inflateViewBinding(): FragmentQrcodePayBinding {
return FragmentQrcodePayBinding.inflate(layoutInflater)
}
override fun initialize() {
payActivity = requireActivity() as PayActivity
foodName = payActivity.foodInfo?.foodName
payAmount = "36.80"
payQrCodePic = R.drawable.ic_qrcode
binding.tvRealAmount.text = getAmountText(payAmount?:"")
binding.ivPayQrCode.load(payQrCodePic)
binding.tvPayResult.text = "待支付(60s)..."
startCountDown()
showSubScreen()
}
override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden)
if (hidden) {
binding.root.postDelayed({
presentation?.dismiss()
}, 500)
return
}
showSubScreen()
}
private fun showSubScreen() {
// 查找副屏(通常索引为1
if (displays.size > 1) {
presentation = QrCodePayPresentation(activity = payActivity, display = displays[1]) {
presentation?.dismiss()
}.also {
it.foodName = foodName
it.payAmount = payAmount
it.payQrCodePic = payQrCodePic
}
presentation?.show()
}
}
private fun getAmountText(amount:String): SpannedString {
return buildSpannedString {
append("¥", AbsoluteSizeSpan(36, true), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
append(amount, AbsoluteSizeSpan(60, true), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
}
private fun startCountDown() {
countDownJob = countDownByFlow(
total = 60,
scope = lifecycleScope,
onStart = {
binding.tvPayResult.text = "待支付(60s)..."
},
onTick = { seconds ->
binding.tvPayResult.text = "待支付(${seconds}s)..."
},
onFinish = {
binding.tvPayResult.text = "支付倒计时结束"
}
)
}
private var countDownJob: Job? = null
override fun onDestroy() {
super.onDestroy()
countDownJob?.cancel() // 自动取消订阅,防止内存泄漏
}
}
@@ -21,7 +21,7 @@ class CollectedFoodNewAdapter(var list: MutableList<CollectedFoodBean>) :
override fun onBindViewHolder(holder: VH, position: Int, item: CollectedFoodBean?) {
holder.binding.tvFoodName.text = item?.foodName
holder.binding.tvCollectedNum.text = "已采集153"
holder.binding.tvCollectedNum.text = "已采集${item?.count}"
}
}
@@ -26,7 +26,7 @@ class SearchFoodAdapter(var list: MutableList<FoodInfo>) :
text = item.foodName
} else {
val score = (item.score / 100.0).format2String(2)
text = "${item.foodName}\n${score}%"
text = item.foodName + if (item.score != 0) "-${score}%" else ""
}
isChecked = item.isChecked
}
@@ -2,8 +2,10 @@ package com.sw.dualscreen.ext
import android.graphics.drawable.GradientDrawable
import android.view.View
import android.widget.ImageView
import androidx.annotation.ColorInt
import androidx.core.graphics.toColorInt
import com.sw.dualscreen.utils.GlideUtils
/**
* 设置圆角边框
@@ -28,3 +30,7 @@ fun View.setRoundedBorder(
private fun View.dpToPx(dp: Float): Float {
return dp * context.resources.displayMetrics.density
}
fun ImageView.load(url: Any?) {
GlideUtils.loadImage(context, url, this)
}
@@ -13,7 +13,9 @@ import java.util.concurrent.TimeUnit
object ApiClient {
private const val BASE_URL = "http://device.shuziweidao.com:8889/"
private const val TIME_OUT = 30L // 超时时间(秒)
//private const val TIME_OUT = 30L // 超时时间(秒)
//todo 临时测试改为5秒
private const val TIME_OUT = 5L // 超时时间(秒)
private val okHttpClient = OkHttpClient.Builder()
.connectTimeout(TIME_OUT, TimeUnit.SECONDS)
@@ -26,4 +26,5 @@ data class FoodCollectionBean(
data class CollectedFoodBean(
var foodName: String?,
var count: Int = 0
)
@@ -10,6 +10,7 @@ import com.sw.dualscreen.MyApp
import com.sw.dualscreen.utils.AssetsTool
import com.sw.dualscreen.utils.GsonUtils
import com.sw.dualscreen.utils.ImageUtil
import com.sw.plate.App
import io.objectbox.Box
import io.objectbox.kotlin.boxFor
import io.objectbox.query.Query
@@ -25,7 +26,7 @@ import java.io.InputStream
object FoodModule {
private const val THRESHOLD = 0.8
private lateinit var module_mobile: Module
private var module_mobile: Module?=null
private lateinit var box: Box<Food>
private lateinit var embeddingsList: List<List<Float>>
private lateinit var labelsList: IntArray
@@ -61,7 +62,10 @@ object FoodModule {
NO_MEAN_RGB, // [0.485, 0.456, 0.406] TORCHVISION_NORM_MEAN_RGB
NO_STD_RGB // [0.229, 0.224, 0.225] TORCHVISION_NORM_STD_RGB
)
val outputTensor = module_mobile.forward(IValue.from(inputTensor)).toTensor()
if (module_mobile == null) {
init(App.getContext())
}
val outputTensor = module_mobile!!.forward(IValue.from(inputTensor)).toTensor()
return outputTensor.dataAsFloatArray
}
@@ -103,6 +107,9 @@ object FoodModule {
fun getFoodScoreList(bitmap: Bitmap, queryCount: Int = 15): List<IdNameScore> {
val floatArray = bitmap2FloatArray(bitmap)
val nameScoreList = queryFoodNameScore(floatArray, queryCount)
if (nameScoreList.isEmpty()) {
return emptyList()
}
val maxScoreList = nameScoreList
.filter { it.score < 1 - THRESHOLD }
.groupBy { it.name }
@@ -0,0 +1,962 @@
package com.sw.dualscreen.presentation
import android.Manifest
import android.app.Presentation
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Outline
import android.graphics.Point
import android.hardware.Camera
import android.os.Build
import android.os.Bundle
import android.view.Display
import android.view.View
import android.view.ViewGroup
import android.view.ViewOutlineProvider
import android.view.ViewTreeObserver
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import com.arcsoft.face.ErrorInfo
import com.sw.dualscreen.GlobalKey
import com.sw.dualscreen.R
import com.sw.dualscreen.activity.MainActivity
import com.sw.dualscreen.databinding.PresentationMainScreenBinding
import com.sw.dualscreen.ext.dp
import com.sw.dualscreen.ext.format2String
import com.sw.dualscreen.ext.gone
import com.sw.dualscreen.ext.maskName
import com.sw.dualscreen.ext.visible
import com.sw.dualscreen.model.request.UserNutritionParam
import com.sw.dualscreen.model.response.DinnerTypeInfo
import com.sw.dualscreen.model.response.FoodInfo
import com.sw.dualscreen.model.response.UserNutritionData
import com.sw.dualscreen.utils.Debouncer
import com.sw.dualscreen.utils.GlideUtils
import com.sw.dualscreen.utils.GsonUtils
import com.sw.dualscreen.utils.SPUtil
import com.sw.dualscreen.viewmodel.UserViewModel
import com.sw.plate.utils.LightManager
import com.sw.plate.utils.ToastUtils
import com.sw.plate.utils.arcface.ConfigUtil
import com.sw.plate.utils.arcface.ErrorCodeUtil
import com.sw.plate.utils.arcface.FaceRectTransformer
import com.sw.plate.utils.arcface.FaceRectView
import com.sw.plate.utils.arcface.FaceRectView.DrawInfo
import com.sw.plate.utils.arcface.PreviewConfig
import com.sw.plate.utils.arcface.camera.CameraListener
import com.sw.plate.utils.arcface.camera.DualCameraHelper
import com.sw.plate.utils.arcface.face.constants.LivenessType
import com.sw.plate.utils.arcface.face.model.CompareResult
import com.sw.plate.utils.arcface.face.model.FacePreviewInfo
import com.sw.plate.utils.arcface.face.model.RecognizeConfiguration
import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.launch
import timber.log.Timber
import kotlin.math.roundToInt
class MainScreenPresentation(
val activity: MainActivity,
display: Display,
val userViewModel: UserViewModel,
val recognizeViewModel: RecognizeViewModel,
private val onDismissListener: () -> Unit = {}
) : Presentation(activity, display), ViewTreeObserver.OnGlobalLayoutListener {
companion object {
const val TAG = "SubScreenPresentation"
}
private lateinit var binding: PresentationMainScreenBinding
private lateinit var stepChangeCallback: (Int) -> Unit
// 当前步骤
var currentStep: Int = 0
private var isGoStep1 = false;//防止持续调用
// 虹软人脸配置 ⬇
private var isRecognition = false
private var rgbCameraHelper: DualCameraHelper? = null
private var rgbFaceRectTransformer: FaceRectTransformer? = null
private val livenessType = LivenessType.IR
private var openRectInfoDraw = false
private var irCameraHelper: DualCameraHelper? = null
private var irFaceRectTransformer: FaceRectTransformer? = null
private var detectWeight = 0.0 //识别菜品时的重量
private var lastWeight = 0.0 // 上一次的计算热量结果
private var userNutritionData: UserNutritionData? = null
private var dinnerTypeInfo: DinnerTypeInfo? = null
private val debouncer = Debouncer(500)
private var recognitionTime: Long = 0L // 人脸识别时的时间
private var recognitionWeight: Double = 0.0 // 人脸识别时的重量
private var lastFaceTrackId: Int = -1 // 上一次的人脸信息
var mealPickupMode: Int = 0 // 取餐模式 0 即放即取 1 余量取餐
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = PresentationMainScreenBinding.inflate(layoutInflater)
setContentView(binding.root)
mealPickupMode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0) ?: 0
window?.setBackgroundDrawableResource(android.R.color.transparent)
initView()
setupArcCamera()
registerDataChange()
}
fun updateWeight(weight: Double) {
lastWeight = weight * 1000 // 将千克转成克
Timber.tag(TAG).d("updateWeight lastWeight = $weight, currentStep = $currentStep, isGoStep1 = $isGoStep1")
if ((currentStep == 2) &&
lastWeight <= 5 &&
isGoStep1
) {//人脸识别过程中,秤上物品拿走
isGoStep1 = false
activity.runOnUiThread {
Timber.tag(TAG).e("切回step1")
step1FoodRecognizing()
}
}
// if (currentStep != 3) {
// return
// }
if (currentFood == null) {
Timber.tag(TAG).e("updateWeight currentFood is null")
return
}
if (userNutritionData == null) {
Timber.tag(TAG).e("updateWeight userNutritionData is null")
return
}
if (dinnerTypeInfo == null) {
Timber.tag(TAG).e("updateWeight dinnerType is null")
return
}
calculateNutrition(recognitionWeight - lastWeight)
}
fun setStepChangeCallback(callback: (Int) -> Unit) {
stepChangeCallback = callback
}
override fun onGlobalLayout() {
Timber.tag(TAG).d("onGlobalLayout")
binding.dualCameraTexturePreviewRgb.getViewTreeObserver().removeOnGlobalLayoutListener(this)
openCamera()
}
private fun openCamera() {
Timber.tag(TAG).d("openCamera")
if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) !=
PackageManager.PERMISSION_GRANTED
) {
ToastUtils.showToast("无摄像头权限")
return
}
try {
val cameraCount = Camera.getNumberOfCameras()
if (cameraCount < 3) {
ToastUtils.showToast("摄像头数量异常")
return
}
recognizeViewModel.init(
PreviewConfig(
2, 1, 90, 90
)
)
initRgbCamera()
if (DualCameraHelper.hasDualCamera() && livenessType === LivenessType.IR) {
initIrCamera()
}
} catch (e: Exception) {
Timber.tag(TAG).e(e, "打开摄像头失败")
}
}
/**
* 启用虹软人脸失败
*/
private fun setupArcCamera() {
initArcViewModel()
initArcView()
openRectInfoDraw = true
}
private fun registerDataChange() {
activity.lifecycleScope.launch {
userViewModel.loadFaceResult.collect { needUpdate ->
if (needUpdate) {
recognizeViewModel.refreshFaceList()
}
}
}
activity.lifecycleScope.launch {
userViewModel.nutritionData.collect {
Timber.tag(TAG).d("registerDataChange nutritionData = $it")
userNutritionData = it
if (it == null) return@collect
step3ShowRecognizeResult()
//binding.nutritionInclude.tvUserName.text = it.userName.maskName()
//binding.nutritionInclude.tvRecommendHeat.text = "推荐热量:${it.recommendMin}-${it.recommendMax}"
//updateWeight(lastWeight / 1000)
}
}
activity.lifecycleScope.launch {
userViewModel.dinnerTypeInfo.drop(1).collect {
Timber.tag(TAG).d("registerDataChange dinnerTypeInfo = $it")
dinnerTypeInfo = it
}
}
}
/**
* 显示识别出的菜品信息
*/
fun step3ShowRecognizeResult() {
Timber.tag(TAG).d("step3")
LightManager.closeRedLight()
LightManager.openGreenLight()
currentStep = 3
stepChangeCallback(currentStep)
if (currentFood == null) {
ToastUtils.showToast("选中物品为空")
return
}
updateFoodInfo(currentFood!!)
// binding.flPreview.visibility = View.VISIBLE
// binding.ivRecImage.visibility = View.VISIBLE
// binding.flFace.visibility = View.INVISIBLE
//// binding.ivFaceBg.visibility = View.GONE
// binding.tvFaceTip.visibility = View.GONE
//
//// binding.tvBottomTip.visibility = View.GONE
// binding.nutritionInclude.clNutritionData.visibility = View.VISIBLE
foodRecSuccess(currentFood!!)
}
private fun initView() {
binding.ivRecImage.let {
it.outlineProvider = object : ViewOutlineProvider() {
override fun getOutline(view: View, outline: Outline) {
outline.setRoundRect(0, 0, view.width, view.height, 12f.dp)
}
}
it.clipToOutline = true
}
}
override fun onDisplayRemoved() {
super.onDisplayRemoved()
onDismissListener()
}
/**
* 加载待机页面
*/
fun loadStandbyScreen() {
binding.llStandbyPage.visible()
}
fun hideStandbyScreen() {
binding.llStandbyPage.gone()
}
/**
* 餐品识别成功,计费模式
*/
fun loadBilledMode(foodInfo: FoodInfo) {
binding.calorieInclude.root.visible()
binding.nutritionInclude.root.gone()
binding.flCameraView.visible()
binding.ivFaceRecMask.gone()
binding.tvFaceTip.gone()
binding.tvFoodRecPrompt.gone()
binding.llPriceInfo.visible()
binding.ivRecImage.gone()
//updateFoodInfo(foodInfo)
userNutritionData?.let {
Timber.tag(TAG).d("loadBilledMode${GsonUtils.toJson(it)}")
}
}
/**
* 餐品识别成功,不计费模式
*/
fun loadUnbilledMode(foodInfo: FoodInfo) {
binding.calorieInclude.root.gone()
binding.nutritionInclude.root.visible()
binding.flCameraView.visible()
binding.ivFaceRecMask.gone()
binding.tvFaceTip.gone()
binding.tvFoodRecPrompt.gone()
binding.llPriceInfo.gone()
binding.ivRecImage.gone()
//updateFoodInfo(foodInfo)
userNutritionData?.let {
binding.nutritionInclude.tvUserName.text = it.userName.maskName()
binding.nutritionInclude.tvRecommendHeat.text = "推荐热量:${it.recommendMin}-${it.recommendMax}kcal"
updateWeight(lastWeight / 1000)
}
}
/**
* 餐品识别中
*/
fun step1FoodRecognizing() {
hideStandbyScreen()
activity.resetTouchTime()
Timber.tag(TAG).d("step1")
//if (abs(lastWeight.toInt()) <= 5) {
// activity.runOnUiThread {
// ToastUtils.showToast("秤上重量几乎为0,请确认")
// }
// return
//}
currentStep = 1
LightManager.closeGreenLight()
LightManager.closeRedLight()
dinnerTypeInfo = null
currentFood = null
userNutritionData = null
recognitionWeight = 0.0
stepChangeCallback(currentStep)
binding.calorieInclude.root.gone()
binding.nutritionInclude.root.gone()
binding.tvFoodName.text = "餐品识别中..."
binding.tvFoodRecPrompt.visible()
binding.flCameraView.visible()
binding.ivPreviewImage.visible()
binding.ivRecImage.gone()
binding.ivFaceRecMask.gone()
binding.tvFaceTip.gone()
binding.llPriceInfo.gone()
pauseCamera()
activity.clearFoodList()
}
fun foodRecSuccess(foodInfo: FoodInfo) {
//识别逻辑完成根据是否计费显示不同页面
val mode = SPUtil.getInstance().get(GlobalKey.KEY_CHARGE_MODE, 0)
if (mode == 0) {
//计费
loadBilledMode(foodInfo)
} else {
//不计费
loadUnbilledMode(foodInfo)
}
}
/**
* 用户人脸识别中
*/
fun step2FaceRecognizing(foodInfo: FoodInfo) {
isGoStep1 = true
Timber.tag(TAG).d("step2")
currentStep = 2
LightManager.closeRedLight()
binding.calorieInclude.root.gone()
binding.nutritionInclude.root.gone()
binding.tvFoodName.text = foodInfo.foodName
binding.flFace.visible()
binding.flCameraView.visible()
binding.ivFaceRecMask.visible()
binding.tvFaceTip.visible()
binding.tvFoodRecPrompt.visible()
binding.llPriceInfo.gone()
binding.ivRecImage.gone()
binding.ivPreviewImage.gone()
dinnerTypeInfo = null
userNutritionData = null
recognitionWeight = 0.0
stepChangeCallback(currentStep)
currentFood = foodInfo
userViewModel.getDinnerType()
resumeCamera()
}
// 当前食物信息
private var currentFood: FoodInfo? = null
fun updateFood(foodInfo: FoodInfo?) {
Timber.tag(TAG).d("updateFood")
currentFood = foodInfo
if (foodInfo != null) {
if (currentStep == 1) {
step2FaceRecognizing(foodInfo)
} else {
updateFoodInfo(foodInfo)
}
}
}
private fun updateFoodInfo(foodInfo: FoodInfo) {
binding.tvFoodName.text = foodInfo.foodName
binding.ivPreviewImage.gone()
binding.ivRecImage.visible()
if (foodInfo.photoUri != null) {
GlideUtils.loadRoundCornerImage(
context,
url = foodInfo.photoUri,
imageView = binding.ivRecImage,
radius = 12
)
} else {
GlideUtils.loadRoundCornerImage(
context,
url = foodInfo.imgUrl,
imageView = binding.ivRecImage,
radius = 12
)
}
}
private fun calculateNutrition(weight1: Double) {
var weight = weight1
if (weight < 0) weight = 0.0
debouncer.debounce {
Timber.tag(TAG).d("calculateNutrition weight = $weight, recognitionWeight = $recognitionWeight")
val dinnerType = dinnerTypeInfo!!.dinnerType!!.dinnerType!!
Timber.tag(TAG).d("calculateNutrition foodName = ${currentFood!!.foodName}, dinnerType = $dinnerType, userId = ${userNutritionData!!.userId}")
val calcResultInfo = UserNutritionUtils.calculateNutrition(
currentFood!!,
userNutritionData!!,
weight,
dinnerType = dinnerType
)
Timber.tag(TAG).d("calculateNutrition calcResultInfo = $calcResultInfo")
val columnMax = UserNutritionUtils.getMaxInt(calcResultInfo)
activity.runOnUiThread {
val totalKcal = calcResultInfo.totalKcal
val grain = calcResultInfo.grain
val fruits = calcResultInfo.fruits
val meat = calcResultInfo.meat
// 设置热量
binding.nutritionInclude.customKcalColumn.setImageDrawable(
true,
totalKcal.max < totalKcal.current
)
var maxKcal = totalKcal.max
Timber.tag(TAG).d("maxKcal = $maxKcal")
// 避免0作为被除数
if (maxKcal <= 0.0) {
maxKcal = 1.0
}
Timber.tag(TAG).d("maxKcal 1 = $maxKcal")
binding.nutritionInclude.totalKcalTv.text = totalKcal.current.format2String()
var totalKcalHeight = (totalKcal.current / maxKcal).toFloat()
if (totalKcalHeight == 0F) {
binding.nutritionInclude.divKcalView.visibility = View.VISIBLE
} else {
binding.nutritionInclude.divKcalView.visibility = View.GONE
totalKcalHeight += 0.1F
}
binding.nutritionInclude.customKcalColumn.setCustomHeightPercent(
totalKcalHeight,
true
)
// 设置主食
binding.nutritionInclude.customFoodColumn.setImageDrawable(
false,
grain.max < grain.current
)
binding.nutritionInclude.totalFoodTv.text = grain.current.format2String()
var grainHeight = (grain.current / columnMax).toFloat()
if (grainHeight == 0F) {
binding.nutritionInclude.divTotalFoodView.visibility = View.VISIBLE
} else {
binding.nutritionInclude.divTotalFoodView.visibility = View.GONE
grainHeight += 0.1F
}
binding.nutritionInclude.customFoodColumn.setCustomHeightPercent(
grainHeight,
true
)
// 设置果蔬
binding.nutritionInclude.customVegetableColumn.setImageDrawable(
false,
fruits.max < fruits.current
)
binding.nutritionInclude.totalVegetableTv.text = fruits.current.format2String()
var fruitsHeight = (fruits.current / columnMax).toFloat()
if (fruitsHeight == 0F) {
binding.nutritionInclude.divVegetableView.visibility = View.VISIBLE
} else {
binding.nutritionInclude.divVegetableView.visibility = View.GONE
fruitsHeight += 0.1F
}
binding.nutritionInclude.customVegetableColumn.setCustomHeightPercent(
fruitsHeight,
true
)
// 设置肉蛋
binding.nutritionInclude.customMeatColumn.setImageDrawable(
false,
meat.max < meat.current
)
binding.nutritionInclude.totalMeatTv.text = meat.current.format2String()
var meatHeight = (meat.current / columnMax).toFloat()
if (meatHeight == 0F) {
binding.nutritionInclude.divMeatView.visibility = View.VISIBLE
} else {
binding.nutritionInclude.divMeatView.visibility = View.GONE
meatHeight += 0.1F
}
binding.nutritionInclude.customMeatColumn.setCustomHeightPercent(
meatHeight,
true
)
}
}
}
fun updateImage(bitmap: Bitmap) {
activity.runOnUiThread {
binding.ivPreviewImage.setImageBitmap(bitmap)
}
}
/**
* 余量取餐
*/
fun updateMealPickupMode(mode: Int) {
Timber.tag(TAG).d("updateMealPickupMode mode = $mode")
mealPickupMode = mode
step1FoodRecognizing()
}
override fun onStop() {
if (rgbCameraHelper != null) {
rgbCameraHelper!!.release()
rgbCameraHelper = null
}
if (irCameraHelper != null) {
irCameraHelper!!.release()
irCameraHelper = null
}
recognizeViewModel.destroy()
super.onStop()
}
fun resumeCamera() {
Timber.tag(TAG).d("resumeCamera isRecognition = $isRecognition")
isRecognition = true
if (rgbCameraHelper != null && rgbCameraHelper!!.isStopped) {
rgbCameraHelper!!.start()
}
}
fun pauseCamera() {
Timber.tag(TAG).d("pauseCamera isRecognition = $isRecognition")
isRecognition = false
recognizeViewModel.onPreviewFrame(ByteArray(1382400), true)
}
private fun initArcViewModel() {
recognizeViewModel.setLiveType(livenessType)
recognizeViewModel.ftInitCode.observe(activity, Observer { ftInitCode: Int? ->
if (ftInitCode != ErrorInfo.MOK) {
val error: String? = context.getString(
R.string.specific_engine_init_failed, "ftEngine",
ftInitCode, ErrorCodeUtil.arcFaceErrorCodeToFieldName(ftInitCode!!)
)
Timber.tag(TAG).e("ftInitCode observe = $error")
ToastUtils.showToast(error)
}
})
recognizeViewModel.frInitCode.observe(activity, Observer { frInitCode: Int? ->
if (frInitCode != ErrorInfo.MOK) {
val error: String? = context.getString(
R.string.specific_engine_init_failed, "frEngine",
frInitCode, ErrorCodeUtil.arcFaceErrorCodeToFieldName(frInitCode!!)
)
Timber.tag(TAG).e("frInitCode observe = $error")
ToastUtils.showToast(error)
}
})
recognizeViewModel.flInitCode.observe(activity, Observer { flInitCode: Int? ->
if (flInitCode != ErrorInfo.MOK) {
val error: String? = context.getString(
R.string.specific_engine_init_failed, "flEngine",
flInitCode, ErrorCodeUtil.arcFaceErrorCodeToFieldName(flInitCode!!)
)
Timber.tag(TAG).e("flInitCode observe = $error")
ToastUtils.showToast(error)
}
})
recognizeViewModel.recognizeConfiguration
.observe(activity, Observer { recognizeConfiguration: RecognizeConfiguration? ->
Timber.tag(TAG).i("recognizeConfiguration: observe = ${recognizeConfiguration.toString()}")
})
recognizeViewModel.recognizeNotice.observe(activity, Observer { notice: String? ->
Timber.tag(TAG).i("recognizeNotice observe notice = $notice")
})
recognizeViewModel.recognizeUserId.observe(
activity,
Observer { compareResult: CompareResult ->
Timber.tag(TAG).i("recognizeUserId observe compareResult = ${compareResult.trackId}, userId = ${compareResult.faceEntity.userName}")
recognitionTime = System.currentTimeMillis()
recognitionWeight = lastWeight
lastFaceTrackId = compareResult.trackId
val faceEntity = compareResult.faceEntity
val userId = faceEntity.userName
if (userId == null ) return@Observer
if (currentFood == null) {
//重新识别
activity.runOnUiThread {
ToastUtils.showToast("currentFood == null")
}
//activity.recognizeFood()
//step1FoodRecognizing()
return@Observer
}
userViewModel.getUserNutritionData(
userId = userId,
foodId = currentFood!!.id!!
)
})
recognizeViewModel.drawRectInfoText.observe(activity, Observer { info ->
Timber.tag(TAG).i("drawRectInfoText observe info = $info")
})
}
private fun initArcView() {
//在布局结束后才做初始化操作
binding.dualCameraTexturePreviewRgb.getViewTreeObserver().addOnGlobalLayoutListener(this)
recognizeViewModel.getCompareResultList().getValue()
}
/**
* 调整View的宽高,使预览显示正常且采集框固定为
*
* @param rgbPreview RGB预览View
* @param previewView 显示预览数据的view
* @param faceRectView 画框的view
* @param previewSize 预览大小
* @param displayOrientation 相机旋转角度
* @param scale 缩放比例
* @return 调整后的LayoutParams
*/
private fun adjustPreviewViewSize(
rgbPreview: View,
previewView: View,
faceRectView: FaceRectView,
previewSize: Camera.Size,
displayOrientation: Int,
scale: Float
): ViewGroup.LayoutParams {
// val layoutParams = previewView.layoutParams
// val measuredWidth = previewView.measuredWidth
// val measuredHeight = previewView.measuredHeight
// var ratio = (previewSize.height.toFloat()) / previewSize.width.toFloat()
// if (ratio > 1) {
// ratio = 1 / ratio
// }
// if (displayOrientation % 180 == 0) {
// layoutParams.width = measuredWidth
// layoutParams.height = (measuredWidth * ratio).toInt()
// } else {
// layoutParams.height = measuredHeight
// layoutParams.width = (measuredHeight * ratio).toInt()
// }
// if (scale < 1f) {
// val rgbParam = rgbPreview.getLayoutParams()
// layoutParams.width = (rgbParam.width * scale).toInt()
// layoutParams.height = (rgbParam.height * scale).toInt()
// } else {
// layoutParams.width = (layoutParams.width * scale).toInt()
// layoutParams.height = (layoutParams.height * scale).toInt()
// }
//
// val metrics = DisplayMetrics()
// activity.windowManager.defaultDisplay.getMetrics(metrics)
//
// if (layoutParams.width >= metrics.widthPixels) {
// val viewRatio = layoutParams.width / (metrics.widthPixels.toFloat())
// layoutParams.width = (layoutParams.width / viewRatio).toInt()
// layoutParams.height = (layoutParams.height / viewRatio).toInt()
// }
// if (layoutParams.height >= metrics.heightPixels) {
// val viewRatio = layoutParams.height / (metrics.heightPixels.toFloat())
// layoutParams.width = (layoutParams.width / viewRatio).toInt()
// layoutParams.height = (layoutParams.height / viewRatio).toInt()
// }
val layoutParams = previewView.layoutParams
// layoutParams.width = (608.dp * 1.5).roundToInt()
// layoutParams.height = (456.dp * 1.5).roundToInt()
layoutParams.width = (600.dp * 1.5).roundToInt()
layoutParams.height = (400.dp * 1.5).roundToInt()
previewView.setLayoutParams(layoutParams)
faceRectView.setLayoutParams(layoutParams)
return layoutParams
}
private fun initRgbCamera() {
val cameraListener: CameraListener = object : CameraListener {
override fun onCameraOpened(
camera: Camera,
cameraId: Int,
displayOrientation: Int,
isMirror: Boolean
) {
Timber.tag(TAG).d("initRgbCamera Rgb onCameraOpened: cameraId = $cameraId, displayOrientation = $displayOrientation")
activity.runOnUiThread({
val previewSizeRgb = camera.getParameters().getPreviewSize()
val layoutParams = adjustPreviewViewSize(
binding.dualCameraTexturePreviewRgb,
binding.dualCameraTexturePreviewRgb, binding.dualCameraFaceRectView,
previewSizeRgb, displayOrientation, 0.6F
)
Timber.tag(TAG).d("initRgbCamera previewSizeRgb = ${previewSizeRgb.width}-${previewSizeRgb.height}")
Timber.tag(TAG).d("initRgbCamera layoutParams = ${layoutParams.width}-${layoutParams.height}")
Timber.tag(TAG).d(
"initRgbCamera isMirror = ${isMirror}, isDrawRgbRectHorizontalMirror = ${
ConfigUtil.isDrawRgbRectHorizontalMirror(
context
)
}, isDrawRgbRectVerticalMirror = ${
ConfigUtil.isDrawRgbRectVerticalMirror(
context
)
}"
)
// 调整识别窗口位置
rgbFaceRectTransformer = FaceRectTransformer(
// previewSizeRgb.width,
// previewSizeRgb.height,
layoutParams.width,
layoutParams.height,
layoutParams.width,
layoutParams.height,
90,
cameraId,
isMirror,
true,
true
)
recognizeViewModel.onRgbCameraOpened(camera)
recognizeViewModel.setRgbFaceRectTransformer(rgbFaceRectTransformer)
})
}
@RequiresApi(api = Build.VERSION_CODES.Q)
override fun onPreview(nv21: ByteArray?, camera: Camera?) {
if (!isRecognition) {
return
}
binding.dualCameraFaceRectView.clearFaceInfo()
val facePreviewInfoList: MutableList<FacePreviewInfo?>? =
recognizeViewModel.onPreviewFrame(nv21, true)
if (facePreviewInfoList != null && rgbFaceRectTransformer != null) {
drawPreviewInfo(facePreviewInfoList)
}
recognizeViewModel.clearLeftFace(facePreviewInfoList)
}
override fun onCameraClosed() {
Timber.tag(TAG).i("initRgbCamera onCameraClosed: ")
}
override fun onCameraError(e: java.lang.Exception) {
Timber.tag(TAG).i("initRgbCamera onCameraError: %s", e.message)
e.printStackTrace()
}
override fun onCameraConfigurationChanged(cameraID: Int, displayOrientation: Int) {
Timber.tag(TAG).i("initRgbCamera onCameraConfigurationChanged: threadName = ${Thread.currentThread().name}")
if (rgbFaceRectTransformer != null) {
rgbFaceRectTransformer!!.cameraDisplayOrientation = displayOrientation
}
Timber.tag(TAG).i("initRgbCamera onCameraConfigurationChanged: $cameraID $displayOrientation")
}
}
val measuredWidth = binding.dualCameraTexturePreviewRgb.measuredWidth
val measuredHeight = binding.dualCameraTexturePreviewRgb.measuredHeight
Timber.tag(TAG).i("initRgbCamera measuredWidth=$measuredWidthmeasuredHeight=$measuredHeight")
val previewConfig: PreviewConfig = recognizeViewModel.previewConfig
rgbCameraHelper = DualCameraHelper.Builder()
.previewViewSize(Point(measuredWidth, measuredHeight))
.rotation(activity.windowManager.defaultDisplay.rotation)
.additionalRotation(previewConfig.rgbAdditionalDisplayOrientation) // 角度
.previewSize(recognizeViewModel.loadPreviewSize())
.specificCameraId(previewConfig.rgbCameraId)
.isMirror(true)
.previewOn(binding.dualCameraTexturePreviewRgb)
.cameraListener(cameraListener)
.build()
rgbCameraHelper!!.init()
// rgbCameraHelper!!.start()
}
/**
* 初始化红外相机,若活体检测类型是可见光活体检测或不启用活体,则不需要启用
*/
private fun initIrCamera() {
Timber.tag(TAG).d("initIrCamera: livenessType = $livenessType")
if (livenessType === LivenessType.RGB) {
return
}
val irCameraListener: CameraListener = object : CameraListener {
override fun onCameraOpened(
camera: Camera,
cameraId: Int,
displayOrientation: Int,
isMirror: Boolean
) {
Timber.tag(TAG).d("initIrCamera IR onCameraOpened: cameraId = $cameraId, displayOrientation = $displayOrientation")
val previewSizeIr = camera.getParameters().getPreviewSize()
val layoutParams = adjustPreviewViewSize(
binding.dualCameraTexturePreviewRgb,
binding.dualCameraTexturePreviewIr, binding.dualCameraFaceRectViewIr,
previewSizeIr, displayOrientation, 0.25f
)
irFaceRectTransformer = FaceRectTransformer(
// previewSizeIr.width, previewSizeIr.height,
layoutParams.width, layoutParams.height,
layoutParams.width, layoutParams.height, displayOrientation, cameraId, isMirror,
ConfigUtil.isDrawIrRectHorizontalMirror(context),
ConfigUtil.isDrawIrRectVerticalMirror(context)
)
recognizeViewModel.onIrCameraOpened(camera)
recognizeViewModel.setIrFaceRectTransformer(irFaceRectTransformer)
}
override fun onPreview(nv21: ByteArray?, camera: Camera?) {
recognizeViewModel.refreshIrPreviewData(nv21)
}
override fun onCameraClosed() {
Timber.tag(TAG).i("initIrCamera onCameraClosed: ")
}
override fun onCameraError(e: java.lang.Exception) {
Timber.tag(TAG).i("initIrCamera onCameraError: ${e.message}")
e.printStackTrace()
}
override fun onCameraConfigurationChanged(
cameraID: Int,
displayOrientation: Int
) {
if (irFaceRectTransformer != null) {
irFaceRectTransformer!!.cameraDisplayOrientation = displayOrientation
}
Timber.tag(TAG).i("initIrCamera onCameraConfigurationChanged: cameraID = $cameraID, displayOrientation = $displayOrientation")
}
}
val previewConfig = recognizeViewModel.previewConfig
irCameraHelper = DualCameraHelper.Builder()
.previewViewSize(
Point(
binding.dualCameraTexturePreviewIr.measuredWidth,
binding.dualCameraTexturePreviewIr.measuredHeight
)
)
.rotation(activity.windowManager.defaultDisplay.rotation)
.specificCameraId(previewConfig.irCameraId)
.previewOn(binding.dualCameraTexturePreviewIr)
.cameraListener(irCameraListener)
.isMirror(true)
.previewSize(recognizeViewModel.loadPreviewSize()) //相机预览大小设置,RGB与IR需使用相同大小
.additionalRotation(previewConfig.irAdditionalDisplayOrientation) //额外旋转角度
.build()
irCameraHelper!!.init()
try {
irCameraHelper!!.start()
} catch (e: RuntimeException) {
ToastUtils.showToast(e.message + context.getString(R.string.camera_error_notice))
}
}
/**
* 绘制RGB、IR画面的实时人脸信息
*
* @param facePreviewInfoList RGB画面的实时人脸信息
*/
private fun drawPreviewInfo(facePreviewInfoList: MutableList<FacePreviewInfo?>) {
// Timber.tag(TAG).d("drawPreviewInfo facePreviewInfoList = ${facePreviewInfoList.size}, rgbFaceRectTransformer = ${rgbFaceRectTransformer != null}")
if (rgbFaceRectTransformer != null) {
val rgbDrawInfoList: MutableList<DrawInfo?>? = recognizeViewModel.getDrawInfo(
facePreviewInfoList,
LivenessType.RGB,
openRectInfoDraw
)
// 识别成功
binding.dualCameraFaceRectView.drawRealtimeFaceInfo(rgbDrawInfoList)
}
if (facePreviewInfoList.isEmpty() || (lastFaceTrackId != facePreviewInfoList[0]!!.trackId)) {
if (lastFaceTrackId != -1) {
mealPickupMode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0) ?: 0
Timber.tag(TAG).i("$lastFaceTrackId 用户离开")
lastFaceTrackId = -1
postUserData()
if (mealPickupMode == 0) {
step1FoodRecognizing()
} else {
step2FaceRecognizing(currentFood!!)
}
}
}
}
fun postUserData() {
Timber.tag(TAG).d("postUserData")
if (userNutritionData == null || currentFood == null) {
Timber.tag(TAG).d("postUserData userNutritionData = ${userNutritionData == null}, currentFood = ${currentFood == null}")
return
}
var eatWeight = 0.0
eatWeight = if (mealPickupMode == 0) {
//即放即取
recognitionWeight - 0
} else {
recognitionWeight - lastWeight
}
val userNutritionParam = UserNutritionParam(
userId = userNutritionData?.userId!!,
foodId = currentFood?.id!!,
faceTime = recognitionTime,
faceEndTime = System.currentTimeMillis(),
eatWeight = eatWeight,//lastWeight
foodWeight = lastWeight
)
userViewModel.postUserNutritionData(listOf(userNutritionParam))
}
}
@@ -0,0 +1,44 @@
package com.sw.dualscreen.presentation.pay
import android.R
import android.app.Presentation
import android.os.Bundle
import android.text.Spanned
import android.text.SpannedString
import android.text.style.AbsoluteSizeSpan
import android.view.Display
import androidx.core.text.buildSpannedString
import com.sw.dualscreen.activity.PayActivity
import com.sw.dualscreen.databinding.PresentationCashPayBinding
import com.sw.dualscreen.ext.load
class CashPayPresentation(
val activity: PayActivity,
display: Display,
private val onDismissListener: () -> Unit = {}
) : Presentation(activity, display) {
private lateinit var binding: PresentationCashPayBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = PresentationCashPayBinding.inflate(layoutInflater)
setContentView(binding.root)
window?.setBackgroundDrawableResource(R.color.transparent)
initView()
}
var foodName: String? = null
var payAmount: String? = null
private fun initView() {
binding.tvFoodName.text = foodName
binding.tvRealAmount.text = getAmountText(payAmount ?: "")
}
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)
}
}
}
@@ -0,0 +1,571 @@
package com.sw.dualscreen.presentation.pay
import android.Manifest
import android.app.Presentation
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Point
import android.hardware.Camera
import android.os.Build
import android.os.Bundle
import android.text.Spanned
import android.text.SpannedString
import android.text.style.AbsoluteSizeSpan
import android.view.Display
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import androidx.core.text.buildSpannedString
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import com.arcsoft.face.ErrorInfo
import com.sw.dualscreen.GlobalKey
import com.sw.dualscreen.R
import com.sw.dualscreen.activity.PayActivity
import com.sw.dualscreen.databinding.PresentationFacePayBinding
import com.sw.dualscreen.ext.dp
import com.sw.dualscreen.presentation.MainScreenPresentation
import com.sw.dualscreen.utils.SPUtil
import com.sw.dualscreen.viewmodel.UserViewModel
import com.sw.plate.utils.ToastUtils
import com.sw.plate.utils.arcface.ConfigUtil
import com.sw.plate.utils.arcface.ErrorCodeUtil
import com.sw.plate.utils.arcface.FaceRectTransformer
import com.sw.plate.utils.arcface.FaceRectView
import com.sw.plate.utils.arcface.FaceRectView.DrawInfo
import com.sw.plate.utils.arcface.PreviewConfig
import com.sw.plate.utils.arcface.camera.CameraListener
import com.sw.plate.utils.arcface.camera.DualCameraHelper
import com.sw.plate.utils.arcface.face.constants.LivenessType
import com.sw.plate.utils.arcface.face.model.CompareResult
import com.sw.plate.utils.arcface.face.model.FacePreviewInfo
import com.sw.plate.utils.arcface.face.model.RecognizeConfiguration
import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel
import kotlinx.coroutines.launch
import timber.log.Timber
import kotlin.math.roundToInt
class FacePayPresentation(
val activity: PayActivity,
display: Display,
val userViewModel: UserViewModel,
val recognizeViewModel: RecognizeViewModel,
private val onDismissListener: () -> Unit = {}
) : Presentation(activity, display) , ViewTreeObserver.OnGlobalLayoutListener {
companion object {
private const val TAG = "FacePayPresentation"
}
private lateinit var binding: PresentationFacePayBinding
// 虹软人脸配置 ⬇
private var isRecognition = false
private var rgbCameraHelper: DualCameraHelper? = null
private var rgbFaceRectTransformer: FaceRectTransformer? = null
private val livenessType = LivenessType.IR
private var openRectInfoDraw = false
private var irCameraHelper: DualCameraHelper? = null
private var irFaceRectTransformer: FaceRectTransformer? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = PresentationFacePayBinding.inflate(layoutInflater)
setContentView(binding.root)
window?.setBackgroundDrawableResource(R.color.transparent)
initView()
setupArcCamera()
registerDataChange()
}
override fun show() {
super.show()
resumeCamera()
}
var foodName: String? = null
var payAmount: String? = null
private fun initView() {
binding.tvFoodName.text = foodName
binding.tvRealAmount.text = getAmountText(payAmount ?: "")
}
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 onGlobalLayout() {
Timber.tag(TAG).d("onGlobalLayout")
binding.dualCameraTexturePreviewRgb.getViewTreeObserver().removeOnGlobalLayoutListener(this)
openCamera()
}
private fun openCamera() {
Timber.tag(TAG).d("openCamera")
if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) !=
PackageManager.PERMISSION_GRANTED
) {
ToastUtils.showToast("无摄像头权限")
return
}
try {
val cameraCount = Camera.getNumberOfCameras()
if (cameraCount < 3) {
ToastUtils.showToast("摄像头数量异常")
return
}
recognizeViewModel.init(
PreviewConfig(
2, 1, 90, 90
)
)
initRgbCamera()
if (DualCameraHelper.hasDualCamera() && livenessType === LivenessType.IR) {
initIrCamera()
}
} catch (e: Exception) {
Timber.tag(TAG).e(e, "打开摄像头失败")
}
}
/**
* 启用虹软人脸失败
*/
private fun setupArcCamera() {
initArcViewModel()
initArcView()
openRectInfoDraw = true
}
private fun registerDataChange() {
activity.lifecycleScope.launch {
userViewModel.loadFaceResult.collect { needUpdate ->
if (needUpdate) {
recognizeViewModel.refreshFaceList()
}
}
}
// activity.lifecycleScope.launch {
// userViewModel.nutritionData.collect {
// Timber.tag(TAG).d("registerDataChange nutritionData = $it")
// userNutritionData = it
// if (it == null) return@collect
// step3ShowRecognizeResult()
// //binding.nutritionInclude.tvUserName.text = it.userName.maskName()
// //binding.nutritionInclude.tvRecommendHeat.text = "推荐热量:${it.recommendMin}-${it.recommendMax}"
// //updateWeight(lastWeight / 1000)
// }
// }
// activity.lifecycleScope.launch {
// userViewModel.dinnerTypeInfo.drop(1).collect {
// Timber.tag(TAG).d("registerDataChange dinnerTypeInfo = $it")
// dinnerTypeInfo = it
// }
// }
}
override fun onStop() {
if (rgbCameraHelper != null) {
rgbCameraHelper!!.release()
rgbCameraHelper = null
}
if (irCameraHelper != null) {
irCameraHelper!!.release()
irCameraHelper = null
}
recognizeViewModel.destroy()
super.onStop()
}
fun resumeCamera() {
Timber.tag(TAG).d("resumeCamera isRecognition = $isRecognition")
isRecognition = true
if (rgbCameraHelper != null && rgbCameraHelper!!.isStopped) {
rgbCameraHelper!!.start()
}
}
fun pauseCamera() {
Timber.tag(TAG).d("pauseCamera isRecognition = $isRecognition")
isRecognition = false
recognizeViewModel.onPreviewFrame(ByteArray(1382400), true)
}
private fun initArcViewModel() {
recognizeViewModel.setLiveType(livenessType)
recognizeViewModel.ftInitCode.observe(activity, Observer { ftInitCode: Int? ->
if (ftInitCode != ErrorInfo.MOK) {
val error: String? = context.getString(
R.string.specific_engine_init_failed, "ftEngine",
ftInitCode, ErrorCodeUtil.arcFaceErrorCodeToFieldName(ftInitCode!!)
)
Timber.tag(TAG).e("ftInitCode observe = $error")
ToastUtils.showToast(error)
}
})
recognizeViewModel.frInitCode.observe(activity, Observer { frInitCode: Int? ->
if (frInitCode != ErrorInfo.MOK) {
val error: String? = context.getString(
R.string.specific_engine_init_failed, "frEngine",
frInitCode, ErrorCodeUtil.arcFaceErrorCodeToFieldName(frInitCode!!)
)
Timber.tag(TAG).e("frInitCode observe = $error")
ToastUtils.showToast(error)
}
})
recognizeViewModel.flInitCode.observe(activity, Observer { flInitCode: Int? ->
if (flInitCode != ErrorInfo.MOK) {
val error: String? = context.getString(
R.string.specific_engine_init_failed, "flEngine",
flInitCode, ErrorCodeUtil.arcFaceErrorCodeToFieldName(flInitCode!!)
)
Timber.tag(TAG).e("flInitCode observe = $error")
ToastUtils.showToast(error)
}
})
recognizeViewModel.recognizeConfiguration
.observe(activity, Observer { recognizeConfiguration: RecognizeConfiguration? ->
Timber.tag(TAG).i("recognizeConfiguration: observe = ${recognizeConfiguration.toString()}")
})
recognizeViewModel.recognizeNotice.observe(activity, Observer { notice: String? ->
Timber.tag(TAG).i("recognizeNotice observe notice = $notice")
})
recognizeViewModel.recognizeUserId.observe(
activity,
Observer { compareResult: CompareResult ->
Timber.tag(TAG).i("recognizeUserId observe compareResult = ${compareResult.trackId}, userId = ${compareResult.faceEntity.userName}")
// recognitionTime = System.currentTimeMillis()
// recognitionWeight = lastWeight
lastFaceTrackId = compareResult.trackId
val faceEntity = compareResult.faceEntity
val userId = faceEntity.userName
if (userId == null ) return@Observer
activity.runOnUiThread {
ToastUtils.showToast("获取用户id成功")
}
// if (currentFood == null) {
// //重新识别
// activity.runOnUiThread {
// ToastUtils.showToast("currentFood == null")
// }
// //activity.recognizeFood()
// //step1FoodRecognizing()
// return@Observer
// }
// userViewModel.getUserNutritionData(
// userId = userId,
// foodId = currentFood!!.id!!
// )
})
recognizeViewModel.drawRectInfoText.observe(activity, Observer { info ->
Timber.tag(TAG).i("drawRectInfoText observe info = $info")
})
}
private fun initArcView() {
//在布局结束后才做初始化操作
binding.dualCameraTexturePreviewRgb.getViewTreeObserver().addOnGlobalLayoutListener(this)
recognizeViewModel.getCompareResultList().getValue()
}
/**
* 调整View的宽高,使预览显示正常且采集框固定为
*
* @param rgbPreview RGB预览View
* @param previewView 显示预览数据的view
* @param faceRectView 画框的view
* @param previewSize 预览大小
* @param displayOrientation 相机旋转角度
* @param scale 缩放比例
* @return 调整后的LayoutParams
*/
private fun adjustPreviewViewSize(
rgbPreview: View,
previewView: View,
faceRectView: FaceRectView,
previewSize: Camera.Size,
displayOrientation: Int,
scale: Float
): ViewGroup.LayoutParams {
// val layoutParams = previewView.layoutParams
// val measuredWidth = previewView.measuredWidth
// val measuredHeight = previewView.measuredHeight
// var ratio = (previewSize.height.toFloat()) / previewSize.width.toFloat()
// if (ratio > 1) {
// ratio = 1 / ratio
// }
// if (displayOrientation % 180 == 0) {
// layoutParams.width = measuredWidth
// layoutParams.height = (measuredWidth * ratio).toInt()
// } else {
// layoutParams.height = measuredHeight
// layoutParams.width = (measuredHeight * ratio).toInt()
// }
// if (scale < 1f) {
// val rgbParam = rgbPreview.getLayoutParams()
// layoutParams.width = (rgbParam.width * scale).toInt()
// layoutParams.height = (rgbParam.height * scale).toInt()
// } else {
// layoutParams.width = (layoutParams.width * scale).toInt()
// layoutParams.height = (layoutParams.height * scale).toInt()
// }
//
// val metrics = DisplayMetrics()
// activity.windowManager.defaultDisplay.getMetrics(metrics)
//
// if (layoutParams.width >= metrics.widthPixels) {
// val viewRatio = layoutParams.width / (metrics.widthPixels.toFloat())
// layoutParams.width = (layoutParams.width / viewRatio).toInt()
// layoutParams.height = (layoutParams.height / viewRatio).toInt()
// }
// if (layoutParams.height >= metrics.heightPixels) {
// val viewRatio = layoutParams.height / (metrics.heightPixels.toFloat())
// layoutParams.width = (layoutParams.width / viewRatio).toInt()
// layoutParams.height = (layoutParams.height / viewRatio).toInt()
// }
val layoutParams = previewView.layoutParams
// layoutParams.width = (608.dp * 1.5).roundToInt()
// layoutParams.height = (456.dp * 1.5).roundToInt()
layoutParams.width = (600.dp * 1.5).roundToInt()
layoutParams.height = (400.dp * 1.5).roundToInt()
previewView.setLayoutParams(layoutParams)
faceRectView.setLayoutParams(layoutParams)
return layoutParams
}
private fun initRgbCamera() {
val cameraListener: CameraListener = object : CameraListener {
override fun onCameraOpened(
camera: Camera,
cameraId: Int,
displayOrientation: Int,
isMirror: Boolean
) {
Timber.tag(TAG).d("initRgbCamera Rgb onCameraOpened: cameraId = $cameraId, displayOrientation = $displayOrientation")
activity.runOnUiThread({
val previewSizeRgb = camera.getParameters().getPreviewSize()
val layoutParams = adjustPreviewViewSize(
binding.dualCameraTexturePreviewRgb,
binding.dualCameraTexturePreviewRgb, binding.dualCameraFaceRectView,
previewSizeRgb, displayOrientation, 0.6F
)
Timber.tag(TAG).d("initRgbCamera previewSizeRgb = ${previewSizeRgb.width}-${previewSizeRgb.height}")
Timber.tag(TAG).d("initRgbCamera layoutParams = ${layoutParams.width}-${layoutParams.height}")
Timber.tag(TAG).d(
"initRgbCamera isMirror = ${isMirror}, isDrawRgbRectHorizontalMirror = ${
ConfigUtil.isDrawRgbRectHorizontalMirror(
context
)
}, isDrawRgbRectVerticalMirror = ${
ConfigUtil.isDrawRgbRectVerticalMirror(
context
)
}"
)
// 调整识别窗口位置
rgbFaceRectTransformer = FaceRectTransformer(
// previewSizeRgb.width,
// previewSizeRgb.height,
layoutParams.width,
layoutParams.height,
layoutParams.width,
layoutParams.height,
90,
cameraId,
isMirror,
true,
true
)
recognizeViewModel.onRgbCameraOpened(camera)
recognizeViewModel.setRgbFaceRectTransformer(rgbFaceRectTransformer)
})
}
@RequiresApi(api = Build.VERSION_CODES.Q)
override fun onPreview(nv21: ByteArray?, camera: Camera?) {
if (!isRecognition) {
return
}
binding.dualCameraFaceRectView.clearFaceInfo()
val facePreviewInfoList: MutableList<FacePreviewInfo?>? =
recognizeViewModel.onPreviewFrame(nv21, true)
if (facePreviewInfoList != null && rgbFaceRectTransformer != null) {
drawPreviewInfo(facePreviewInfoList)
}
recognizeViewModel.clearLeftFace(facePreviewInfoList)
}
override fun onCameraClosed() {
Timber.tag(TAG).i("initRgbCamera onCameraClosed: ")
}
override fun onCameraError(e: java.lang.Exception) {
Timber.tag(TAG).i("initRgbCamera onCameraError: %s", e.message)
e.printStackTrace()
}
override fun onCameraConfigurationChanged(cameraID: Int, displayOrientation: Int) {
Timber.tag(TAG).i("initRgbCamera onCameraConfigurationChanged: threadName = ${Thread.currentThread().name}")
if (rgbFaceRectTransformer != null) {
rgbFaceRectTransformer!!.cameraDisplayOrientation = displayOrientation
}
Timber.tag(TAG).i("initRgbCamera onCameraConfigurationChanged: $cameraID $displayOrientation")
}
}
val measuredWidth = binding.dualCameraTexturePreviewRgb.measuredWidth
val measuredHeight = binding.dualCameraTexturePreviewRgb.measuredHeight
Timber.tag(TAG).i("initRgbCamera measuredWidth=$measuredWidthmeasuredHeight=$measuredHeight")
val previewConfig: PreviewConfig = recognizeViewModel.previewConfig
rgbCameraHelper = DualCameraHelper.Builder()
.previewViewSize(Point(measuredWidth, measuredHeight))
.rotation(activity.windowManager.defaultDisplay.rotation)
.additionalRotation(previewConfig.rgbAdditionalDisplayOrientation) // 角度
.previewSize(recognizeViewModel.loadPreviewSize())
.specificCameraId(previewConfig.rgbCameraId)
.isMirror(true)
.previewOn(binding.dualCameraTexturePreviewRgb)
.cameraListener(cameraListener)
.build()
rgbCameraHelper!!.init()
// rgbCameraHelper!!.start()
}
/**
* 初始化红外相机,若活体检测类型是可见光活体检测或不启用活体,则不需要启用
*/
private fun initIrCamera() {
Timber.tag(TAG).d("initIrCamera: livenessType = $livenessType")
if (livenessType === LivenessType.RGB) {
return
}
val irCameraListener: CameraListener = object : CameraListener {
override fun onCameraOpened(
camera: Camera,
cameraId: Int,
displayOrientation: Int,
isMirror: Boolean
) {
Timber.tag(TAG).d("initIrCamera IR onCameraOpened: cameraId = $cameraId, displayOrientation = $displayOrientation")
val previewSizeIr = camera.getParameters().getPreviewSize()
val layoutParams = adjustPreviewViewSize(
binding.dualCameraTexturePreviewRgb,
binding.dualCameraTexturePreviewIr, binding.dualCameraFaceRectViewIr,
previewSizeIr, displayOrientation, 0.25f
)
irFaceRectTransformer = FaceRectTransformer(
// previewSizeIr.width, previewSizeIr.height,
layoutParams.width, layoutParams.height,
layoutParams.width, layoutParams.height, displayOrientation, cameraId, isMirror,
ConfigUtil.isDrawIrRectHorizontalMirror(context),
ConfigUtil.isDrawIrRectVerticalMirror(context)
)
recognizeViewModel.onIrCameraOpened(camera)
recognizeViewModel.setIrFaceRectTransformer(irFaceRectTransformer)
}
override fun onPreview(nv21: ByteArray?, camera: Camera?) {
recognizeViewModel.refreshIrPreviewData(nv21)
}
override fun onCameraClosed() {
Timber.tag(TAG).i("initIrCamera onCameraClosed: ")
}
override fun onCameraError(e: java.lang.Exception) {
Timber.tag(TAG).i("initIrCamera onCameraError: ${e.message}")
e.printStackTrace()
}
override fun onCameraConfigurationChanged(
cameraID: Int,
displayOrientation: Int
) {
if (irFaceRectTransformer != null) {
irFaceRectTransformer!!.cameraDisplayOrientation = displayOrientation
}
Timber.tag(TAG).i("initIrCamera onCameraConfigurationChanged: cameraID = $cameraID, displayOrientation = $displayOrientation")
}
}
val previewConfig = recognizeViewModel.previewConfig
irCameraHelper = DualCameraHelper.Builder()
.previewViewSize(
Point(
binding.dualCameraTexturePreviewIr.measuredWidth,
binding.dualCameraTexturePreviewIr.measuredHeight
)
)
.rotation(activity.windowManager.defaultDisplay.rotation)
.specificCameraId(previewConfig.irCameraId)
.previewOn(binding.dualCameraTexturePreviewIr)
.cameraListener(irCameraListener)
.isMirror(true)
.previewSize(recognizeViewModel.loadPreviewSize()) //相机预览大小设置,RGB与IR需使用相同大小
.additionalRotation(previewConfig.irAdditionalDisplayOrientation) //额外旋转角度
.build()
irCameraHelper!!.init()
try {
irCameraHelper!!.start()
} catch (e: RuntimeException) {
ToastUtils.showToast(e.message + context.getString(R.string.camera_error_notice))
}
}
/**
* 绘制RGB、IR画面的实时人脸信息
*
* @param facePreviewInfoList RGB画面的实时人脸信息
*/
private fun drawPreviewInfo(facePreviewInfoList: MutableList<FacePreviewInfo?>) {
// Timber.tag(TAG).d("drawPreviewInfo facePreviewInfoList = ${facePreviewInfoList.size}, rgbFaceRectTransformer = ${rgbFaceRectTransformer != null}")
if (rgbFaceRectTransformer != null) {
val rgbDrawInfoList: MutableList<DrawInfo?>? = recognizeViewModel.getDrawInfo(
facePreviewInfoList,
LivenessType.RGB,
openRectInfoDraw
)
// 识别成功
binding.dualCameraFaceRectView.drawRealtimeFaceInfo(rgbDrawInfoList)
ToastUtils.showToast("识别到人脸信息")
}
if (facePreviewInfoList.isEmpty() || (lastFaceTrackId != facePreviewInfoList[0]!!.trackId)) {
if (lastFaceTrackId != -1) {
// mealPickupMode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0) ?: 0
// Timber.tag(TAG).i("$lastFaceTrackId 用户离开")
// lastFaceTrackId = -1
// postUserData()
// if (mealPickupMode == 0) {
// step1FoodRecognizing()
// } else {
// step2FaceRecognizing(currentFood!!)
// }
}
}
}
private var lastFaceTrackId: Int = -1 // 上一次的人脸信息
fun updateImage(bitmap: Bitmap) {
activity.runOnUiThread {
// binding.ivPreviewImage.setImageBitmap(bitmap)
}
}
}
@@ -0,0 +1,45 @@
package com.sw.dualscreen.presentation.pay
import android.app.Presentation
import android.os.Bundle
import android.text.Spanned
import android.text.SpannedString
import android.text.style.AbsoluteSizeSpan
import android.view.Display
import androidx.core.text.buildSpannedString
import com.sw.dualscreen.activity.PayActivity
import com.sw.dualscreen.databinding.PresentationQrcodePayBinding
import com.sw.dualscreen.ext.load
class QrCodePayPresentation(
val activity: PayActivity,
display: Display,
private val onDismissListener: () -> Unit = {}
) : Presentation(activity, display) {
private lateinit var binding: PresentationQrcodePayBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = PresentationQrcodePayBinding.inflate(layoutInflater)
setContentView(binding.root)
window?.setBackgroundDrawableResource(android.R.color.transparent)
initView()
}
var foodName: String? = null
var payAmount: String? = null
var payQrCodePic: Any? = null
private fun initView() {
binding.tvFoodName.text = foodName
binding.tvRealAmount.text = getAmountText(payAmount ?: "")
binding.ivPayQrCode.load(payQrCodePic)
}
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)
}
}
}
@@ -0,0 +1,32 @@
package com.sw.dualscreen.utils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
// 倒计时Flow扩展函数
fun countDownByFlow(
total: Int,
scope: CoroutineScope,
onTick: (Int) -> Unit,
onStart: (() -> Unit)? = null,
onFinish: (() -> Unit)? = null
): Job {
return flow {
for (i in total downTo 0) {
emit(i)
if (i != 0) delay(1000)
}
}.flowOn(Dispatchers.Main)
.onStart { onStart?.invoke() }
.onCompletion { onFinish?.invoke() }
.onEach { onTick.invoke(it) }
.launchIn(scope)
}
@@ -1,5 +1,6 @@
package com.sw.dualscreen.view
import android.annotation.SuppressLint
import android.content.DialogInterface
import android.graphics.Color
import android.graphics.Typeface
@@ -14,6 +15,7 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.sw.dualscreen.R
import com.sw.dualscreen.adapter.GenericItemAdapter
import com.sw.dualscreen.adapter.GridSpacingItemDecoration
import com.sw.dualscreen.adapter.SearchFoodAdapter
import com.sw.dualscreen.adapter.dpToPx
import com.sw.dualscreen.databinding.BottomSheetDialogBinding
import com.sw.dualscreen.databinding.ItemSearchFoodInfoBinding
@@ -27,12 +29,29 @@ import timber.log.Timber
/**
* 底部搜索结果弹窗
*/
@SuppressLint("NotifyDataSetChanged")
class CustomBottomSheetDialog(
val viewModel: UserViewModel,
val itemClickCallback: (FoodInfo) -> Unit
) : BottomSheetDialogFragment() {
private lateinit var binding: BottomSheetDialogBinding
private lateinit var adapter: GenericItemAdapter<FoodInfo, ItemSearchFoodInfoBinding>
// lateinit var adapter: GenericItemAdapter<FoodInfo, ItemSearchFoodInfoBinding>
private val searchFoodList = mutableListOf<FoodInfo>()
private val adapter by lazy {
SearchFoodAdapter(searchFoodList).apply {
setOnItemClickListener { adapter, view, position ->
searchFoodList.forEachIndexed { index, info ->
searchFoodList[index].isChecked = index == position
}
val item = searchFoodList[position]
Timber.d("itemClick ${item.foodName}, position = $position")
itemClickCallback(item)
checkedItem = item
notifyDataSetChanged()
dismissAllowingStateLoss()
}
}
}
private var checkedItem: FoodInfo? = null
private val debouncer = Debouncer(2000)
@@ -83,18 +102,21 @@ class CustomBottomSheetDialog(
}
private fun initView() {
binding.viewClose.setOnClickListener { dismiss() }
adapter = createAdapter()
binding.recyclerview.layoutManager = GridLayoutManager(context, 2)
// 添加间距装饰(12dp)
binding.recyclerview.addItemDecoration(
GridSpacingItemDecoration(
spanCount = 2,
spacing = requireContext().dpToPx(30),
includeEdge = false // 包含边缘间距
)
)
binding.recyclerview.adapter = adapter
//binding.recyclerview.addItemDecoration(
// GridSpacingItemDecoration(
// spanCount = 2,
// spacing = requireContext().dpToPx(30),
// includeEdge = false // 包含边缘间距
// )
//)
//adapter = createAdapter()
//binding.recyclerview.adapter = adapter
binding.viewClose.setOnClickListener { dismiss() }
binding.recyclerview.let {
it.layoutManager = GridLayoutManager(context, 2)
it.adapter = adapter
}
binding.ivSearch.setOnClickListener {
searchInfo()
}
@@ -110,41 +132,44 @@ class CustomBottomSheetDialog(
}
}
@SuppressLint("NotifyDataSetChanged")
private fun registerDataChange() {
lifecycleScope.launch {
viewModel.searchFoodInfoList.collect {
adapter.updateData(it)
}
}
}
private fun createAdapter(): GenericItemAdapter<FoodInfo, ItemSearchFoodInfoBinding> {
return GenericItemAdapter(
items = emptyList(),
bindingInflater = ItemSearchFoodInfoBinding::inflate,
bindCallback = { item, position ->
this.tvName.text = item.foodName
if (item.id != checkedItem?.id) {
this.tvName.typeface = Typeface.defaultFromStyle(Typeface.NORMAL)
this.tvName.setTextColor(resources.getColor(R.color.search_normal))
this.llRoot.setBackgroundResource(R.drawable.grid_search_item_normal)
} else {
this.tvName.typeface = Typeface.defaultFromStyle(Typeface.BOLD)
this.tvName.setTextColor(resources.getColor(R.color.search_checked))
this.llRoot.setBackgroundResource(R.drawable.grid_search_item_checked)
}
this.llRoot.setOnClickListener {
Timber.d("itemClick ${item.foodName}, position = $position")
checkedItem = item
// viewModel.updateCurrentItem(item)
itemClickCallback(item)
searchFoodList.clear()
searchFoodList.addAll(it)
adapter.notifyDataSetChanged()
}
}
)
}
// private fun createAdapter(): GenericItemAdapter<FoodInfo, ItemSearchFoodInfoBinding> {
// return GenericItemAdapter(
// items = emptyList(),
// bindingInflater = ItemSearchFoodInfoBinding::inflate,
// bindCallback = { item, position ->
// this.tvName.text = item.foodName
// if (item.id != checkedItem?.id) {
// this.tvName.typeface = Typeface.defaultFromStyle(Typeface.NORMAL)
// this.tvName.setTextColor(resources.getColor(R.color.search_normal))
// this.llRoot.setBackgroundResource(R.drawable.grid_search_item_normal)
// } else {
// this.tvName.typeface = Typeface.defaultFromStyle(Typeface.BOLD)
// this.tvName.setTextColor(resources.getColor(R.color.search_checked))
// this.llRoot.setBackgroundResource(R.drawable.grid_search_item_checked)
// }
//
// this.llRoot.setOnClickListener {
// Timber.d("itemClick ${item.foodName}, position = $position")
// checkedItem = item
//// viewModel.updateCurrentItem(item)
// itemClickCallback(item)
// adapter.notifyDataSetChanged()
// }
// }
// )
// }
override fun onCancel(dialog: DialogInterface) {
Timber.d("onCancel")
viewModel.cleanSearchFoodInfoList()
@@ -29,7 +29,8 @@ public class CustomImageView extends androidx.appcompat.widget.AppCompatImageVie
/**
* 最大高度
*/
private static final int MAX_HEIGHT = 280;
// private static final int MAX_HEIGHT = 280;
private static final int MAX_HEIGHT = 250;
private boolean isDrawText = false;
private Drawable bigRedDrawable;
@@ -100,6 +101,14 @@ public class CustomImageView extends androidx.appcompat.widget.AppCompatImageVie
}
}
public void setImageDrawable2(boolean isBig, Boolean isRed) {
if (isBig) {
setImageDrawable(isRed ? bigRedDrawable : bigGreenDrawable);
} else {
setImageDrawable(isRed ? redDrawable : greenDrawable);
}
}
public void setCustomHeightPercent(float percent, boolean isAnimation) {
Timber.d("setCustomHeightPercent percent = %s", percent);
if (percent<= 0){
@@ -65,7 +65,7 @@ abstract class BaseViewModel() : ViewModel() {
fun parseEquipmentInfo(equipmentInfo: EquipmentInfo) {
GlobalData.appBaseUrl = equipmentInfo.appPackageUrl!!
GlobalData.appBaseUrl="http://192.168.1.210/gateway/local"
//GlobalData.appBaseUrl="http://192.168.1.210/gateway/local"
GlobalData.sdkKey = equipmentInfo.arcsoftSdkKey!!
GlobalData.appId = equipmentInfo.arcsoftAppId!!
// GlobalData.activeKey = equipmentInfo.arcsoftActiveKey!!
@@ -86,6 +86,8 @@ class UserViewModel : BaseViewModel() {
SPUtil.getInstance().put(GlobalKey.KEY_FIRST_RUN, true)
withContext(Dispatchers.Default) {
val list: List<UserFaceModel> = response.result?.data ?: emptyList()
val item = list.firstOrNull { it.userId == "1951105919342936066" }
Timber.d("getUserFaceCache userId = ${item?.userId}")
val faceEntity = list.map {
FaceEntity(it.userId, null, Base64.decode(it.faceFeatureString))
}
@@ -221,6 +223,8 @@ class UserViewModel : BaseViewModel() {
val list = response.result ?: emptyList()
_identifiedFoodInfoList2.value = list
action(list)
} else {
action(emptyList())
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:width="29dp" android:height="100dp">
<shape android:shape="rectangle">
<corners android:radius="15dp"/>
<solid android:color="#FFE7EFF8"/>
</shape>
</item>
<item android:width="13dp" android:height="84dp" android:gravity="center">
<shape android:shape="rectangle">
<corners android:radius="7dp"/>
<solid android:color="@color/white"/>
</shape>
</item>
</layer-list>
+2 -5
View File
@@ -1,10 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#fff8f8f8" />
<solid android:color="#FFF5F6F8" />
<corners
android:bottomLeftRadius="12dp"
android:bottomRightRadius="12dp"
android:topLeftRadius="12dp"
android:topRightRadius="12dp" />
android:radius="43dp" />
</shape>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#FFF97443"/>
<corners android:radius="10dp"/>
</shape>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#FFFB8AB0"/>
<corners android:radius="10dp"/>
</shape>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#FF49C4F1"/>
<corners android:radius="10dp"/>
</shape>
@@ -3,5 +3,5 @@
android:shape="rectangle">
<solid android:color="@color/white"/>
<stroke android:width="2dp" android:color="#FFC4CFDA"/>
<corners android:radius="50dp"/>
<corners android:radius="45dp"/>
</shape>
@@ -1,10 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#ffdcdcdc" />
<solid android:color="#FFB1B1C0" />
<corners
android:bottomLeftRadius="5dp"
android:bottomRightRadius="5dp"
android:topLeftRadius="5dp"
android:topRightRadius="5dp" />
android:radius="4dp" />
</shape>
+2 -2
View File
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:width="294dp" android:height="60dp">
<item android:width="196dp" android:height="40dp">
<shape android:shape="rectangle">
<stroke android:width="2dp" android:color="#FFFF3232"/>
<!-- <corners android:topRightRadius="6dp" android:bottomRightRadius="6dp"/>-->
<corners android:radius="6dp"/>
</shape>
</item>
<item android:gravity="start">
<item android:gravity="start" android:height="40dp" android:width="67dp">
<bitmap android:src="@drawable/ic_vip_level"/>
</item>
</layer-list>
+27 -19
View File
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -19,18 +20,18 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="32dp"
android:layout_marginStart="28dp"
android:textColor="#ff0a1428"
android:textSize="30sp"
android:textSize="26sp"
android:textStyle="bold"
tools:text="2025年11月11日 周二" />
<ImageView
android:id="@+id/ivSetting"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_width="58dp"
android:layout_height="58dp"
android:layout_gravity="end|center_vertical"
android:layout_marginEnd="22dp"
android:layout_marginEnd="18dp"
android:padding="10dp"
android:src="@drawable/ic_setting"
tools:ignore="ContentDescription" />
@@ -55,19 +56,20 @@
<TextView
android:id="@+id/tv_food_name"
style="@style/text_title_blob"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="130dp"
android:layout_marginTop="60dp"
android:textStyle="bold"
android:textSize="52sp"
android:textColor="#FF0A1428"
android:text="羊肉臊子荞面饸饹" />
<FrameLayout
android:id="@+id/foodDisplayLayout"
android:layout_width="match_parent"
android:layout_height="531dp"
android:layout_marginHorizontal="60dp"
android:layout_marginTop="110dp"
android:layout_marginBottom="90dp"
android:layout_height="500dp"
android:layout_marginHorizontal="28dp"
android:layout_marginTop="100dp"
android:visibility="visible">
<androidx.camera.view.PreviewView
@@ -86,7 +88,7 @@
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="30dp"
android:layout_margin="27dp"
android:background="@drawable/preview_border" />
</FrameLayout>
@@ -94,7 +96,12 @@
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginHorizontal="60dp"
app:spanCount="2"
tools:itemCount="8"
tools:listitem="@layout/list_item_search_food"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
android:layout_marginHorizontal="14dp"
android:layout_marginTop="42dp"
android:layout_weight="1" />
<LinearLayout
@@ -130,13 +137,14 @@
android:id="@+id/tv_to_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:layout_marginBottom="60dp"
android:layout_marginTop="38dp"
android:layout_marginBottom="54dp"
android:padding="10dp"
android:text="以上都不是,手动搜索"
android:textStyle="bold"
android:textColor="#FFFF3232"
android:textSize="36sp" />
android:textSize="31sp"
android:visibility="gone"/>
<FrameLayout
android:id="@+id/flPay"
@@ -148,11 +156,11 @@
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnPay"
android:layout_width="match_parent"
android:layout_margin="32dp"
android:layout_height="100dp"
android:layout_margin="28dp"
android:layout_height="87dp"
android:textStyle="bold"
android:text="去结算"
android:textSize="40sp"
android:textSize="35sp"
android:background="@drawable/bg_btn_save"
android:textColor="@color/white"/>
+38 -27
View File
@@ -1,63 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/bottom_sheet_layout"
android:layout_width="match_parent"
android:layout_height="1000dp"
android:layout_height="900dp"
android:background="@drawable/bottom_sheet_background"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingHorizontal="60dp"
android:paddingTop="30dp"
android:paddingBottom="0dp">
android:orientation="vertical">
<View
android:id="@+id/view_close"
android:layout_width="90dp"
android:layout_height="10dp"
android:layout_width="78dp"
android:layout_height="9dp"
android:layout_marginTop="28dp"
android:background="@drawable/view_shape_point" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="46dp"
android:layout_marginTop="41dp"
android:text="菜品检索"
android:textColor="#ff333333"
android:textSize="36sp" />
android:textStyle="bold"
android:textColor="#FF0A1428"
android:textSize="31sp" />
<LinearLayout
<FrameLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_marginTop="50dp"
android:layout_height="87dp"
android:layout_marginTop="56dp"
android:layout_marginHorizontal="28dp"
android:background="@drawable/bg_search"
android:gravity="center_vertical"
android:orientation="horizontal">
<EditText
android:id="@+id/et_search"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingHorizontal="40dp"
android:background="@null"
android:hint="输入菜品名称"
android:imeOptions="actionSearch"
android:maxLines="1"
android:textStyle="bold"
android:inputType="text"
android:textColor="#333333"
android:textColorHint="#ffc8c8c8"
android:textSize="36sp" />
android:textColor="#FF0A1428"
android:textColorHint="#FFB1B1C0"
android:textSize="31sp" />
<ImageView
android:id="@+id/iv_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="30dp"
android:src="@drawable/ic_search" />
</LinearLayout>
android:layout_width="100dp"
android:layout_height="85dp"
android:paddingStart="25dp"
android:paddingEnd="40dp"
android:layout_gravity="end|center_vertical"
android:src="@drawable/ic_search2" />
</FrameLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="700dp"
android:layout_marginTop="50dp" />
android:layout_height="match_parent"
android:minHeight="500dp"
android:layout_marginTop="42dp"
android:layout_marginBottom="20dp"
android:layout_marginHorizontal="14dp"
app:spanCount="2"
tools:itemCount="12"
tools:listitem="@layout/list_item_search_food"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"/>
</LinearLayout>
@@ -11,6 +11,7 @@
android:layout_height="wrap_content"
android:background="@drawable/setting_border"
android:orientation="vertical"
android:layout_marginHorizontal="32dp"
android:paddingHorizontal="32dp"
android:paddingTop="35dp"
android:paddingBottom="42dp">
@@ -73,6 +74,7 @@
android:background="@drawable/setting_border"
android:orientation="vertical"
android:paddingHorizontal="32dp"
android:layout_marginHorizontal="32dp"
android:paddingTop="35dp"
android:paddingBottom="42dp">
@@ -177,4 +179,20 @@
</LinearLayout>
<Space
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnConfirm"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_marginHorizontal="32dp"
android:layout_marginBottom="48dp"
android:text="确定"
android:background="@drawable/bg_btn_save"
android:textColor="@color/white"
android:textSize="40sp" />
</LinearLayout>
+2 -1
View File
@@ -22,7 +22,7 @@
android:layout_marginStart="32dp"
android:layout_marginTop="35dp"
android:drawablePadding="17dp"
android:text="计费设置"
android:text="餐品采集"
android:textColor="@color/black"
android:textSize="36sp"
android:textStyle="bold"
@@ -139,6 +139,7 @@
android:background="@drawable/setting_border_gray3"
android:gravity="center_vertical"
android:hint="输入菜品名称"
android:inputType="text"
android:imeOptions="actionSearch"
android:maxLines="1"
android:paddingStart="47dp"
@@ -30,16 +30,16 @@
android:id="@+id/ivFacePhoto"
android:layout_width="match_parent"
android:layout_height="510dp"
android:src="@mipmap/ic_launcher"
android:scaleType="centerCrop"
tools:ignore="ContentDescription" />
tools:ignore="ContentDescription"
tools:src="@mipmap/ic_launcher" />
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:adjustViewBounds="true"
android:src="@drawable/face_mask"
android:layout_gravity="bottom"
tools:ignore="ContentDescription" />
<TextView
@@ -6,26 +6,6 @@
android:gravity="center"
android:orientation="vertical">
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/vip_level"/>
<TextView
android:layout_width="200dp"
android:layout_height="60dp"
android:textColor="#FFFF3232"
android:textSize="30sp"
android:textStyle="bold"
android:gravity="center"
android:includeFontPadding="false"
android:layout_gravity="end|center_vertical"
android:text="6.80 元/份"/>
</FrameLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -56,12 +56,12 @@
app:layout_constraintTop_toBottomOf="@id/tvUserName"
tools:text="138****8000" />
<com.google.android.material.divider.MaterialDivider
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="32dp"
app:dividerColor="#FFDBE5FB"
android:background="#FFDBE5FB"
app:layout_constraintTop_toBottomOf="@id/ivHeadPic" />
<TextView
@@ -31,16 +31,16 @@
android:layout_width="310dp"
android:layout_height="310dp"
android:layout_marginTop="64dp"
tools:src="@drawable/ic_qrcode"
tools:ignore="ContentDescription" />
tools:ignore="ContentDescription"
tools:src="@drawable/ic_qrcode" />
<TextView
android:id="@+id/tvPayResult"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="64dp"
tools:text="待支付(60s)..."
android:textColor="#ff5e7585"
android:textSize="30sp"
android:textStyle="bold" />
android:textStyle="bold"
tools:text="待支付(60s)..." />
</LinearLayout>
@@ -0,0 +1,300 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/cl_nutritionData"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="visible">
<TextView
android:id="@+id/tv_user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FF5E7585"
android:textSize="24sp"
android:textStyle="bold"
android:layout_marginTop="33dp"
android:layout_marginStart="41dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="夏*秋" />
<TextView
android:id="@+id/tv_recommend_heat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FF5E7585"
android:textSize="24sp"
android:textStyle="bold"
android:layout_marginEnd="41dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/tv_user_name"
app:layout_constraintBottom_toBottomOf="@id/tv_user_name"
tools:text="推荐热量:680kcal" />
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="33dp"
android:layout_marginHorizontal="28dp"
android:background="#FF5E7585"
app:layout_constraintTop_toBottomOf="@id/tv_user_name" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="465dp"
app:layout_constraintTop_toBottomOf="@id/divider"
android:paddingBottom="60dp"
android:orientation="horizontal">
<Space
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1.1"/>
<!-- 热量 -->
<androidx.constraintlayout.widget.ConstraintLayout
style="@style/takeFoodLayoutNew">
<LinearLayout
android:id="@+id/kcalValueLayout"
style="@style/takeFoodWeightLayout"
app:layout_constraintBottom_toTopOf="@id/divKcalView"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent">
<TextView
android:id="@+id/totalKcalTv"
style="@style/takeFoodValueTv"
android:textSize="24sp"
android:text="0" />
<TextView
style="@style/takeFoodValueTv"
android:text="千卡"
android:textSize="12sp" />
</LinearLayout>
<View
android:id="@+id/divKcalView"
android:layout_width="1dp"
android:layout_height="@dimen/dp30"
app:layout_constraintBottom_toTopOf="@id/customKcalColumn"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<com.sw.dualscreen.view.CustomImageView
android:id="@+id/customKcalColumn"
android:layout_width="@dimen/dp130"
android:layout_height="0dp"
android:layout_marginBottom="6dp"
android:scaleType="fitXY"
android:src="@drawable/img_big_red_column"
app:layout_constraintBottom_toBottomOf="@id/imgKcal"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<ImageView
android:id="@+id/imgKcal"
android:layout_width="@dimen/dp107"
android:layout_height="@dimen/dp44"
android:scaleType="fitXY"
android:src="@drawable/ic_heat"
app:layout_constraintBottom_toTopOf="@id/kcalTv"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<TextView
android:id="@+id/kcalTv"
style="@style/takeFoodNameTv"
android:text="热量"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<Space
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1.0"/>
<!-- 主食 -->
<androidx.constraintlayout.widget.ConstraintLayout style="@style/takeFoodLayoutNew">
<LinearLayout
android:id="@+id/foodValueLayout"
style="@style/takeFoodWeightLayout"
app:layout_constraintBottom_toTopOf="@id/divTotalFoodView"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent">
<TextView
android:id="@+id/totalFoodTv"
style="@style/takeFoodValueTv"
android:text="0"
android:textSize="24sp"/>
<TextView
style="@style/takeFoodValueTv"
android:text="克"
android:textSize="12sp" />
</LinearLayout>
<View
android:id="@+id/divTotalFoodView"
android:layout_width="1dp"
android:layout_height="@dimen/dp30"
app:layout_constraintBottom_toTopOf="@id/customFoodColumn"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<com.sw.dualscreen.view.CustomImageView
android:id="@+id/customFoodColumn"
style="@style/takeFoodCustomColumn"
app:layout_constraintBottom_toBottomOf="@id/imgFood"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<ImageView
android:id="@+id/imgFood"
style="@style/takeFoodImg"
android:src="@drawable/ic_staple_food"
app:layout_constraintBottom_toTopOf="@id/foodTv"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<TextView
android:id="@+id/foodTv"
style="@style/takeFoodNameTv"
android:text="主食"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<Space
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1.0"/>
<!-- 果蔬 -->
<androidx.constraintlayout.widget.ConstraintLayout style="@style/takeFoodLayoutNew">
<LinearLayout
android:id="@+id/vegetableValueLayout"
style="@style/takeFoodWeightLayout"
app:layout_constraintBottom_toTopOf="@id/divVegetableView"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent">
<TextView
android:id="@+id/totalVegetableTv"
style="@style/takeFoodValueTv"
android:text="0"
android:textSize="24sp" />
<TextView
style="@style/takeFoodValueTv"
android:text="克"
android:textSize="12sp" />
</LinearLayout>
<View
android:id="@+id/divVegetableView"
android:layout_width="1dp"
android:layout_height="@dimen/dp30"
app:layout_constraintBottom_toTopOf="@id/customVegetableColumn"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<com.sw.dualscreen.view.CustomImageView
android:id="@+id/customVegetableColumn"
style="@style/takeFoodCustomColumn"
app:layout_constraintBottom_toBottomOf="@id/imgVegetable"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<ImageView
android:id="@+id/imgVegetable"
style="@style/takeFoodImg"
android:src="@drawable/ic_fruits_vegetables"
app:layout_constraintBottom_toTopOf="@id/vegetableTv"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<TextView
android:id="@+id/vegetableTv"
style="@style/takeFoodNameTv"
android:text="果蔬"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<Space
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1.0"/>
<!-- 蛋肉 -->
<androidx.constraintlayout.widget.ConstraintLayout style="@style/takeFoodLayoutNew">
<LinearLayout
android:id="@+id/meatValueLayout"
style="@style/takeFoodWeightLayout"
app:layout_constraintBottom_toTopOf="@id/divMeatView"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent">
<TextView
android:id="@+id/totalMeatTv"
style="@style/takeFoodValueTv"
android:text="0"
android:textSize="24sp" />
<TextView
style="@style/takeFoodValueTv"
android:text="克"
android:textSize="12sp" />
</LinearLayout>
<View
android:id="@+id/divMeatView"
android:layout_width="1dp"
android:layout_height="@dimen/dp30"
app:layout_constraintBottom_toTopOf="@id/customMeatColumn"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<com.sw.dualscreen.view.CustomImageView
android:id="@+id/customMeatColumn"
style="@style/takeFoodCustomColumn2"
app:layout_constraintBottom_toBottomOf="@id/imgMeat"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<ImageView
android:id="@+id/imgMeat"
style="@style/takeFoodImg"
android:src="@drawable/ic_meat_eggs"
app:layout_constraintBottom_toTopOf="@id/meatTv"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<TextView
android:id="@+id/meatTv"
style="@style/takeFoodNameTv"
android:text="蛋肉"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<Space
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1.1"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -0,0 +1,178 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:layout_gravity="center"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="热量"
android:layout_marginTop="65dp"
android:textColor="#ff5e7585"
android:textSize="24sp"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginVertical="15dp"
android:textColor="#ff0a1428"
android:textSize="40sp"
android:textStyle="bold"
tools:text="117" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ff5e7585"
android:textSize="20sp"
android:textStyle="bold"
tools:text="kcal/100g" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginVertical="28dp"
android:layout_marginHorizontal="28dp"
android:background="#FFDBE5FB" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="154dp"
android:layout_marginHorizontal="80dp">
<LinearLayout
android:id="@+id/llFat"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_constraintEnd_toStartOf="@+id/llProtein"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintStart_toStartOf="parent">
<TextView
android:id="@+id/tvFatRate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="13%"
android:textColor="#ff5e7585"
android:textSize="16sp" />
<FrameLayout
android:layout_width="29dp"
android:layout_height="100dp"
android:background="@drawable/bg_nutrition_rate">
<View
android:id="@+id/viewFatLine"
android:layout_width="13dp"
android:layout_height="19dp"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="12dp"
android:background="@drawable/rate_orange" />
</FrameLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="脂肪"
android:textColor="#ff5e7585"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/llProtein"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_constraintEnd_toStartOf="@+id/llCarbohydrate"
app:layout_constraintStart_toEndOf="@id/llFat">
<TextView
android:id="@+id/tvProteinRate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="47%"
android:textColor="#ff5e7585"
android:textSize="16sp" />
<FrameLayout
android:layout_width="29dp"
android:layout_height="100dp"
android:background="@drawable/bg_nutrition_rate">
<View
android:id="@+id/viewProteinLine"
android:layout_width="13dp"
android:layout_height="48dp"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="12dp"
android:background="@drawable/rate_sky_blue" />
</FrameLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="蛋白质"
android:textColor="#ff5e7585"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/llCarbohydrate"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/llProtein">
<TextView
android:id="@+id/tvCarbohydrateRate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="40%"
android:textColor="#ff5e7585"
android:textSize="16sp" />
<FrameLayout
android:layout_width="29dp"
android:layout_height="100dp"
android:background="@drawable/bg_nutrition_rate">
<View
android:id="@+id/viewCarbohydrateLine"
android:layout_width="13dp"
android:layout_height="41dp"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="12dp"
android:background="@drawable/rate_pink" />
</FrameLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="碳水"
android:textColor="#ff5e7585"
android:textSize="16sp" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="(所示营养数据为每百克含量)"
android:textColor="#ff889ac2"
android:layout_marginTop="56dp"
android:layout_marginBottom="27dp"
android:textSize="16sp" />
</LinearLayout>
@@ -33,12 +33,12 @@
app:layout_constraintStart_toEndOf="@id/tvFoodName"
android:src="@drawable/ic_close" />
<com.google.android.material.divider.MaterialDivider
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginHorizontal="30dp"
app:layout_constraintTop_toBottomOf="@id/tvFoodName"
app:dividerColor="#FFE6E6E6"/>
android:background="#FFE6E6E6"/>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -8,8 +8,8 @@
android:ellipsize="end"
android:gravity="center"
android:maxLines="1"
android:layout_marginHorizontal="16dp"
android:layout_marginVertical="8dp"
android:layout_marginHorizontal="14dp"
android:layout_marginVertical="14dp"
android:paddingHorizontal="15dp"
android:textAlignment="center"
android:textColor="@color/color_search_food"
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFE7EFF8">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:layout_margin="28dp"
android:background="@drawable/setting_border"
android:orientation="vertical">
<TextView
android:id="@+id/tvFoodName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FF0A1428"
android:layout_marginTop="210dp"
android:textSize="40sp"
android:textStyle="bold"
tools:text="岐山臊子面" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="55dp"
android:text="应付金额"
android:textColor="#FF5E7585"
android:textSize="24sp"
android:textStyle="bold"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/tvRealAmount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="28dp"
android:textColor="#FFFF3232"
android:textSize="32sp"
android:textStyle="bold"
tools:text="¥36.80" />
<!-- <TextView-->
<!-- android:id="@+id/tvPayTip"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_marginTop="55dp"-->
<!-- android:text="请扫码支付或出示付款码"-->
<!-- android:textColor="#FF5E7585"-->
<!-- android:textSize="24sp"-->
<!-- android:textStyle="bold"-->
<!-- tools:ignore="HardcodedText" />-->
</LinearLayout>
</FrameLayout>
@@ -0,0 +1,136 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFE7EFF8"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="28dp"
android:layout_marginTop="28dp"
android:background="@drawable/setting_border"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingVertical="56dp">
<TextView
android:id="@+id/tvFoodName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FF0A1428"
android:textSize="40sp"
android:textStyle="bold"
tools:text="岐山臊子面" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="55dp"
android:text="应付金额"
android:textColor="#FF5E7585"
android:textSize="24sp"
android:textStyle="bold"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/tvRealAmount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="28dp"
android:textColor="#FFFF3232"
android:textSize="32sp"
android:textStyle="bold"
tools:text="¥36.80" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="28dp"
android:background="@drawable/setting_border"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="79dp"
android:text="会员验证"
android:textColor="#ff0a1428"
android:textSize="27sp"
android:textStyle="bold"
tools:ignore="HardcodedText" />
<FrameLayout
android:id="@+id/flFace"
android:layout_width="match_parent"
android:layout_height="400dp"
android:layout_marginHorizontal="32dp"
android:layout_marginTop="56dp">
<TextureView
android:id="@+id/dual_camera_texture_preview_rgb"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible" />
<com.sw.plate.utils.arcface.FaceRectView
android:id="@+id/dual_camera_face_rect_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- <ImageView-->
<!-- android:id="@+id/ivFaceRecMask"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_gravity="bottom"-->
<!-- android:adjustViewBounds="true"-->
<!-- android:src="@drawable/face_mask"-->
<!-- android:visibility="visible" />-->
<!-- <TextView-->
<!-- android:id="@+id/tvFaceTip"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_gravity="bottom|center"-->
<!-- android:layout_marginBottom="56dp"-->
<!-- android:text="请正视屏幕进行面部识别..."-->
<!-- android:textColor="@color/white"-->
<!-- android:textSize="24sp"-->
<!-- android:visibility="v" />-->
<FrameLayout
android:id="@+id/fl_recognize_ir"
android:layout_width="15dp"
android:layout_height="10dp"
android:layout_gravity="bottom"
android:visibility="visible">
<TextureView
android:id="@+id/dual_camera_texture_preview_ir"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.sw.plate.utils.arcface.FaceRectView
android:id="@+id/dual_camera_face_rect_view_ir"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</FrameLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="55dp"
android:text="请正视屏幕进行面部识别..."
android:textColor="#ff5e7585"
android:textSize="24sp"
android:textStyle="bold"
tools:ignore="HardcodedText" />
</LinearLayout>
</LinearLayout>
@@ -0,0 +1,249 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFE7EFF8"
android:orientation="vertical"
android:padding="28dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/setting_border"
android:orientation="vertical"
android:paddingBottom="28dp">
<TextView
android:id="@+id/tvFoodName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginVertical="60dp"
android:text="餐品识别中..."
android:textColor="#ff0a1428"
android:textSize="40sp"
android:textStyle="bold" />
<FrameLayout
android:id="@+id/flCameraView"
android:layout_width="match_parent"
android:layout_height="400dp"
android:layout_marginHorizontal="28dp"
android:visibility="visible">
<FrameLayout
android:id="@+id/flFace"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<TextureView
android:id="@+id/dual_camera_texture_preview_rgb"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible" />
<com.sw.plate.utils.arcface.FaceRectView
android:id="@+id/dual_camera_face_rect_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/ivFaceRecMask"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:adjustViewBounds="true"
android:src="@drawable/face_mask"
android:visibility="gone" />
<TextView
android:id="@+id/tvFaceTip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center"
android:layout_marginBottom="56dp"
android:text="请正视屏幕进行面部识别..."
android:textColor="@color/white"
android:textSize="24sp"
android:visibility="gone" />
</FrameLayout>
<ImageView
android:id="@+id/ivPreviewImage"
android:layout_width="match_parent"
android:layout_height="400dp"
tools:background="@mipmap/ic_launcher"/>
<ImageView
android:id="@+id/ivRecImage"
android:layout_width="match_parent"
android:layout_height="400dp"
tools:background="@mipmap/ic_launcher"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="28dp"
android:background="@drawable/ic_camera_rect" />
</FrameLayout>
<LinearLayout
android:id="@+id/llPriceInfo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="28dp"
android:layout_gravity="center_horizontal"
android:gravity="center_vertical"
android:visibility="gone">
<TextView
android:id="@+id/tvOldPrice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ff0a1428"
android:textSize="48sp"
android:textStyle="bold"
tools:text="36.80 元/份" />
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="40dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/vip_level" />
<TextView
android:id="@+id/tvVipPrice"
android:layout_width="134dp"
android:layout_height="40dp"
android:layout_gravity="end|center_vertical"
android:gravity="center"
android:includeFontPadding="false"
android:textColor="#FFFF3232"
android:textSize="20sp"
android:textStyle="bold"
tools:text="6.80 元/份" />
</FrameLayout>
</LinearLayout>
</LinearLayout>
<FrameLayout
android:id="@+id/flBottomView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="28dp"
android:background="@drawable/setting_border">
<TextView
android:id="@+id/tvFoodRecPrompt"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:shadowColor="#ffffffff"
android:shadowDx="0"
android:shadowDy="1"
android:shadowRadius="3.0"
android:text="先识别 后取餐"
android:textColor="#ffff3232"
android:textSize="80sp"
android:textStyle="bold"
android:layout_marginTop="42dp"
android:background="@drawable/setting_border" />
<include
android:id="@+id/calorieInclude"
layout="@layout/layout_user_calorie"
android:visibility="gone" />
<include
android:id="@+id/nutritionInclude"
layout="@layout/item_user_nutrition_new"
android:visibility="gone" />
</FrameLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/llStandbyPage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFE7EFF8"
android:orientation="vertical"
android:padding="28dp"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="640dp"
android:background="@drawable/setting_border"
android:orientation="vertical"
android:paddingBottom="28dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginVertical="60dp"
android:text="欢迎用餐"
android:textColor="#ff0a1428"
android:textSize="40sp"
android:textStyle="bold" />
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="28dp"
android:adjustViewBounds="true"
android:src="@drawable/placeholder_waiting" />
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="554dp"
android:gravity="center"
android:shadowColor="#ffffffff"
android:shadowDx="0"
android:shadowDy="1"
android:shadowRadius="3.0"
android:text="先识别 后取餐"
android:textColor="#ffff3232"
android:textSize="80sp"
android:textStyle="bold"
android:layout_marginTop="28dp"
android:background="@drawable/setting_border" />
</LinearLayout>
<FrameLayout
android:id="@+id/fl_recognize_ir"
android:layout_width="15dp"
android:layout_height="10dp"
android:layout_gravity="bottom"
android:visibility="visible">
<TextureView
android:id="@+id/dual_camera_texture_preview_ir"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.sw.plate.utils.arcface.FaceRectView
android:id="@+id/dual_camera_face_rect_view_ir"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</FrameLayout>
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFE7EFF8">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:layout_margin="28dp"
android:background="@drawable/setting_border"
android:orientation="vertical">
<TextView
android:id="@+id/tvFoodName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="210dp"
android:textColor="#FF0A1428"
android:textSize="40sp"
android:textStyle="bold"
tools:text="岐山臊子面" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="55dp"
android:text="应付金额"
android:textColor="#FF5E7585"
android:textSize="24sp"
android:textStyle="bold"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/tvRealAmount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="28dp"
android:textColor="#FFFF3232"
android:textSize="32sp"
android:textStyle="bold"
tools:text="¥36.80" />
<ImageView
android:id="@+id/ivPayQrCode"
android:layout_width="333dp"
android:layout_height="333dp"
android:layout_marginTop="56dp"
tools:ignore="ContentDescription"
tools:src="@drawable/ic_qrcode" />
<TextView
android:id="@+id/tvPayTip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="55dp"
android:text="请扫码支付或出示付款码"
android:textColor="#FF5E7585"
android:textSize="24sp"
android:textStyle="bold"
tools:ignore="HardcodedText" />
</LinearLayout>
</FrameLayout>
+5
View File
@@ -2,11 +2,16 @@
<resources>
<dimen name="bottom_sheet_height">1000dp</dimen>
<dimen name="sp11">11sp</dimen>
<dimen name="sp36">36sp</dimen>
<dimen name="sp18">18sp</dimen>
<dimen name="sp20">20sp</dimen>
<dimen name="sp30">30sp</dimen>
<dimen name="dp30">30dp</dimen>
<dimen name="dp10">10dp</dimen>
<dimen name="dp80">80dp</dimen>
<dimen name="dp87">87dp</dimen>
<dimen name="dp107">107dp</dimen>
<dimen name="dp44">44dp</dimen>
<dimen name="dp53">53dp</dimen>
<dimen name="dp130">130dp</dimen>
</resources>
+23 -6
View File
@@ -74,6 +74,13 @@
<item name="android:layout_weight">1</item>
</style>
<style name="takeFoodLayoutNew">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">match_parent</item>
<item name="android:gravity">bottom</item>
</style>
<!-- <item name="android:layout_weight">1</item>-->
<style name="takeFoodWeightLayout">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
@@ -86,12 +93,12 @@
<style name="takeFoodValueTv">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textSize">@dimen/sp20</item>
<item name="android:textColor">#F0E6DC</item>
<item name="android:textSize">@dimen/sp36</item>
<item name="android:textColor">#FF0A1428</item>
</style>
<style name="takeFoodCustomColumn">
<item name="android:layout_width">@dimen/dp53</item>
<item name="android:layout_width">@dimen/dp80</item>
<item name="android:layout_height">0dp</item>
<item name="android:layout_centerHorizontal">true</item>
<!-- <item name="android:layout_marginBottom">-36dp</item>-->
@@ -100,6 +107,16 @@
<item name="android:src">@drawable/img_green_column</item>
</style>
<style name="takeFoodCustomColumn2">
<item name="android:layout_width">@dimen/dp80</item>
<item name="android:layout_height">0dp</item>
<item name="android:layout_centerHorizontal">true</item>
<!-- <item name="android:layout_marginBottom">-36dp</item>-->
<item name="android:scaleType">fitXY</item>
<item name="android:layout_marginBottom">6dp</item>
<item name="android:src">@drawable/img_red_column</item>
</style>
<style name="takeFoodImg">
<item name="android:layout_width">@dimen/dp80</item>
<item name="android:layout_height">@dimen/dp44</item>
@@ -109,10 +126,10 @@
<style name="takeFoodNameTv">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">36dp</item>
<item name="android:layout_height">50dp</item>
<item name="android:gravity">bottom</item>
<item name="android:textColor">#F0E6DC</item>
<item name="android:textSize">@dimen/sp20</item>
<item name="android:textColor">#FF0A1428</item>
<item name="android:textSize">@dimen/sp30</item>
<item name="android:textStyle">bold</item>
<item name="android:layout_centerHorizontal">true</item>
</style>
@@ -87,7 +87,8 @@ public class ConfigUtil {
/**
* 默认相机分辨率
*/
private static final String DEFAULT_PREVIEW_SIZE = "1280x720";
// private static final String DEFAULT_PREVIEW_SIZE = "1280x720";
private static final String DEFAULT_PREVIEW_SIZE = "600x400";
// private static final String DEFAULT_PREVIEW_SIZE = "400x640";