首页、支付功能
This commit is contained in:
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -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=$measuredWidth,measuredHeight=$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=$measuredWidth,measuredHeight=$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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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"/>
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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";
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user