1280 lines
45 KiB
Kotlin
1280 lines
45 KiB
Kotlin
package com.sw.dualscreen.activity
|
||
|
||
import android.annotation.SuppressLint
|
||
import android.content.Context
|
||
import android.content.Intent
|
||
import android.graphics.Outline
|
||
import android.hardware.display.DisplayManager
|
||
import android.net.Uri
|
||
import android.os.Handler
|
||
import android.os.Looper
|
||
import android.text.TextUtils
|
||
import android.view.Display
|
||
import android.view.View
|
||
import android.view.ViewOutlineProvider
|
||
import androidx.activity.OnBackPressedCallback
|
||
import androidx.activity.viewModels
|
||
import androidx.camera.core.CameraSelector
|
||
import androidx.camera.core.ImageAnalysis
|
||
import androidx.camera.core.ImageCapture
|
||
import androidx.camera.core.ImageCaptureException
|
||
import androidx.camera.core.Preview
|
||
import androidx.camera.lifecycle.ProcessCameraProvider
|
||
import androidx.core.content.ContextCompat
|
||
import androidx.recyclerview.widget.GridLayoutManager
|
||
import com.google.common.util.concurrent.ListenableFuture
|
||
import com.sw.dualscreen.GlobalData
|
||
import com.sw.dualscreen.GlobalKey
|
||
import com.sw.dualscreen.activity.fragment.CollectFragment
|
||
import com.sw.dualscreen.adapter.SearchFoodAdapter
|
||
import com.sw.dualscreen.databinding.ActivityMainBinding
|
||
import com.sw.dualscreen.dialog.RemindDialog
|
||
import com.sw.dualscreen.ext.dp
|
||
import com.sw.dualscreen.ext.gone
|
||
import com.sw.dualscreen.ext.load
|
||
import com.sw.dualscreen.ext.visible
|
||
import com.sw.dualscreen.model.response.ChargeModeEvent
|
||
import com.sw.dualscreen.model.response.ClickBackEvent
|
||
import com.sw.dualscreen.model.response.FoodInfo
|
||
import com.sw.dualscreen.model.response.FoodOrder
|
||
import com.sw.dualscreen.model.response.PaySuccessEvent
|
||
import com.sw.dualscreen.model.response.ResetBoxEvent
|
||
import com.sw.dualscreen.model.response.UserFaceModel
|
||
import com.sw.dualscreen.objbox.Food
|
||
import com.sw.dualscreen.objbox.FoodModule
|
||
import com.sw.dualscreen.objbox.FoodModule.IdNameScore
|
||
import com.sw.dualscreen.objbox.ObjectBox
|
||
import com.sw.dualscreen.presentation.MainScreenPresentation
|
||
import com.sw.dualscreen.sdk.SensorScaleUtils
|
||
import com.sw.dualscreen.socket.TcpClient
|
||
import com.sw.dualscreen.utils.ActivityManager
|
||
import com.sw.dualscreen.utils.BitmapSaver
|
||
import com.sw.dualscreen.utils.Debouncer
|
||
import com.sw.dualscreen.utils.GsonUtils
|
||
import com.sw.dualscreen.utils.ImageUtil
|
||
import com.sw.dualscreen.utils.IntervalExecutor
|
||
import com.sw.dualscreen.utils.SPUtil
|
||
import com.sw.dualscreen.utils.SpTool
|
||
import com.sw.dualscreen.view.CustomBottomSheetDialog
|
||
import com.sw.dualscreen.viewmodel.BaseViewModel
|
||
import com.sw.dualscreen.viewmodel.UserViewModel
|
||
import com.sw.plate.utils.Base64
|
||
import com.sw.plate.utils.LightManager
|
||
import com.sw.plate.utils.ToastUtils
|
||
import com.sw.plate.utils.arcface.facedb.FaceDatabase
|
||
import com.sw.plate.utils.arcface.facedb.entity.FaceEntity
|
||
import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel
|
||
import io.objectbox.Box
|
||
import io.objectbox.kotlin.boxFor
|
||
import kotlinx.coroutines.Job
|
||
import kotlinx.coroutines.runBlocking
|
||
import okhttp3.RequestBody
|
||
import okhttp3.RequestBody.Companion.toRequestBody
|
||
import org.greenrobot.eventbus.Subscribe
|
||
import org.greenrobot.eventbus.ThreadMode
|
||
import org.json.JSONObject
|
||
import timber.log.Timber
|
||
import java.io.File
|
||
import java.time.LocalDateTime
|
||
import java.time.ZoneId
|
||
import java.time.format.DateTimeFormatter
|
||
import java.util.Locale
|
||
import java.util.concurrent.Executors
|
||
import kotlin.collections.get
|
||
import kotlin.math.roundToInt
|
||
|
||
/**
|
||
* 服务员显示界面
|
||
*/
|
||
@SuppressLint("NotifyDataSetChanged")
|
||
class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||
|
||
companion object {
|
||
private const val TAG = "MainActivity"
|
||
const val TIME_OUT = 5 * 60 * 1000L
|
||
}
|
||
|
||
private val viewModel by viewModels<UserViewModel>()
|
||
private var imageCapture: ImageCapture? = null
|
||
var isAnalyzing = true // 控制是否进行图像分析
|
||
|
||
// private var presentation: SecondaryScreenPresentation? = null
|
||
private var presentation: MainScreenPresentation? = null
|
||
private val executor = Executors.newSingleThreadExecutor()
|
||
private val handler by lazy { Handler(Looper.getMainLooper()) }
|
||
private val searchFoodList = mutableListOf<FoodInfo>()
|
||
private val adapter by lazy {
|
||
SearchFoodAdapter(searchFoodList).apply {
|
||
setOnItemClickListener { adapter, view, position ->
|
||
if (searchFoodList[position].isChecked) {
|
||
return@setOnItemClickListener
|
||
}
|
||
searchFoodList.forEachIndexed { index, info ->
|
||
searchFoodList[index].isChecked = index == position
|
||
}
|
||
val item = searchFoodList[position]
|
||
Timber.d("itemClick ${item.foodName}, position = $position")
|
||
item.photoUri = null
|
||
checkedItem = item
|
||
notifyDataSetChanged()
|
||
updateCurrentFood(item)
|
||
|
||
val mode = SPUtil.getInstance().get(GlobalKey.KEY_CHARGE_MODE, 0)
|
||
//不计费模式切换菜品,重新加载数据
|
||
if (mode == 1) {
|
||
presentation?.loadUnbilledMode()
|
||
}
|
||
}
|
||
}
|
||
}
|
||
var checkedItem: FoodInfo? = null
|
||
private var imageAnalysis: ImageAnalysis? = null
|
||
private var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>? = null
|
||
private var canIdentify: Boolean = false
|
||
private var bottomSheetDialog: CustomBottomSheetDialog? = null
|
||
private var lastWeight: Double = 0.0
|
||
private var lastPhotoUri: Uri? = null // 最后拍照的图片
|
||
private var debouncer = Debouncer(2000)
|
||
private var isRecognitionFood = true
|
||
private var isFirstOpen = true
|
||
|
||
private var isStartRecognize = false
|
||
|
||
override fun getViewModel(): BaseViewModel {
|
||
return viewModel
|
||
}
|
||
|
||
override fun inflateViewBinding(): ActivityMainBinding {
|
||
return ActivityMainBinding.inflate(layoutInflater)
|
||
}
|
||
|
||
override fun initialize() {
|
||
super.initialize()
|
||
box = ObjectBox.boxStore.boxFor(Food::class)
|
||
isPageVisible = true
|
||
addBackEventListener()
|
||
//FoodModule.init(this)
|
||
initView()
|
||
viewModel.getUserFaceCache(pageNo = 1)
|
||
//setupSecondaryDisplay()
|
||
setupCamera()
|
||
//presentation?.step1FoodRecognizing()
|
||
initData()
|
||
}
|
||
|
||
private fun initData() {
|
||
// viewModel.getEquipmentToken()
|
||
//viewModel.getUserFaceCache2()
|
||
|
||
startFaceTask()
|
||
|
||
// addSocketListener()
|
||
}
|
||
|
||
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.getDeviceConfig { deviceConfig ->
|
||
runOnUiThread {
|
||
if (deviceConfig == null) {
|
||
ToastUtils.showToast("获取设备配置数据失败")
|
||
return@runOnUiThread
|
||
}
|
||
GlobalData.appId = deviceConfig.arcsoftAppId ?: ""
|
||
GlobalData.sdkKey = deviceConfig.arcsoftSdkKey ?: ""
|
||
GlobalData.activeKey = deviceConfig.arcsoftActiveKey ?: ""
|
||
viewModel.activeEngine()
|
||
}
|
||
}
|
||
updateDateTime()
|
||
binding.ivSetting.setOnClickListener {
|
||
startActivity(Intent(this, SettingActivity::class.java))
|
||
//finish()
|
||
}
|
||
//binding.foodDisplayLayout.setOnClickListener {
|
||
// val intent = Intent(this, FoodCollectionActivity::class.java)
|
||
// startActivity(intent)
|
||
// finish()
|
||
//}
|
||
binding.previewView.outlineProvider = object : ViewOutlineProvider() {
|
||
override fun getOutline(view: View, outline: Outline) {
|
||
outline.setRoundRect(0, 0, view.width, view.height, 12f.dp)
|
||
}
|
||
}
|
||
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")
|
||
}
|
||
|
||
//val spinnerAdapter = ArrayAdapter<String>(
|
||
// this,
|
||
// R.layout.item_for_custom_spinner,
|
||
// arrayOf("即放即取", "余量取餐")
|
||
//)
|
||
//binding.spinner.adapter = spinnerAdapter
|
||
// 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<*>?,
|
||
// view: View?,
|
||
// position: Int,
|
||
// id: Long
|
||
// ) {
|
||
// SPUtil.getInstance().put(GlobalKey.KEY_PICKUP_MODE, position)
|
||
// updateCurrentFood(null)
|
||
// isRecognitionFood = true
|
||
// presentation?.updateMealPickupMode(position)
|
||
//
|
||
// SensorScaleUtils.zero()
|
||
// }
|
||
//
|
||
// override fun onNothingSelected(parent: AdapterView<*>?) {}
|
||
//}
|
||
//adapter = createAdapter()
|
||
binding.recyclerview.let {
|
||
it.layoutManager = GridLayoutManager(this, 2)
|
||
it.adapter = adapter
|
||
}
|
||
// 添加间距装饰(12dp)
|
||
//binding.recyclerview.addItemDecoration(
|
||
// GridSpacingItemDecoration(
|
||
// spanCount = 2,
|
||
// spacing = dpToPx(30),
|
||
// includeEdge = false // 包含边缘间距
|
||
// )
|
||
//)
|
||
//binding.recyclerview.adapter = adapter
|
||
binding.tvFoodName.setOnClickListener {
|
||
SensorScaleUtils.zero()
|
||
}
|
||
// binding.tvRescan.setOnClickListener {
|
||
// debouncer.debounce { recognizeFood() }
|
||
// }
|
||
binding.btnPay.setOnClickListener {
|
||
clickPayButton()
|
||
}
|
||
}
|
||
|
||
private var recognizeWeight = 0.0
|
||
fun updateCurrentFood(foodInfo: FoodInfo?) {
|
||
if (foodInfo == null) {
|
||
binding.tvFoodName.text = "-"
|
||
binding.previewView.visibility = View.VISIBLE
|
||
binding.ivImg.visibility = View.GONE
|
||
resumeAnalysis()
|
||
// viewModel.cleanIdentifiedFoodInfoList()
|
||
return
|
||
}
|
||
pauseAnalysis()
|
||
recognizeWeight = lastWeight
|
||
Timber.tag(TAG)
|
||
.d("registerDataChange,副屏updateFood调用前耗时:${System.currentTimeMillis() - startTime}")
|
||
startTime = System.currentTimeMillis()
|
||
presentation?.updateFood(foodInfo)
|
||
binding.tvFoodName.text = foodInfo.foodName
|
||
binding.previewView.gone()
|
||
binding.ivImg.visible()
|
||
// if (foodInfo.foodImg.isNullOrBlank()) {
|
||
// takePhoto(successCallback = { imgUri ->
|
||
// binding.ivImg.load(imgUri)
|
||
// presentation?.updateFood(foodInfo.copy(foodImg = imgUri.toString()))
|
||
// }, failureCallback = {
|
||
//
|
||
// })
|
||
// } else {
|
||
// binding.ivImg.load(foodInfo.foodImg)
|
||
// }
|
||
binding.ivImg.load(foodInfo.foodImg)
|
||
//pauseAnalysis()
|
||
}
|
||
|
||
fun updateImage(imgUrl: Any?) {
|
||
binding.ivImg.load(imgUrl)
|
||
}
|
||
|
||
override fun registerDataChange() {
|
||
super.registerDataChange()
|
||
//// lifecycleScope.launch {
|
||
//// viewModel.identifiedFoodInfoList2.collect { list ->
|
||
//// updateFoodInfo(list)
|
||
//// }
|
||
//// }
|
||
// lifecycleScope.launch {
|
||
// 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
|
||
// }
|
||
// }
|
||
// }
|
||
|
||
SensorScaleUtils.addWeightListener { weight ->
|
||
Timber.d("registerDataChange weight = $weight")
|
||
if (isPageVisible.not() || isStartRecognize.not()) {
|
||
if (ActivityManager.currentActivity() is MainActivity) {
|
||
isPageVisible = true
|
||
}
|
||
return@addWeightListener
|
||
}
|
||
val isWeightChange = weight - lastWeight > 0.05
|
||
recognizeByWeight(weight, isWeightChange)
|
||
}
|
||
}
|
||
|
||
private fun recognizeByWeight(weight: Double, isWeightChange: Boolean, block: () -> Unit = {}) {
|
||
presentation?.updateWeight(weight)
|
||
|
||
if (weight <= 0.005) {
|
||
////余量取餐,检测到秤上没有东西,重新启动识别菜品 && presentation?.mealPickupMode == 1
|
||
isRecognitionFood = true
|
||
return
|
||
}
|
||
|
||
if (isWeightChange && isRecognitionFood) { // 大于50g
|
||
if (presentation?.mealPickupMode == 1) {
|
||
isRecognitionFood = false
|
||
}
|
||
debouncer.debounce {
|
||
recognizeFood()
|
||
}
|
||
} else {
|
||
block()
|
||
}
|
||
lastWeight = weight
|
||
}
|
||
|
||
private var startTime = 0L
|
||
|
||
fun recognizeFood() {
|
||
startTime = System.currentTimeMillis()
|
||
// LightManager.openRedLight()
|
||
LightManager.closeRedLight()
|
||
LightManager.openGreenLight()
|
||
showWaitingDialog("识别中,请稍后……")
|
||
takePhoto(
|
||
successCallback = { photoUri ->
|
||
Thread{
|
||
onTakePhotoSuccess(photoUri)
|
||
}.start()
|
||
failCount = 0
|
||
}, failureCallback = {
|
||
runOnUiThread {
|
||
if (failCount >= 10) {
|
||
//ToastUtils.showToast("相机异常,请稍后重试")
|
||
shutdownCamera()
|
||
setupCamera()
|
||
return@runOnUiThread
|
||
}
|
||
}
|
||
failCount++
|
||
hideWaitingDialog()
|
||
|
||
shutdownCamera()
|
||
setupCamera()
|
||
|
||
binding.root.postDelayed({
|
||
recognizeFood()
|
||
}, 1000)
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 识别相机异常次数
|
||
*/
|
||
private var failCount = 0
|
||
|
||
@SuppressLint("NotifyDataSetChanged")
|
||
private fun updateFoodInfo(list: MutableList<FoodInfo>, scoreList: List<IdNameScore>) {
|
||
val queryData = GsonUtils.toJson(list)
|
||
val recData = GsonUtils.toJson(scoreList)
|
||
Timber.d("registerDataChange识别后查询接口数据:$queryData,识别数据:$recData")
|
||
if (list.isEmpty()) {
|
||
binding.tvToSearch.let {
|
||
it.text = "未查询到,手动搜索"
|
||
it.visible()
|
||
}
|
||
return
|
||
}
|
||
list.forEach { foodInfo ->
|
||
val scoreItem = scoreList.firstOrNull { it.name == foodInfo.foodName }
|
||
val score = scoreItem?.score ?: 0.0
|
||
foodInfo.score = ((1 - score) * 10000).roundToInt()
|
||
}
|
||
//接口已处理排序
|
||
// val list2 = try {
|
||
// val orderList = scoreList.map { it.name.trim() }
|
||
// list.sortedBy { orderList.indexOf(it.foodName) }
|
||
// } catch (e: Exception) {
|
||
// e.printStackTrace()
|
||
// list
|
||
// }
|
||
searchFoodList.clear()
|
||
searchFoodList.addAll(list)
|
||
adapter.notifyDataSetChanged()
|
||
|
||
checkedItem = list[0].also {
|
||
it.photoUri = lastPhotoUri
|
||
it.isChecked = true
|
||
}
|
||
adapter.notifyItemChanged(0)
|
||
updateCurrentFood(checkedItem)
|
||
// lastPhotoUri = null
|
||
LightManager.closeRedLight()
|
||
|
||
binding.tvToSearch.let {
|
||
it.text = "以上都不是,手动搜索"
|
||
it.visible()
|
||
}
|
||
}
|
||
|
||
// private fun createAdapter(): GenericItemAdapter<FoodInfo, ItemFoodInfoBinding> {
|
||
// return GenericItemAdapter(
|
||
// items = emptyList(),
|
||
// bindingInflater = ItemFoodInfoBinding::inflate,
|
||
// bindCallback = { item, position ->
|
||
// val score = (item.score / 100.0).format2String(2)
|
||
// this.tvName.text = "${item.foodName}\n${score}%"
|
||
// if (item.id != checkedItem?.id) {
|
||
// this.tvName.typeface = Typeface.defaultFromStyle(Typeface.NORMAL)
|
||
// this.tvName.setTextColor(resources.getColor(R.color.identify_normal))
|
||
// this.llRoot.setBackgroundResource(R.drawable.grid_item_normal)
|
||
// } else {
|
||
// this.tvName.typeface = Typeface.defaultFromStyle(Typeface.BOLD)
|
||
// this.tvName.setTextColor(resources.getColor(R.color.identify_checked))
|
||
// this.llRoot.setBackgroundResource(R.drawable.grid_item_checked)
|
||
// }
|
||
////
|
||
// this.llRoot.setOnClickListener {
|
||
// Timber.d("itemClick ${item.foodName}, position = $position")
|
||
// item.photoUri = null
|
||
// checkedItem = item
|
||
// adapter.notifyDataSetChanged()
|
||
// updateCurrentFood(item)
|
||
// }
|
||
// }
|
||
// )
|
||
// }
|
||
|
||
private fun setupCamera() {
|
||
Timber.d("setupCamera")
|
||
cameraProviderFuture = ProcessCameraProvider.getInstance(this)
|
||
cameraProviderFuture!!.addListener({
|
||
val cameraProvider = cameraProviderFuture!!.get()
|
||
|
||
// 1. 创建预览用例
|
||
val preview = Preview.Builder()
|
||
.build()
|
||
.also {
|
||
it.setSurfaceProvider(binding.previewView.surfaceProvider)
|
||
}
|
||
|
||
// 2. 创建图像分析用例(用于副屏显示)
|
||
imageAnalysis = ImageAnalysis.Builder()
|
||
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
||
// .setTargetResolution(Size(640, 480)) // 降低分辨率减轻负担
|
||
.build()
|
||
.also { analysis ->
|
||
analysis.setAnalyzer(executor) { imageProxy ->
|
||
// 仅在步骤1时传递
|
||
if (isAnalyzing) {
|
||
val bitmap = imageProxy.toBitmap()
|
||
presentation?.updateImage(bitmap) // 更新副屏
|
||
isStartRecognize = true
|
||
}
|
||
imageProxy.close() // 必须关闭以释放资源
|
||
}
|
||
}
|
||
|
||
// 3. 创建拍照用例
|
||
imageCapture = ImageCapture.Builder()
|
||
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
|
||
// .setTargetResolution(Size(1920, 1080)) // 设置拍照分辨率
|
||
.build()
|
||
|
||
// 4. 选择摄像头
|
||
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
|
||
|
||
try {
|
||
// 解绑所有用例
|
||
cameraProvider.unbindAll()
|
||
|
||
// 绑定三个用例到生命周期
|
||
cameraProvider.bindToLifecycle(
|
||
this, cameraSelector, preview, imageAnalysis, imageCapture
|
||
)
|
||
} catch (exc: Exception) {
|
||
Timber.e(exc.message)
|
||
}
|
||
}, ContextCompat.getMainExecutor(this))
|
||
}
|
||
|
||
fun pauseAnalysis() {
|
||
Timber.d("pauseAnalysis")
|
||
isAnalyzing = false
|
||
imageAnalysis?.clearAnalyzer()
|
||
}
|
||
|
||
fun resumeAnalysis() {
|
||
Timber.d("resumeAnalysis isAnalyzing = $isAnalyzing")
|
||
if (isAnalyzing) return
|
||
// isAnalyzing = true
|
||
imageAnalysis?.setAnalyzer(executor) { imageProxy ->
|
||
if (presentation?.currentStep == 1) {
|
||
val bitmap = imageProxy.toBitmap()
|
||
presentation?.updateImage(bitmap)
|
||
}
|
||
imageProxy.close()
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 停止相机识别
|
||
*/
|
||
private fun shutdownCamera() {
|
||
Timber.d("shutdownCamera")
|
||
try {
|
||
// 1. 首先停止图像分析
|
||
imageAnalysis?.clearAnalyzer()
|
||
|
||
// 2. 解除所有绑定
|
||
cameraProviderFuture?.get()?.unbindAll()
|
||
|
||
// 3. 关闭相关资源
|
||
imageCapture = null
|
||
imageAnalysis = null
|
||
|
||
// 4. 停止副屏更新
|
||
isAnalyzing = false
|
||
} catch (e: Exception) {
|
||
Timber.e(e, "Error shutting down camera")
|
||
}
|
||
}
|
||
|
||
// 拍照功能
|
||
private fun takePhoto(successCallback: (Uri) -> Unit, failureCallback: () -> Unit) {
|
||
Timber.d("takePhoto")
|
||
// 临时停止分析以避免干扰
|
||
isAnalyzing = false
|
||
|
||
// 创建输出选项
|
||
val executor = ContextCompat.getMainExecutor(this)
|
||
val cacheDir = cacheDir
|
||
val photoFile = File.createTempFile(
|
||
"IMG_${System.currentTimeMillis()}",
|
||
".jpg",
|
||
cacheDir
|
||
)
|
||
|
||
val cacheOutputOptions =
|
||
ImageCapture.OutputFileOptions.Builder(photoFile).build()
|
||
// 执行拍照
|
||
imageCapture?.takePicture(
|
||
cacheOutputOptions,
|
||
executor,
|
||
object : ImageCapture.OnImageSavedCallback {
|
||
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
|
||
val savedUri = outputFileResults.savedUri
|
||
Timber.d("takePhoto savedUri = $savedUri")
|
||
if (savedUri != null) {
|
||
successCallback(savedUri)
|
||
}
|
||
// 恢复图像分析
|
||
isAnalyzing = true
|
||
}
|
||
|
||
override fun onError(exception: ImageCaptureException) {
|
||
Timber.e("拍照失败: ${exception.message}")
|
||
failureCallback()
|
||
//ToastUtils.showToast("拍照失败: ${exception.message}")
|
||
// 恢复图像分析
|
||
isAnalyzing = true
|
||
}
|
||
}
|
||
)
|
||
}
|
||
|
||
private fun getSecondaryDisplay(): Display? {
|
||
val displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
||
val displays = displayManager.displays
|
||
|
||
// 查找副屏(通常索引为1)
|
||
if (displays.size > 1) return displays[1]
|
||
return null
|
||
}
|
||
|
||
private val recognizeViewModel by lazy { viewModels<RecognizeViewModel>().value }
|
||
|
||
private fun setupSecondaryDisplay() {
|
||
val secondaryDisplay = getSecondaryDisplay()
|
||
if (secondaryDisplay == null) {
|
||
ToastUtils.showToast("获取副屏失败")
|
||
return
|
||
}
|
||
// 创建副屏的Presentation
|
||
presentation = MainScreenPresentation(
|
||
activity = this,
|
||
display = secondaryDisplay,
|
||
userViewModel = viewModel,
|
||
recognizeViewModel = recognizeViewModel
|
||
).apply {
|
||
setStepChangeCallback { step ->
|
||
if (step == 1) {
|
||
Timber.d("setStepChangeCallback${step}")
|
||
updateCurrentFood(null)
|
||
bottomSheetDialog?.dismiss()
|
||
}
|
||
}
|
||
show()
|
||
}
|
||
//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()
|
||
// }
|
||
// }
|
||
}
|
||
|
||
@SuppressLint("NotifyDataSetChanged")
|
||
fun clearFoodList() {
|
||
//adapter.updateData(listOf())
|
||
searchFoodList.clear()
|
||
adapter.notifyDataSetChanged()
|
||
}
|
||
|
||
private fun updateDateTime() {
|
||
val formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 E", Locale.CHINA)
|
||
val dateTime = formatter.format(LocalDateTime.now(ZoneId.of("Asia/Shanghai")))
|
||
binding.tvTitleTime.text = dateTime
|
||
}
|
||
|
||
public var isPageVisible = true
|
||
|
||
override fun onResume() {
|
||
super.onResume()
|
||
isPageVisible = true
|
||
// 启动定时器
|
||
handler.postDelayed(timeoutRunnable, TIME_OUT)
|
||
// ToastUtils.showToast("isPageVisible=$isPageVisible")
|
||
if (isFirstOpen) {
|
||
//首次
|
||
setupSecondaryDisplay()
|
||
presentation?.step1FoodRecognizing()
|
||
isFirstOpen = false
|
||
} else {
|
||
//副屏未显示重新加载
|
||
isRecognitionFood = true
|
||
if (presentation == null || !presentation!!.isShowing) {
|
||
lastWeight = 0.0
|
||
setupSecondaryDisplay()
|
||
presentation?.step1FoodRecognizing()
|
||
} else {
|
||
if (isRefreshPage) {
|
||
//已显示副屏,则进行更新
|
||
val weight = lastWeight
|
||
lastWeight = 0.0
|
||
presentation?.hideRecImage()
|
||
presentation?.step1FoodRecognizing()
|
||
|
||
binding.root.postDelayed({
|
||
recognizeByWeight(weight, true)
|
||
}, 500)
|
||
}
|
||
isRefreshPage = !isRefreshPage
|
||
}
|
||
}
|
||
|
||
//根据付费模式确认是否显示按钮
|
||
binding.btnPay.run {
|
||
val mode = SPUtil.getInstance().get(GlobalKey.KEY_CHARGE_MODE, 0)
|
||
if (mode == 0) visible() else gone()
|
||
}
|
||
}
|
||
|
||
// fun retry() {
|
||
// val weight = lastWeight
|
||
// lastWeight = 0.0
|
||
// presentation?.hideRecImage()
|
||
// recognizeByWeight(weight, true)
|
||
// }
|
||
|
||
override fun onPause() {
|
||
super.onPause()
|
||
isPageVisible = false
|
||
// 取消定时器
|
||
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()
|
||
}
|
||
|
||
fun createOrder(
|
||
foodInfo: FoodInfo,
|
||
foodWeight: Int,
|
||
eatWeight: Int,
|
||
eatNum: Int,
|
||
userId: String?,
|
||
block: () -> Unit
|
||
) {
|
||
Timber.tag(TAG).d("foodWeight=$foodWeight,eatWeight=$eatWeight")
|
||
val pickupMode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0) ?: 0
|
||
if (pickupMode == 0) {
|
||
if (foodWeight <= 5 || eatWeight <= 5) {
|
||
ToastUtils.showToast("请取走餐品")
|
||
return
|
||
}
|
||
}
|
||
if (eatNum == 0) {
|
||
ToastUtils.showToast("您好,当前重量不足一份")
|
||
return
|
||
}
|
||
showWaitingDialog("加载中,请稍候……")
|
||
val chargeMode = SPUtil.getInstance().get(GlobalKey.KEY_CHARGE_MODE, 0)
|
||
val order = FoodOrder(
|
||
deviceId = GlobalData.deviceId,
|
||
foodId = foodInfo.foodId,
|
||
foodName = foodInfo.foodName ?: "",
|
||
foodMaterialId = foodInfo.foodMaterialId ?: "",
|
||
specId = foodInfo.specId ?: "",
|
||
foodWeight = foodWeight,
|
||
eatWeight = eatWeight,
|
||
//根据specId对应规格重量计算
|
||
eatNum = eatNum,
|
||
userId = userId,
|
||
notPay = chargeMode != 0,
|
||
//即放即取-1,称重-2
|
||
mode = if (pickupMode == 1) 1 else 2
|
||
)
|
||
debouncer.debounce {
|
||
submitOrder(order, block)
|
||
}
|
||
}
|
||
|
||
private fun submitOrder(order: FoodOrder, block: () -> Unit) {
|
||
viewModel.createOrder(order) { orderId ->
|
||
runOnUiThread {
|
||
binding.root.postDelayed({
|
||
hideWaitingDialog()
|
||
if (orderId.isBlank()) {
|
||
//ToastUtils.showToast("订单id为空")
|
||
return@postDelayed
|
||
}
|
||
foodOrderId = orderId
|
||
block()
|
||
|
||
if (checkedItem!!.isFromSearch == true) {
|
||
//当前菜名为手动搜索选择,非识别结果,保存向量数据
|
||
Thread{
|
||
saveFoodVector(checkedItem!!)
|
||
}.start()
|
||
}
|
||
}, 300)
|
||
}
|
||
}
|
||
}
|
||
|
||
fun getEatNum(realWeight: Double?, specWeight: Double?): Int {
|
||
if (realWeight == null || realWeight == 0.0 || specWeight == null || specWeight == 0.0) return 1
|
||
return (realWeight / specWeight).roundToInt()
|
||
}
|
||
|
||
private var foodOrderId: String = ""
|
||
private var isExist = false
|
||
fun addBackEventListener() {
|
||
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
|
||
override fun handleOnBackPressed() {
|
||
if (isExist.not()) {
|
||
ToastUtils.showToast("再按一次退出")
|
||
isExist = true
|
||
binding.root.postDelayed({
|
||
isExist = false
|
||
}, 2000)
|
||
return
|
||
}
|
||
finish()
|
||
}
|
||
})
|
||
}
|
||
|
||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||
fun onPaySuccessEvent(event: PaySuccessEvent) {
|
||
isRefreshPage = true
|
||
val chargeMode = SPUtil.getInstance().get(GlobalKey.KEY_CHARGE_MODE, 0) ?: 0
|
||
presentation?.hideNutritionView(chargeMode)
|
||
presentation?.cancel()
|
||
}
|
||
|
||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||
fun onChargeModeEvent(event: ChargeModeEvent) {
|
||
isRefreshPage = true
|
||
isStartRecognize = false
|
||
presentation?.hideNutritionView(event.chargeMode)
|
||
// presentation?.cancel()
|
||
|
||
if (event.chargeMode == 1) {
|
||
presentation?.let {
|
||
it.onStop()
|
||
it.setupArcCamera()
|
||
}
|
||
}
|
||
}
|
||
|
||
private var isRefreshPage = false
|
||
|
||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||
fun onClickBackEvent(event: ClickBackEvent) {
|
||
isRefreshPage = false
|
||
}
|
||
|
||
private fun clickPayButton() {
|
||
if (checkedItem == null) {
|
||
ToastUtils.showToast("暂无识别数据,请搜索选择")
|
||
return
|
||
}
|
||
|
||
//val realWeight = (lastWeight * 1000).roundToInt()
|
||
val mealPickupMode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0) ?: 0
|
||
val chargeMode = SPUtil.getInstance().get(GlobalKey.KEY_CHARGE_MODE, 0) ?: 0
|
||
var foodWeight = 0.0
|
||
var eatWeight = 0.0
|
||
if (chargeMode == 0) {
|
||
//计费
|
||
if (mealPickupMode == 0) {
|
||
//即放即取
|
||
eatWeight = lastWeight * 1000
|
||
foodWeight = lastWeight * 1000
|
||
} else {
|
||
//余量计量
|
||
eatWeight = (recognizeWeight - lastWeight) * 1000
|
||
foodWeight = recognizeWeight * 1000
|
||
}
|
||
} else {
|
||
//不计费
|
||
foodWeight = recognizeWeight * 1000
|
||
eatWeight = if (mealPickupMode == 0) {
|
||
//即放即取
|
||
recognizeWeight * 1000
|
||
} else {
|
||
//余量计量
|
||
(recognizeWeight - lastWeight) * 1000
|
||
}
|
||
}
|
||
val eatNum = getEatNum(eatWeight, checkedItem!!.specWeight)
|
||
createOrder(
|
||
foodInfo = checkedItem!!,
|
||
foodWeight = foodWeight.roundToInt(),
|
||
eatWeight = eatWeight.roundToInt(),
|
||
eatNum = eatNum,
|
||
userId = null,
|
||
) {
|
||
// TODO: 暂不关闭首页副屏
|
||
//presentation?.dismiss()
|
||
startActivity(Intent(this, PayActivity::class.java).apply {
|
||
putExtra(PayActivity.FOOD_INFO, checkedItem!!)
|
||
putExtra(PayActivity.FOOD_EAT_NUM, eatNum)
|
||
putExtra(PayActivity.FOOD_ORDER_ID, foodOrderId)
|
||
})
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 将uri转为文件和向量数据并提交
|
||
*/
|
||
private fun saveFoodVector(foodInfo: FoodInfo) {
|
||
try {
|
||
if (lastPhotoUri == null) {
|
||
return
|
||
}
|
||
uri2File(lastPhotoUri!!) { imageFile, imageVector ->
|
||
runBlocking {
|
||
uploadCollectFoodPics(foodInfo, imageFile, imageVector)
|
||
}
|
||
}
|
||
} catch (e: Exception) {
|
||
e.printStackTrace()
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 上传菜品信息
|
||
*/
|
||
private suspend fun uploadCollectFoodPics(
|
||
foodInfo: FoodInfo,
|
||
imageFile: File?,
|
||
imageVector: FloatArray?
|
||
) {
|
||
if (imageFile == null || imageVector == null) {
|
||
Timber.tag(TAG)
|
||
.d("是否null判断,imageFile == null:${imageFile == null},imageVector == null:${imageVector == null}")
|
||
return
|
||
}
|
||
val foodId = foodInfo.foodId
|
||
val foodName = foodInfo.foodName ?: ""
|
||
val foodModelVersion = GlobalData.foodModelVersion
|
||
val params = HashMap<String, RequestBody>()
|
||
params["foodId"] = foodId.toRequestBody()
|
||
params["foodName"] = foodName.toRequestBody()
|
||
params["version"] = foodModelVersion.toRequestBody()
|
||
val files = listOf(imageFile)
|
||
val vectors = listOf(imageVector)
|
||
val foodVectorList = vectors.map {
|
||
it.joinToString(
|
||
separator = ",",
|
||
prefix = "[",
|
||
postfix = "]"
|
||
)
|
||
}
|
||
val foodVectorJson =
|
||
foodVectorList.joinToString(separator = ",", prefix = "[", postfix = "]")
|
||
Timber.tag(TAG).d("json=$foodVectorJson")
|
||
params["foodVector"] = foodVectorJson.toRequestBody()
|
||
val idList = viewModel.uploadCollectFoodPics(files, params)
|
||
if (idList.isNullOrEmpty()) {
|
||
loadRemindDialog("未返回id")
|
||
Timber.tag(TAG).d("idList为空")
|
||
return
|
||
}
|
||
Timber.tag(TAG)
|
||
.d("uploadCollectFoodPics-已采集向量总数:${box.all.filter { it.isDel.not() }.size}条")
|
||
val filterList = box.all.filter { it.collectId == idList[0] }
|
||
if (filterList.isNotEmpty()) {
|
||
loadRemindDialog("返回的id:${idList[0]}已存在")
|
||
return
|
||
}
|
||
Thread {
|
||
box.put(
|
||
Food(
|
||
collectId = idList[0],
|
||
foodId = foodId,
|
||
foodName = foodName,
|
||
foodVector = imageVector,
|
||
version = foodModelVersion
|
||
)
|
||
)
|
||
}.start()
|
||
}
|
||
|
||
private fun loadRemindDialog(msg: String) {
|
||
runOnUiThread {
|
||
RemindDialog(
|
||
context = context,
|
||
content = msg,
|
||
confirmBlock = {}
|
||
).show()
|
||
}
|
||
}
|
||
|
||
private lateinit var box: Box<Food>
|
||
|
||
private fun uri2File(uri: Uri, block: (File?, FloatArray?) -> Unit) {
|
||
ImageUtil.uriToBitmap(this, uri)?.let { bitmap ->
|
||
val imageVector = try {
|
||
FoodModule.bitmap2FloatArray(bitmap)
|
||
} catch (e: Exception) {
|
||
e.printStackTrace()
|
||
return@let
|
||
}
|
||
val imageFile = BitmapSaver.saveToAppFilesDir(
|
||
bitmap, this, "IMG_CROP_${System.currentTimeMillis()}.jpg"
|
||
)
|
||
Timber.d("${this.javaClass.simpleName}-cameraCallback-裁剪bitmap保存文件路径:${imageFile?.absolutePath}")
|
||
block(imageFile, imageVector)
|
||
if (bitmap.isRecycled.not()) {
|
||
bitmap.recycle()
|
||
}
|
||
}
|
||
}
|
||
|
||
override fun onDestroy() {
|
||
//SensorScaleUtils.closeScale()
|
||
faceTaskJob?.cancel()
|
||
presentation?.cancel()
|
||
shutdownCamera()
|
||
super.onDestroy()
|
||
}
|
||
|
||
private val intervalExecutor by lazy { IntervalExecutor() }
|
||
private var faceTaskJob: Job? = null
|
||
|
||
// private val initialDelay = 5 * 60 * 1000L
|
||
// private val dealyMillis = 10 * 60 * 1000L
|
||
private val initialDelay = 60 * 1000L
|
||
private val dealyMillis = 30 * 1000L
|
||
private var taskPageNo = 1
|
||
fun startFaceTask() {
|
||
faceTaskJob =
|
||
intervalExecutor.startIntervalTaskWithInitialDelay(initialDelay, dealyMillis) {
|
||
val timestamp = SpTool.getLastFaceTimestamp()
|
||
if (timestamp == 0L) {
|
||
return@startIntervalTaskWithInitialDelay
|
||
}
|
||
viewModel.getFaceIncrementList(
|
||
pageNo = taskPageNo,
|
||
timestamp = timestamp,
|
||
onAllQueryFinished = {
|
||
taskPageNo = 1
|
||
},
|
||
onPageQueryFinished = { list ->
|
||
runOnUiThread {
|
||
if (list.isEmpty()) {
|
||
return@runOnUiThread
|
||
}
|
||
updateFaceData(list)
|
||
}
|
||
}
|
||
)
|
||
}
|
||
}
|
||
|
||
private fun updateFaceData(list: List<UserFaceModel>) {
|
||
Thread {
|
||
val faceList = mutableListOf<FaceEntity>()
|
||
try {
|
||
list.forEach { model ->
|
||
if (model.faceDeleted == true) {
|
||
//删除数据
|
||
FaceDatabase.getInstance(this).faceDao().deleteFaceById(model.userId)
|
||
} else {
|
||
//保存数据
|
||
val faceEntity = FaceEntity(
|
||
model.userId,
|
||
null,
|
||
Base64.decode(model.faceFeatureStr)
|
||
).also {
|
||
it.userType = "1"
|
||
}
|
||
faceList.add(faceEntity)
|
||
}
|
||
}
|
||
} catch (e: Exception) {
|
||
e.printStackTrace()
|
||
}
|
||
try {
|
||
if (faceList.isNotEmpty()) {
|
||
FaceDatabase.getInstance(this).faceDao().insert(faceList)
|
||
}
|
||
recognizeViewModel.refreshFaceList();
|
||
} catch (e: Exception) {
|
||
e.printStackTrace()
|
||
}
|
||
}.start()
|
||
}
|
||
|
||
private fun sendFaceData(faceData: String) {
|
||
// 给某个客户端发送
|
||
val jsonObject = JSONObject().also {
|
||
it.put("type", "faceFeature")
|
||
it.put("content", faceData)
|
||
}
|
||
tcpClient?.send(jsonObject)
|
||
}
|
||
|
||
private var tcpClient: TcpClient? = null
|
||
|
||
// private fun addSocketListener() {
|
||
// //val ipAddress = NetworkUtils.getIPAddress(true)
|
||
// //Log.d(TAG, "addSocketListener,ipAddress: $ipAddress");
|
||
// tcpClient = TcpClient(
|
||
// "192.168.1.95",
|
||
// 5000,
|
||
// GlobalData.deviceId, // clientId
|
||
// 5000, // connectTimeoutMs
|
||
// 10 * 1000, // heartbeatIntervalMs
|
||
// 30 * 1000 // heartbeatTimeoutMs
|
||
// )
|
||
//
|
||
// tcpClient?.setListener(object : TcpClientListenerImpl() {
|
||
// override fun onSendSuccess(json: JSONObject?) {
|
||
// super.onSendSuccess(json)
|
||
//// toast("发送成功")
|
||
// }
|
||
//
|
||
// override fun onSendFailed(json: JSONObject?, e: java.lang.Exception?) {
|
||
// super.onSendFailed(json, e)
|
||
// ToastUtils.showToast("发送失败${e?.message}")
|
||
// }
|
||
//
|
||
// override fun onMessage(json: JSONObject?) {
|
||
// super.onMessage(json)
|
||
// json?.let { receiveMessage(json!!) }
|
||
// }
|
||
// })
|
||
// tcpClient?.start();
|
||
//
|
||
// try {
|
||
// LanServer.getInstance().let {
|
||
// it.setListener(object : LanServerListenerImpl() {
|
||
// override fun onMessageReceived(clientId: String?, message: JSONObject?) {
|
||
// super.onMessageReceived(clientId, message)
|
||
// //toast("收到消息clientId=$clientId")
|
||
// }
|
||
// })
|
||
// it.start()
|
||
// }
|
||
// } catch (e: Exception) {
|
||
// e.printStackTrace()
|
||
// }
|
||
// }
|
||
|
||
// /**
|
||
// * 接收服务端发送的消息
|
||
// *
|
||
// * @param json 人脸数据
|
||
// */
|
||
// private fun receiveMessage(json: JSONObject) {
|
||
// try {
|
||
// val faceId = json.getString("faceId");
|
||
// val faceFeature = json.getString("faceFeature");
|
||
// queryByUserName(faceId, { faceEntity ->
|
||
// if (faceEntity != null) {
|
||
// //已存在用户faceId的人脸数据
|
||
// return@queryByUserName
|
||
// }
|
||
// //不存在用户faceId,保存数据
|
||
// val faceEntity2 = FaceEntity(faceId, null, Base64.decode(faceFeature))
|
||
// //设置用户类型:1-内部员工、2-临时用户、3-普通会员、或者其它待定类型
|
||
// faceEntity2.setUserType("2")
|
||
// saveFaceData(faceEntity2)
|
||
// })
|
||
// } catch (e: Exception) {
|
||
// e.printStackTrace()
|
||
// }
|
||
// }
|
||
|
||
// private fun saveFaceData(faceEntity: FaceEntity) {
|
||
// Thread {
|
||
// FaceDatabase.getInstance(this).faceDao().insert(faceEntity)
|
||
// }.start()
|
||
// }
|
||
//
|
||
// private fun queryByUserName(userName: String, block: (FaceEntity) -> Unit) {
|
||
// Thread {
|
||
// val faceEntity = FaceDatabase.getInstance(this).faceDao().queryByUserName(userName)
|
||
// runOnUiThread {
|
||
// block(faceEntity)
|
||
// }
|
||
// }.start()
|
||
// }
|
||
|
||
private fun onTakePhotoSuccess(photoUri: Uri) {
|
||
lastPhotoUri = photoUri
|
||
Timber.tag(TAG).d("main,takePhoto耗时:${System.currentTimeMillis() - startTime}")
|
||
startTime = System.currentTimeMillis()
|
||
Timber.d("registerDataChange photoUri = ${photoUri.path}")
|
||
// viewModel.getIdentifiedFoodList()
|
||
ImageUtil.uriToBitmap(this, photoUri)?.let { bitmap ->
|
||
Timber.tag(TAG).d("main,uriToBitmap耗时:${System.currentTimeMillis() - startTime}")
|
||
startTime = System.currentTimeMillis()
|
||
Timber.d("registerDataChange photoUri 拿到bitmap")
|
||
// val bmp = BitmapCropper.cropCenter(bitmap, 1300, 900)
|
||
Timber.d("registerDataChange photoUri bitmap裁剪完成")
|
||
val file = BitmapSaver.saveToAppFilesDir(
|
||
bitmap, this, "IMG_CROP_${System.currentTimeMillis()}.jpg"
|
||
)
|
||
Timber.tag(TAG).d("main,saveToAppFilesDir耗时:${System.currentTimeMillis() - startTime}")
|
||
startTime = System.currentTimeMillis()
|
||
Timber.d("registerDataChange photoUri bitmap保存文件路径:${file?.absolutePath}")
|
||
//val nameList = FoodModule.queryFood(bitmap)
|
||
val scoreList = FoodModule.getFoodScoreList(bitmap)
|
||
val recDataJson = GsonUtils.toJson(scoreList)
|
||
Timber.tag(TAG).d("main,getFoodScoreList耗时:${System.currentTimeMillis() - startTime}")
|
||
startTime = System.currentTimeMillis()
|
||
Timber.d("registerDataChange photoUri 拿到识别数据:$recDataJson")
|
||
val nameList = scoreList.map { it.name }
|
||
val foodName = nameList.joinToString(separator = ",")
|
||
|
||
hideWaitingDialog()
|
||
Timber.d("registerDataChange photoUri 识别数据名称:$foodName")
|
||
runOnUiThread {
|
||
binding.flPay.run {
|
||
val chargeMode = SPUtil.getInstance().get(GlobalKey.KEY_CHARGE_MODE, 0)
|
||
//0-计费,1-不计费
|
||
if (chargeMode == 0) visible() else gone()
|
||
}
|
||
}
|
||
if (TextUtils.isEmpty(foodName)) {
|
||
LightManager.closeGreenLight()
|
||
LightManager.closeRedLight()
|
||
//binding.layoutRescan.visibility = View.VISIBLE
|
||
|
||
runOnUiThread {
|
||
binding.tvToSearch.let {
|
||
it.text = "未识别到,手动搜索"
|
||
it.visible()
|
||
}
|
||
}
|
||
} else {
|
||
//binding.layoutRescan.visibility = View.GONE
|
||
showWaitingDialog("正在查询菜品信息,请稍后……")
|
||
viewModel.getFoodInfo(foodName) { list ->
|
||
runOnUiThread {
|
||
hideWaitingDialog()
|
||
Timber.tag(TAG)
|
||
.d("main,getFoodInfo耗时:${System.currentTimeMillis() - startTime}")
|
||
startTime = System.currentTimeMillis()
|
||
updateFoodInfo(list.toMutableList(), scoreList)
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
// @Subscribe(threadMode = ThreadMode.MAIN)
|
||
// public fun onResetBox(event: ResetBoxEvent) {
|
||
// ObjectBox.boxStore.close()
|
||
// ObjectBox.init(this)
|
||
// }
|
||
|
||
} |