添加了副屏显示逻辑控制
This commit is contained in:
@@ -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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user