This commit is contained in:
2025-12-19 17:34:31 +08:00
parent af56fb4aeb
commit 960b429e69
18 changed files with 294 additions and 181 deletions
@@ -1,11 +1,11 @@
package com.sw.dualscreen.activity
import android.app.Dialog
import android.content.Context
import android.graphics.drawable.ColorDrawable
import android.hardware.display.DisplayManager
import android.os.Bundle
import android.text.TextUtils
import android.view.Display
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
@@ -13,19 +13,17 @@ import android.widget.TextView
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import androidx.viewbinding.ViewBinding
import com.sw.dualscreen.R
import com.sw.dualscreen.databinding.DialogWaitingBinding
import com.sw.dualscreen.model.IActivityResult
import com.sw.dualscreen.model.response.PostEvent
import com.sw.dualscreen.view.CustomDialog
import com.sw.dualscreen.viewmodel.BaseViewModel
import kotlinx.coroutines.launch
import com.sw.plate.utils.ToastUtils
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import timber.log.Timber
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
@@ -84,17 +82,14 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
/**
* 显示等待提示框
*/
fun showWaitingDialog(tip: String?){
fun showWaitingDialog(tip: String?) {
runOnUiThread {
hideWaitingDialog()
val view = View.inflate(this, R.layout.dialog_waiting, null)
if (!TextUtils.isEmpty(tip)) (view.findViewById<View?>(R.id.tvTip) as TextView).text =
tip
mDialogWaiting = CustomDialog(this, view, R.style.MyDialog).apply {
setCancelable(true)
setCanceledOnTouchOutside(true)
show()
}
//val view = View.inflate(this, R.layout.dialog_waiting, null)
val dialogBinding = DialogWaitingBinding.inflate(LayoutInflater.from(this))
dialogBinding.tvTip.text = tip
mDialogWaiting = CustomDialog(this, dialogBinding.root)
mDialogWaiting?.show()
}
}
@@ -113,8 +108,7 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
hideWaitingDialog()
val view = View.inflate(this, R.layout.dialog_waiting, null)
mDialogWaiting = CustomDialog(this, view, R.style.MyDialog)
mDialogWaiting!!.show()
mDialogWaiting!!.setCancelable(true)
mDialogWaiting?.show()
}
val contentView = mDialogWaiting?.findViewById<ViewGroup>(android.R.id.content)
val tvTip = contentView?.findViewById<TextView>(R.id.tvTip)
@@ -32,6 +32,7 @@ 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
@@ -114,6 +115,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
private var isRecognitionFood = true
private var isFirstOpen = true
private var isStartRecognize = false
override fun getViewModel(): BaseViewModel {
return viewModel
}
@@ -240,17 +243,23 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
}
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 { imgUri ->
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 ?: foodInfo.photoUri)
//pauseAnalysis()
}
@@ -283,6 +292,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
SensorScaleUtils.addWeightListener { weight ->
Timber.d("registerDataChange weight = $weight")
if (isPageVisible.not() || isStartRecognize.not()) return@addWeightListener
val isWeightChange = weight - lastWeight > 0.05
recognizeByWeight(weight, isWeightChange)
}
@@ -308,15 +318,23 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
lastWeight = weight
}
private var startTime = 0L
fun recognizeFood() {
startTime = System.currentTimeMillis()
LightManager.openRedLight()
LightManager.openGreenLight()
takePhoto { photoUri ->
showWaitingDialog("识别中,请稍后……")
takePhoto(successCallback = { photoUri ->
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裁剪完成")
@@ -325,19 +343,21 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
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)
Timber.d(
"registerDataChange photoUri 拿到识别数据:${
GsonUtils.toJson(
scoreList
)
}"
)
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")
if (TextUtils.isEmpty(foodName)) {
LightManager.closeGreenLight()
@@ -350,15 +370,28 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
}
} 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)
}
}
}
}
}
}, failureCallback = {
runOnUiThread {
hideWaitingDialog()
ToastUtils.showToast("拍照异常,请手动放置后重新识别")
}
// binding.root.postDelayed({
// recognizeFood()
// }, 500)
})
}
@SuppressLint("NotifyDataSetChanged")
@@ -447,6 +480,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
if (isAnalyzing) {
val bitmap = imageProxy.toBitmap()
presentation?.updateImage(bitmap) // 更新副屏
isStartRecognize = true
}
imageProxy.close() // 必须关闭以释放资源
}
@@ -518,7 +552,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
}
// 拍照功能
private fun takePhoto(callback: (Uri) -> Unit) {
private fun takePhoto(successCallback: (Uri) -> Unit, failureCallback: () -> Unit) {
Timber.d("takePhoto")
// 临时停止分析以避免干扰
isAnalyzing = false
@@ -545,7 +579,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
Timber.d("takePhoto savedUri = $savedUri")
// ToastUtils.showToast("照片保存成功: $savedUri")
if (savedUri != null) {
callback(savedUri)
successCallback(savedUri)
}
}
// 恢复图像分析
@@ -554,6 +588,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
override fun onError(exception: ImageCaptureException) {
Timber.e("拍照失败: ${exception.message}")
failureCallback()
//runOnUiThread {
// ToastUtils.showToast("拍照失败: ${exception.message}")
//}
@@ -631,14 +666,16 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
binding.tvTitleTime.text = dateTime
}
private var isPageVisible = true
override fun onResume() {
super.onResume()
Timber.tag("onResume").d("=================onResume================")
isPageVisible = true
// 启动定时器
handler.postDelayed(timeoutRunnable, TIME_OUT)
if (isFirstOpen) {
//首次
//首次
setupSecondaryDisplay()
presentation?.step1FoodRecognizing()
isFirstOpen = false
@@ -650,11 +687,14 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
setupSecondaryDisplay()
presentation?.step1FoodRecognizing()
} else {
//已显示副屏,则进行更新
val weight = lastWeight
lastWeight = 0.0
presentation?.hideRecImage()
recognizeByWeight(weight, true)
if (isRefreshPage) {
//已显示副屏,则进行更新
val weight = lastWeight
lastWeight = 0.0
presentation?.hideRecImage()
recognizeByWeight(weight, true)
}
isRefreshPage = !isRefreshPage
}
}
@@ -674,6 +714,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
override fun onPause() {
super.onPause()
isPageVisible = false
// 取消定时器
handler.removeCallbacks(timeoutRunnable)
}
@@ -770,16 +811,24 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
@Subscribe(threadMode = ThreadMode.MAIN)
fun onPaySuccessEvent(event: PaySuccessEvent) {
val chargeMode = SPUtil.getInstance().get(GlobalKey.KEY_CHARGE_MODE, 0)?:0
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()
}
private var isRefreshPage = false
@Subscribe(threadMode = ThreadMode.MAIN)
fun onClickBackEvent(event: ClickBackEvent) {
isRefreshPage = false
}
private fun clickPayButton() {
if (checkedItem == null) {
@@ -1,5 +1,6 @@
package com.sw.dualscreen.activity
import androidx.activity.OnBackPressedCallback
import androidx.activity.viewModels
import androidx.fragment.app.Fragment
import com.sw.dualscreen.R
@@ -7,8 +8,10 @@ import com.sw.dualscreen.activity.fragment.BusinessFragment
import com.sw.dualscreen.activity.fragment.CollectFragment
import com.sw.dualscreen.databinding.ActivitySettingBinding
import com.sw.dualscreen.model.response.FoodInfo
import com.sw.dualscreen.model.response.ClickBackEvent
import com.sw.dualscreen.viewmodel.BaseViewModel
import com.sw.dualscreen.viewmodel.UserViewModel
import org.greenrobot.eventbus.EventBus
class SettingActivity : BaseActivity<ActivitySettingBinding>() {
@@ -28,10 +31,12 @@ class SettingActivity : BaseActivity<ActivitySettingBinding>() {
override fun initialize() {
super.initialize()
initBackDispatcher()
binding.include.let {
it.ivPageBack.setOnClickListener {
// val intent = Intent(this, MainActivity::class.java)
// startActivity(intent)
EventBus.getDefault().post(ClickBackEvent())
finish()
}
it.tvPageTitle.text = "设置"
@@ -65,4 +70,17 @@ class SettingActivity : BaseActivity<ActivitySettingBinding>() {
viewModel.searchByFoodName(foodName, action)
}
private lateinit var backPressedCallback: OnBackPressedCallback
private fun initBackDispatcher() {
// 创建回调,true表示初始启用状态
backPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
EventBus.getDefault().post(ClickBackEvent())
}
}
// 注册回调,使用lifecycleOwner确保生命周期安全
onBackPressedDispatcher.addCallback(this, backPressedCallback)
}
}
@@ -45,8 +45,9 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
companion object {
private const val TAG = "CollectFragment"
const val MAX_COUNT = 102
const val MAX_COUNT = 15
}
private var selectedFoodId: String? = ""
private var selectedFoodName: String? = ""
@@ -58,17 +59,17 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
}
private val searchFoodList = mutableListOf<FoodInfo>()
private var settingActivity: SettingActivity?=null
private var settingActivity: SettingActivity? = null
private var checkedItem: FoodInfo? = null
private val searchFoodAdapter by lazy {
SearchFoodAdapter(searchFoodList).apply {
setOnItemClickListener { adapter, view, position ->
checkedItem = searchFoodList[position]
selectedFoodId = checkedItem!!.foodId
selectedFoodName = checkedItem!!.foodName
val isChecked = checkedItem!!.isChecked
searchFoodList[position].isChecked = isChecked.not()
notifyItemChanged(position)
selectedFoodId = checkedItem!!.foodId
selectedFoodName = checkedItem!!.foodName
}
}
}
@@ -85,6 +86,7 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
it.bitmap = null
it.imageVector = null
it.imageFile = null
it.imageUri = null
it.isShowCamera = true
it.isFinish = false
it.uploadSuccess = false
@@ -93,6 +95,7 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
}
}
}
override fun inflateViewBinding(): FragmentCollectBinding {
return FragmentCollectBinding.inflate(LayoutInflater.from(context))
}
@@ -104,14 +107,25 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
if (index == -1) {
ToastUtils.showToast("每次只允许保存${MAX_COUNT}条数据")
settingActivity?.hideWaitingDialog()
rerurn@cameraCallback
rerurn@ cameraCallback
}
activity?.runOnUiThread {
foodCollectionList[index].let {
it.imageVector = null
it.bitmap = null
it.isShowCamera = false
it.imageFile = null
it.imageUri = uri
}
collectionAdapter.notifyItemChanged(index)
}
// settingActivity?.hideWaitingDialog()
ImageUtil.uriToBitmap(requireActivity(), uri)?.let { bitmap ->
// val cropBitmap = BitmapCropper.cropCenter(
// original = bitmap,
// targetWidth = 1300, targetHeight = 900,
// //offsetX = 30, offsetY = 100
// )
// val cropBitmap = BitmapCropper.cropCenter(
// original = bitmap,
// targetWidth = 1300, targetHeight = 900,
// //offsetX = 30, offsetY = 100
// )
initBox()
val imageVector = try {
FoodModule.bitmap2FloatArray(bitmap)
@@ -154,7 +168,7 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
}
settingActivity?.showWaitingDialog("拍照中……")
cameraUtils.takePhoto(cameraCallback) { errMsg ->
ToastUtils.showToast(errMsg)
ToastUtils.showToast("拍照异常,请重新操作")
settingActivity?.hideWaitingDialog()
}
}
@@ -173,7 +187,8 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
cameraUtils.setPreviewController(previewView)
binding.rvFoodList.let {
it.layoutManager = GridLayoutManager(requireActivity(), 3, GridLayoutManager.VERTICAL, false)
it.layoutManager =
GridLayoutManager(requireActivity(), 3, GridLayoutManager.VERTICAL, false)
it.adapter = collectionAdapter
}
@@ -182,12 +197,12 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
}
binding.btnSave.setOnClickListener {
if (selectedFoodName.isNullOrBlank()) {
Toast.makeText(requireActivity(), "请选择菜品名称", Toast.LENGTH_SHORT).show()
if (checkedItem == null || checkedItem!!.isChecked.not()) {
ToastUtils.showToast("请选择菜品名称")
return@setOnClickListener
}
if (foodCollectionList.size <= 1) {
Toast.makeText(requireActivity(), "请拍摄菜品照片", Toast.LENGTH_SHORT).show()
ToastUtils.showToast("请拍摄菜品照片")
return@setOnClickListener
}
upload()
@@ -235,9 +250,12 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
//params["foodVector"] = foodVector.toRequestBody()
ImageUploader(
totalList = foodCollectionList,
uploadImage = { batch->
uploadImage = { batch ->
val files = batch.map { it.imageFile }
(requireActivity() as SettingActivity).viewModel.uploadCollectFoodPics(files,params)
(requireActivity() as SettingActivity).viewModel.uploadCollectFoodPics(
files,
params
)
},
onProgress = { count, batch ->
activity?.runOnUiThread {
@@ -347,6 +365,7 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
it.bitmap = null
it.imageVector = null
it.imageFile = null
it.imageUri = null
it.isShowCamera = true
it.isFinish = false
it.uploadSuccess = false