采集向量优化调试
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -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": []
|
||||
|
||||
@@ -50,7 +50,8 @@
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.MainActivity"
|
||||
android:exported="true">
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
|
||||
</activity>
|
||||
<!-- <activity-->
|
||||
<!-- android:name=".activity.FoodCollectionActivity"-->
|
||||
|
||||
@@ -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<ActivityCollectedDataBinding>() {
|
||||
// private val viewModel by viewModels<UserViewModel>()
|
||||
// override fun getViewModel(): BaseViewModel {
|
||||
// return viewModel
|
||||
// }
|
||||
//
|
||||
// override fun inflateViewBinding(): ActivityCollectedDataBinding {
|
||||
// return ActivityCollectedDataBinding.inflate(layoutInflater)
|
||||
// }
|
||||
//
|
||||
// private val list: MutableList<CollectedFoodInfo> = 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<Food>? = 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()
|
||||
// }
|
||||
//}
|
||||
@@ -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<ActivityCollectedFoodBinding>() {
|
||||
|
||||
@@ -47,6 +51,7 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
|
||||
|
||||
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<ActivityCollectedFoodBinding>() {
|
||||
}
|
||||
|
||||
|
||||
private var box: Box<Food>? = null
|
||||
private lateinit var box: Box<Food>
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun getCollectGoods(searchName: String? = null) {
|
||||
@@ -181,7 +186,7 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
|
||||
}
|
||||
|
||||
private fun loadDeleteDialog(position: Int) {
|
||||
WarnDialog(
|
||||
RemindDialog(
|
||||
context = context,
|
||||
content = "请确认是否删除菜品:${list[position].foodName}?",
|
||||
confirmBlock = {
|
||||
@@ -195,8 +200,16 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
|
||||
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)
|
||||
|
||||
@@ -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<ActivityFoodCollectionBinding>() {
|
||||
// companion object {
|
||||
// val MAX_COUNT = 100
|
||||
// }
|
||||
// private val viewModel by viewModels<UserViewModel>()
|
||||
// private var selectedFoodId: String? = ""
|
||||
// private var selectedFoodName: String? = ""
|
||||
//
|
||||
// private var box: Box<Food>? = null
|
||||
// private val foodCollectionList: MutableList<FoodCollectionBean> = mutableListOf()
|
||||
//
|
||||
//// private lateinit var cameraHelper: CameraHelper
|
||||
//
|
||||
// private lateinit var foodAdapter: GenericItemAdapter<FoodInfo, ItemSearchFoodInfoBinding>
|
||||
// private val foodList = mutableListOf<FoodInfo>() // 适配器内部维护的数据列表
|
||||
// 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<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()
|
||||
// }
|
||||
// }
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// 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
|
||||
// }
|
||||
//
|
||||
//}
|
||||
@@ -51,7 +51,7 @@ class InitActivity : BaseActivity<ActivityInitBinding>() {
|
||||
}
|
||||
}
|
||||
}
|
||||
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<FoodVector>) {
|
||||
val vectorList = list.map { vt ->
|
||||
val foodVector = vt.foodVector?.removeSurrounding("[", "]")?.split(",")?.map { it.toFloatOrNull()?:0.0f }?.toFloatArray()
|
||||
@@ -63,6 +63,7 @@ class InitActivity : BaseActivity<ActivityInitBinding>() {
|
||||
foodVector = foodVector
|
||||
)
|
||||
}
|
||||
val box = ObjectBox.boxStore.boxFor(Food::class)
|
||||
box.put(vectorList)
|
||||
}
|
||||
|
||||
|
||||
@@ -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<ActivityMainBinding>() {
|
||||
|
||||
override fun initialize() {
|
||||
super.initialize()
|
||||
box = ObjectBox.boxStore.boxFor(Food::class)
|
||||
isPageVisible = true
|
||||
addBackEventListener()
|
||||
//FoodModule.init(this)
|
||||
@@ -262,7 +275,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
binding.previewView.visibility = View.VISIBLE
|
||||
binding.ivImg.visibility = View.GONE
|
||||
resumeAnalysis()
|
||||
viewModel.cleanIdentifiedFoodInfoList()
|
||||
// viewModel.cleanIdentifiedFoodInfoList()
|
||||
return
|
||||
}
|
||||
pauseAnalysis()
|
||||
@@ -318,6 +331,9 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
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<ActivityMainBinding>() {
|
||||
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<ActivityMainBinding>() {
|
||||
}
|
||||
failCount++
|
||||
hideWaitingDialog()
|
||||
// ToastUtils.showToast("拍照异常,请手动放置后重新识别")
|
||||
|
||||
shutdownCamera()
|
||||
setupCamera()
|
||||
@@ -389,40 +406,43 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
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
|
||||
|
||||
checkedItem = list[0].also {
|
||||
it.photoUri = lastPhotoUri
|
||||
it.isChecked = true
|
||||
}
|
||||
adapter.notifyItemChanged(0)
|
||||
updateCurrentFood(checkedItem)
|
||||
lastPhotoUri = null
|
||||
// lastPhotoUri = null
|
||||
LightManager.closeRedLight()
|
||||
|
||||
binding.flPay.visible()
|
||||
binding.tvToSearch.let {
|
||||
it.text = "以上都不是,手动搜索"
|
||||
it.visible()
|
||||
}
|
||||
} else {
|
||||
binding.flPay.gone()
|
||||
binding.tvToSearch.gone()
|
||||
}
|
||||
}
|
||||
|
||||
// private fun createAdapter(): GenericItemAdapter<FoodInfo, ItemFoodInfoBinding> {
|
||||
@@ -653,14 +673,14 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
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<ActivityMainBinding>() {
|
||||
}
|
||||
foodOrderId = orderId
|
||||
block()
|
||||
|
||||
if (checkedItem!!.isFromSearch == true) {
|
||||
//当前菜名为手动搜索选择,非识别结果,保存向量数据
|
||||
saveFoodVector(checkedItem!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -886,6 +911,111 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将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<String, RequestBody>()
|
||||
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<Food>
|
||||
|
||||
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<ActivityMainBinding>() {
|
||||
|
||||
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<ActivityMainBinding>() {
|
||||
}
|
||||
}
|
||||
|
||||
// @Subscribe(threadMode = ThreadMode.MAIN)
|
||||
// public fun onResetBox(event: ResetBoxEvent) {
|
||||
// ObjectBox.boxStore.close()
|
||||
// ObjectBox.init(this)
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -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<FragmentCollectBinding>() {
|
||||
private var selectedFoodId: String? = ""
|
||||
private var selectedFoodName: String? = ""
|
||||
|
||||
private val box by lazy { ObjectBox.boxStore.boxFor(Food::class) }
|
||||
private var box: Box<Food>?=null
|
||||
fun initBox() {
|
||||
if (box == null) {
|
||||
box = ObjectBox.boxStore.boxFor(Food::class)
|
||||
}
|
||||
}
|
||||
private val foodCollectionList = mutableListOf<FoodCollectionBean>().apply {
|
||||
repeat(MAX_COUNT) {
|
||||
add(FoodCollectionBean(isShowCamera = true))
|
||||
@@ -192,7 +198,7 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
}
|
||||
|
||||
binding.btnFoodSearch.setOnClickListener {
|
||||
searchInfo()
|
||||
searchFood()
|
||||
}
|
||||
|
||||
binding.btnSave.setOnClickListener {
|
||||
@@ -209,7 +215,7 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
|
||||
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<FragmentCollectBinding>() {
|
||||
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<String, RequestBody>()
|
||||
//params["placeId"] = restId.toRequestBody()
|
||||
params["foodId"] = checkedItem!!.foodId.toRequestBody()
|
||||
@@ -256,7 +261,13 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
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")
|
||||
@@ -264,12 +275,13 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
settingActivity?.viewModel?.uploadCollectFoodPics(files, params)
|
||||
},
|
||||
onProgress = { count, batch, idList ->
|
||||
activity?.runOnUiThread {
|
||||
settingActivity?.showWaitingDialog2("图片上传中$count/$totalFileCount")
|
||||
//batch.forEach {
|
||||
// it.uploadSuccess = true
|
||||
//}
|
||||
Thread {
|
||||
runBlocking {
|
||||
activity?.runOnUiThread {
|
||||
settingActivity?.showWaitingDialog2("图片上传中$count/$totalFileCount")
|
||||
}
|
||||
val foodList = batch.mapIndexed { index, it ->
|
||||
Food(
|
||||
collectId = if (index < idList.size) idList[index] else null,
|
||||
@@ -279,23 +291,28 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
version = GlobalData.foodModelVersion
|
||||
)
|
||||
}
|
||||
box.put(foodList)
|
||||
initBox()
|
||||
box?.put(foodList)
|
||||
activity?.runOnUiThread {
|
||||
batch.forEach { it.uploadSuccess = true }
|
||||
batch.forEach { it.isFinish = true }
|
||||
collectionAdapter.notifyDataSetChanged()
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
},
|
||||
onError = {
|
||||
settingActivity?.hideWaitingDialog()
|
||||
activity?.runOnUiThread {
|
||||
binding.root.postDelayed({
|
||||
settingActivity?.hideWaitingDialog()
|
||||
ToastUtils.showToast("上传失败,请稍后重试")
|
||||
}, 1000)
|
||||
}
|
||||
},
|
||||
onComplete = {
|
||||
//vectorThread()
|
||||
binding.root.postDelayed({
|
||||
settingActivity?.hideWaitingDialog()
|
||||
ToastUtils.showToast("上传成功")
|
||||
}, 1000)
|
||||
}
|
||||
).processUploads()
|
||||
}
|
||||
@@ -320,59 +337,59 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
// }
|
||||
}
|
||||
|
||||
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<FragmentCollectBinding>() {
|
||||
|
||||
clickIndex = -1
|
||||
binding.editFoodName.setText("")
|
||||
searchFoodList.clear()
|
||||
searchFoodAdapter.notifyDataSetChanged()
|
||||
searchFood()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
||||
+1
-2
@@ -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(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -145,3 +145,7 @@ data class FoodVector(
|
||||
val foodVector: String? = null,
|
||||
val version: String? = null
|
||||
)
|
||||
|
||||
data class ResetBoxEvent(
|
||||
var num: Int = 0
|
||||
)
|
||||
@@ -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<FoodTypeAndRealIntakeVo>? = listOf(),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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<Food>
|
||||
private lateinit var embeddingsList: List<List<Float>>
|
||||
private lateinit var labelsList: IntArray
|
||||
private lateinit var classInfo: FoodClassInfo
|
||||
|
||||
// private lateinit var embeddingsList: List<List<Float>>
|
||||
// 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<String>? {
|
||||
return uri2FloatArray(uri)?.let {
|
||||
queryFood(it, queryCount)
|
||||
}
|
||||
}
|
||||
// fun queryFood(uri: Uri, queryCount: Int = 15): List<String>? {
|
||||
// return uri2FloatArray(uri)?.let {
|
||||
// queryFood(it, queryCount)
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 返回识别物品名称列表
|
||||
*/
|
||||
fun queryFood(bitmap: Bitmap, queryCount: Int = 15): List<String> {
|
||||
val floatArray = bitmap2FloatArray(bitmap)
|
||||
return queryFood(floatArray, queryCount)
|
||||
}
|
||||
// fun queryFood(bitmap: Bitmap, queryCount: Int = 15): List<String> {
|
||||
// val floatArray = bitmap2FloatArray(bitmap)
|
||||
// return queryFood(floatArray, queryCount)
|
||||
// }
|
||||
|
||||
/**
|
||||
* 返回识别物品IdNameScore对象列表
|
||||
*/
|
||||
fun queryFoodNameScore(bitmap: Bitmap, queryCount: Int = 15): List<IdNameScore> {
|
||||
val floatArray = bitmap2FloatArray(bitmap)
|
||||
return queryFoodNameScore(floatArray, queryCount)
|
||||
}
|
||||
// fun queryFoodNameScore(bitmap: Bitmap, queryCount: Int = 15): List<IdNameScore> {
|
||||
// val floatArray = bitmap2FloatArray(bitmap)
|
||||
// return queryFoodNameScore(floatArray, queryCount)
|
||||
// }
|
||||
|
||||
fun queryFoodNameScore(floatArray: FloatArray, queryCount: Int = 15): List<IdNameScore> {
|
||||
val startTime = System.currentTimeMillis()
|
||||
val query: Query<Food> =
|
||||
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<IdNameScore>()
|
||||
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<IdNameScore> {
|
||||
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<String> {
|
||||
val query: Query<Food> =
|
||||
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<String, Int>()
|
||||
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<String> {
|
||||
// val query = box.query()
|
||||
// .equal(Food_.isDel, false)
|
||||
// .and()
|
||||
// .nearestNeighbors(Food_.foodVector, floatArray, queryCount)
|
||||
// .build()
|
||||
//// val query: Query<Food> =
|
||||
//// 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<String, Int>()
|
||||
// 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<String, Int>()
|
||||
//// 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
|
||||
// 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<List<List<Float>>>() {}.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()
|
||||
// }
|
||||
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<List<List<Float>>>() {}.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()
|
||||
|
||||
@@ -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<Food>): VectorIndex? {
|
||||
// return try {
|
||||
// // 核心修复:使用自动生成的 Food_.foodVector 而非字符串,避免拼写错误
|
||||
// val foodVectorProperty = Food_.foodVector
|
||||
// // 获取向量索引(适配 ObjectBox 3.5+ 所有版本)
|
||||
// foodVectorProperty.vectorIndex
|
||||
// } catch (e: Exception) {
|
||||
// // 容错处理:打印错误信息,避免崩溃
|
||||
// e.printStackTrace()
|
||||
// null
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 刷新索引的调用示例(结合你之前的更新场景)
|
||||
// fun refreshFoodVectorIndex(box: Box<Food>) {
|
||||
// val vectorIndex = getFoodVectorIndex(box)
|
||||
// vectorIndex?.run {
|
||||
// // 同步刷新索引(解决查询异常问题)
|
||||
// refresh()
|
||||
// // 等待索引构建完成(超时 5 秒,避免无限等待)
|
||||
// waitUntilBuilt(5000)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 更新 isDel 字段后的完整调用流程
|
||||
// fun markFoodAsDeleted(box: Box<Food>, foodId: Long) {
|
||||
// val food = box[foodId]
|
||||
// if (food != null) {
|
||||
// food.isDel = true
|
||||
// box.put(food)
|
||||
// // 更新后刷新索引
|
||||
// refreshFoodVectorIndex(box)
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -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秒")}")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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<FoodInfo, ItemSearchFoodInfoBinding> {
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -40,12 +40,12 @@ class UserViewModel : BaseViewModel() {
|
||||
private val faceApi: FaceApi = FaceApi()
|
||||
|
||||
// 识别出的列表
|
||||
private val _identifiedFoodInfoList = MutableStateFlow<List<FoodInfo>>(emptyList())
|
||||
val identifiedFoodInfoList: StateFlow<List<FoodInfo>> = _identifiedFoodInfoList
|
||||
// private val _identifiedFoodInfoList = MutableStateFlow<List<FoodInfo>>(emptyList())
|
||||
// val identifiedFoodInfoList: StateFlow<List<FoodInfo>> = _identifiedFoodInfoList
|
||||
|
||||
// 搜索出的食物列表
|
||||
private val _searchFoodInfoList = MutableStateFlow<List<FoodInfo>>(emptyList())
|
||||
val searchFoodInfoList: StateFlow<List<FoodInfo>> = _searchFoodInfoList
|
||||
// private val _searchFoodInfoList = MutableStateFlow<List<FoodInfo>>(emptyList())
|
||||
// val searchFoodInfoList: StateFlow<List<FoodInfo>> = _searchFoodInfoList
|
||||
|
||||
// 就餐数据
|
||||
// private val _nutritionData = MutableStateFlow<UserNutritionData?>(null)
|
||||
@@ -225,6 +225,8 @@ class UserViewModel : BaseViewModel() {
|
||||
val list: List<CollectedFoodInfo> = 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<FoodInfo>()
|
||||
}
|
||||
// fun cleanIdentifiedFoodInfoList() {
|
||||
// Timber.tag(TAG).d("cleanIdentifiedFoodInfoList")
|
||||
// _identifiedFoodInfoList.value = emptyList<FoodInfo>()
|
||||
// }
|
||||
|
||||
/**
|
||||
* 搜索食物
|
||||
@@ -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<FoodInfo>()
|
||||
}
|
||||
// fun cleanSearchFoodInfoList() {
|
||||
// Timber.tag(TAG).d("cleanSearchFoodInfoList")
|
||||
// _searchFoodInfoList.value = emptyList<FoodInfo>()
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * 获取用户就餐数据
|
||||
@@ -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())
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 9.3 KiB |
@@ -2,5 +2,5 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#FFFF3232"/>
|
||||
<corners android:radius="10dp"/>
|
||||
<corners android:radius="12dp"/>
|
||||
</shape>
|
||||
+2
-2
@@ -3,8 +3,8 @@
|
||||
android:color="#30000000">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#FF0032C8" />
|
||||
<corners android:radius="10dp" />
|
||||
<solid android:color="#FFFF3232" />
|
||||
<corners android:radius="12dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
||||
+2
-2
@@ -4,8 +4,8 @@
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/white"/>
|
||||
<stroke android:color="#FF0032C8" android:width="1dp"/>
|
||||
<corners android:radius="10dp"/>
|
||||
<stroke android:color="#FFFF3232" android:width="1dp"/>
|
||||
<corners android:radius="12dp"/>
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
||||
@@ -3,14 +3,14 @@
|
||||
<item android:state_checked="true">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#FFFF3232"/>
|
||||
<size android:width="120dp" android:height="5dp"/>
|
||||
<size android:width="120dp" android:height="3dp"/>
|
||||
<corners android:radius="5dp"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item android:state_checked="false">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/transparent"/>
|
||||
<size android:width="120dp" android:height="5dp"/>
|
||||
<size android:width="120dp" android:height="3dp"/>
|
||||
<corners android:radius="5dp"/>
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="88dp"
|
||||
android:layout_marginTop="24dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivBack"
|
||||
android:layout_width="68dp"
|
||||
android:layout_height="68dp"
|
||||
android:padding="10dp"
|
||||
android:src="@drawable/ic_back" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:text="已采集菜品"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="26sp" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/flSearchBlock"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="80dp"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
android:background="@drawable/bg_gray">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etInputGoods"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#00000000"
|
||||
android:gravity="center"
|
||||
android:hint="输入菜品名称"
|
||||
android:maxLines="1"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="80dp"
|
||||
android:textColor="#FF666666"
|
||||
android:textColorHint="#FFC7C8DC"
|
||||
android:textSize="30sp"
|
||||
android:inputType="text"
|
||||
android:imeOptions="actionSearch" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivGoodsSearch"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:padding="14dp"
|
||||
android:src="@drawable/ic_goods_search" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<!-- <com.scwang.smart.refresh.layout.SmartRefreshLayout-->
|
||||
<!-- android:id="@+id/refreshLayout"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="0dp"-->
|
||||
<!-- android:layout_marginHorizontal="130dp"-->
|
||||
<!-- android:layout_marginTop="40dp"-->
|
||||
<!-- android:layout_marginBottom="58dp"-->
|
||||
<!-- app:layout_constraintBottom_toTopOf="@id/dividerLine"-->
|
||||
<!-- app:layout_constraintTop_toBottomOf="@id/flSearchBlock">-->
|
||||
|
||||
<!-- <com.scwang.smart.refresh.header.ClassicsHeader-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content" />-->
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvFoodList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:overScrollMode="never"
|
||||
android:layout_margin="10dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:itemCount="8"
|
||||
tools:listitem="@layout/list_item_collected_data" />
|
||||
|
||||
<!-- <com.scwang.smart.refresh.footer.ClassicsFooter-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content" />-->
|
||||
<!-- </com.scwang.smart.refresh.layout.SmartRefreshLayout>-->
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,182 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="88dp"
|
||||
android:layout_marginTop="24dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivBack"
|
||||
android:layout_width="68dp"
|
||||
android:layout_height="68dp"
|
||||
android:padding="15dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:src="@drawable/ic_back" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:text="菜品采集"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="26sp" />
|
||||
</FrameLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="24dp">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/flPreview"
|
||||
android:layout_width="180dp"
|
||||
android:layout_height="180dp"
|
||||
android:layout_marginStart="24dp">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/flCameraPreview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="2dp"
|
||||
app:cardCornerRadius="10dp"
|
||||
app:cardElevation="0dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llCameraFlag"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:background="@color/white"
|
||||
android:orientation="vertical"
|
||||
android:visibility="visible">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="center"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/ic_camera256"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:text="图片采集"
|
||||
android:textColor="#ffb4b4c8"
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="20dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
<Button
|
||||
android:id="@+id/btnTakePhoto"
|
||||
android:layout_width="140dp"
|
||||
android:layout_height="80dp"
|
||||
android:gravity="center"
|
||||
android:text="开始拍照"
|
||||
android:textColor="#ffffff"
|
||||
android:textSize="18sp" />
|
||||
<Button
|
||||
android:id="@+id/btnClearData"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="80dp"
|
||||
android:gravity="center"
|
||||
android:text="清除"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:textColor="#ffffff"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnCollectedGoods"
|
||||
android:layout_width="220dp"
|
||||
android:layout_height="80dp"
|
||||
android:gravity="center"
|
||||
android:text="已采集菜品"
|
||||
android:textColor="#ffffff"
|
||||
android:textSize="18sp" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvFoodCollection"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginHorizontal="3dp"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:spanCount="3"
|
||||
tools:itemCount="3"
|
||||
tools:listitem="@layout/list_item_food_collection" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="66dp"
|
||||
android:layout_marginLeft="24dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginRight="24dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editFoodName"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/bg_gray"
|
||||
android:gravity="center_vertical"
|
||||
android:hint="输入菜品名称"
|
||||
android:imeOptions="actionSearch"
|
||||
android:paddingStart="15dp"
|
||||
android:paddingEnd="15dp"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="26sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnFoodSearch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:text="菜品检索"
|
||||
android:textColor="#ffffff"
|
||||
android:textSize="26sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnSave"
|
||||
android:layout_width="260dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginVertical="24dp"
|
||||
android:text="保存"
|
||||
android:textColor="#ffffff"
|
||||
android:textSize="26sp" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -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">
|
||||
<!-- android:background="@color/white"-->
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnPay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_margin="28dp"
|
||||
android:layout_height="87dp"
|
||||
android:layout_margin="32dp"
|
||||
android:layout_height="100dp"
|
||||
android:textStyle="bold"
|
||||
android:text="去结算"
|
||||
android:textSize="35sp"
|
||||
android:textSize="40sp"
|
||||
android:background="@drawable/bg_btn_save"
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#FFE7EFF8"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include
|
||||
@@ -16,42 +16,53 @@
|
||||
android:layout_height="90dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbBusiness"
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="50dp"
|
||||
android:layout_marginEnd="25dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbBusiness"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:button="@null"
|
||||
tools:checked="true"
|
||||
android:drawableBottom="@drawable/line_shape_horizontal"
|
||||
android:gravity="center_horizontal"
|
||||
android:maxLines="1"
|
||||
android:paddingHorizontal="50dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="35dp"
|
||||
android:text="经营设置"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingBottom="32dp"
|
||||
android:textColor="@color/setting_radio"
|
||||
android:textSize="30sp"
|
||||
android:drawableBottom="@drawable/line_shape_horizontal"
|
||||
android:textStyle="bold" />
|
||||
android:textStyle="bold"
|
||||
tools:checked="true" />
|
||||
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbCollect"
|
||||
android:layout_width="0dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="25dp"
|
||||
android:layout_marginEnd="50dp"
|
||||
android:layout_weight="1"
|
||||
android:button="@null"
|
||||
android:drawableBottom="@drawable/line_shape_horizontal"
|
||||
android:gravity="center_horizontal"
|
||||
android:maxLines="1"
|
||||
android:paddingHorizontal="50dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="35dp"
|
||||
android:text="餐品采集"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingBottom="32dp"
|
||||
android:textColor="@color/setting_radio"
|
||||
android:textSize="30sp"
|
||||
android:drawableBottom="@drawable/line_shape_horizontal"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1" />
|
||||
</RadioGroup>
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
|
||||
@@ -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"/>
|
||||
</LinearLayout>
|
||||
@@ -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 @@
|
||||
<ImageView
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:src="@drawable/ic_tip_warn"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvWarnContent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginHorizontal="30dp"
|
||||
android:layout_marginVertical="30dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:gravity="center"
|
||||
tools:text="存在未收货的物品,请确认是否放弃收货,若放弃则数据不会保存?"
|
||||
tools:text="这里显示弹窗内容?\n这里显示弹窗内容?\n这里显示弹窗内容?"
|
||||
android:textColor="#ff141428"
|
||||
android:maxLines="3"
|
||||
android:ellipsize="end"
|
||||
android:textSize="22sp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="90dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnCancel"
|
||||
android:layout_width="130dp"
|
||||
android:layout_height="60dp"
|
||||
android:background="@drawable/bg_white_stroke_blue_ripple"
|
||||
android:layout_width="135dp"
|
||||
android:layout_height="50dp"
|
||||
android:background="@drawable/bg_white_stroke_red_ripple"
|
||||
android:text="取消"
|
||||
android:gravity="center"
|
||||
android:textColor="#FF0033CC"
|
||||
android:textColor="#FFFF3232"
|
||||
android:textSize="22sp" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnConfirm"
|
||||
android:layout_width="130dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_marginStart="40dp"
|
||||
android:background="@drawable/bg_blue_ripple"
|
||||
android:layout_width="135dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginStart="50dp"
|
||||
android:background="@drawable/bg_red_ripple"
|
||||
android:text="确认"
|
||||
android:gravity="center"
|
||||
android:textColor="@color/white"
|
||||
|
||||
@@ -187,9 +187,9 @@
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnConfirm"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="87dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginHorizontal="32dp"
|
||||
android:layout_marginBottom="48dp"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:text="确定"
|
||||
android:background="@drawable/bg_btn_save"
|
||||
android:textColor="@color/white"
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvFoodList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="400dp"
|
||||
android:layout_height="382dp"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
@@ -164,20 +164,22 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginTop="18dp"
|
||||
android:minHeight="348dp"
|
||||
android:layout_weight="1"
|
||||
app:spanCount="2"
|
||||
tools:itemCount="10"
|
||||
android:overScrollMode="never"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
tools:listitem="@layout/list_item_search_food"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnSave"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="87dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginHorizontal="32dp"
|
||||
android:layout_marginBottom="48dp"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="保存"
|
||||
android:background="@drawable/bg_btn_save"
|
||||
android:textColor="@color/white"
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="80dp"
|
||||
android:layout_height="100dp"
|
||||
android:src="@drawable/ic_empty_view"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
@@ -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" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvContent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="请确认菜品名称,或稍后重试"
|
||||
android:textColor="#ff999999"
|
||||
android:gravity="center"
|
||||
android:textSize="30sp" />
|
||||
<!-- <TextView-->
|
||||
<!-- android:id="@+id/tvContent"-->
|
||||
<!-- android:layout_width="wrap_content"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:text="请确认菜品名称,或稍后重试"-->
|
||||
<!-- android:textColor="#ff999999"-->
|
||||
<!-- android:gravity="center"-->
|
||||
<!-- android:textSize="30sp" />-->
|
||||
</LinearLayout>
|
||||
@@ -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"/>
|
||||
|
||||
Reference in New Issue
Block a user