添加菜品采集
This commit is contained in:
@@ -1,7 +1,14 @@
|
||||
package com.sw.dualscreen.activity
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Typeface
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.activity.viewModels
|
||||
@@ -13,17 +20,28 @@ import androidx.camera.core.Preview
|
||||
import androidx.camera.lifecycle.ProcessCameraProvider
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.example.utils.FloatBase64Utils
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import com.sw.dualscreen.R
|
||||
import com.sw.dualscreen.adapter.FoodCollectionAdapter
|
||||
import com.sw.dualscreen.adapter.GenericItemAdapter
|
||||
import com.sw.dualscreen.adapter.GridSpacingItemDecoration
|
||||
import com.sw.dualscreen.adapter.dpToPx
|
||||
import com.sw.dualscreen.databinding.ActivityFoodCollectionBinding
|
||||
import com.sw.dualscreen.databinding.ItemSearchFoodInfoBinding
|
||||
import com.sw.dualscreen.databinding.ItemSearchFoodInfoBinding.inflate
|
||||
import com.sw.dualscreen.model.response.FoodInfo
|
||||
import com.sw.dualscreen.objbox.Food
|
||||
import com.sw.dualscreen.objbox.FoodCollectionBean
|
||||
import com.sw.dualscreen.objbox.FoodModule
|
||||
import com.sw.dualscreen.objbox.ObjectBox
|
||||
import com.sw.dualscreen.presentation.SecondaryScreenPresentation
|
||||
import com.sw.dualscreen.sdk.SensorScaleUtils
|
||||
import com.sw.dualscreen.utils.CameraHelper
|
||||
import com.sw.dualscreen.utils.Debouncer
|
||||
import com.sw.dualscreen.utils.GlideUtils
|
||||
import com.sw.dualscreen.utils.ImageUtil
|
||||
import com.sw.dualscreen.utils.StorageUtils
|
||||
import com.sw.dualscreen.view.CustomBottomSheetDialog
|
||||
@@ -32,6 +50,7 @@ import com.sw.dualscreen.viewmodel.UserViewModel
|
||||
import com.sw.plate.utils.ToastUtils
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.kotlin.boxFor
|
||||
import kotlinx.coroutines.launch
|
||||
import org.pytorch.IValue
|
||||
import org.pytorch.Module
|
||||
import org.pytorch.torchvision.TensorImageUtils
|
||||
@@ -50,6 +69,7 @@ class FoodCollectionActivity : BaseActivity<ActivityFoodCollectionBinding>() {
|
||||
private val executor = Executors.newSingleThreadExecutor()
|
||||
|
||||
private val viewModel by viewModels<UserViewModel>()
|
||||
private var selectedFoodId: String? = ""
|
||||
private var selectedFoodName: String? = ""
|
||||
private var bottomSheetDialog: CustomBottomSheetDialog? = null
|
||||
|
||||
@@ -60,11 +80,17 @@ class FoodCollectionActivity : BaseActivity<ActivityFoodCollectionBinding>() {
|
||||
private var box: Box<Food>? = null
|
||||
private val list: MutableList<FoodCollectionBean> = mutableListOf()
|
||||
|
||||
private lateinit var cameraHelper: CameraHelper
|
||||
|
||||
private lateinit var foodAdapter: GenericItemAdapter<FoodInfo, ItemSearchFoodInfoBinding>
|
||||
private val debouncer = Debouncer(2000)
|
||||
|
||||
private val adapter: FoodCollectionAdapter by lazy {
|
||||
FoodCollectionAdapter(list).apply {
|
||||
setOnItemClickListener { _, _, position ->
|
||||
if (list[position].isShowCamera) {
|
||||
takePhoto()
|
||||
// takePhoto()
|
||||
cameraHelper.openCamera()
|
||||
}
|
||||
}
|
||||
addOnItemChildClickListener(R.id.ivDelete) { _, _, position ->
|
||||
@@ -84,7 +110,28 @@ class FoodCollectionActivity : BaseActivity<ActivityFoodCollectionBinding>() {
|
||||
}
|
||||
|
||||
override fun initialize() {
|
||||
binding.ivBack.setOnClickListener { finish() }
|
||||
|
||||
// 初始化 CameraHelper
|
||||
cameraHelper = CameraHelper(
|
||||
context = this,
|
||||
caller = this,
|
||||
authority = "${packageName}.fileprovider"
|
||||
) { uri, path ->
|
||||
if (list.size < 6) {
|
||||
val insertIndex = if (list.isEmpty()) 0 else list.size - 1
|
||||
list.add(insertIndex, FoodCollectionBean(imageUri = uri))
|
||||
} else {
|
||||
list[list.size - 1] = FoodCollectionBean(imageUri = uri)
|
||||
}
|
||||
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
binding.ivBack.setOnClickListener {
|
||||
val intent = Intent(this, MainActivity::class.java)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
list.add(FoodCollectionBean(isShowCamera = true))
|
||||
binding.rvFoodCollection.let {
|
||||
it.layoutManager = GridLayoutManager(this, 3, GridLayoutManager.VERTICAL, false)
|
||||
@@ -92,11 +139,7 @@ class FoodCollectionActivity : BaseActivity<ActivityFoodCollectionBinding>() {
|
||||
}
|
||||
|
||||
binding.btnFoodSearch.setOnClickListener {
|
||||
bottomSheetDialog = CustomBottomSheetDialog.newInstance(viewModel) {
|
||||
selectedFoodName = it.foodName
|
||||
binding.tvSelectedFood.text = selectedFoodName
|
||||
}
|
||||
bottomSheetDialog!!.show(supportFragmentManager, "CustomBottomSheetDialog")
|
||||
searchInfo()
|
||||
}
|
||||
|
||||
binding.btnSave.setOnClickListener {
|
||||
@@ -110,39 +153,41 @@ class FoodCollectionActivity : BaseActivity<ActivityFoodCollectionBinding>() {
|
||||
}
|
||||
vectorThread()
|
||||
}
|
||||
}
|
||||
|
||||
private fun takePhoto() {
|
||||
StorageUtils.requestCameraPermission(this) {
|
||||
val outputFile = File(this.filesDir, "IMG_${System.currentTimeMillis()}.jpg")
|
||||
outputFileUri =
|
||||
FileProvider.getUriForFile(this, "${this.packageName}.fileprovider", outputFile)
|
||||
takePictureLauncher.launch(outputFileUri!!)
|
||||
}
|
||||
}
|
||||
|
||||
private var outputFileUri: Uri? = null
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private val takePictureLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.TakePicture()) { success ->
|
||||
if (success) {
|
||||
//Loading.show(this)
|
||||
if (list.size < 6) {
|
||||
list.add(list.size - 1, FoodCollectionBean(imageUri = outputFileUri))
|
||||
} else {
|
||||
list[list.size - 1] = FoodCollectionBean(imageUri = outputFileUri)
|
||||
}
|
||||
adapter.notifyDataSetChanged()
|
||||
binding.editFoodName.setOnEditorActionListener { v, actionId, event ->
|
||||
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
|
||||
searchInfo()
|
||||
val imm =
|
||||
v.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
imm.hideSoftInputFromWindow(v.windowToken, 0)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
foodAdapter = createAdapter()
|
||||
binding.recyclerview.layoutManager = GridLayoutManager(context, 2)
|
||||
// 添加间距装饰(12dp)
|
||||
binding.recyclerview.addItemDecoration(
|
||||
GridSpacingItemDecoration(
|
||||
spanCount = 2,
|
||||
spacing = dpToPx(30),
|
||||
includeEdge = false // 包含边缘间距
|
||||
)
|
||||
)
|
||||
binding.recyclerview.adapter = foodAdapter
|
||||
|
||||
}
|
||||
|
||||
private fun vectorThread() {
|
||||
//Loading.show(this)
|
||||
showWaitingDialog("加载中……")
|
||||
Thread {
|
||||
list.forEachIndexed { index, it ->
|
||||
image2VectorTask(it.imageUri!!, index)
|
||||
|
||||
if (!it.isShowCamera)
|
||||
image2VectorTask(it.imageUri!!, index)
|
||||
}
|
||||
runOnUiThread {
|
||||
window.decorView.postDelayed({
|
||||
@@ -169,17 +214,76 @@ class FoodCollectionActivity : BaseActivity<ActivityFoodCollectionBinding>() {
|
||||
NO_STD_RGB // [0.229, 0.224, 0.225] TORCHVISION_NORM_STD_RGB
|
||||
)
|
||||
val outputTensor = module!!.forward(IValue.from(inputTensor)).toTensor()
|
||||
|
||||
val base64Str = FloatBase64Utils.floatArrayToBase64(outputTensor.dataAsFloatArray)
|
||||
Timber.tag("mzf1").e(base64Str)
|
||||
val imageVector = outputTensor.dataAsFloatArray
|
||||
box?.put(Food(name = selectedFoodName, foodIdx = 0, foodVector = imageVector))
|
||||
list[position].let {
|
||||
it.imageVector = imageVector
|
||||
it.isFinish = true
|
||||
|
||||
Timber
|
||||
viewModel.postImageData(
|
||||
context,
|
||||
foodId = selectedFoodId.toString(),
|
||||
foodName = selectedFoodName.toString(),
|
||||
foodVector = base64Str,
|
||||
uri = imageUri
|
||||
)
|
||||
}
|
||||
runOnUiThread {
|
||||
adapter.notifyItemChanged(position)
|
||||
}
|
||||
Timber.tag("registerDataChange").e("box.all.size=${box?.all?.size}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun searchInfo() {
|
||||
debouncer.debounce {
|
||||
viewModel.searchByFoodName(binding.editFoodName.text.toString())
|
||||
}
|
||||
}
|
||||
|
||||
override fun registerDataChange() {
|
||||
super.registerDataChange()
|
||||
lifecycleScope.launch {
|
||||
viewModel.searchFoodInfoList.collect {
|
||||
foodAdapter.updateData(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private var checkedItem: FoodInfo? = null
|
||||
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)
|
||||
selectedFoodName = item.foodName
|
||||
selectedFoodId = item.id
|
||||
|
||||
foodAdapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.sw.dualscreen.activity
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Outline
|
||||
import android.graphics.Typeface
|
||||
import android.hardware.display.DisplayManager
|
||||
@@ -43,6 +44,7 @@ import com.sw.dualscreen.utils.ImageUtil
|
||||
import com.sw.dualscreen.view.CustomBottomSheetDialog
|
||||
import com.sw.dualscreen.viewmodel.BaseViewModel
|
||||
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
|
||||
@@ -93,6 +95,11 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
|
||||
private fun initView() {
|
||||
viewModel.activeEngine()
|
||||
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)
|
||||
@@ -202,6 +209,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
|
||||
if (weight - lastWeight > 0.1 && presentation?.mealPickupMode == 0) { // 大于100g
|
||||
debouncer.debounce {
|
||||
LightManager.openRedLight()
|
||||
LightManager.openGreenLight()
|
||||
takePhoto { photoUri ->
|
||||
lastPhotoUri = photoUri
|
||||
Timber.d("registerDataChange photoUri = ${photoUri.path}")
|
||||
@@ -211,7 +220,11 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
Timber.d("registerDataChange photoUri 拿到bitmap")
|
||||
val bmp = BitmapCropper.cropCenter(bitmap, 1300, 900)
|
||||
Timber.d("registerDataChange photoUri bitmap裁剪完成")
|
||||
val file = BitmapSaver.saveToAppFilesDir(bmp, this, "IMG_CROP_${System.currentTimeMillis()}.jpg")
|
||||
val file = BitmapSaver.saveToAppFilesDir(
|
||||
bmp,
|
||||
this,
|
||||
"IMG_CROP_${System.currentTimeMillis()}.jpg"
|
||||
)
|
||||
Timber.d("registerDataChange photoUri bitmap保存文件路径:${file?.absolutePath}")
|
||||
val resultList = FoodModule.queryFood(bmp)
|
||||
Timber.d("registerDataChange photoUri 拿到识别数据")
|
||||
|
||||
Reference in New Issue
Block a user