添加了副屏显示逻辑控制

This commit is contained in:
zxj
2025-08-01 15:57:12 +08:00
parent de4c5f1c75
commit c78279a66e
27 changed files with 817 additions and 191 deletions
@@ -2,11 +2,13 @@ package com.sw.dualscreen.activity
import android.content.Context
import android.graphics.Outline
import android.graphics.Typeface
import android.hardware.display.DisplayManager
import android.os.Bundle
import android.view.View
import android.view.ViewOutlineProvider
import android.widget.AdapterView
import android.widget.ArrayAdapter
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
@@ -17,27 +19,41 @@ 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.R
import com.sw.dualscreen.adapter.GenericItemAdapter
import com.sw.dualscreen.adapter.GridSpacingItemDecoration
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.model.response.FoodInfo
import com.sw.dualscreen.presentation.SecondaryScreenPresentation
import com.sw.dualscreen.sdk.SensorScaleUtils
import com.sw.dualscreen.utils.GlideUtils
import com.sw.dualscreen.view.CustomBottomSheetDialog
import com.sw.dualscreen.viewmodel.UserViewModel
import com.sw.plate.utils.AppUtil
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.util.concurrent.Executors
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>
private val viewModel by viewModels<UserViewModel>()
private lateinit var imageCapture: ImageCapture
private var imageCapture: ImageCapture? = null
private var isAnalyzing = true // 控制是否进行图像分析
private lateinit var presentation: SecondaryScreenPresentation
private val executor = Executors.newSingleThreadExecutor()
private lateinit var adapter: GenericItemAdapter<FoodInfo, ItemFoodInfoBinding>
private var checkedItem: FoodInfo? = null
private var imageAnalysis: ImageAnalysis? = null
private var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -54,6 +70,7 @@ class MainActivity : AppCompatActivity() {
private fun initData() {
viewModel.getEquipmentToken()
presentation.step1()
}
private fun initView() {
@@ -64,12 +81,20 @@ class MainActivity : AppCompatActivity() {
}
}
binding.previewView.clipToOutline = true
binding.tvToSearch.setOnClickListener {
val bottomSheetDialog = CustomBottomSheetDialog.newInstance()
val bottomSheetDialog = CustomBottomSheetDialog.newInstance(viewModel) {
updateCurrentFood(it)
}
bottomSheetDialog.show(supportFragmentManager, "CustomBottomSheetDialog")
}
binding.spinner.setItems(arrayOf("即放即取", "持续取餐"))
val spinnerAdapter = ArrayAdapter<String>(
this,
R.layout.item_for_custom_spinner,
arrayOf("即放即取", "持续取餐")
)
binding.spinner.adapter = spinnerAdapter
binding.spinner.setSelection(0)
binding.spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?,
@@ -84,13 +109,90 @@ class MainActivity : AppCompatActivity() {
}
}
adapter = createAdapter()
binding.recyclerview.layoutManager = GridLayoutManager(this, 2)
// 添加间距装饰(12dp)
binding.recyclerview.addItemDecoration(
GridSpacingItemDecoration(
spanCount = 2,
spacing = dpToPx(30),
includeEdge = false // 包含边缘间距
)
)
binding.recyclerview.adapter = adapter
binding.tvFoodName.setOnClickListener {
presentation.step3()
}
registerDataChange()
}
fun updateCurrentFood(foodInfo: FoodInfo?) {
presentation.updateFood(foodInfo)
if (foodInfo != null) {
binding.tvFoodName.text = foodInfo.foodName
binding.previewView.visibility = View.GONE
binding.ivImg.visibility = View.VISIBLE
GlideUtils.loadRoundCornerImage(this, foodInfo.imgUrl, binding.ivImg, 12)
pauseAnalysis()
} else {
binding.tvFoodName.text = "-"
binding.previewView.visibility = View.VISIBLE
binding.ivImg.visibility = View.GONE
resumeAnalysis()
viewModel.cleanIdentifiedFoodInfoList()
}
}
private fun registerDataChange() {
lifecycleScope.launch {
viewModel.identifiedFoodInfoList.collect { list ->
binding.recyclerview
adapter.updateData(list)
// presentation.step2()
}
}
viewModel.getIdentifiedFoodList()
// SensorScaleUtils.startScale { weight ->
// Timber.d("weight = $weight")
// if (weight > 0.0) {
// if (canIndentify) {
// viewModel.getIdentifiedFoodList()
// }
// }
// }
}
private fun createAdapter(): GenericItemAdapter<FoodInfo, ItemFoodInfoBinding> {
return GenericItemAdapter(
items = emptyList(),
bindingInflater = ItemFoodInfoBinding::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.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")
checkedItem = item
adapter.notifyDataSetChanged()
updateCurrentFood(item)
}
}
)
}
private fun setupCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
val cameraProvider = cameraProviderFuture.get()
Timber.d("setupCamera")
cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture!!.addListener({
val cameraProvider = cameraProviderFuture!!.get()
// 1. 创建预览用例
val preview = Preview.Builder()
@@ -100,17 +202,16 @@ class MainActivity : AppCompatActivity() {
}
// 2. 创建图像分析用例(用于副屏显示)
val imageAnalysis = ImageAnalysis.Builder()
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()
runOnUiThread {
presentation.updateImage(bitmap) // 更新副屏
}
presentation.updateImage(bitmap) // 更新副屏
}
imageProxy.close() // 必须关闭以释放资源
}
@@ -139,6 +240,48 @@ class MainActivity : AppCompatActivity() {
}, 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() {
// 临时停止分析以避免干扰
@@ -151,7 +294,7 @@ class MainActivity : AppCompatActivity() {
)
).build()
// 执行拍照
imageCapture.takePicture(
imageCapture?.takePicture(
outputFileOptions,
executor,
object : ImageCapture.OnImageSavedCallback {
@@ -191,8 +334,18 @@ class MainActivity : AppCompatActivity() {
viewModel = viewModel,
recognizeViewModel = viewModels<RecognizeViewModel>().value
)
presentation.setStepChangeCallback { step ->
if (step == 1) {
updateCurrentFood(null)
}
}
presentation.show()
}
}
override fun onDestroy() {
SensorScaleUtils.closeScale()
presentation.dismiss()
super.onDestroy()
}
}