相机优化、对接识别名称搜索新接口、替换模型文件

This commit is contained in:
2025-10-30 15:40:36 +08:00
parent 7930ba0cd1
commit 89fabe7322
17 changed files with 333 additions and 209 deletions
Binary file not shown.
+35 -29
View File
@@ -1,38 +1,44 @@
{ {
"class_names": [ "class_names": [
"上海青", "小白菜",
"土豆", "番茄[西红柿]",
"胡萝卜", "胡萝卜(红)[金笋,丁香萝卜]",
"芹菜", "芹菜(茎)[旱芹,药芹]",
"茄子", "茄子(代表值)",
"蒜", "蒜薹(带帽)",
"西红柿", "西兰花[绿菜花]",
"青椒", "辣椒(青,尖)",
"黄瓜" "韭菜",
"马铃薯[土豆、洋芋]",
"黄瓜(鲜)[胡瓜]"
], ],
"class_to_idx": { "class_to_idx": {
"上海青": 0, "小白菜": 0,
"土豆": 1, "番茄[西红柿]": 1,
"胡萝卜": 2, "胡萝卜(红)[金笋,丁香萝卜]": 2,
"芹菜": 3, "芹菜(茎)[旱芹,药芹]": 3,
"茄子": 4, "茄子(代表值)": 4,
"蒜": 5, "蒜薹(带帽)": 5,
"西红柿": 6, "西兰花[绿菜花]": 6,
"青椒": 7, "辣椒(青,尖)": 7,
"黄瓜": 8 "韭菜": 8,
"马铃薯[土豆、洋芋]": 9,
"黄瓜(鲜)[胡瓜]": 10
}, },
"idx_to_class": { "idx_to_class": {
"0": "上海青", "0": "小白菜",
"1": "土豆", "1": "番茄[西红柿]",
"2": "胡萝卜", "2": "胡萝卜(红)[金笋,丁香萝卜]",
"3": "芹菜", "3": "芹菜(茎)[旱芹,药芹]",
"4": "茄子", "4": "茄子(代表值)",
"5": "蒜", "5": "蒜薹(带帽)",
"6": "西红柿", "6": "西兰花[绿菜花]",
"7": "青椒", "7": "辣椒(青,尖)",
"8": "黄瓜" "8": "韭菜",
"9": "马铃薯[土豆、洋芋]",
"10": "黄瓜(鲜)[胡瓜]"
}, },
"embedding_dim": 512, "embedding_dim": 512,
"total_images": 450, "total_images": 550,
"build_time": "2025-10-21T10:14:29.510882" "build_time": "2025-10-30T14:41:07.359332"
} }
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10]
@@ -7,13 +7,8 @@ import android.text.style.ForegroundColorSpan
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.camera.core.CameraSelector
import androidx.camera.core.Preview
import androidx.camera.view.CameraController
import androidx.camera.view.LifecycleCameraController
import androidx.camera.view.PreviewView import androidx.camera.view.PreviewView
import androidx.core.graphics.toColorInt import androidx.core.graphics.toColorInt
import androidx.core.view.postDelayed
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import com.shuwei.intelligent.shelves.utils.ext.appendText import com.shuwei.intelligent.shelves.utils.ext.appendText
import com.shuwei.intelligent.shelves.utils.ext.buildSpannableString import com.shuwei.intelligent.shelves.utils.ext.buildSpannableString
@@ -36,8 +31,8 @@ import com.sw.inbound.objbox.FoodModule
import com.sw.inbound.sdk.SensorScaleUtils import com.sw.inbound.sdk.SensorScaleUtils
import com.sw.inbound.utils.BitmapCropper import com.sw.inbound.utils.BitmapCropper
import com.sw.inbound.utils.BitmapSaver import com.sw.inbound.utils.BitmapSaver
import com.sw.inbound.utils.CameraUtils
import com.sw.inbound.utils.ImageUtil import com.sw.inbound.utils.ImageUtil
import com.sw.inbound.utils.PhotoCaptureHelper
import com.sw.inbound.utils.ext.dp import com.sw.inbound.utils.ext.dp
import com.sw.inbound.utils.ext.gone import com.sw.inbound.utils.ext.gone
import com.sw.inbound.utils.ext.toJsonString import com.sw.inbound.utils.ext.toJsonString
@@ -46,6 +41,7 @@ import com.sw.inbound.viewmodel.ReceiptViewModel
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import timber.log.Timber import timber.log.Timber
import java.io.File
import kotlin.math.min import kotlin.math.min
@AndroidEntryPoint @AndroidEntryPoint
@@ -75,6 +71,10 @@ abstract class GoodsListActivity : ComponentActivity() {
private lateinit var backPressedCallback: OnBackPressedCallback private lateinit var backPressedCallback: OnBackPressedCallback
private lateinit var previewView: PreviewView private lateinit var previewView: PreviewView
private val cameraUtils: CameraUtils by lazy {
CameraUtils(this@GoodsListActivity)
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityGoodsListBinding.inflate(layoutInflater) binding = ActivityGoodsListBinding.inflate(layoutInflater)
@@ -84,7 +84,7 @@ abstract class GoodsListActivity : ComponentActivity() {
isNewReceipt = intent.getBooleanExtra(IS_NEW_RECEIPT, false) isNewReceipt = intent.getBooleanExtra(IS_NEW_RECEIPT, false)
id = intent.getStringExtra(ID) ?: "" id = intent.getStringExtra(ID) ?: ""
supplierId = intent.getStringExtra(SUPPLIER_ID) ?: "" supplierId = intent.getStringExtra(SUPPLIER_ID) ?: ""
initCamera() cameraUtils.initCamera()
replaceIncludeContent() replaceIncludeContent()
initRecyclerView() initRecyclerView()
initView() initView()
@@ -131,7 +131,7 @@ abstract class GoodsListActivity : ComponentActivity() {
height = 1 height = 1
} }
} }
setPreviewController(previewView) cameraUtils.setPreviewController(previewView)
//viewModel.startSensorScale() //viewModel.startSensorScale()
binding.root.postDelayed({ binding.root.postDelayed({
@@ -151,10 +151,6 @@ abstract class GoodsListActivity : ComponentActivity() {
if (weight < 200) { if (weight < 200) {
return return
} }
if (isShowRecognizeDialog.not()) {
return
}
isShowRecognizeDialog = false
runOnUiThread { runOnUiThread {
recognizeFood() recognizeFood()
} }
@@ -167,7 +163,12 @@ abstract class GoodsListActivity : ComponentActivity() {
} }
fun recognizeFood() { fun recognizeFood() {
takePhoto(takePhotoSuccessCallback) if (isShowRecognizeDialog.not()) {
return
}
isShowRecognizeDialog = false
bindCamera()
cameraUtils.takePhoto(takePhotoSuccessCallback)
} }
private var dropdownPopup: DropdownPopup? = null private var dropdownPopup: DropdownPopup? = null
@@ -196,20 +197,23 @@ abstract class GoodsListActivity : ComponentActivity() {
) )
Timber.d("${this.javaClass.simpleName}-searchFood-裁剪bitmap保存文件路径:${file?.absolutePath}") Timber.d("${this.javaClass.simpleName}-searchFood-裁剪bitmap保存文件路径:${file?.absolutePath}")
val foodNameList = FoodModule.queryFood(newBmp) val foodNameList = FoodModule.queryFood(newBmp)
searchMultipleGoods(foodNameList) { goodsList -> viewModel.recognizeGoodsList(foodNameList) { goodsList ->
//if (goodsList.size != foodNameList.size) {
// toast("系统异常,请稍后重试!!!")
// return@searchMultipleGoods
//}
handleGoodsResult( handleGoodsResult(
if (goodsList.size == foodNameList.size) goodsList goodsList = if (goodsList.size == foodNameList.size) goodsList else mutableListOf(),
else mutableListOf() file = file,
name = foodNameList.joinToString(",")
) )
} }
// searchMultipleGoods(foodNameList) { goodsList ->
// handleGoodsResult(
// if (goodsList.size == foodNameList.size) goodsList
// else mutableListOf()
// )
// }
} }
} }
private fun handleGoodsResult(goodsList: MutableList<SearchGoodsInfo.Record>) { private fun handleGoodsResult(goodsList: List<SearchGoodsInfo.Record>, file: File?, name: String) {
runOnUiThread { runOnUiThread {
if (goodsRecognizeDialog?.isShowing == true) { if (goodsRecognizeDialog?.isShowing == true) {
isShowRecognizeDialog = false isShowRecognizeDialog = false
@@ -217,7 +221,9 @@ abstract class GoodsListActivity : ComponentActivity() {
} }
goodsRecognizeDialog = GoodsRecognizeDialog( goodsRecognizeDialog = GoodsRecognizeDialog(
context = this, context = this,
list = goodsList list = goodsList.toMutableList(),
goodsFile = file,
goodsName = name
) { ) {
GoodsSearchDialog(this).show() GoodsSearchDialog(this).show()
}.apply { }.apply {
@@ -237,61 +243,6 @@ abstract class GoodsListActivity : ComponentActivity() {
private var goodsRecognizeDialog: GoodsRecognizeDialog? = null private var goodsRecognizeDialog: GoodsRecognizeDialog? = null
private var cameraController: LifecycleCameraController? = null
private var photoCaptureHelper: PhotoCaptureHelper? = null
private var isCameraReady = false
fun takePhoto(callback: (Uri) -> Unit) {
cameraController?.let {
if (photoCaptureHelper == null) {
initCaptureHelper()
}
}
photoCaptureHelper?.let {
it.addSuccessCallback(callback)
it.takePhoto()
}
}
fun initCaptureHelper() {
photoCaptureHelper = PhotoCaptureHelper(
context = this,
cameraController = cameraController!!,
onSuccess = {},
onError = { msg ->
//toast(msg)
}
)
}
fun setPreviewController(previewView: PreviewView?) {
previewView?.controller = cameraController
}
fun initCamera() {
if (cameraController == null) {
cameraController = LifecycleCameraController(this).apply {
// 必须设置有效的用例
setEnabledUseCases(
CameraController.IMAGE_CAPTURE or
CameraController.VIDEO_CAPTURE
)
cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
}
cameraController?.bindToLifecycle(this)
}
// if (isCameraReady.not()) {
// try {
// cameraController!!.initializationFuture.addListener({
// isCameraReady = true
// Timber.d("Camera initialized successfully")
// }, ContextCompat.getMainExecutor(this))
// } catch (e: Exception) {
// Timber.d("Camera initialized error = ${e.message}")
// }
// }
}
private fun replaceIncludeContent() { private fun replaceIncludeContent() {
binding.flContainer.removeAllViews() binding.flContainer.removeAllViews()
if (isReceiptPage) { if (isReceiptPage) {
@@ -305,10 +256,23 @@ abstract class GoodsListActivity : ComponentActivity() {
} }
} }
override fun onPause() {
super.onPause()
unBindCamera()
}
fun bindCamera() {
cameraUtils.bind()
}
fun unBindCamera() {
cameraUtils.unbind()
}
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
cameraController?.unbind() //cameraController?.unbind()
isCameraReady = false //isCameraReady = false
} }
fun addGoods(it: GoodsInfo) { fun addGoods(it: GoodsInfo) {
@@ -396,6 +360,10 @@ abstract class GoodsListActivity : ComponentActivity() {
goodsInfo.warehouseName = binding.tvWareHouse.text.toString() goodsInfo.warehouseName = binding.tvWareHouse.text.toString()
} }
} }
if (submitData.isEmpty()) {
toast("请选择收货物品")
return
}
val uploadInfo = UploadInfo( val uploadInfo = UploadInfo(
id = id, id = id,
supplierId = supplierId, supplierId = supplierId,
@@ -463,7 +431,4 @@ abstract class GoodsListActivity : ComponentActivity() {
viewModel.createNewGoods(param = param, callback = callback) viewModel.createNewGoods(param = param, callback = callback)
} }
} }
private var preview: Preview? = null
} }
@@ -17,6 +17,7 @@ import com.sw.inbound.ext.toSafeBigDecimal
import com.sw.inbound.ext.toSafeFloat import com.sw.inbound.ext.toSafeFloat
import com.sw.inbound.model.request.GoodsAddParam import com.sw.inbound.model.request.GoodsAddParam
import com.sw.inbound.model.response.DictType import com.sw.inbound.model.response.DictType
import com.sw.inbound.utils.CameraUtils
import com.sw.inbound.utils.ext.dp import com.sw.inbound.utils.ext.dp
import com.sw.inbound.utils.ext.gone import com.sw.inbound.utils.ext.gone
import com.sw.inbound.utils.ext.toast import com.sw.inbound.utils.ext.toast
@@ -40,13 +41,16 @@ class AddGoodsDialog(
private val dropdownList: MutableList<DictType> = mutableListOf() private val dropdownList: MutableList<DictType> = mutableListOf()
private var activity: GoodsListActivity? = null private var activity: GoodsListActivity? = null
private val cameraUtils: CameraUtils by lazy {
CameraUtils(activity!!)
}
override fun show() { override fun show() {
super.show() super.show()
cameraUtils.setPreviewController(previewView)
cameraUtils.bind()
window?.decorView?.postDelayed({ window?.decorView?.postDelayed({
activity?.setPreviewController(previewView) binding.llCameraFlag.gone()
}, 600) }, 1000)
} }
private val takePhotoCallback: (Uri) -> Unit = { uri -> private val takePhotoCallback: (Uri) -> Unit = { uri ->
@@ -59,6 +63,11 @@ class AddGoodsDialog(
private lateinit var previewView: PreviewView private lateinit var previewView: PreviewView
override fun initView() { override fun initView() {
activity = getReceiptActivity() activity = getReceiptActivity()
activity?.unBindCamera()
setOnDismissListener {
cameraUtils.unbind()
}
cameraUtils.initCamera()
val previewBinding = val previewBinding =
LayoutCameraPreviewBinding.inflate(LayoutInflater.from(context), binding.flCameraPreview) LayoutCameraPreviewBinding.inflate(LayoutInflater.from(context), binding.flCameraPreview)
previewBinding.previewView.let { previewBinding.previewView.let {
@@ -84,7 +93,7 @@ class AddGoodsDialog(
binding.flCameraPreview.visible() binding.flCameraPreview.visible()
} }
binding.btnTakePhoto.setOnClickListener { binding.btnTakePhoto.setOnClickListener {
activity?.takePhoto(takePhotoCallback) cameraUtils.takePhoto(takePhotoCallback)
} }
binding.btnUploadImg.gone() binding.btnUploadImg.gone()
binding.btnUploadImg.setOnClickListener { binding.btnUploadImg.setOnClickListener {
@@ -5,15 +5,19 @@ import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import coil.load
import com.sw.inbound.activity.GoodsListActivity import com.sw.inbound.activity.GoodsListActivity
import com.sw.inbound.adapter.RecognizeAdapter import com.sw.inbound.adapter.RecognizeAdapter
import com.sw.inbound.databinding.DialogGoodsRecognizeBinding import com.sw.inbound.databinding.DialogGoodsRecognizeBinding
import com.sw.inbound.databinding.LayoutEmptyRecognizeBinding import com.sw.inbound.databinding.LayoutEmptyRecognizeBinding
import com.sw.inbound.model.response.SearchGoodsInfo import com.sw.inbound.model.response.SearchGoodsInfo
import java.io.File
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
class GoodsRecognizeDialog( class GoodsRecognizeDialog(
context: Context, context: Context,
var goodsFile: File?=null,
var goodsName: String?=null,
var list: MutableList<SearchGoodsInfo.Record>, var list: MutableList<SearchGoodsInfo.Record>,
var findGoods: () -> Unit var findGoods: () -> Unit
) : BaseDialog(context) { ) : BaseDialog(context) {
@@ -26,6 +30,12 @@ class GoodsRecognizeDialog(
return binding.root return binding.root
} }
// override fun show() {
// super.show()
// binding.ivGoods.load(goodsFile)
// binding.tvGoods.text = goodsName
// }
private val recognizeAdapter by lazy { private val recognizeAdapter by lazy {
RecognizeAdapter(list).apply { RecognizeAdapter(list).apply {
isStateViewEnable = true isStateViewEnable = true
@@ -47,7 +47,12 @@ class GoodsSearchDialog(
binding.etInputGoods.setText(goodsName) binding.etInputGoods.setText(goodsName)
searchGoods() searchGoods()
} }
).show() ).apply {
setOnDismissListener {
//activity?.bindCamera()
}
show()
}
} }
binding.btnConfirm.setOnClickListener { binding.btnConfirm.setOnClickListener {
if (list.isEmpty()) { if (list.isEmpty()) {
@@ -0,0 +1,5 @@
package com.sw.inbound.model.response
data class RecGoodsName(
var nameList: List<String>
)
@@ -11,13 +11,12 @@ import com.sw.inbound.model.response.DictType
import com.sw.inbound.model.response.EquipmentInfo import com.sw.inbound.model.response.EquipmentInfo
import com.sw.inbound.model.response.GoodsType import com.sw.inbound.model.response.GoodsType
import com.sw.inbound.model.response.PurchaseInfo import com.sw.inbound.model.response.PurchaseInfo
import com.sw.inbound.model.response.RecGoodsName
import com.sw.inbound.model.response.SearchGoodsInfo import com.sw.inbound.model.response.SearchGoodsInfo
import com.sw.inbound.model.response.StorageType import com.sw.inbound.model.response.StorageType
import com.sw.inbound.model.response.SupplierInfo import com.sw.inbound.model.response.SupplierInfo
import com.sw.inbound.model.response.SupplierResponse
import com.sw.inbound.model.response.User import com.sw.inbound.model.response.User
import okhttp3.MultipartBody import okhttp3.MultipartBody
import okhttp3.RequestBody
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Header import retrofit2.http.Header
@@ -162,11 +161,17 @@ interface ApiService {
suspend fun searchGoodsInfoList( suspend fun searchGoodsInfoList(
@Url url: String = "${GlobalData.appBaseUrl}${apiPrefix}/pad/goodsInfoList", @Url url: String = "${GlobalData.appBaseUrl}${apiPrefix}/pad/goodsInfoList",
@Body param:Map<String, String> @Body param:Map<String, String>
// @Query("goodsName") goodsName: String,
// @Query("pageNum" ) pageNo: Int,
// @Query("pageSize" ) pageSize: Int,
): ApiResponse<SearchGoodsInfo> ): ApiResponse<SearchGoodsInfo>
/**
* 物品列表(识别图片后根据名称查询,名称支持多个)
*/
@POST
suspend fun recognizeGoodsList(
@Url url: String = "${GlobalData.appBaseUrl}${apiPrefix}/pad/goodsList",
@Body recGoodsName: RecGoodsName
): ApiResponse<List<SearchGoodsInfo.Record>>
/** /**
* 自采添加商品 * 自采添加商品
*/ */
@@ -33,9 +33,9 @@ object FoodModule {
Thread { Thread {
module_mobile = Module.load(copyAssetToCache(context, "best_embedding_model_mobile.pt")) module_mobile = Module.load(copyAssetToCache(context, "best_embedding_model_mobile.pt"))
box = ObjectBox.boxStore.boxFor(Food::class) box = ObjectBox.boxStore.boxFor(Food::class)
// if (box.all.isNotEmpty()) { if (box.all.isNotEmpty()) {
// box.removeAll() box.removeAll()
// } }
if (box.all.isEmpty()) { if (box.all.isEmpty()) {
initFoodData(context) initFoodData(context)
} }
@@ -11,16 +11,14 @@ import com.sw.inbound.model.response.DictType
import com.sw.inbound.model.response.EquipmentInfo import com.sw.inbound.model.response.EquipmentInfo
import com.sw.inbound.model.response.GoodsType import com.sw.inbound.model.response.GoodsType
import com.sw.inbound.model.response.PurchaseInfo import com.sw.inbound.model.response.PurchaseInfo
import com.sw.inbound.model.response.RecGoodsName
import com.sw.inbound.model.response.SearchGoodsInfo import com.sw.inbound.model.response.SearchGoodsInfo
import com.sw.inbound.model.response.StorageType import com.sw.inbound.model.response.StorageType
import com.sw.inbound.model.response.SupplierInfo import com.sw.inbound.model.response.SupplierInfo
import com.sw.inbound.model.response.SupplierResponse
import com.sw.inbound.model.response.User import com.sw.inbound.model.response.User
import com.sw.inbound.network.api.ApiService import com.sw.inbound.network.api.ApiService
import com.sw.inbound.utils.ContextUtils import com.sw.inbound.utils.ContextUtils
import com.sw.inbound.utils.FileUtils import com.sw.inbound.utils.FileUtils
import okhttp3.MultipartBody
import okhttp3.RequestBody
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@@ -162,6 +160,12 @@ class RemoteRepository @Inject constructor(
} }
} }
suspend fun recognizeGoodsList(nameList: List<String>): ApiResponse<List<SearchGoodsInfo.Record>> {
return safeApiCall {
apiService.recognizeGoodsList(recGoodsName = RecGoodsName(nameList))
}
}
/** /**
* 自采购-添加商品 * 自采购-添加商品
*/ */
@@ -0,0 +1,76 @@
package com.sw.inbound.utils
import android.net.Uri
import androidx.camera.core.CameraSelector
import androidx.camera.view.CameraController
import androidx.camera.view.LifecycleCameraController
import androidx.camera.view.PreviewView
import com.sw.inbound.activity.GoodsListActivity
class CameraUtils(private var activity: GoodsListActivity) {
private var cameraController: LifecycleCameraController? = null
private var photoCaptureHelper: PhotoCaptureHelper? = null
// private var isCameraReady = false
fun takePhoto(callback: (Uri) -> Unit) {
cameraController?.let {
if (photoCaptureHelper == null) {
initCaptureHelper()
}
}
photoCaptureHelper?.let {
it.addSuccessCallback(callback)
it.takePhoto()
}
}
private fun initCaptureHelper() {
photoCaptureHelper = PhotoCaptureHelper(
context = activity,
cameraController = cameraController!!,
onSuccess = {},
onError = { msg ->
//toast(msg)
}
)
}
fun setPreviewController(previewView: PreviewView?) {
if (previewView?.controller == null) {
previewView?.controller = cameraController
}
}
fun initCamera() {
if (cameraController == null) {
cameraController = LifecycleCameraController(activity).apply {
// 必须设置有效的用例
setEnabledUseCases(
CameraController.IMAGE_CAPTURE
// or CameraController.VIDEO_CAPTURE
)
cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
}
//cameraController?.bindToLifecycle(activity)
}
// if (isCameraReady.not()) {
// try {
// cameraController!!.initializationFuture.addListener({
// isCameraReady = true
// Timber.d("Camera initialized successfully")
// }, ContextCompat.getMainExecutor(this))
// } catch (e: Exception) {
// Timber.d("Camera initialized error = ${e.message}")
// }
// }
}
fun bind() {
cameraController?.bindToLifecycle(activity)
}
fun unbind() {
cameraController?.unbind()
}
}
@@ -148,27 +148,27 @@ abstract class BaseViewModel(
/** /**
* 获取识别列表 * 获取识别列表
*/ */
fun getIdentityList(photoUri: Uri) { // fun getIdentityList(photoUri: Uri) {
Timber.d("getIdentityList") // Timber.d("getIdentityList")
launchWithLoading { // launchWithLoading {
_searchListItems.value = emptyList<SearchGoodsInfo.Record>() // _searchListItems.value = emptyList<SearchGoodsInfo.Record>()
// 有收货列表,则返回收货列表 // // 有收货列表,则返回收货列表
if (_receiptList.value.isNotEmpty()) { // if (_receiptList.value.isNotEmpty()) {
_identityListItems.value = _receiptList.value // _identityListItems.value = _receiptList.value
if (_identityListItems.value.isNotEmpty()) { // if (_identityListItems.value.isNotEmpty()) {
handleIdentityItem(_identityListItems.value[0]) // handleIdentityItem(_identityListItems.value[0])
} // }
} else { // } else {
val response = repository.searchGoodsInfoList("", 1, 10) // val response = repository.searchGoodsInfoList("", 1, 10)
if (parseResponse(response)) { // if (parseResponse(response)) {
_identityListItems.value = response.result?.records ?: emptyList() // _identityListItems.value = response.result?.records ?: emptyList()
if (_identityListItems.value.isNotEmpty()) { // if (_identityListItems.value.isNotEmpty()) {
handleIdentityItem(_identityListItems.value[0]) // handleIdentityItem(_identityListItems.value[0])
} // }
} // }
} // }
} // }
} // }
/** /**
* 处理识别后要选中的值 * 处理识别后要选中的值
@@ -190,43 +190,54 @@ abstract class BaseViewModel(
): List<SearchGoodsInfo.Record> { ): List<SearchGoodsInfo.Record> {
val response = repository.searchGoodsInfoList(goodsName, pageNo, pageSize) val response = repository.searchGoodsInfoList(goodsName, pageNo, pageSize)
return if (parseResponse(response)) { return if (parseResponse(response)) {
response.result?.records ?: mutableListOf() response.result?.records ?: listOf()
} else { } else {
mutableListOf() listOf()
} }
} }
fun searchGoodsInfoList( fun recognizeGoodsList(
goodsName: String, nameList: List<String>,
pageNo: Int = 1, callback: (List<SearchGoodsInfo.Record>) -> Unit
pageSize: Int = 10,
searchCallback: ((pageNo: Int, List<SearchGoodsInfo.Record>) -> Unit)? = null
) { ) {
launch { launch {
if (pageNo > 1) { val response = repository.recognizeGoodsList(nameList)
_isMoreLoading.value = true val list = if (parseResponse(response)) response.data?:listOf() else listOf()
} callback(list)
_identityListItems.value = emptyList<SearchGoodsInfo.Record>()
val response = repository.searchGoodsInfoList(goodsName, pageNo, pageSize)
_isMoreLoading.value = false
if (parseResponse(response)) {
val data = response.result
data?.records?.let {
_canLoadMore.value = data.records.size >= pageSize
if (pageNo > 1) {
_searchListItems.value = _searchListItems.value + data.records
} else {
_searchListItems.value = data.records
}
searchCallback?.invoke(pageNo, _searchListItems.value)
}
} else {
_searchListItems.value = emptyList<SearchGoodsInfo.Record>()
searchCallback?.invoke(pageNo, _searchListItems.value)
}
} }
} }
// fun searchGoodsInfoList(
// goodsName: String,
// pageNo: Int = 1,
// pageSize: Int = 10,
// searchCallback: ((pageNo: Int, List<SearchGoodsInfo.Record>) -> Unit)? = null
// ) {
// launch {
// if (pageNo > 1) {
// _isMoreLoading.value = true
// }
// _identityListItems.value = emptyList<SearchGoodsInfo.Record>()
// val response = repository.searchGoodsInfoList(goodsName, pageNo, pageSize)
// _isMoreLoading.value = false
// if (parseResponse(response)) {
// val data = response.result
// data?.records?.let {
// _canLoadMore.value = data.records.size >= pageSize
// if (pageNo > 1) {
// _searchListItems.value = _searchListItems.value + data.records
// } else {
// _searchListItems.value = data.records
// }
// searchCallback?.invoke(pageNo, _searchListItems.value)
// }
// } else {
// _searchListItems.value = emptyList<SearchGoodsInfo.Record>()
// searchCallback?.invoke(pageNo, _searchListItems.value)
// }
// }
// }
fun getDictType() { fun getDictType() {
Timber.d("获取所有字典列表") Timber.d("获取所有字典列表")
launchWithLoading { launchWithLoading {
@@ -74,8 +74,8 @@
<FrameLayout <FrameLayout
android:id="@+id/flCameraPreview" android:id="@+id/flCameraPreview"
android:layout_width="1dp" android:layout_width="wrap_content"
android:layout_height="1dp" android:layout_height="wrap_content"
android:layout_marginStart="5dp" /> android:layout_marginStart="5dp" />
<Space <Space
+33 -28
View File
@@ -40,37 +40,13 @@
app:layout_constraintTop_toTopOf="@+id/llGoodsInfo" app:layout_constraintTop_toTopOf="@+id/llGoodsInfo"
android:background="@drawable/bg_white_radius10_stroke2_dash"> android:background="@drawable/bg_white_radius10_stroke2_dash">
<!-- <LinearLayout--> <androidx.cardview.widget.CardView
<!-- android:id="@+id/llStartPreview"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_gravity="center"-->
<!-- android:gravity="center"-->
<!-- android:orientation="vertical"-->
<!-- android:visibility="gone">-->
<!-- <ImageView-->
<!-- android:layout_width="96dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_gravity="center"-->
<!-- android:scaleType="centerCrop"-->
<!-- android:src="@mipmap/ic_camera_gray"-->
<!-- 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="24sp" />-->
<!-- </LinearLayout>-->
<FrameLayout
android:id="@+id/flCameraPreview" android:id="@+id/flCameraPreview"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center"/> android:layout_gravity="center"
app:cardCornerRadius="10dp"
app:cardElevation="0dp"/>
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:id="@+id/cvImageBg" android:id="@+id/cvImageBg"
@@ -90,6 +66,35 @@
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
<LinearLayout
android:id="@+id/llCameraFlag"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:background="@drawable/bg_white_radius10_stroke2_dash"
android:orientation="vertical">
<ImageView
android:layout_width="96dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:scaleType="centerCrop"
android:src="@mipmap/ic_camera_gray"
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="24sp" />
</LinearLayout>
</FrameLayout> </FrameLayout>
<Button <Button
@@ -95,4 +95,27 @@
app:layout_constraintBottom_toTopOf="@id/dividerLine" app:layout_constraintBottom_toTopOf="@id/dividerLine"
android:layout_marginBottom="60dp"/> android:layout_marginBottom="60dp"/>
<!-- <LinearLayout-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:orientation="horizontal"-->
<!-- android:gravity="center_vertical"-->
<!-- android:layout_marginStart="20dp"-->
<!-- app:layout_constraintStart_toEndOf="@id/btnAgainRecognize"-->
<!-- app:layout_constraintTop_toTopOf="@id/btnAgainRecognize"-->
<!-- app:layout_constraintBottom_toBottomOf="@id/btnAgainRecognize"-->
<!-- app:layout_constraintEnd_toEndOf="parent">-->
<!-- <ImageView-->
<!-- android:id="@+id/ivGoods"-->
<!-- android:layout_width="100dp"-->
<!-- android:layout_height="wrap_content"/>-->
<!-- <TextView-->
<!-- android:id="@+id/tvGoods"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"/>-->
<!-- </LinearLayout>-->
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>