diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 742ec1d..7bfcfe6 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -114,6 +114,7 @@ dependencies {
val objectboxVersion = "5.0.1"
debugImplementation("io.objectbox:objectbox-android-objectbrowser:$objectboxVersion")
// releaseImplementation("io.objectbox:objectbox-android:$objectboxVersion")
+// implementation("io.objectbox:objectbox-fulltext:5.0.1")
implementation(libs.androidx.recyclerview)
diff --git a/app/objectbox-models/default.json b/app/objectbox-models/default.json
index 6259a14..7dd04ba 100644
--- a/app/objectbox-models/default.json
+++ b/app/objectbox-models/default.json
@@ -5,7 +5,7 @@
"entities": [
{
"id": "1:594331511073099531",
- "lastPropertyId": "9:4394472487596452023",
+ "lastPropertyId": "11:7949771231314213889",
"name": "Food",
"properties": [
{
@@ -45,6 +45,16 @@
"id": "9:4394472487596452023",
"name": "otherField",
"type": 9
+ },
+ {
+ "id": "10:5704890122356491756",
+ "name": "createTime",
+ "type": 9
+ },
+ {
+ "id": "11:7949771231314213889",
+ "name": "isDel",
+ "type": 1
}
],
"relations": []
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4917f68..f9f5f13 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -50,7 +50,8 @@
+ android:exported="true"
+ android:theme="@style/Theme.AppCompat.Light.NoActionBar">
diff --git a/app/src/main/java/com/sw/dualscreen/activity/CollectedDataActivity.kt b/app/src/main/java/com/sw/dualscreen/activity/CollectedDataActivity.kt
deleted file mode 100644
index 1527313..0000000
--- a/app/src/main/java/com/sw/dualscreen/activity/CollectedDataActivity.kt
+++ /dev/null
@@ -1,134 +0,0 @@
-//package com.sw.dualscreen.activity
-//
-//import android.annotation.SuppressLint
-//import androidx.activity.viewModels
-//import androidx.core.widget.addTextChangedListener
-//import androidx.recyclerview.widget.LinearLayoutManager
-//import com.sw.dualscreen.R
-//import com.sw.dualscreen.adapter.CollectedFoodAdapter
-//import com.sw.dualscreen.databinding.ActivityCollectedDataBinding
-//import com.sw.dualscreen.databinding.LayoutEmptySearchBinding
-//import com.sw.dualscreen.dialog.WarnDialog
-//import com.sw.dualscreen.ext.addOnActionSearchListener
-//import com.sw.dualscreen.ext.hideKeyboard
-//import com.sw.dualscreen.objbox.CollectedFoodInfo
-//import com.sw.dualscreen.objbox.Food
-//import com.sw.dualscreen.objbox.ObjectBox
-//import com.sw.dualscreen.viewmodel.BaseViewModel
-//import com.sw.dualscreen.viewmodel.UserViewModel
-//import com.sw.plate.utils.ToastUtils
-//import io.objectbox.Box
-//import io.objectbox.kotlin.boxFor
-//import kotlin.getValue
-//
-//class CollectedDataActivity : BaseActivity() {
-// private val viewModel by viewModels()
-// override fun getViewModel(): BaseViewModel {
-// return viewModel
-// }
-//
-// override fun inflateViewBinding(): ActivityCollectedDataBinding {
-// return ActivityCollectedDataBinding.inflate(layoutInflater)
-// }
-//
-// private val list: MutableList = mutableListOf()
-// private val adapter by lazy {
-// CollectedFoodAdapter(list).apply {
-// isStateViewEnable = true
-// addOnItemChildClickListener(R.id.ivDeleteFood) { _, _, position ->
-// deleteGoods(position)
-// }
-// }
-// }
-//
-// override fun initialize() {
-// super.initialize()
-// binding.rvFoodList.let {
-// it.layoutManager = LinearLayoutManager(this)
-// it.adapter = adapter
-// }
-// binding.ivBack.setOnClickListener { finish() }
-// binding.ivGoodsSearch.setOnClickListener {
-// val searchName = binding.etInputGoods.text.toString().trim()
-// if (searchName.isBlank()) {
-// ToastUtils.showToast("请输入物品名称")
-// return@setOnClickListener
-// }
-// getCollectGoods(searchName)
-// }
-// binding.etInputGoods.let { v ->
-// v.addOnActionSearchListener {
-// val searchName = binding.etInputGoods.text.toString().trim()
-// if (searchName.isBlank()) {
-// ToastUtils.showToast("请输入物品名称")
-// return@addOnActionSearchListener
-// }
-// getCollectGoods(searchName)
-// }
-// v.addTextChangedListener {
-// if (it.isNullOrBlank()) {
-// getCollectGoods()
-// }
-// }
-// }
-// getCollectGoods()
-// }
-//
-//
-// private var box: Box? = null
-//
-// @SuppressLint("NotifyDataSetChanged")
-// private fun getCollectGoods(searchName:String?=null) {
-// if (box == null) {
-// box = ObjectBox.boxStore.boxFor(Food::class)
-// }
-// list.clear()
-// var queryList = box?.all?.distinctBy { it.name }
-// if (searchName.isNullOrBlank().not()) {
-// queryList = queryList?.filter { it.name?.contains(searchName) == true }
-// }
-// queryList?.forEach {
-// list.add(CollectedFoodInfo(foodName = it.name))
-// }
-// adapter.notifyDataSetChanged()
-// if (list.isEmpty()) {
-// loadEmptyView()
-// }
-// binding.root.hideKeyboard()
-// }
-//
-// private var emptyBinding: LayoutEmptySearchBinding? = null
-// private fun loadEmptyView() {
-// if (emptyBinding == null) {
-// emptyBinding = LayoutEmptySearchBinding.inflate(layoutInflater, binding.rvFoodList, false)
-// }
-// emptyBinding?.root?.let { layout ->
-// layout.setOnClickListener { layout.hideKeyboard() }
-// adapter.stateView = layout
-// }
-// }
-//
-// private fun deleteGoods(position: Int) {
-// WarnDialog(
-// context = context,
-// content = "请确认是否删除菜品:${list[position].foodName}?",
-// confirmBlock = {
-// Thread {
-// runOnUiThread {
-// showWaitingDialog("加载中……")
-// }
-// val name = list[position].foodName
-// val filterIdList = box?.all?.filter { it.name == name }?.map { it.id }
-// box?.removeByIds(filterIdList)
-// runOnUiThread {
-// hideWaitingDialog()
-// adapter.removeAt(position)
-// ToastUtils.showToast("删除成功")
-// if (list.isEmpty()) {
-// loadEmptyView()
-// }
-// }
-// }.start()
-// }).show()
-// }
-//}
\ No newline at end of file
diff --git a/app/src/main/java/com/sw/dualscreen/activity/CollectedFoodActivity.kt b/app/src/main/java/com/sw/dualscreen/activity/CollectedFoodActivity.kt
index 09b33ac..1c07a3a 100644
--- a/app/src/main/java/com/sw/dualscreen/activity/CollectedFoodActivity.kt
+++ b/app/src/main/java/com/sw/dualscreen/activity/CollectedFoodActivity.kt
@@ -8,17 +8,21 @@ import com.sw.dualscreen.GlobalData
import com.sw.dualscreen.R
import com.sw.dualscreen.adapter.CollectedFoodNewAdapter
import com.sw.dualscreen.databinding.ActivityCollectedFoodBinding
-import com.sw.dualscreen.dialog.WarnDialog
+import com.sw.dualscreen.dialog.RemindDialog
import com.sw.dualscreen.ext.addOnActionSearchListener
import com.sw.dualscreen.ext.gone
import com.sw.dualscreen.ext.hideKeyboard
import com.sw.dualscreen.ext.visible
+import com.sw.dualscreen.model.response.ResetBoxEvent
import com.sw.dualscreen.objbox.CollectedFoodInfo
import com.sw.dualscreen.objbox.Food
+import com.sw.dualscreen.objbox.ObjectBox
import com.sw.dualscreen.viewmodel.BaseViewModel
import com.sw.dualscreen.viewmodel.UserViewModel
import com.sw.plate.utils.ToastUtils
import io.objectbox.Box
+import io.objectbox.kotlin.boxFor
+import org.greenrobot.eventbus.EventBus
class CollectedFoodActivity : BaseActivity() {
@@ -47,6 +51,7 @@ class CollectedFoodActivity : BaseActivity() {
override fun initialize() {
super.initialize()
+ box = ObjectBox.boxStore.boxFor(Food::class)
//binding.root.setOnClickListener { it.hideKeyboard() }
binding.rvFoodList.let {
it.layoutManager = LinearLayoutManager(this)
@@ -99,7 +104,7 @@ class CollectedFoodActivity : BaseActivity() {
}
- private var box: Box? = null
+ private lateinit var box: Box
@SuppressLint("NotifyDataSetChanged")
private fun getCollectGoods(searchName: String? = null) {
@@ -181,7 +186,7 @@ class CollectedFoodActivity : BaseActivity() {
}
private fun loadDeleteDialog(position: Int) {
- WarnDialog(
+ RemindDialog(
context = context,
content = "请确认是否删除菜品:${list[position].foodName}?",
confirmBlock = {
@@ -195,8 +200,16 @@ class CollectedFoodActivity : BaseActivity() {
viewModel.deleteCollectFood(food.foodId, GlobalData.foodModelVersion) { deleteSuccess ->
if (deleteSuccess) {
Thread {
- val filterIdList = box?.all?.filter { it.foodName == food.foodName }?.map { it.id }
- box?.removeByIds(filterIdList)
+ val filterFoodList = box.all.filter { it.foodName == food.foodName }
+ filterFoodList.forEach { it.isDel = true }
+ box.put(filterFoodList)
+// val filterIdList = filterFoodList.map { it.id }
+////// ObjectBox.boxStore.runInTx {
+//// box.removeByIds(filterIdList)
+////// EventBus.getDefault().post(ResetBoxEvent())
+////// }
+
+
runOnUiThread {
hideWaitingDialog()
adapter.removeAt(position)
diff --git a/app/src/main/java/com/sw/dualscreen/activity/FoodCollectionActivity.kt b/app/src/main/java/com/sw/dualscreen/activity/FoodCollectionActivity.kt
deleted file mode 100644
index be4bcc0..0000000
--- a/app/src/main/java/com/sw/dualscreen/activity/FoodCollectionActivity.kt
+++ /dev/null
@@ -1,370 +0,0 @@
-//package com.sw.dualscreen.activity
-//
-//import android.annotation.SuppressLint
-//import android.content.Context
-//import android.content.Intent
-//import android.graphics.Bitmap
-//import android.graphics.Typeface
-//import android.net.Uri
-//import android.view.View
-//import android.view.inputmethod.EditorInfo
-//import android.view.inputmethod.InputMethodManager
-//import android.widget.Toast
-//import androidx.activity.viewModels
-//import androidx.camera.view.PreviewView
-//import androidx.core.net.toUri
-//import androidx.core.view.updateLayoutParams
-//import androidx.lifecycle.lifecycleScope
-//import androidx.recyclerview.widget.GridLayoutManager
-//import com.example.utils.FloatBase64Utils
-//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.LayoutCameraPreviewBinding
-//import com.sw.dualscreen.ext.clickWithDebounce
-//import com.sw.dualscreen.ext.dp
-//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.utils.BitmapCropper
-//import com.sw.dualscreen.utils.BitmapSaver
-//import com.sw.dualscreen.utils.CameraHelper
-//import com.sw.dualscreen.utils.CameraUtils
-//import com.sw.dualscreen.utils.Debouncer
-//import com.sw.dualscreen.utils.ImageUtil
-//import com.sw.dualscreen.viewmodel.BaseViewModel
-//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
-//import timber.log.Timber
-//import kotlin.math.max
-//
-//class FoodCollectionActivity : BaseActivity() {
-// companion object {
-// val MAX_COUNT = 100
-// }
-// private val viewModel by viewModels()
-// private var selectedFoodId: String? = ""
-// private var selectedFoodName: String? = ""
-//
-// private var box: Box? = null
-// private val foodCollectionList: MutableList = mutableListOf()
-//
-//// private lateinit var cameraHelper: CameraHelper
-//
-// private lateinit var foodAdapter: GenericItemAdapter
-// private val foodList = mutableListOf() // 适配器内部维护的数据列表
-// private val debouncer = Debouncer(2000)
-// private lateinit var previewView: PreviewView
-//
-// private val cameraUtils: CameraUtils by lazy {
-// CameraUtils(this)
-// }
-//
-// private val collectionAdapter: FoodCollectionAdapter by lazy {
-// FoodCollectionAdapter(foodCollectionList).apply {
-//// setOnItemClickListener { _, _, position ->
-//// if (list[position].isShowCamera) {
-////// takePhoto()
-//// cameraHelper.openCamera()
-//// }
-//// }
-// addOnItemChildClickListener(R.id.ivDelete) { _, _, position ->
-//// list.removeAt(position)
-//// collectionAdapter.notifyItemRemoved(position)
-//// collectionAdapter.notifyItemRangeChanged(position, list.size)
-// foodCollectionList[position].let {
-// it.bitmap = null
-// it.isShowCamera = true
-// it.isFinish = false
-// }
-// notifyItemChanged(position)
-// }
-// }
-// }
-//
-// override fun getViewModel(): BaseViewModel {
-// return viewModel
-// }
-//
-// override fun inflateViewBinding(): ActivityFoodCollectionBinding {
-// return ActivityFoodCollectionBinding.inflate(layoutInflater)
-// }
-//
-// private val cameraCallback: (Uri) -> Unit = { uri ->
-// val index = foodCollectionList.indexOfFirst { it.bitmap == null }
-// if (index == -1) {
-// ToastUtils.showToast("每次只允许保存${MAX_COUNT}条数据")
-// rerurn@ cameraCallback
-// }
-// ImageUtil.uriToBitmap(this, uri)?.let { bitmap ->
-//// val cropBitmap = BitmapCropper.cropCenter(
-//// original = bitmap,
-//// targetWidth = 1300, targetHeight = 900,
-//// //offsetX = 30, offsetY = 100
-//// )
-// val file = BitmapSaver.saveToAppFilesDir(
-// bitmap, this, "IMG_CROP_${System.currentTimeMillis()}.jpg"
-// )
-// Timber.d("${this.javaClass.simpleName}-cameraCallback-裁剪bitmap保存文件路径:${file?.absolutePath}")
-//
-// foodCollectionList[index].let {
-// it.bitmap = bitmap
-// it.isShowCamera = false
-// it.imageUri = file?.toUri()
-// }
-// collectionAdapter.notifyItemChanged(index)
-// }
-// }
-//
-// @SuppressLint("NotifyDataSetChanged")
-// private fun takePhoto() {
-// val count = foodCollectionList.count { it.bitmap != null }
-// if (count >= MAX_COUNT) {
-// ToastUtils.showToast("每次只允许保存${MAX_COUNT}条数据")
-// return
-// }
-// cameraUtils.takePhoto(cameraCallback)
-// }
-//
-// override fun initialize() {
-// cameraUtils.initCamera()
-// val previewBinding =
-// LayoutCameraPreviewBinding.inflate(layoutInflater, binding.flCameraPreview)
-// previewView = previewBinding.previewView.also {
-// it.updateLayoutParams {
-// width = 180.dp
-// height = 180.dp
-// }
-// }
-// cameraUtils.setPreviewController(previewView)
-// // 初始化 CameraHelper
-//// cameraHelper = CameraHelper(
-//// context = this,
-//// caller = this,
-//// authority = "${packageName}.fileprovider"
-//// ) { uri, path ->
-//// //if (foodCollectionList.size < 6) {
-//// // val insertIndex = if (foodCollectionList.isEmpty()) 0 else foodCollectionList.size - 1
-//// // foodCollectionList.add(insertIndex, FoodCollectionBean(imageUri = uri))
-//// //} else {
-//// // foodCollectionList[foodCollectionList.size - 1] = FoodCollectionBean(imageUri = uri)
-//// //}
-//// //collectionAdapter.notifyDataSetChanged()
-//// cameraCallback(uri)
-//// }
-//
-// binding.ivBack.setOnClickListener {
-// val intent = Intent(this, MainActivity::class.java)
-// startActivity(intent)
-// finish()
-// }
-// repeat(MAX_COUNT) {
-// foodCollectionList.add(FoodCollectionBean(isShowCamera = true))
-// }
-// binding.rvFoodCollection.let {
-// it.layoutManager = GridLayoutManager(this, 3, GridLayoutManager.VERTICAL, false)
-// it.adapter = collectionAdapter
-// }
-//
-// binding.btnFoodSearch.setOnClickListener {
-// searchInfo()
-// }
-//
-// binding.btnSave.setOnClickListener {
-// if (selectedFoodName.isNullOrBlank()) {
-// Toast.makeText(this, "请选择菜品名称", Toast.LENGTH_SHORT).show()
-// return@setOnClickListener
-// }
-// if (foodCollectionList.size <= 1) {
-// Toast.makeText(this, "请拍摄菜品照片", Toast.LENGTH_SHORT).show()
-// return@setOnClickListener
-// }
-// vectorThread()
-// }
-//
-// 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
-// }
-// }
-//
-// binding.btnCollectedGoods.setOnClickListener {
-// startActivity(Intent(this, CollectedDataActivity::class.java))
-// }
-// binding.btnTakePhoto.clickWithDebounce {
-// binding.btnTakePhoto.text = "拍照"
-// val count = foodCollectionList.count { it.bitmap != null }
-// if (count >= MAX_COUNT) {
-// ToastUtils.showToast("每次只允许保存${MAX_COUNT}条数据")
-// return@clickWithDebounce
-// }
-//// cameraHelper.openCamera()
-// takePhoto()
-// }
-// binding.btnClearData.setOnClickListener { clearData() }
-// 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 {
-// foodCollectionList
-// .filter { it.bitmap != null }
-// .forEachIndexed { index, it ->
-// image2VectorTask(it, index)
-// }
-// runOnUiThread {
-// window.decorView.postDelayed({
-// //Loading.dismiss()
-// hideWaitingDialog()
-// }, 1000)
-// }
-// }.start()
-// }
-//
-// private fun initBox() {
-// if (box == null) {
-// box = ObjectBox.boxStore.boxFor(Food::class)
-// }
-// }
-//
-// private fun image2VectorTask(item: FoodCollectionBean, position: Int) {
-// initBox()
-// val imageVector = FoodModule.bitmap2FloatArray(item.bitmap!!)
-//
-// val base64Str = FloatBase64Utils.floatArrayToBase64(imageVector)
-// Timber.tag("mzf1").e(base64Str)
-//// viewModel.postImageData(
-//// context,
-//// foodId = selectedFoodId.toString(),
-//// foodName = selectedFoodName.toString(),
-//// foodVector = base64Str,
-//// uri = item.imageUri!!
-//// )
-// box?.put(Food(name = checkedItem!!.foodName, foodIdx = 0, foodVector = imageVector))
-// foodCollectionList[position].let {
-// it.imageVector = imageVector
-// it.isFinish = true
-// }
-//
-// runOnUiThread {
-// collectionAdapter.notifyItemChanged(position)
-// }
-// }
-//
-//
-// private fun searchInfo() {
-// debouncer.debounce {
-// viewModel.searchByFoodName(binding.editFoodName.text.toString())
-// }
-// }
-//
-// override fun registerDataChange() {
-// super.registerDataChange()
-// lifecycleScope.launch {
-// viewModel.searchFoodInfoList.collect {
-//// foodList.clear()
-//// foodList.addAll(it)
-// foodAdapter.updateData(it)
-// }
-// }
-// }
-//
-//
-// private var checkedItem: FoodInfo? = null
-// private fun createAdapter(): GenericItemAdapter {
-// 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()
-// }
-// }
-// )
-// }
-//
-// private var clickIndex = -1
-//
-// @SuppressLint("NotifyDataSetChanged")
-// private fun clearData() {
-// foodCollectionList.forEach {
-// it.bitmap = null
-// it.isShowCamera = true
-// it.isFinish = false
-// }
-// collectionAdapter.notifyDataSetChanged()
-//
-// clickIndex = -1
-// binding.editFoodName.setText("")
-//// foodList.clear()
-//// foodAdapter.updateData(mutableListOf())
-//// foodAdapter.notifyDataSetChanged()
-// //loadEmptyView()
-// }
-//
-// override fun onResume() {
-// super.onResume()
-// cameraUtils.bind()
-// binding.llCameraFlag.run {
-// visibility = View.VISIBLE
-// postDelayed({
-// visibility = View.GONE
-// }, 3000)
-// }
-// }
-//
-// override fun onPause() {
-// super.onPause()
-// cameraUtils.unbind()
-// binding.llCameraFlag.visibility = View.VISIBLE
-// }
-//
-//}
\ No newline at end of file
diff --git a/app/src/main/java/com/sw/dualscreen/activity/InitActivity.kt b/app/src/main/java/com/sw/dualscreen/activity/InitActivity.kt
index ef16bc9..2a1c9df 100644
--- a/app/src/main/java/com/sw/dualscreen/activity/InitActivity.kt
+++ b/app/src/main/java/com/sw/dualscreen/activity/InitActivity.kt
@@ -51,7 +51,7 @@ class InitActivity : BaseActivity() {
}
}
}
- private val box by lazy { ObjectBox.boxStore.boxFor(Food::class) }
+// private val box by lazy { ObjectBox.boxStore.boxFor(Food::class) }
private fun saveFoodVector(list: List) {
val vectorList = list.map { vt ->
val foodVector = vt.foodVector?.removeSurrounding("[", "]")?.split(",")?.map { it.toFloatOrNull()?:0.0f }?.toFloatArray()
@@ -63,6 +63,7 @@ class InitActivity : BaseActivity() {
foodVector = foodVector
)
}
+ val box = ObjectBox.boxStore.boxFor(Food::class)
box.put(vectorList)
}
diff --git a/app/src/main/java/com/sw/dualscreen/activity/MainActivity.kt b/app/src/main/java/com/sw/dualscreen/activity/MainActivity.kt
index d32a596..7b2fbbc 100644
--- a/app/src/main/java/com/sw/dualscreen/activity/MainActivity.kt
+++ b/app/src/main/java/com/sw/dualscreen/activity/MainActivity.kt
@@ -25,8 +25,10 @@ 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
@@ -36,12 +38,16 @@ 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
@@ -58,7 +64,12 @@ 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
@@ -69,6 +80,7 @@ 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
/**
@@ -135,6 +147,7 @@ class MainActivity : BaseActivity() {
override fun initialize() {
super.initialize()
+ box = ObjectBox.boxStore.boxFor(Food::class)
isPageVisible = true
addBackEventListener()
//FoodModule.init(this)
@@ -262,7 +275,7 @@ class MainActivity : BaseActivity() {
binding.previewView.visibility = View.VISIBLE
binding.ivImg.visibility = View.GONE
resumeAnalysis()
- viewModel.cleanIdentifiedFoodInfoList()
+// viewModel.cleanIdentifiedFoodInfoList()
return
}
pauseAnalysis()
@@ -318,6 +331,9 @@ class MainActivity : BaseActivity() {
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
@@ -328,8 +344,10 @@ class MainActivity : BaseActivity() {
private fun recognizeByWeight(weight: Double, isWeightChange: Boolean, block: () -> Unit = {}) {
presentation?.updateWeight(weight)
- if (weight <= 0.005) {//余量取餐,检测到秤上没有东西,重新启动识别菜品 && presentation?.mealPickupMode == 1
+ if (weight <= 0.005) {
+ ////余量取餐,检测到秤上没有东西,重新启动识别菜品 && presentation?.mealPickupMode == 1
isRecognitionFood = true
+ return
}
if (isWeightChange && isRecognitionFood) { // 大于50g
@@ -368,7 +386,6 @@ class MainActivity : BaseActivity() {
}
failCount++
hideWaitingDialog()
-// ToastUtils.showToast("拍照异常,请手动放置后重新识别")
shutdownCamera()
setupCamera()
@@ -389,39 +406,42 @@ class MainActivity : BaseActivity() {
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
- }
+ //接口已处理排序
+// 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(list2)
+ searchFoodList.addAll(list)
adapter.notifyDataSetChanged()
- //adapter.updateData(list)
- if (list2.isNotEmpty()) {
- checkedItem = list2[0]
- checkedItem!!.photoUri = lastPhotoUri
- checkedItem!!.isChecked = true
- adapter.notifyItemChanged(0)
- updateCurrentFood(checkedItem)
- lastPhotoUri = null
- LightManager.closeRedLight()
- binding.flPay.visible()
- binding.tvToSearch.let {
- it.text = "以上都不是,手动搜索"
- it.visible()
- }
- } else {
- binding.flPay.gone()
- binding.tvToSearch.gone()
+ 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()
}
}
@@ -653,14 +673,14 @@ class MainActivity : BaseActivity() {
binding.tvTitleTime.text = dateTime
}
- private var isPageVisible = true
+ public var isPageVisible = true
override fun onResume() {
super.onResume()
isPageVisible = true
// 启动定时器
handler.postDelayed(timeoutRunnable, TIME_OUT)
-
+// ToastUtils.showToast("isPageVisible=$isPageVisible")
if (isFirstOpen) {
//首次
setupSecondaryDisplay()
@@ -778,6 +798,11 @@ class MainActivity : BaseActivity() {
}
foodOrderId = orderId
block()
+
+ if (checkedItem!!.isFromSearch == true) {
+ //当前菜名为手动搜索选择,非识别结果,保存向量数据
+ saveFoodVector(checkedItem!!)
+ }
}
}
}
@@ -886,6 +911,111 @@ class MainActivity : BaseActivity() {
}
}
+ /**
+ * 将uri转为文件和向量数据并提交
+ */
+ private fun saveFoodVector(foodInfo: FoodInfo) {
+ try {
+ lastPhotoUri?.let { uri ->
+ uri2File(uri) { 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()
+ 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
+ }
+ box.put(
+ Food(
+ collectId = idList[0],
+ foodId = foodId,
+ foodName = foodName,
+ foodVector = imageVector,
+ version = foodModelVersion
+ )
+ )
+ }
+
+ private fun loadRemindDialog(msg: String) {
+ runOnUiThread {
+ RemindDialog(
+ context = context,
+ content = msg,
+ confirmBlock = {}
+ ).show()
+ }
+ }
+
+ private lateinit var box: Box
+
+ 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()
@@ -1091,13 +1221,18 @@ class MainActivity : BaseActivity() {
hideWaitingDialog()
Timber.d("registerDataChange photoUri 识别数据名称:$foodName")
+ 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
- binding.flPay.visible()
+
binding.tvToSearch.let {
- it.text = "未识别到,去手动搜索"
+ it.text = "未识别到,手动搜索"
it.visible()
}
} else {
@@ -1117,4 +1252,10 @@ class MainActivity : BaseActivity() {
}
}
+// @Subscribe(threadMode = ThreadMode.MAIN)
+// public fun onResetBox(event: ResetBoxEvent) {
+// ObjectBox.boxStore.close()
+// ObjectBox.init(this)
+// }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/sw/dualscreen/activity/fragment/CollectFragment.kt b/app/src/main/java/com/sw/dualscreen/activity/fragment/CollectFragment.kt
index c49432b..5e9b8cd 100644
--- a/app/src/main/java/com/sw/dualscreen/activity/fragment/CollectFragment.kt
+++ b/app/src/main/java/com/sw/dualscreen/activity/fragment/CollectFragment.kt
@@ -36,6 +36,7 @@ import com.sw.plate.utils.ToastUtils
import io.objectbox.Box
import io.objectbox.kotlin.boxFor
import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import timber.log.Timber
@@ -51,7 +52,12 @@ class CollectFragment : BaseFragment() {
private var selectedFoodId: String? = ""
private var selectedFoodName: String? = ""
- private val box by lazy { ObjectBox.boxStore.boxFor(Food::class) }
+ private var box: Box?=null
+ fun initBox() {
+ if (box == null) {
+ box = ObjectBox.boxStore.boxFor(Food::class)
+ }
+ }
private val foodCollectionList = mutableListOf().apply {
repeat(MAX_COUNT) {
add(FoodCollectionBean(isShowCamera = true))
@@ -192,7 +198,7 @@ class CollectFragment : BaseFragment() {
}
binding.btnFoodSearch.setOnClickListener {
- searchInfo()
+ searchFood()
}
binding.btnSave.setOnClickListener {
@@ -209,7 +215,7 @@ class CollectFragment : BaseFragment() {
binding.editFoodName.setOnEditorActionListener { v, actionId, event ->
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
- searchInfo()
+ searchFood()
val imm =
v.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(v.windowToken, 0)
@@ -236,15 +242,14 @@ class CollectFragment : BaseFragment() {
it.adapter = searchFoodAdapter
}
- searchInfo()
+ searchFood()
}
-
@SuppressLint("NotifyDataSetChanged")
private fun upload() {
lifecycleScope.launch {
val totalFileCount = foodCollectionList.count { it.imageFile != null }
- settingActivity?.showWaitingDialog("图片上传中0/$totalFileCount")
+ settingActivity?.showWaitingDialog2("图片上传中0/$totalFileCount")
val params = HashMap()
//params["placeId"] = restId.toRequestBody()
params["foodId"] = checkedItem!!.foodId.toRequestBody()
@@ -256,46 +261,58 @@ class CollectFragment : BaseFragment() {
uploadImage = { batch ->
val files = batch.map { it.imageFile }
val foodVectorList =
- batch.filter { it.imageVector != null }.map { it.imageVector!!.joinToString(separator = ",", prefix = "[", postfix = "]")}
+ batch.filter { it.imageVector != null }.map {
+ it.imageVector!!.joinToString(
+ separator = ",",
+ prefix = "[",
+ postfix = "]"
+ )
+ }
val foodVectorJson =
foodVectorList.joinToString(separator = ",", prefix = "[", postfix = "]")
Timber.tag(TAG).d("json=$foodVectorJson")
params["foodVector"] = foodVectorJson.toRequestBody()
settingActivity?.viewModel?.uploadCollectFoodPics(files, params)
},
- onProgress = { count, batch, idList->
- activity?.runOnUiThread {
- settingActivity?.showWaitingDialog2("图片上传中$count/$totalFileCount")
- //batch.forEach {
- // it.uploadSuccess = true
- //}
- Thread {
- val foodList = batch.mapIndexed { index, it ->
- Food(
- collectId = if (index < idList.size) idList[index] else null,
- foodId = checkedItem!!.foodId,
- foodName = checkedItem!!.foodName,
- foodVector = it.imageVector,
- version = GlobalData.foodModelVersion
- )
- }
- box.put(foodList)
- activity?.runOnUiThread {
- batch.forEach { it.uploadSuccess = true }
- collectionAdapter.notifyDataSetChanged()
- }
- }.start()
+ onProgress = { count, batch, idList ->
+ //batch.forEach {
+ // it.uploadSuccess = true
+ //}
+ runBlocking {
+ activity?.runOnUiThread {
+ settingActivity?.showWaitingDialog2("图片上传中$count/$totalFileCount")
+ }
+ val foodList = batch.mapIndexed { index, it ->
+ Food(
+ collectId = if (index < idList.size) idList[index] else null,
+ foodId = checkedItem!!.foodId,
+ foodName = checkedItem!!.foodName,
+ foodVector = it.imageVector,
+ version = GlobalData.foodModelVersion
+ )
+ }
+ initBox()
+ box?.put(foodList)
+ activity?.runOnUiThread {
+ batch.forEach { it.isFinish = true }
+ collectionAdapter.notifyDataSetChanged()
+ }
}
},
onError = {
- settingActivity?.hideWaitingDialog()
activity?.runOnUiThread {
- ToastUtils.showToast("上传失败,请稍后重试")
+ binding.root.postDelayed({
+ settingActivity?.hideWaitingDialog()
+ ToastUtils.showToast("上传失败,请稍后重试")
+ }, 1000)
}
},
onComplete = {
//vectorThread()
- settingActivity?.hideWaitingDialog()
+ binding.root.postDelayed({
+ settingActivity?.hideWaitingDialog()
+ ToastUtils.showToast("上传成功")
+ }, 1000)
}
).processUploads()
}
@@ -320,59 +337,59 @@ class CollectFragment : BaseFragment() {
// }
}
- private fun vectorThread() {
- settingActivity?.showWaitingDialog("加载中……")
- Thread {
- foodCollectionList
-// .filter { it.bitmap != null }
-// .filter { it.imageVector != null }
- .forEachIndexed { index, it ->
- if (it.imageVector == null) {
- return@forEachIndexed
- }
- image2VectorTask(imageVector = it.imageVector!!, index)
- }
- activity?.runOnUiThread {
- binding.root.postDelayed({
- settingActivity?.hideWaitingDialog()
- }, 1000)
- }
- }.start()
- }
-
- private fun image2VectorTask(imageVector: FloatArray?, position: Int) {
- if (imageVector == null) return
-//// val imageVector = FoodModule.bitmap2FloatArray(item.bitmap!!)
-////
-////// val base64Str = FloatBase64Utils.floatArrayToBase64(imageVector)
-////// Timber.tag("mzf1").e(base64Str)
-//////// viewModel.postImageData(
-//////// context,
-//////// foodId = selectedFoodId.toString(),
-//////// foodName = selectedFoodName.toString(),
-//////// foodVector = base64Str,
-//////// uri = item.imageUri!!
-//////// )
-// box.put(
-// Food(
-// collectId = null,
-// foodId = checkedItem!!.foodId,
-// foodName = checkedItem!!.foodName,
-// foodVector = imageVector,
-// version = "1.0.0"
-// )
-// )
-// if (position > -1) {
-// foodCollectionList[position].isFinish = true
+// private fun vectorThread() {
+// settingActivity?.showWaitingDialog("加载中……")
+// Thread {
+// foodCollectionList
+//// .filter { it.bitmap != null }
+//// .filter { it.imageVector != null }
+// .forEachIndexed { index, it ->
+// if (it.imageVector == null) {
+// return@forEachIndexed
+// }
+// image2VectorTask(imageVector = it.imageVector!!, index)
+// }
// activity?.runOnUiThread {
-// collectionAdapter.notifyItemChanged(position)
+// binding.root.postDelayed({
+// settingActivity?.hideWaitingDialog()
+// }, 1000)
// }
-// }
- }
+// }.start()
+// }
+
+// private fun image2VectorTask(imageVector: FloatArray?, position: Int) {
+// if (imageVector == null) return
+////// val imageVector = FoodModule.bitmap2FloatArray(item.bitmap!!)
+//////
+//////// val base64Str = FloatBase64Utils.floatArrayToBase64(imageVector)
+//////// Timber.tag("mzf1").e(base64Str)
+////////// viewModel.postImageData(
+////////// context,
+////////// foodId = selectedFoodId.toString(),
+////////// foodName = selectedFoodName.toString(),
+////////// foodVector = base64Str,
+////////// uri = item.imageUri!!
+////////// )
+//// box.put(
+//// Food(
+//// collectId = null,
+//// foodId = checkedItem!!.foodId,
+//// foodName = checkedItem!!.foodName,
+//// foodVector = imageVector,
+//// version = "1.0.0"
+//// )
+//// )
+//// if (position > -1) {
+//// foodCollectionList[position].isFinish = true
+//// activity?.runOnUiThread {
+//// collectionAdapter.notifyItemChanged(position)
+//// }
+//// }
+// }
@SuppressLint("NotifyDataSetChanged")
- private fun searchInfo() {
+ private fun searchFood() {
debouncer.debounce {
settingActivity?.searchByFoodName(binding.editFoodName.text.toString()) {
searchFoodList.clear()
@@ -399,8 +416,7 @@ class CollectFragment : BaseFragment() {
clickIndex = -1
binding.editFoodName.setText("")
- searchFoodList.clear()
- searchFoodAdapter.notifyDataSetChanged()
+ searchFood()
}
override fun onResume() {
diff --git a/app/src/main/java/com/sw/dualscreen/dialog/WarnDialog.kt b/app/src/main/java/com/sw/dualscreen/dialog/RemindDialog.kt
similarity index 97%
rename from app/src/main/java/com/sw/dualscreen/dialog/WarnDialog.kt
rename to app/src/main/java/com/sw/dualscreen/dialog/RemindDialog.kt
index c70b2d2..f696eb6 100644
--- a/app/src/main/java/com/sw/dualscreen/dialog/WarnDialog.kt
+++ b/app/src/main/java/com/sw/dualscreen/dialog/RemindDialog.kt
@@ -5,7 +5,7 @@ import android.view.LayoutInflater
import android.view.View
import com.sw.dualscreen.databinding.DialogWarnBinding
-class WarnDialog(
+class RemindDialog(
context: Context,
var content:String,
var cancelBlock: () -> Unit = {},
@@ -31,5 +31,4 @@ class WarnDialog(
}
}
-
}
\ No newline at end of file
diff --git a/app/src/main/java/com/sw/dualscreen/model/response/DataBean.kt b/app/src/main/java/com/sw/dualscreen/model/response/DataBean.kt
index 237f0b3..3606b6b 100644
--- a/app/src/main/java/com/sw/dualscreen/model/response/DataBean.kt
+++ b/app/src/main/java/com/sw/dualscreen/model/response/DataBean.kt
@@ -144,4 +144,8 @@ data class FoodVector(
val foodName: String? = null,
val foodVector: String? = null,
val version: String? = null
+)
+
+data class ResetBoxEvent(
+ var num: Int = 0
)
\ No newline at end of file
diff --git a/app/src/main/java/com/sw/dualscreen/model/response/FoodInfo.kt b/app/src/main/java/com/sw/dualscreen/model/response/FoodInfo.kt
index 97c0b01..c6646b4 100644
--- a/app/src/main/java/com/sw/dualscreen/model/response/FoodInfo.kt
+++ b/app/src/main/java/com/sw/dualscreen/model/response/FoodInfo.kt
@@ -12,33 +12,33 @@ import kotlinx.parcelize.Parcelize
@Parcelize
data class FoodInfo(
//菜品id
- val foodId:String,
+ val foodId: String,
//菜品名称
- val foodName:String? = null,
+ val foodName: String? = null,
//能量
- val calorie:Double? = null,
+ val calorie: Double? = null,
//蛋白质
- val protein:Double? = null,
+ val protein: Double? = null,
//脂肪
- val fat:Double? = null,
+ val fat: Double? = null,
//碳水化合物
- val carbohydrate:Double? = null,
+ val carbohydrate: Double? = null,
//规格售卖价格(元)
val specPrice: Double? = null,
//VIP售卖价(元)
- val vipPrice:Double? = null,
+ val vipPrice: Double? = null,
//餐品营养id
- val foodMaterialId:String? = null,
+ val foodMaterialId: String? = null,
//菜品规格id
- val specId:String? = null,
+ val specId: String? = null,
//规格重量(g)
- val specWeight:Double? = null,
+ val specWeight: Double? = null,
//主食
- val stapleFood:Double? = null,
+ val stapleFood: Double? = null,
//果蔬
- val fruitsVegetables:Double? = null,
+ val fruitsVegetables: Double? = null,
//肉蛋
- val meatEggs:Double? = null,
+ val meatEggs: Double? = null,
//推荐能量
val recommendCalorie: Double? = null,
//图片
@@ -48,6 +48,8 @@ data class FoodInfo(
var score: Int = 0,
var isChecked: Boolean = false,
var photoUri: Uri? = null,
+ //true-搜索结果数据,false-识别查询数据
+ var isFromSearch: Boolean? = null
// @SerializedName("foodTypeAndRealIntakeVoList")
// val foodTypeAndRealIntakeVoList: List? = listOf(),
diff --git a/app/src/main/java/com/sw/dualscreen/objbox/DataBean.kt b/app/src/main/java/com/sw/dualscreen/objbox/DataBean.kt
index 8096f52..2fa7f70 100644
--- a/app/src/main/java/com/sw/dualscreen/objbox/DataBean.kt
+++ b/app/src/main/java/com/sw/dualscreen/objbox/DataBean.kt
@@ -25,7 +25,35 @@ data class FoodCollectionBean(
var isShowCamera: Boolean = false,
var isFinish:Boolean = false,
var uploadSuccess:Boolean = false
-)
+) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as FoodCollectionBean
+
+ if (isShowCamera != other.isShowCamera) return false
+ if (isFinish != other.isFinish) return false
+ if (uploadSuccess != other.uploadSuccess) return false
+ if (imageFile != other.imageFile) return false
+ if (imageUri != other.imageUri) return false
+ if (bitmap != other.bitmap) return false
+ if (!imageVector.contentEquals(other.imageVector)) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = isShowCamera.hashCode()
+ result = 31 * result + isFinish.hashCode()
+ result = 31 * result + uploadSuccess.hashCode()
+ result = 31 * result + (imageFile?.hashCode() ?: 0)
+ result = 31 * result + (imageUri?.hashCode() ?: 0)
+ result = 31 * result + (bitmap?.hashCode() ?: 0)
+ result = 31 * result + (imageVector?.contentHashCode() ?: 0)
+ return result
+ }
+}
data class CollectedFoodInfo(
// var id: String? = null,
diff --git a/app/src/main/java/com/sw/dualscreen/objbox/Food.kt b/app/src/main/java/com/sw/dualscreen/objbox/Food.kt
index d1b53db..410dcdf 100644
--- a/app/src/main/java/com/sw/dualscreen/objbox/Food.kt
+++ b/app/src/main/java/com/sw/dualscreen/objbox/Food.kt
@@ -1,9 +1,11 @@
package com.sw.dualscreen.objbox
+import com.sw.dualscreen.utils.DateTimeUtil
import io.objectbox.annotation.Entity
import io.objectbox.annotation.HnswIndex
import io.objectbox.annotation.Id
import io.objectbox.annotation.VectorDistanceType
+import java.time.LocalDateTime
@Entity
data class Food(
@@ -12,6 +14,8 @@ data class Food(
var foodId: String? = null,
var foodName: String? = null,
var version: String? = null,
+ var createTime: String = DateTimeUtil.formatDateTime(LocalDateTime.now()),
+ var isDel: Boolean = false,
var otherField: String? = null,
@HnswIndex(dimensions = 512, distanceType = VectorDistanceType.DOT_PRODUCT)
var foodVector: FloatArray? = null
@@ -23,10 +27,12 @@ data class Food(
other as Food
if (id != other.id) return false
+ if (isDel != other.isDel) return false
if (collectId != other.collectId) return false
if (foodId != other.foodId) return false
if (foodName != other.foodName) return false
if (version != other.version) return false
+ if (createTime != other.createTime) return false
if (otherField != other.otherField) return false
if (!foodVector.contentEquals(other.foodVector)) return false
@@ -35,14 +41,15 @@ data class Food(
override fun hashCode(): Int {
var result = id.hashCode()
+ result = 31 * result + isDel.hashCode()
result = 31 * result + (collectId?.hashCode() ?: 0)
result = 31 * result + (foodId?.hashCode() ?: 0)
result = 31 * result + (foodName?.hashCode() ?: 0)
result = 31 * result + (version?.hashCode() ?: 0)
+ result = 31 * result + createTime.hashCode()
result = 31 * result + (otherField?.hashCode() ?: 0)
result = 31 * result + (foodVector?.contentHashCode() ?: 0)
return result
}
-
}
\ No newline at end of file
diff --git a/app/src/main/java/com/sw/dualscreen/objbox/FoodModule.kt b/app/src/main/java/com/sw/dualscreen/objbox/FoodModule.kt
index 2cd5eb3..09775e9 100644
--- a/app/src/main/java/com/sw/dualscreen/objbox/FoodModule.kt
+++ b/app/src/main/java/com/sw/dualscreen/objbox/FoodModule.kt
@@ -1,18 +1,14 @@
package com.sw.dualscreen.objbox
+import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Bitmap
import android.net.Uri
-import com.google.gson.Gson
-import com.google.gson.reflect.TypeToken
import com.sw.dualscreen.MyApp
-import com.sw.dualscreen.utils.AssetsTool
import com.sw.dualscreen.utils.GsonUtils
import com.sw.dualscreen.utils.ImageUtil
-import com.sw.plate.App
import io.objectbox.Box
import io.objectbox.kotlin.boxFor
-import io.objectbox.query.Query
import org.pytorch.IValue
import org.pytorch.Module
import org.pytorch.torchvision.TensorImageUtils
@@ -23,22 +19,27 @@ import java.io.IOException
import java.io.InputStream
object FoodModule {
-
+ private const val TAG = "FoodModule"
private const val THRESHOLD = 0.8
- private var module_mobile: Module? = null
+
+ // private const val THRESHOLD = 0.0
+ private lateinit var module: Module
private lateinit var box: Box
- private lateinit var embeddingsList: List>
- private lateinit var labelsList: IntArray
- private lateinit var classInfo: FoodClassInfo
+
+ // private lateinit var embeddingsList: List>
+// private lateinit var labelsList: IntArray
+// private lateinit var classInfo: FoodClassInfo
val NO_MEAN_RGB = floatArrayOf(0.0f, 0.0f, 0.0f)
val NO_STD_RGB = floatArrayOf(1.0f, 1.0f, 1.0f)
- val DEFAULT_FOOD_INDEX = -1
+// val DEFAULT_FOOD_INDEX = -1
+ @SuppressLint("SuspiciousIndentation")
fun init(context: Context, block: () -> Unit = {}) {
Thread {
- module_mobile = Module.load(copyAssetToCache(context, "best_embedding_model_mobile.pt"))
+ module = Module.load(copyAssetToCache(context, "best_embedding_model_mobile.pt"))
box = ObjectBox.boxStore.boxFor(Food::class)
//初始化默认重新拉取数据,先清空本地数据
+// ObjectBox.boxStore.runInTx {
if (box.all.isNotEmpty()) {
box.removeAll()
}
@@ -58,68 +59,79 @@ object FoodModule {
}
fun bitmap2FloatArray(bitmap: Bitmap): FloatArray {
+ var tensorStartTime = System.currentTimeMillis()
val inputTensor = TensorImageUtils.bitmapToFloat32Tensor(
bitmap,
NO_MEAN_RGB, // [0.485, 0.456, 0.406] TORCHVISION_NORM_MEAN_RGB
NO_STD_RGB // [0.229, 0.224, 0.225] TORCHVISION_NORM_STD_RGB
)
- if (module_mobile == null) {
- init(App.getContext())
- }
- val outputTensor = module_mobile!!.forward(IValue.from(inputTensor)).toTensor()
+ Timber.tag(TAG).d("bitmap2FloatArray-inputTensor耗时:${System.currentTimeMillis() - tensorStartTime}")
+// if (module_mobile == null) {
+// init(App.getContext())
+// }
+ tensorStartTime = System.currentTimeMillis()
+ val outputTensor = module.forward(IValue.from(inputTensor)).toTensor()
+ Timber.tag(TAG).d("bitmap2FloatArray-toTensor耗时:${System.currentTimeMillis() - tensorStartTime}")
return outputTensor.dataAsFloatArray
}
- fun queryFood(uri: Uri, queryCount: Int = 15): List? {
- return uri2FloatArray(uri)?.let {
- queryFood(it, queryCount)
- }
- }
+// fun queryFood(uri: Uri, queryCount: Int = 15): List? {
+// return uri2FloatArray(uri)?.let {
+// queryFood(it, queryCount)
+// }
+// }
/**
* 返回识别物品名称列表
*/
- fun queryFood(bitmap: Bitmap, queryCount: Int = 15): List {
- val floatArray = bitmap2FloatArray(bitmap)
- return queryFood(floatArray, queryCount)
- }
+// fun queryFood(bitmap: Bitmap, queryCount: Int = 15): List {
+// val floatArray = bitmap2FloatArray(bitmap)
+// return queryFood(floatArray, queryCount)
+// }
/**
* 返回识别物品IdNameScore对象列表
*/
- fun queryFoodNameScore(bitmap: Bitmap, queryCount: Int = 15): List {
- val floatArray = bitmap2FloatArray(bitmap)
- return queryFoodNameScore(floatArray, queryCount)
- }
+// fun queryFoodNameScore(bitmap: Bitmap, queryCount: Int = 15): List {
+// val floatArray = bitmap2FloatArray(bitmap)
+// return queryFoodNameScore(floatArray, queryCount)
+// }
fun queryFoodNameScore(floatArray: FloatArray, queryCount: Int = 15): List {
val startTime = System.currentTimeMillis()
- val query: Query =
- box.query(Food_.foodVector.nearestNeighbors(floatArray, queryCount)).build()
+ Timber.tag(TAG).d("queryFoodNameScore-已采集向量总数:${box.all.filter { it.isDel.not() }.size}")
+ box.store.runInTx { }
+ val query = box.query()
+ .equal(Food_.isDel, false)
+ .and()
+ .nearestNeighbors(Food_.foodVector, floatArray, queryCount)
+ .build()
//查询比较分数
// val tempList = query.findWithScores().sortedBy { it.score }.map { "${it.get().name}|${it.get().foodIdx}|${it.score}" }
- val idScoreList = query.findIdsWithScores()
+// val idScoreList = query.findIdsWithScores()
+ val objScoreList = query.findWithScores()
+ Timber.tag(TAG).d("idScoreList:${GsonUtils.toJson(objScoreList)}")
val nameScoreList = mutableListOf()
- idScoreList.forEach {
+ objScoreList.forEach {
+ val food = it.get()
nameScoreList.add(
IdNameScore(
- id = it.id,
- name = box.get(it.id).foodName ?: "",
+ id = food.id,
+ name = food.foodName ?: "",
score = it.score
)
)
}
val nameScoreData = GsonUtils.toJson(nameScoreList)
- Timber.tag("FoodModule")
- .d("registerDataChange,queryFood,耗时:${System.currentTimeMillis() - startTime},数据:$nameScoreData")
+ Timber.tag(TAG).d("queryFood,耗时:${System.currentTimeMillis() - startTime},数据:$nameScoreData")
+ query.close()
return nameScoreList
}
fun getFoodScoreList(bitmap: Bitmap, queryCount: Int = 15): List {
val startTime = System.currentTimeMillis()
val floatArray = bitmap2FloatArray(bitmap)
- Timber.tag("FoodModule")
- .d("registerDataChange,bitmap2FloatArray,耗时:${System.currentTimeMillis() - startTime}")
+ Timber.tag(TAG).d("bitmap2FloatArray,耗时:${System.currentTimeMillis() - startTime}")
val nameScoreList = queryFoodNameScore(floatArray, queryCount)
if (nameScoreList.isEmpty()) {
return emptyList()
@@ -147,35 +159,40 @@ object FoodModule {
return sortedScoreList
}
- fun queryFood(floatArray: FloatArray, queryCount: Int = 15): List {
- val query: Query =
- box.query(Food_.foodVector.nearestNeighbors(floatArray, queryCount)).build()
- //查询比较分数
-// val tempList = query.findWithScores().sortedBy { it.score }.map { "${it.get().name}|${it.get().foodIdx}|${it.score}" }
- val map = mutableMapOf()
- query.findIdsWithScores().forEach {
- val food = box.get(it.id)
- Timber.d("${food.foodName}|${food.foodId}|${food.collectId}|${food.version}|${food.otherField}|${it.score}")
- if (1 - it.score >= THRESHOLD) {
- //FoodQueryResult(id = it.id, name = food.name, foodIdx = food.foodIdx, score = it.score)
- food.foodName?.let { key ->
- val count = map[key] ?: 0
- map.put(key, count + 1)
- }
- }
- }
- val list = map.entries.sortedByDescending { it.value }.map { it.key }
- return list
-
+// fun queryFood(floatArray: FloatArray, queryCount: Int = 15): List {
+// val query = box.query()
+// .equal(Food_.isDel, false)
+// .and()
+// .nearestNeighbors(Food_.foodVector, floatArray, queryCount)
+// .build()
+//// val query: Query =
+//// box.query(Food_.foodVector.nearestNeighbors(floatArray, queryCount)).build()
+// //查询比较分数
+//// val tempList = query.findWithScores().sortedBy { it.score }.map { "${it.get().name}|${it.get().foodIdx}|${it.score}" }
// val map = mutableMapOf()
-// val nameScoreList = queryFoodNameScore(floatArray, queryCount)
-// nameScoreList.filter { it.score < 0.05 }.forEach {
-// val count = map[it.name] ?: 0
-// map[it.name] = count + 1
+// query.findIdsWithScores().forEach {
+// val food = box.get(it.id)
+// Timber.d("${food.foodName}|${food.foodId}|${food.collectId}|${food.version}|${food.otherField}|${it.score}")
+// if (1 - it.score >= THRESHOLD) {
+// //FoodQueryResult(id = it.id, name = food.name, foodIdx = food.foodIdx, score = it.score)
+// food.foodName?.let { key ->
+// val count = map[key] ?: 0
+// map.put(key, count + 1)
+// }
+// }
// }
// val list = map.entries.sortedByDescending { it.value }.map { it.key }
// return list
- }
+//
+//// val map = mutableMapOf()
+//// val nameScoreList = queryFoodNameScore(floatArray, queryCount)
+//// nameScoreList.filter { it.score < 0.05 }.forEach {
+//// val count = map[it.name] ?: 0
+//// map[it.name] = count + 1
+//// }
+//// val list = map.entries.sortedByDescending { it.value }.map { it.key }
+//// return list
+// }
data class IdNameScore(
val id: Long,
@@ -183,36 +200,36 @@ object FoodModule {
val score: Double
)
- fun initDefFoodData(context: Context, action: () -> Unit = {}) {
- //val count = box.all.count { it.foodIdx == DEFAULT_FOOD_INDEX }
- //if (count > 0) {
- // return
- //}
- val embeddingsJson = AssetsTool.readAssetsFile(context, "data/embeddings.json")
- val labelsJson = AssetsTool.readAssetsFile(context, "data/labels.json")
- val classInfoJson = AssetsTool.readAssetsFile(context, "data/class_info.json")
-
- embeddingsList =
- Gson().fromJson(embeddingsJson, object : TypeToken>>() {}.type)
- labelsList = Gson().fromJson(labelsJson, IntArray::class.java)
- classInfo =
- Gson().fromJson(classInfoJson, FoodClassInfo::class.java)
-
- val foodMap = classInfo.idx_to_class
- embeddingsList.forEachIndexed { index, floatList ->
- val classIdx = labelsList[index]
- val foodName = foodMap["$classIdx"]
- val array = floatList.toFloatArray()
- box.put(
- Food(
- foodName = foodName,
- foodVector = array,
- //foodIdx = DEFAULT_FOOD_INDEX
- )
- )
- }
- action()
- }
+// fun initDefFoodData(context: Context, action: () -> Unit = {}) {
+// //val count = box.all.count { it.foodIdx == DEFAULT_FOOD_INDEX }
+// //if (count > 0) {
+// // return
+// //}
+// val embeddingsJson = AssetsTool.readAssetsFile(context, "data/embeddings.json")
+// val labelsJson = AssetsTool.readAssetsFile(context, "data/labels.json")
+// val classInfoJson = AssetsTool.readAssetsFile(context, "data/class_info.json")
+//
+// embeddingsList =
+// Gson().fromJson(embeddingsJson, object : TypeToken>>() {}.type)
+// labelsList = Gson().fromJson(labelsJson, IntArray::class.java)
+// classInfo =
+// Gson().fromJson(classInfoJson, FoodClassInfo::class.java)
+//
+// val foodMap = classInfo.idx_to_class
+// embeddingsList.forEachIndexed { index, floatList ->
+// val classIdx = labelsList[index]
+// val foodName = foodMap["$classIdx"]
+// val array = floatList.toFloatArray()
+// box.put(
+// Food(
+// foodName = foodName,
+// foodVector = array,
+// //foodIdx = DEFAULT_FOOD_INDEX
+// )
+// )
+// }
+// action()
+// }
/**
@@ -225,7 +242,7 @@ object FoodModule {
*/
fun copyAssetToCache(context: Context, fileName: String): String? {
// 此app的缓存目录 --> 会默认在 cache目录...,可以自己去看看哦
- val cacheDir = context.getCacheDir()
+ val cacheDir = context.cacheDir
if (!cacheDir.exists()) {
cacheDir.mkdirs() // TODO 如果没有缓存目录,就创建
}
@@ -239,7 +256,7 @@ object FoodModule {
// 创建文件,如果创建成功,就返回true
val res = outPath.createNewFile()
if (res) {
- `is` = context.getAssets().open(fileName) // 拿到main/assets目录的输入流,用于读取字节
+ `is` = context.assets.open(fileName) // 拿到main/assets目录的输入流,用于读取字节
fos = FileOutputStream(outPath) // 读取出来的字节最终写到outPath
val buf = ByteArray(`is`.available()) // 缓存区
var byteCount: Int
@@ -248,7 +265,7 @@ object FoodModule {
while ((`is`.read(buf).also { byteCount = it }) != -1) {
fos.write(buf, 0, byteCount)
}
- return outPath.getAbsolutePath()
+ return outPath.absolutePath
}
} catch (e: IOException) {
e.printStackTrace()
diff --git a/app/src/main/java/com/sw/dualscreen/objbox/ObjectBox.kt b/app/src/main/java/com/sw/dualscreen/objbox/ObjectBox.kt
index 127f817..6f82890 100644
--- a/app/src/main/java/com/sw/dualscreen/objbox/ObjectBox.kt
+++ b/app/src/main/java/com/sw/dualscreen/objbox/ObjectBox.kt
@@ -18,10 +18,12 @@ package com.sw.dualscreen.objbox
import android.content.Context
import android.util.Log
+import io.objectbox.Box
import io.objectbox.BoxStore
import io.objectbox.BoxStoreBuilder
import io.objectbox.android.Admin
import io.objectbox.android.ObjectBoxLiveData
+import io.objectbox.config.DebugFlags
import io.objectbox.exception.DbException
import io.objectbox.exception.FileCorruptException
import io.objectbox.sync.Sync
@@ -64,6 +66,7 @@ object ObjectBox {
boxStore = try {
MyObjectBox.builder()
.androidContext(context.applicationContext)
+ .debugFlags(DebugFlags.LOG_QUERY_PARAMETERS)
.build()
} catch (e: DbException) {
if (e.javaClass == DbException::class.java || e is FileCorruptException) {
@@ -111,4 +114,42 @@ object ObjectBox {
return true
}
+// /**
+// * 安全获取 Food 实体 foodVector 字段的向量索引(推荐写法)
+// */
+// fun getFoodVectorIndex(box: Box): VectorIndex? {
+// return try {
+// // 核心修复:使用自动生成的 Food_.foodVector 而非字符串,避免拼写错误
+// val foodVectorProperty = Food_.foodVector
+// // 获取向量索引(适配 ObjectBox 3.5+ 所有版本)
+// foodVectorProperty.vectorIndex
+// } catch (e: Exception) {
+// // 容错处理:打印错误信息,避免崩溃
+// e.printStackTrace()
+// null
+// }
+// }
+//
+// // 刷新索引的调用示例(结合你之前的更新场景)
+// fun refreshFoodVectorIndex(box: Box) {
+// val vectorIndex = getFoodVectorIndex(box)
+// vectorIndex?.run {
+// // 同步刷新索引(解决查询异常问题)
+// refresh()
+// // 等待索引构建完成(超时 5 秒,避免无限等待)
+// waitUntilBuilt(5000)
+// }
+// }
+//
+// // 更新 isDel 字段后的完整调用流程
+// fun markFoodAsDeleted(box: Box, foodId: Long) {
+// val food = box[foodId]
+// if (food != null) {
+// food.isDel = true
+// box.put(food)
+// // 更新后刷新索引
+// refreshFoodVectorIndex(box)
+// }
+// }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/sw/dualscreen/utils/DateTimeUtil.kt b/app/src/main/java/com/sw/dualscreen/utils/DateTimeUtil.kt
new file mode 100644
index 0000000..8dec5b2
--- /dev/null
+++ b/app/src/main/java/com/sw/dualscreen/utils/DateTimeUtil.kt
@@ -0,0 +1,32 @@
+package com.sw.dualscreen.utils
+
+import java.time.LocalDateTime
+import java.time.ZoneId
+import java.time.format.DateTimeFormatter
+import java.util.Date
+
+
+object DateTimeUtil {
+
+ const val YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"
+
+ fun formatDateTime(dateTime: LocalDateTime, pattern: String = YYYY_MM_DD_HH_MM_SS): String {
+ val formatter = DateTimeFormatter.ofPattern(pattern)
+ return dateTime.format(formatter)
+ }
+
+ fun convert(dateStr: String, pattern: String = YYYY_MM_DD_HH_MM_SS): Date {
+ val formatter = DateTimeFormatter.ofPattern(pattern)
+ val ldt = LocalDateTime.parse(dateStr, formatter)
+ val zdt = ldt.atZone(ZoneId.systemDefault())
+ return Date.from(zdt.toInstant())
+ }
+
+ fun main() {
+ val now = LocalDateTime.now()
+ println("默认格式: ${formatDateTime(now)}")
+ println("自定义格式: ${formatDateTime(now, "yyyy年MM月dd日 HH时mm分ss秒")}")
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/sw/dualscreen/view/CustomBottomSheetDialog.kt b/app/src/main/java/com/sw/dualscreen/view/CustomBottomSheetDialog.kt
index f8a0c1d..dba726d 100644
--- a/app/src/main/java/com/sw/dualscreen/view/CustomBottomSheetDialog.kt
+++ b/app/src/main/java/com/sw/dualscreen/view/CustomBottomSheetDialog.kt
@@ -3,8 +3,6 @@ package com.sw.dualscreen.view
import android.annotation.SuppressLint
import android.content.DialogInterface
import android.graphics.Color
-import android.graphics.Typeface
-import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -13,18 +11,15 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.sw.dualscreen.R
-import com.sw.dualscreen.adapter.GenericItemAdapter
-import com.sw.dualscreen.adapter.GridSpacingItemDecoration
import com.sw.dualscreen.adapter.SearchFoodAdapter
-import com.sw.dualscreen.adapter.dpToPx
import com.sw.dualscreen.databinding.BottomSheetDialogBinding
-import com.sw.dualscreen.databinding.ItemSearchFoodInfoBinding
import com.sw.dualscreen.model.response.FoodInfo
import com.sw.dualscreen.utils.Debouncer
import com.sw.dualscreen.utils.KeyboardUtils
import com.sw.dualscreen.viewmodel.UserViewModel
import kotlinx.coroutines.launch
import timber.log.Timber
+import androidx.core.graphics.drawable.toDrawable
/**
* 底部搜索结果弹窗
@@ -43,7 +38,7 @@ class CustomBottomSheetDialog(
searchFoodList.forEachIndexed { index, info ->
searchFoodList[index].isChecked = index == position
}
- val item = searchFoodList[position]
+ val item = searchFoodList[position].also { it.isFromSearch = true }
Timber.d("itemClick ${item.foodName}, position = $position")
itemClickCallback(item)
checkedItem = item
@@ -79,7 +74,7 @@ class CustomBottomSheetDialog(
private fun setDialogStyle() {
dialog?.window?.let {
- it.decorView.background = ColorDrawable(Color.TRANSPARENT)
+ it.decorView.background = Color.TRANSPARENT.toDrawable()
it.decorView.setPadding(0, 0, 0, 0)
it.attributes?.apply {
width = ViewGroup.LayoutParams.MATCH_PARENT
@@ -118,15 +113,17 @@ class CustomBottomSheetDialog(
it.adapter = adapter
}
binding.ivSearch.setOnClickListener {
- searchInfo()
+ searchFood()
}
KeyboardUtils.setupEditorAction(binding.etSearch) {
- searchInfo()
+ searchFood()
}
registerDataChange()
+
+ searchFood()
}
- private fun searchInfo(){
+ private fun searchFood(){
debouncer.debounce {
viewModel.searchByFoodName(binding.etSearch.text.toString()) {
searchFoodList.clear()
@@ -138,13 +135,13 @@ class CustomBottomSheetDialog(
@SuppressLint("NotifyDataSetChanged")
private fun registerDataChange() {
- lifecycleScope.launch {
- viewModel.searchFoodInfoList.collect {
- searchFoodList.clear()
- searchFoodList.addAll(it)
- adapter.notifyDataSetChanged()
- }
- }
+// lifecycleScope.launch {
+// viewModel.searchFoodInfoList.collect {
+// searchFoodList.clear()
+// searchFoodList.addAll(it)
+// adapter.notifyDataSetChanged()
+// }
+// }
}
// private fun createAdapter(): GenericItemAdapter {
@@ -176,13 +173,13 @@ class CustomBottomSheetDialog(
override fun onCancel(dialog: DialogInterface) {
Timber.d("onCancel")
- viewModel.cleanSearchFoodInfoList()
+// viewModel.cleanSearchFoodInfoList()
super.onCancel(dialog)
}
override fun dismiss() {
Timber.d("dismiss")
- viewModel.cleanSearchFoodInfoList()
+// viewModel.cleanSearchFoodInfoList()
super.dismiss()
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/sw/dualscreen/viewmodel/UserViewModel.kt b/app/src/main/java/com/sw/dualscreen/viewmodel/UserViewModel.kt
index 4ab49f7..6c64050 100644
--- a/app/src/main/java/com/sw/dualscreen/viewmodel/UserViewModel.kt
+++ b/app/src/main/java/com/sw/dualscreen/viewmodel/UserViewModel.kt
@@ -40,12 +40,12 @@ class UserViewModel : BaseViewModel() {
private val faceApi: FaceApi = FaceApi()
// 识别出的列表
- private val _identifiedFoodInfoList = MutableStateFlow>(emptyList())
- val identifiedFoodInfoList: StateFlow> = _identifiedFoodInfoList
+// private val _identifiedFoodInfoList = MutableStateFlow>(emptyList())
+// val identifiedFoodInfoList: StateFlow> = _identifiedFoodInfoList
// 搜索出的食物列表
- private val _searchFoodInfoList = MutableStateFlow>(emptyList())
- val searchFoodInfoList: StateFlow> = _searchFoodInfoList
+// private val _searchFoodInfoList = MutableStateFlow>(emptyList())
+// val searchFoodInfoList: StateFlow> = _searchFoodInfoList
// 就餐数据
// private val _nutritionData = MutableStateFlow(null)
@@ -225,6 +225,8 @@ class UserViewModel : BaseViewModel() {
val list: List = response.data ?: emptyList()
block(list)
}
+ } else {
+ block(emptyList())
}
}
}
@@ -278,10 +280,10 @@ class UserViewModel : BaseViewModel() {
// }
// }
- fun cleanIdentifiedFoodInfoList() {
- Timber.tag(TAG).d("cleanIdentifiedFoodInfoList")
- _identifiedFoodInfoList.value = emptyList()
- }
+// fun cleanIdentifiedFoodInfoList() {
+// Timber.tag(TAG).d("cleanIdentifiedFoodInfoList")
+// _identifiedFoodInfoList.value = emptyList()
+// }
/**
* 搜索食物
@@ -293,15 +295,15 @@ class UserViewModel : BaseViewModel() {
if (parseResponse(response)) {
val list = response.data ?: emptyList()
action(list)
- _identifiedFoodInfoList.value = response.data ?: emptyList()
+// _identifiedFoodInfoList.value = response.data ?: emptyList()
}
}
}
- fun cleanSearchFoodInfoList() {
- Timber.tag(TAG).d("cleanSearchFoodInfoList")
- _searchFoodInfoList.value = emptyList()
- }
+// fun cleanSearchFoodInfoList() {
+// Timber.tag(TAG).d("cleanSearchFoodInfoList")
+// _searchFoodInfoList.value = emptyList()
+// }
// /**
// * 获取用户就餐数据
@@ -383,7 +385,7 @@ class UserViewModel : BaseViewModel() {
val response = repository.getFoodInfo(foodName = foodName)
if (parseResponse(response)) {
val list = response.data ?: emptyList()
- _identifiedFoodInfoList.value = list
+// _identifiedFoodInfoList.value = list
action(list)
} else {
action(emptyList())
diff --git a/app/src/main/res/drawable-xxhdpi/ic_empty_view.png b/app/src/main/res/drawable-xxhdpi/ic_empty_view.png
index 30b581d..d0e860f 100644
Binary files a/app/src/main/res/drawable-xxhdpi/ic_empty_view.png and b/app/src/main/res/drawable-xxhdpi/ic_empty_view.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_tip_warn.png b/app/src/main/res/drawable-xxhdpi/ic_tip_warn.png
index 385d7e5..a6dfec0 100644
Binary files a/app/src/main/res/drawable-xxhdpi/ic_tip_warn.png and b/app/src/main/res/drawable-xxhdpi/ic_tip_warn.png differ
diff --git a/app/src/main/res/drawable/bg_btn_save.xml b/app/src/main/res/drawable/bg_btn_save.xml
index 83c1497..05736a9 100644
--- a/app/src/main/res/drawable/bg_btn_save.xml
+++ b/app/src/main/res/drawable/bg_btn_save.xml
@@ -2,5 +2,5 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/bg_blue_ripple.xml b/app/src/main/res/drawable/bg_red_ripple.xml
similarity index 70%
rename from app/src/main/res/drawable/bg_blue_ripple.xml
rename to app/src/main/res/drawable/bg_red_ripple.xml
index 7ea674f..e640de6 100644
--- a/app/src/main/res/drawable/bg_blue_ripple.xml
+++ b/app/src/main/res/drawable/bg_red_ripple.xml
@@ -3,8 +3,8 @@
android:color="#30000000">
-
-
-
+
+
diff --git a/app/src/main/res/drawable/bg_white_stroke_blue_ripple.xml b/app/src/main/res/drawable/bg_white_stroke_red_ripple.xml
similarity index 72%
rename from app/src/main/res/drawable/bg_white_stroke_blue_ripple.xml
rename to app/src/main/res/drawable/bg_white_stroke_red_ripple.xml
index df460db..e50fd2c 100644
--- a/app/src/main/res/drawable/bg_white_stroke_blue_ripple.xml
+++ b/app/src/main/res/drawable/bg_white_stroke_red_ripple.xml
@@ -4,8 +4,8 @@
-
-
-
+
+
diff --git a/app/src/main/res/drawable/line_shape_horizontal.xml b/app/src/main/res/drawable/line_shape_horizontal.xml
index 37a5000..de4ba9d 100644
--- a/app/src/main/res/drawable/line_shape_horizontal.xml
+++ b/app/src/main/res/drawable/line_shape_horizontal.xml
@@ -3,14 +3,14 @@
-
-
+
-
-
+
diff --git a/app/src/main/res/layout/activity_collected_data.xml b/app/src/main/res/layout/activity_collected_data.xml
deleted file mode 100644
index f324dcf..0000000
--- a/app/src/main/res/layout/activity_collected_data.xml
+++ /dev/null
@@ -1,92 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_food_collection.xml b/app/src/main/res/layout/activity_food_collection.xml
deleted file mode 100644
index 3d27766..0000000
--- a/app/src/main/res/layout/activity_food_collection.xml
+++ /dev/null
@@ -1,182 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 16663db..8a76872 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -157,17 +157,17 @@
android:id="@+id/flPay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@color/white"
android:visibility="gone">
+
diff --git a/app/src/main/res/layout/activity_setting.xml b/app/src/main/res/layout/activity_setting.xml
index 0d0f6f7..660dd8b 100644
--- a/app/src/main/res/layout/activity_setting.xml
+++ b/app/src/main/res/layout/activity_setting.xml
@@ -1,9 +1,9 @@
-
+
+
+ android:textStyle="bold"
+ tools:checked="true" />
+
+
+
+
+ android:layout_height="match_parent" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/bottom_sheet_dialog.xml b/app/src/main/res/layout/bottom_sheet_dialog.xml
index 50036fe..aafdc29 100644
--- a/app/src/main/res/layout/bottom_sheet_dialog.xml
+++ b/app/src/main/res/layout/bottom_sheet_dialog.xml
@@ -69,6 +69,7 @@
android:layout_marginHorizontal="14dp"
app:spanCount="2"
tools:itemCount="12"
+ android:overScrollMode="never"
tools:listitem="@layout/list_item_search_food"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"/>
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_warn.xml b/app/src/main/res/layout/dialog_warn.xml
index 22afb59..0069048 100644
--- a/app/src/main/res/layout/dialog_warn.xml
+++ b/app/src/main/res/layout/dialog_warn.xml
@@ -3,7 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="500dp"
android:layout_height="300dp"
- android:gravity="center"
+ android:gravity="center_horizontal"
android:orientation="vertical"
android:background="@drawable/bg_white_radius12"
tools:ignore="HardcodedText">
@@ -11,42 +11,46 @@
@@ -18,18 +18,17 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
- android:layout_marginBottom="39dp"
android:text="暂无采集记录"
- android:textColor="#ff000000"
- android:textSize="36sp"
+ android:textColor="@color/black"
+ android:textSize="40sp"
android:textStyle="bold" />
-
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/list_item_collected_food.xml b/app/src/main/res/layout/list_item_collected_food.xml
index 135d006..1c81129 100644
--- a/app/src/main/res/layout/list_item_collected_food.xml
+++ b/app/src/main/res/layout/list_item_collected_food.xml
@@ -42,7 +42,7 @@
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginEnd="75dp"
- android:paddingHorizontal="13dp"
+ android:paddingHorizontal="18dp"
android:src="@drawable/ic_close2"
tools:ignore="ContentDescription"
android:visibility="visible"/>