改造objectbox

This commit is contained in:
2026-01-31 17:49:44 +08:00
parent 4322c9495b
commit af120eabd0
7 changed files with 120 additions and 74 deletions
@@ -6,15 +6,14 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.lifecycle.lifecycleScope
import com.sw.inbound.objbox.FoodModule
import com.sw.inbound.sdk.SensorScaleUtils
import com.sw.inbound.ui.AppScreen
import com.sw.inbound.utils.ContextUtils
import com.sw.inbound.utils.SPUtil
import com.sw.inbound.utils.ThreadUtils
import com.sw.inbound.viewmodel.ReceiptViewModel
import dagger.hilt.android.AndroidEntryPoint
import kotlin.getValue
import kotlinx.coroutines.launch
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
@@ -35,7 +34,9 @@ class MainActivity : ComponentActivity() {
})
}
SensorScaleUtils.startScale(autoScale = true)
FoodModule.init(this)
lifecycleScope.launch {
FoodModule.init(this@MainActivity)
}
}
override fun onDestroy() {
@@ -71,7 +71,7 @@ class FoodCollectionActivity : ComponentActivity() {
const val PAGE_SIZE = 30
}
private var box: Box<Food>? = null
// private var box: Box<Food>? = null
private val collectList: MutableList<FoodCollectionBean> = mutableListOf<FoodCollectionBean>()
private lateinit var binding: ActivityFoodCollectionBinding
private lateinit var previewView: PreviewView
@@ -287,11 +287,17 @@ class FoodCollectionActivity : ComponentActivity() {
}
private val cameraCallback: (Uri) -> Unit = { uri ->
lifecycleScope.launch {
handlePhoto(uri)
}
}
private suspend fun handlePhoto(uri: Uri) {
val index = collectList.indexOfFirst { it.imageFile == null }
if (index == -1) {
toast("每次只允许保存${MAX_COUNT}条数据")
showWaitingDialog2("加载中")
rerurn@ cameraCallback
return
}
// toast("index=$index")
try {
@@ -373,7 +379,7 @@ class FoodCollectionActivity : ComponentActivity() {
private fun vectorThread() {
Loading.show(this)
Thread {
lifecycleScope.launch {
collectList
//.filter { it.bitmap != null }
// .filter { it.imageVector != null }
@@ -389,10 +395,10 @@ class FoodCollectionActivity : ComponentActivity() {
toast("保存成功")
}, 1000)
}
}.start()
}
}
private fun image2VectorTask(
private suspend fun image2VectorTask(
bitmap: Bitmap? = null,
imageVector: FloatArray? = null,
position: Int
@@ -408,9 +414,10 @@ class FoodCollectionActivity : ComponentActivity() {
// }
val goods = searchGoodsList[clickIndex]
val saveName = goods.goodsName + goods.goodsCode
ObjectBox.boxStore.runInTx {
box?.put(Food(name = saveName, foodIdx = 0, foodVector = imageVector))
}
// ObjectBox.boxStore.runInTx {
// box?.put(Food(name = saveName, foodIdx = 0, foodVector = imageVector))
// }
ObjectBox.put(Food(name = saveName, foodIdx = 0, foodVector = imageVector))
collectList[position].let {
// it.imageVector = imageVector
it.isFinish = true
@@ -491,9 +498,9 @@ class FoodCollectionActivity : ComponentActivity() {
}
private fun initBox() {
if (box == null) {
box = ObjectBox.boxStore.boxFor(Food::class)
}
// if (box == null) {
// box = ObjectBox.boxStore.boxFor(Food::class)
// }
}
// private fun uploadAllImage() {
@@ -234,7 +234,7 @@ abstract class GoodsListActivity : ComponentActivity() {
var isFirstRecognize = true
private val takePhotoSuccessCallback: (Uri) -> Unit = { uri ->
Thread {
lifecycleScope.launch {
try {
searchFood(uri)
} catch (e: Exception) {
@@ -307,7 +307,7 @@ abstract class GoodsListActivity : ComponentActivity() {
}
var isShowRecognizeDialog = true
private fun searchFood(uri: Uri) {
private suspend fun searchFood(uri: Uri) {
ImageUtil.uriToBitmap(this, uri)?.let { bitmap ->
val newBmp = BitmapCropper.cropCenter(
original = bitmap,
@@ -4,8 +4,8 @@ import android.annotation.SuppressLint
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import androidx.activity.viewModels
import androidx.core.widget.addTextChangedListener
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import com.sw.inbound.R
@@ -14,14 +14,11 @@ import com.sw.inbound.adapter.CollectSearchAdapter
import com.sw.inbound.databinding.DialogCollectSearchBinding
import com.sw.inbound.databinding.LayoutEmptySearchBinding
import com.sw.inbound.model.response.SearchGoodsInfo
import com.sw.inbound.objbox.Food
import com.sw.inbound.objbox.ObjectBox
import com.sw.inbound.utils.ext.addOnActionSearchListener
import com.sw.inbound.utils.ext.hideKeyboard
import com.sw.inbound.utils.ext.toast
import com.sw.inbound.viewmodel.ReceiptViewModel
import io.objectbox.Box
import io.objectbox.kotlin.boxFor
import kotlinx.coroutines.launch
@SuppressLint("NotifyDataSetChanged")
class CollectSearchDialog(
@@ -69,8 +66,8 @@ class CollectSearchDialog(
dismiss()
return@setOnClickListener
}
deleteGoods(clickIndex) {
it.postDelayed({dismiss()}, 500)
showDeleteDialog(clickIndex) {
it.postDelayed({ dismiss() }, 500)
}
}
binding.rvSearch.let {
@@ -142,25 +139,34 @@ class CollectSearchDialog(
}
list[position].isSelected = true
notifyDataSetChanged()
deleteGoods(position)
showDeleteDialog(position)
}
}
}
private fun deleteGoods(position: Int, deleteAction:()-> Unit = {}) {
private fun showDeleteDialog(position: Int, deleteAction: () -> Unit = {}) {
WarnDialog(
context = context,
content = "请确认是否删除物品:${list[position].goodsName}",
confirmBlock = {
Thread {
// Thread {
// }.start()
activity?.lifecycleScope?.launch {
deleteGoods(position, deleteAction)
}
}).show()
}
private suspend fun deleteGoods(position: Int, deleteAction: () -> Unit = {}) {
activity?.runOnUiThread {
Loading.show(activity!!)
}
val name = list[position].goodsName
val filterIdList = box?.all?.filter { it.name == name }?.map { it.id }
ObjectBox.boxStore.runInTx {
box?.removeByIds(filterIdList)
}
val name = list[position].goodsName ?: ""
// val filterIdList = box?.all?.filter { it.name == name }?.map { it.id }
// ObjectBox.boxStore.runInTx {
// box?.removeByIds(filterIdList)
// }
ObjectBox.remove(name)
activity?.runOnUiThread {
Loading.dismiss()
//list.remove(list[position])
@@ -172,8 +178,6 @@ class CollectSearchDialog(
}
deleteAction()
}
}.start()
}).show()
}
private fun finishRefresh() {
@@ -186,18 +190,19 @@ class CollectSearchDialog(
}
}
private var box: Box<Food>? = null
// private var box: Box<Food>? = null
@SuppressLint("NotifyDataSetChanged")
private fun getCollectGoods(searchName: String? = null) {
if (box == null) {
box = ObjectBox.boxStore.boxFor(Food::class)
}
private fun getCollectGoods(searchName: String? = null) = activity?.lifecycleScope?.launch {
// 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 }
}
// var queryList = box?.all?.distinctBy { it.name }
// if (searchName.isNullOrBlank().not()) {
// queryList = queryList?.filter { it.name?.contains(searchName!!) == true }
// }
val queryList = ObjectBox.filter(searchName)
queryList?.forEach {
list.add((SearchGoodsInfo.Record(goodsName = it.name)))
}
@@ -15,10 +15,9 @@ import androidx.camera.view.PreviewView
import androidx.core.graphics.toColorInt
import androidx.core.view.updateLayoutParams
import androidx.core.widget.addTextChangedListener
import androidx.lifecycle.lifecycleScope
import coil.load
import coil.transform.RoundedCornersTransformation
import com.sw.inbound.utils.ext.appendText
import com.sw.inbound.utils.ext.buildSpannableString
import com.sw.inbound.R
import com.sw.inbound.activity.GoodsListActivity
import com.sw.inbound.databinding.DialogGoodsStoreBinding
@@ -35,6 +34,8 @@ import com.sw.inbound.utils.BitmapCropper
import com.sw.inbound.utils.BitmapSaver
import com.sw.inbound.utils.CameraUtils
import com.sw.inbound.utils.ImageUtil
import com.sw.inbound.utils.ext.appendText
import com.sw.inbound.utils.ext.buildSpannableString
import com.sw.inbound.utils.ext.dp
import com.sw.inbound.utils.ext.gone
import com.sw.inbound.utils.ext.hideKeyboard
@@ -43,11 +44,9 @@ import com.sw.inbound.utils.ext.roundedDecimalPlace
import com.sw.inbound.utils.ext.toast
import com.sw.inbound.utils.ext.visible
import com.sw.inbound.utils.removeTrailingZeros
import io.objectbox.Box
import io.objectbox.kotlin.boxFor
import kotlinx.coroutines.launch
import timber.log.Timber
import java.io.File
import kotlin.Int
import kotlin.math.abs
import kotlin.math.min
@@ -441,8 +440,10 @@ class GoodsStoreDialog(
}
}
}
private var imageFile: File?=null
private var box: Box<Food>? = null
private var imageFile: File? = null
// private var box: Box<Food>? = null
private fun collectGoodsVector() {
try {
ImageUtil.uriToBitmap(context, imageUri!!)?.let { bitmap ->
@@ -458,21 +459,36 @@ class GoodsStoreDialog(
)
imageFile = file
Timber.d("${this.javaClass.simpleName}-collectGoodsVector-裁剪bitmap保存文件路径:${file?.absolutePath}")
if (box == null) {
box = ObjectBox.boxStore.boxFor(Food::class)
}
Thread {
// if (box == null) {
// box = ObjectBox.boxStore.boxFor(Food::class)
// }
activity?.lifecycleScope?.launch {
try {
val imageVector = FoodModule.bitmap2FloatArray(bitmap, true)
if (imageVector != null) {
ObjectBox.boxStore.runInTx {
box?.put(Food(name = goods.goodsName + goods.goodsCode, foodIdx = 0, foodVector = imageVector))
}
val food = Food(
name = goods.goodsName + goods.goodsCode,
foodIdx = 0,
foodVector = imageVector
)
ObjectBox.put(food)
}
} catch (e: Exception) {
e.printStackTrace()
}
}.start()
}
// Thread {
// try {
// val imageVector = FoodModule.bitmap2FloatArray(bitmap, true)
// if (imageVector != null) {
// ObjectBox.boxStore.runInTx {
// box?.put(Food(name = goods.goodsName + goods.goodsCode, foodIdx = 0, foodVector = imageVector))
// }
// }
// } catch (e: Exception) {
// e.printStackTrace()
// }
// }.start()
}
} catch (e: Exception) {
e.printStackTrace()
@@ -47,12 +47,14 @@ object FoodModule {
suspend fun init(context: Context) {
withContext(Dispatchers.IO) {
module_mobile = Module.load(copyAssetToCache(context, "best_embedding_model_mobile.pt"))
ObjectBox.safeDbOp {
val box = ObjectBox.getBox<Food>()
if (box?.all?.isEmpty() == true) {
initDefFoodData(context)
}
}
}
}
// fun uri2FloatArray(uri: Uri): FloatArray? {
// return MyApp.instance?.let { context ->
@@ -174,6 +174,21 @@ object ObjectBox {
getBox<Food>()?.put(entities)
}
suspend fun filter(name:String?) = safeDbOp {
var list = getBox<Food>()?.all?.distinctBy { it.name }
if (name.isNullOrBlank().not()) {
list?.filter { it.name?.contains(name!!) == true }
}
list?:emptyList()
}
suspend fun remove(name:String) = safeDbOp {
getBox<Food>()?.run {
val filterIdList = all.filter { it.name == name }.map { it.id }
removeByIds(filterIdList)
}
}
private val dbMutex = Mutex() // 协程并发锁,保证写入操作原子性
private const val DB_DIR_NAME = "objectbox" // ObjectBox 默认数据库目录
private const val BACKUP_DIR_NAME = "objectbox_backup" // 备份目录