采集向量数据接口调试
This commit is contained in:
Generated
+2
-2
@@ -4,10 +4,10 @@
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DropdownSelection timestamp="2025-12-26T06:22:06.750685100Z">
|
||||
<DropdownSelection timestamp="2025-12-31T07:06:25.405532800Z">
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="Default" identifier="serial=192.168.1.2:5555;connection=bfafe870" />
|
||||
<DeviceId pluginId="Default" identifier="serial=192.168.1.45:5555;connection=52f5d52f" />
|
||||
</handle>
|
||||
</Target>
|
||||
</DropdownSelection>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"entities": [
|
||||
{
|
||||
"id": "1:594331511073099531",
|
||||
"lastPropertyId": "4:1528837175750321569",
|
||||
"lastPropertyId": "9:4394472487596452023",
|
||||
"name": "Food",
|
||||
"properties": [
|
||||
{
|
||||
@@ -14,22 +14,37 @@
|
||||
"type": 6,
|
||||
"flags": 1
|
||||
},
|
||||
{
|
||||
"id": "2:340951913928211738",
|
||||
"name": "name",
|
||||
"type": 9
|
||||
},
|
||||
{
|
||||
"id": "3:5484846939472684412",
|
||||
"name": "foodIdx",
|
||||
"type": 5
|
||||
},
|
||||
{
|
||||
"id": "4:1528837175750321569",
|
||||
"name": "foodVector",
|
||||
"indexId": "1:6010838397086628487",
|
||||
"type": 28,
|
||||
"flags": 8
|
||||
},
|
||||
{
|
||||
"id": "5:5561277398349179032",
|
||||
"name": "collectId",
|
||||
"type": 9
|
||||
},
|
||||
{
|
||||
"id": "6:8313847979709019172",
|
||||
"name": "foodId",
|
||||
"type": 9
|
||||
},
|
||||
{
|
||||
"id": "7:1375536603452792462",
|
||||
"name": "foodName",
|
||||
"type": 9
|
||||
},
|
||||
{
|
||||
"id": "8:7942065542192169896",
|
||||
"name": "version",
|
||||
"type": 9
|
||||
},
|
||||
{
|
||||
"id": "9:4394472487596452023",
|
||||
"name": "otherField",
|
||||
"type": 9
|
||||
}
|
||||
],
|
||||
"relations": []
|
||||
@@ -43,7 +58,10 @@
|
||||
"modelVersionParserMinimum": 5,
|
||||
"retiredEntityUids": [],
|
||||
"retiredIndexUids": [],
|
||||
"retiredPropertyUids": [],
|
||||
"retiredPropertyUids": [
|
||||
340951913928211738,
|
||||
5484846939472684412
|
||||
],
|
||||
"retiredRelationUids": [],
|
||||
"version": 1
|
||||
}
|
||||
@@ -20,8 +20,13 @@ object GlobalData {
|
||||
/**
|
||||
* 具体业务baseurl
|
||||
*/
|
||||
// var appBaseUrl: String = "http://192.168.1.201:14801"
|
||||
var appBaseUrl: String = "https://dev.yixiong-tech.com:8081"
|
||||
var appBaseUrl: String = "http://192.168.1.201:14801"
|
||||
// var appBaseUrl: String = "https://dev.yixiong-tech.com:8081"
|
||||
|
||||
/**
|
||||
* 菜品识别模型版本号,后边以接口返回为准
|
||||
*/
|
||||
var foodModelVersion: String = "1.0.0"
|
||||
|
||||
/**
|
||||
* 横排数量
|
||||
|
||||
@@ -4,10 +4,10 @@ import android.annotation.SuppressLint
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
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.databinding.LayoutEmptySearchBinding
|
||||
import com.sw.dualscreen.dialog.WarnDialog
|
||||
import com.sw.dualscreen.ext.addOnActionSearchListener
|
||||
import com.sw.dualscreen.ext.gone
|
||||
@@ -15,12 +15,10 @@ import com.sw.dualscreen.ext.hideKeyboard
|
||||
import com.sw.dualscreen.ext.visible
|
||||
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
|
||||
|
||||
class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
|
||||
|
||||
@@ -42,7 +40,7 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
|
||||
CollectedFoodNewAdapter(list).apply {
|
||||
// isStateViewEnable = true
|
||||
addOnItemChildClickListener(R.id.ivDeleteFood) { _, _, position ->
|
||||
deleteGoods(position)
|
||||
loadDeleteDialog(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -182,17 +180,22 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
|
||||
//}
|
||||
}
|
||||
|
||||
private fun deleteGoods(position: Int) {
|
||||
private fun loadDeleteDialog(position: Int) {
|
||||
WarnDialog(
|
||||
context = context,
|
||||
content = "请确认是否删除菜品:${list[position].foodName}?",
|
||||
confirmBlock = {
|
||||
deleteGoods(position)
|
||||
}).show()
|
||||
}
|
||||
|
||||
private fun deleteGoods(position: Int) {
|
||||
val food = list[position]
|
||||
showWaitingDialog("加载中……")
|
||||
viewModel.deleteCollectFood(food.foodId, GlobalData.foodModelVersion) { deleteSuccess ->
|
||||
if (deleteSuccess) {
|
||||
Thread {
|
||||
runOnUiThread {
|
||||
showWaitingDialog("加载中……")
|
||||
}
|
||||
val name = list[position].foodName
|
||||
val filterIdList = box?.all?.filter { it.name == name }?.map { it.id }
|
||||
val filterIdList = box?.all?.filter { it.foodName == food.foodName }?.map { it.id }
|
||||
box?.removeByIds(filterIdList)
|
||||
runOnUiThread {
|
||||
hideWaitingDialog()
|
||||
@@ -203,7 +206,8 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
|
||||
}
|
||||
}
|
||||
}.start()
|
||||
}).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,37 +1,86 @@
|
||||
package com.sw.dualscreen.activity
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.sw.dualscreen.GlobalData
|
||||
import com.sw.dualscreen.databinding.ActivityInitBinding
|
||||
import com.sw.dualscreen.utils.QRCodeUtil
|
||||
import com.sw.dualscreen.model.response.FoodVector
|
||||
import com.sw.dualscreen.objbox.Food
|
||||
import com.sw.dualscreen.objbox.FoodModule
|
||||
import com.sw.dualscreen.objbox.ObjectBox
|
||||
import com.sw.dualscreen.viewmodel.BaseViewModel
|
||||
import com.sw.dualscreen.viewmodel.DeviceViewModel
|
||||
import com.sw.plate.utils.AppUtil
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.launch
|
||||
import com.sw.dualscreen.viewmodel.UserViewModel
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.kotlin.boxFor
|
||||
|
||||
class InitActivity : BaseActivity<ActivityInitBinding>() {
|
||||
private val viewModel by viewModels<DeviceViewModel>()
|
||||
companion object {
|
||||
private const val PAGE_SIZE = 100
|
||||
}
|
||||
private val userViewModel by viewModels<UserViewModel>()
|
||||
private var pageNum = 1
|
||||
|
||||
override fun getViewModel(): BaseViewModel {
|
||||
return viewModel
|
||||
return userViewModel
|
||||
}
|
||||
|
||||
override fun inflateViewBinding(): ActivityInitBinding {
|
||||
return ActivityInitBinding.inflate(layoutInflater)
|
||||
}
|
||||
|
||||
private fun getCollectedFoodVector(block:()-> Unit) {
|
||||
userViewModel.getCollectedFoodVector(param = mutableMapOf(
|
||||
"pageNum" to "$pageNum",
|
||||
"pageSize" to "$PAGE_SIZE",
|
||||
"version" to GlobalData.foodModelVersion
|
||||
)) { items ->
|
||||
val list = items ?: emptyList()
|
||||
if (pageNum == 1 && list.isEmpty()) {
|
||||
block()
|
||||
return@getCollectedFoodVector
|
||||
}
|
||||
if (list.size >= PAGE_SIZE) {
|
||||
saveFoodVector(list)
|
||||
pageNum++
|
||||
getCollectedFoodVector(block)
|
||||
return@getCollectedFoodVector
|
||||
}
|
||||
if (list.isNotEmpty()) {
|
||||
saveFoodVector(list)
|
||||
block()
|
||||
}
|
||||
}
|
||||
}
|
||||
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()
|
||||
Food(
|
||||
collectId = vt.id,
|
||||
foodId = vt.foodId,
|
||||
foodName = vt.foodName,
|
||||
version = vt.version,
|
||||
foodVector = foodVector
|
||||
)
|
||||
}
|
||||
box.put(vectorList)
|
||||
}
|
||||
|
||||
override fun initialize() {
|
||||
super.initialize()
|
||||
binding.root.postDelayed({
|
||||
goMainActivity()
|
||||
}, 1000)
|
||||
showWaitingDialog("加载中……")
|
||||
FoodModule.init(this) {
|
||||
getCollectedFoodVector {
|
||||
runOnUiThread {
|
||||
hideWaitingDialog()
|
||||
binding.root.postDelayed({
|
||||
goMainActivity()
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// val isSuccess = viewModel.checkEquipmentInfo()
|
||||
// if (isSuccess) {
|
||||
// goMainActivity()
|
||||
@@ -50,12 +99,12 @@ class InitActivity : BaseActivity<ActivityInitBinding>() {
|
||||
|
||||
override fun registerDataChange() {
|
||||
super.registerDataChange()
|
||||
lifecycleScope.launch {
|
||||
viewModel.deviceInfoResult.drop(1).collect {
|
||||
if (it != true) return@collect
|
||||
goMainActivity()
|
||||
}
|
||||
}
|
||||
// lifecycleScope.launch {
|
||||
// viewModel.deviceInfoResult.drop(1).collect {
|
||||
// if (it != true) return@collect
|
||||
// goMainActivity()
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
private fun goMainActivity() {
|
||||
|
||||
@@ -137,7 +137,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
super.initialize()
|
||||
isPageVisible = true
|
||||
addBackEventListener()
|
||||
FoodModule.init(this)
|
||||
//FoodModule.init(this)
|
||||
initView()
|
||||
viewModel.getUserFaceCache(pageNo = 1)
|
||||
//setupSecondaryDisplay()
|
||||
@@ -353,77 +353,37 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
LightManager.closeRedLight()
|
||||
LightManager.openGreenLight()
|
||||
showWaitingDialog("识别中,请稍后……")
|
||||
takePhoto(successCallback = { photoUri ->
|
||||
lastPhotoUri = photoUri
|
||||
Timber.tag(TAG).d("main,takePhoto耗时:${System.currentTimeMillis() - startTime}")
|
||||
startTime = System.currentTimeMillis()
|
||||
Timber.d("registerDataChange photoUri = ${photoUri.path}")
|
||||
// viewModel.getIdentifiedFoodList()
|
||||
|
||||
ImageUtil.uriToBitmap(this, photoUri)?.let { bitmap ->
|
||||
Timber.tag(TAG).d("main,uriToBitmap耗时:${System.currentTimeMillis() - startTime}")
|
||||
startTime = System.currentTimeMillis()
|
||||
Timber.d("registerDataChange photoUri 拿到bitmap")
|
||||
// val bmp = BitmapCropper.cropCenter(bitmap, 1300, 900)
|
||||
Timber.d("registerDataChange photoUri bitmap裁剪完成")
|
||||
val file = BitmapSaver.saveToAppFilesDir(
|
||||
bitmap,
|
||||
this,
|
||||
"IMG_CROP_${System.currentTimeMillis()}.jpg"
|
||||
)
|
||||
Timber.tag(TAG)
|
||||
.d("main,saveToAppFilesDir耗时:${System.currentTimeMillis() - startTime}")
|
||||
startTime = System.currentTimeMillis()
|
||||
Timber.d("registerDataChange photoUri bitmap保存文件路径:${file?.absolutePath}")
|
||||
//val nameList = FoodModule.queryFood(bitmap)
|
||||
val scoreList = FoodModule.getFoodScoreList(bitmap)
|
||||
val recDataJson = GsonUtils.toJson(scoreList)
|
||||
Timber.tag(TAG)
|
||||
.d("main,getFoodScoreList耗时:${System.currentTimeMillis() - startTime}")
|
||||
startTime = System.currentTimeMillis()
|
||||
Timber.d("registerDataChange photoUri 拿到识别数据:$recDataJson")
|
||||
val nameList = scoreList.map { it.name }
|
||||
val foodName = nameList.joinToString(separator = ",")
|
||||
|
||||
hideWaitingDialog()
|
||||
Timber.d("registerDataChange photoUri 识别数据名称:$foodName")
|
||||
if (TextUtils.isEmpty(foodName)) {
|
||||
LightManager.closeGreenLight()
|
||||
LightManager.closeRedLight()
|
||||
//binding.layoutRescan.visibility = View.VISIBLE
|
||||
binding.flPay.visible()
|
||||
binding.tvToSearch.let {
|
||||
it.text = "未识别到,去手动搜索"
|
||||
it.visible()
|
||||
}
|
||||
} else {
|
||||
//binding.layoutRescan.visibility = View.GONE
|
||||
showWaitingDialog("正在查询菜品信息,请稍后……")
|
||||
viewModel.getFoodInfo(foodName) { list ->
|
||||
runOnUiThread {
|
||||
hideWaitingDialog()
|
||||
Timber.tag(TAG)
|
||||
.d("main,getFoodInfo耗时:${System.currentTimeMillis() - startTime}")
|
||||
startTime = System.currentTimeMillis()
|
||||
updateFoodInfo(list.toMutableList(), scoreList)
|
||||
}
|
||||
takePhoto(
|
||||
successCallback = { photoUri ->
|
||||
onTakePhotoSuccess(photoUri)
|
||||
failCount = 0
|
||||
}, failureCallback = {
|
||||
runOnUiThread {
|
||||
if (failCount >= 10) {
|
||||
ToastUtils.showToast("相机异常,请稍后重试")
|
||||
shutdownCamera()
|
||||
setupCamera()
|
||||
return@runOnUiThread
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}, failureCallback = {
|
||||
hideWaitingDialog()
|
||||
failCount++
|
||||
hideWaitingDialog()
|
||||
// ToastUtils.showToast("拍照异常,请手动放置后重新识别")
|
||||
|
||||
shutdownCamera()
|
||||
setupCamera()
|
||||
shutdownCamera()
|
||||
setupCamera()
|
||||
|
||||
binding.root.postDelayed({
|
||||
recognizeFood()
|
||||
}, 500)
|
||||
})
|
||||
binding.root.postDelayed({
|
||||
recognizeFood()
|
||||
}, 1000)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 识别相机异常次数
|
||||
*/
|
||||
private var failCount = 0
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun updateFoodInfo(list: MutableList<FoodInfo>, scoreList: List<IdNameScore>) {
|
||||
val queryData = GsonUtils.toJson(list)
|
||||
@@ -434,12 +394,19 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
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
|
||||
}
|
||||
searchFoodList.clear()
|
||||
searchFoodList.addAll(list)
|
||||
searchFoodList.addAll(list2)
|
||||
adapter.notifyDataSetChanged()
|
||||
//adapter.updateData(list)
|
||||
if (list.isNotEmpty()) {
|
||||
checkedItem = list[0]
|
||||
if (list2.isNotEmpty()) {
|
||||
checkedItem = list2[0]
|
||||
checkedItem!!.photoUri = lastPhotoUri
|
||||
checkedItem!!.isChecked = true
|
||||
adapter.notifyItemChanged(0)
|
||||
@@ -616,7 +583,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
override fun onError(exception: ImageCaptureException) {
|
||||
Timber.e("拍照失败: ${exception.message}")
|
||||
failureCallback()
|
||||
ToastUtils.showToast("拍照失败: ${exception.message}")
|
||||
//ToastUtils.showToast("拍照失败: ${exception.message}")
|
||||
// 恢复图像分析
|
||||
isAnalyzing = true
|
||||
}
|
||||
@@ -1090,4 +1057,64 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
// }.start()
|
||||
// }
|
||||
|
||||
private fun onTakePhotoSuccess(photoUri: Uri) {
|
||||
lastPhotoUri = photoUri
|
||||
Timber.tag(TAG).d("main,takePhoto耗时:${System.currentTimeMillis() - startTime}")
|
||||
startTime = System.currentTimeMillis()
|
||||
Timber.d("registerDataChange photoUri = ${photoUri.path}")
|
||||
// viewModel.getIdentifiedFoodList()
|
||||
|
||||
ImageUtil.uriToBitmap(this, photoUri)?.let { bitmap ->
|
||||
Timber.tag(TAG).d("main,uriToBitmap耗时:${System.currentTimeMillis() - startTime}")
|
||||
startTime = System.currentTimeMillis()
|
||||
Timber.d("registerDataChange photoUri 拿到bitmap")
|
||||
// val bmp = BitmapCropper.cropCenter(bitmap, 1300, 900)
|
||||
Timber.d("registerDataChange photoUri bitmap裁剪完成")
|
||||
val file = BitmapSaver.saveToAppFilesDir(
|
||||
bitmap,
|
||||
this,
|
||||
"IMG_CROP_${System.currentTimeMillis()}.jpg"
|
||||
)
|
||||
Timber.tag(TAG)
|
||||
.d("main,saveToAppFilesDir耗时:${System.currentTimeMillis() - startTime}")
|
||||
startTime = System.currentTimeMillis()
|
||||
Timber.d("registerDataChange photoUri bitmap保存文件路径:${file?.absolutePath}")
|
||||
//val nameList = FoodModule.queryFood(bitmap)
|
||||
val scoreList = FoodModule.getFoodScoreList(bitmap)
|
||||
val recDataJson = GsonUtils.toJson(scoreList)
|
||||
Timber.tag(TAG)
|
||||
.d("main,getFoodScoreList耗时:${System.currentTimeMillis() - startTime}")
|
||||
startTime = System.currentTimeMillis()
|
||||
Timber.d("registerDataChange photoUri 拿到识别数据:$recDataJson")
|
||||
val nameList = scoreList.map { it.name }
|
||||
val foodName = nameList.joinToString(separator = ",")
|
||||
|
||||
hideWaitingDialog()
|
||||
Timber.d("registerDataChange photoUri 识别数据名称:$foodName")
|
||||
if (TextUtils.isEmpty(foodName)) {
|
||||
LightManager.closeGreenLight()
|
||||
LightManager.closeRedLight()
|
||||
//binding.layoutRescan.visibility = View.VISIBLE
|
||||
binding.flPay.visible()
|
||||
binding.tvToSearch.let {
|
||||
it.text = "未识别到,去手动搜索"
|
||||
it.visible()
|
||||
}
|
||||
} else {
|
||||
//binding.layoutRescan.visibility = View.GONE
|
||||
showWaitingDialog("正在查询菜品信息,请稍后……")
|
||||
viewModel.getFoodInfo(foodName) { list ->
|
||||
runOnUiThread {
|
||||
hideWaitingDialog()
|
||||
Timber.tag(TAG)
|
||||
.d("main,getFoodInfo耗时:${System.currentTimeMillis() - startTime}")
|
||||
startTime = System.currentTimeMillis()
|
||||
updateFoodInfo(list.toMutableList(), scoreList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,11 +8,11 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Toast
|
||||
import androidx.camera.view.PreviewView
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.sw.dualscreen.GlobalData
|
||||
import com.sw.dualscreen.R
|
||||
import com.sw.dualscreen.activity.CollectedFoodActivity
|
||||
import com.sw.dualscreen.activity.SettingActivity
|
||||
@@ -51,7 +51,7 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
private var selectedFoodId: String? = ""
|
||||
private var selectedFoodName: String? = ""
|
||||
|
||||
private var box: Box<Food>? = null
|
||||
private val box by lazy { ObjectBox.boxStore.boxFor(Food::class) }
|
||||
private val foodCollectionList = mutableListOf<FoodCollectionBean>().apply {
|
||||
repeat(MAX_COUNT) {
|
||||
add(FoodCollectionBean(isShowCamera = true))
|
||||
@@ -126,7 +126,6 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
// targetWidth = 1300, targetHeight = 900,
|
||||
// //offsetX = 30, offsetY = 100
|
||||
// )
|
||||
initBox()
|
||||
val imageVector = try {
|
||||
FoodModule.bitmap2FloatArray(bitmap)
|
||||
} catch (e: Exception) {
|
||||
@@ -236,9 +235,12 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
it.layoutManager = GridLayoutManager(context, 2)
|
||||
it.adapter = searchFoodAdapter
|
||||
}
|
||||
|
||||
searchInfo()
|
||||
}
|
||||
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun upload() {
|
||||
lifecycleScope.launch {
|
||||
val totalFileCount = foodCollectionList.count { it.imageFile != null }
|
||||
@@ -247,31 +249,53 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
//params["placeId"] = restId.toRequestBody()
|
||||
params["foodId"] = checkedItem!!.foodId.toRequestBody()
|
||||
params["foodName"] = checkedItem!!.foodName!!.toRequestBody()
|
||||
//params["foodVector"] = foodVector.toRequestBody()
|
||||
params["version"] = GlobalData.foodModelVersion.toRequestBody()
|
||||
|
||||
ImageUploader(
|
||||
totalList = foodCollectionList,
|
||||
uploadImage = { batch ->
|
||||
val files = batch.map { it.imageFile }
|
||||
(requireActivity() as SettingActivity).viewModel.uploadCollectFoodPics(
|
||||
files,
|
||||
params
|
||||
)
|
||||
val foodVectorList =
|
||||
batch.filter { it.imageVector != null }.map { it.imageVector!!.joinToString(separator = ",", prefix = "[", postfix = "]")}
|
||||
val foodVectorJson =
|
||||
foodVectorList.joinToString(separator = ",", prefix = "[", postfix = "]")
|
||||
Timber.tag(TAG).d("json=$foodVectorJson")
|
||||
params["foodVector"] = foodVectorJson.toRequestBody()
|
||||
settingActivity?.viewModel?.uploadCollectFoodPics(files, params)
|
||||
},
|
||||
onProgress = { count, batch ->
|
||||
onProgress = { count, batch, idList->
|
||||
activity?.runOnUiThread {
|
||||
settingActivity?.showWaitingDialog2("图片上传中$count/$totalFileCount")
|
||||
batch.forEach {
|
||||
it.uploadSuccess = true
|
||||
}
|
||||
//batch.forEach {
|
||||
// it.uploadSuccess = true
|
||||
//}
|
||||
Thread {
|
||||
val foodList = batch.mapIndexed { index, it ->
|
||||
Food(
|
||||
collectId = if (index < idList.size) idList[index] else null,
|
||||
foodId = checkedItem!!.foodId,
|
||||
foodName = checkedItem!!.foodName,
|
||||
foodVector = it.imageVector,
|
||||
version = GlobalData.foodModelVersion
|
||||
)
|
||||
}
|
||||
box.put(foodList)
|
||||
activity?.runOnUiThread {
|
||||
batch.forEach { it.uploadSuccess = true }
|
||||
collectionAdapter.notifyDataSetChanged()
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
},
|
||||
onError = {
|
||||
settingActivity?.hideWaitingDialog()
|
||||
activity?.runOnUiThread {
|
||||
ToastUtils.showToast("上传失败,请稍后重试")
|
||||
}
|
||||
},
|
||||
onComplete = {
|
||||
vectorThread()
|
||||
//vectorThread()
|
||||
settingActivity?.hideWaitingDialog()
|
||||
}
|
||||
).processUploads()
|
||||
}
|
||||
@@ -316,33 +340,34 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
}.start()
|
||||
}
|
||||
|
||||
private fun initBox() {
|
||||
if (box == null) {
|
||||
box = ObjectBox.boxStore.boxFor(Food::class)
|
||||
}
|
||||
}
|
||||
|
||||
private fun image2VectorTask(imageVector: FloatArray, 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
|
||||
}
|
||||
activity?.runOnUiThread {
|
||||
collectionAdapter.notifyItemChanged(position)
|
||||
}
|
||||
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)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -119,13 +119,13 @@ data class RespCodeMsg(
|
||||
)
|
||||
|
||||
|
||||
data class PostEvent(var name:String="")
|
||||
data class PostEvent(var name: String = "")
|
||||
data class PaySuccessEvent(
|
||||
var name: String = ""
|
||||
)
|
||||
|
||||
data class ChargeModeEvent(
|
||||
var chargeMode:Int = 0
|
||||
var chargeMode: Int = 0
|
||||
)
|
||||
|
||||
data class ClickBackEvent(
|
||||
@@ -133,7 +133,15 @@ data class ClickBackEvent(
|
||||
)
|
||||
|
||||
data class DeviceConfig(
|
||||
var arcsoftAppId:String?=null,
|
||||
var arcsoftSdkKey:String?=null,
|
||||
var arcsoftActiveKey:String?=null
|
||||
var arcsoftAppId: String? = null,
|
||||
var arcsoftSdkKey: String? = null,
|
||||
var arcsoftActiveKey: String? = null
|
||||
)
|
||||
|
||||
data class FoodVector(
|
||||
val id: String? = null,
|
||||
val foodId: String? = null,
|
||||
val foodName: String? = null,
|
||||
val foodVector: String? = null,
|
||||
val version: String? = null
|
||||
)
|
||||
@@ -7,8 +7,9 @@ import com.sw.dualscreen.model.response.DeviceConfig
|
||||
import com.sw.dualscreen.model.response.DinnerType
|
||||
import com.sw.dualscreen.model.response.FaceData
|
||||
import com.sw.dualscreen.model.response.FoodInfo
|
||||
import com.sw.dualscreen.model.response.FoodSearchReq
|
||||
import com.sw.dualscreen.model.response.FoodOrder
|
||||
import com.sw.dualscreen.model.response.FoodSearchReq
|
||||
import com.sw.dualscreen.model.response.FoodVector
|
||||
import com.sw.dualscreen.model.response.MemberInfo
|
||||
import com.sw.dualscreen.model.response.UserFaceModel
|
||||
import com.sw.dualscreen.model.response.UserNutrition
|
||||
@@ -16,12 +17,16 @@ import com.sw.dualscreen.objbox.CollectedFoodInfo
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.DELETE
|
||||
import retrofit2.http.FieldMap
|
||||
import retrofit2.http.FormUrlEncoded
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Multipart
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Part
|
||||
import retrofit2.http.PartMap
|
||||
import retrofit2.http.Query
|
||||
import retrofit2.http.QueryMap
|
||||
import retrofit2.http.Url
|
||||
|
||||
interface ApiService {
|
||||
@@ -246,11 +251,11 @@ interface ApiService {
|
||||
@Multipart
|
||||
@POST
|
||||
suspend fun uploadCollectFoodPics(
|
||||
// @Url url: String = "http://192.168.1.201:14801/terminal/neglect/dishCollectionVectorData/add",
|
||||
@Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/boothMachine/app/dishCollectionVectorData/add",
|
||||
@PartMap params: HashMap<String, RequestBody>,
|
||||
@Part foodPics: List<MultipartBody.Part>
|
||||
): ApiResponse<String>
|
||||
): ApiResponse<List<String>?>
|
||||
|
||||
|
||||
/**
|
||||
* 获取设备配置数据
|
||||
@@ -268,4 +273,22 @@ interface ApiService {
|
||||
@Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/common/app/faceFeature/increment/list",
|
||||
@Body param: Map<String, Long>
|
||||
): ApiResponse<List<UserFaceModel>?>
|
||||
|
||||
/**
|
||||
* 档口机向量数据分页查询
|
||||
*/
|
||||
@POST
|
||||
suspend fun getCollectedFoodVector(
|
||||
@Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/boothMachine/app/collectionVectorPage",
|
||||
@Body param: MutableMap<String, String>
|
||||
): ApiResponse<List<FoodVector>?>
|
||||
|
||||
/**
|
||||
* 档口机向量数据分页查询
|
||||
*/
|
||||
@DELETE
|
||||
suspend fun deleteCollectFood(
|
||||
@Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/boothMachine/app/deleteBoothMachineCollection",
|
||||
@QueryMap param: MutableMap<String, String?>
|
||||
): ApiResponse<Any?>
|
||||
}
|
||||
@@ -28,11 +28,11 @@ data class FoodCollectionBean(
|
||||
)
|
||||
|
||||
data class CollectedFoodInfo(
|
||||
var id: String? = null,
|
||||
var placeId: String? = null,
|
||||
// var id: String? = null,
|
||||
// var placeId: String? = null,
|
||||
var foodId: String? = null,
|
||||
var foodName: String? = null,
|
||||
var foodPic: Any? = null,
|
||||
var foodVector: Any? = null,
|
||||
// var foodPic: Any? = null,
|
||||
// var foodVector: Any? = null,
|
||||
var foodCount: Int = 0
|
||||
)
|
||||
|
||||
@@ -8,8 +8,11 @@ import io.objectbox.annotation.VectorDistanceType
|
||||
@Entity
|
||||
data class Food(
|
||||
@Id var id: Long = 0,
|
||||
var name: String? = null,
|
||||
var foodIdx: Int = 0,
|
||||
var collectId: String? = null,
|
||||
var foodId: String? = null,
|
||||
var foodName: String? = null,
|
||||
var version: String? = null,
|
||||
var otherField: String? = null,
|
||||
@HnswIndex(dimensions = 512, distanceType = VectorDistanceType.DOT_PRODUCT)
|
||||
var foodVector: FloatArray? = null
|
||||
) {
|
||||
@@ -20,8 +23,11 @@ data class Food(
|
||||
other as Food
|
||||
|
||||
if (id != other.id) return false
|
||||
if (foodIdx != other.foodIdx) return false
|
||||
if (name != other.name) 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 (otherField != other.otherField) return false
|
||||
if (!foodVector.contentEquals(other.foodVector)) return false
|
||||
|
||||
return true
|
||||
@@ -29,9 +35,14 @@ data class Food(
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = id.hashCode()
|
||||
result = 31 * result + foodIdx
|
||||
result = 31 * result + (name?.hashCode() ?: 0)
|
||||
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 + (otherField?.hashCode() ?: 0)
|
||||
result = 31 * result + (foodVector?.contentHashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package com.sw.dualscreen.objbox
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.util.SparseLongArray
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.sw.dualscreen.MyApp
|
||||
@@ -26,7 +25,7 @@ import java.io.InputStream
|
||||
object FoodModule {
|
||||
|
||||
private const val THRESHOLD = 0.8
|
||||
private var module_mobile: Module?=null
|
||||
private var module_mobile: Module? = null
|
||||
private lateinit var box: Box<Food>
|
||||
private lateinit var embeddingsList: List<List<Float>>
|
||||
private lateinit var labelsList: IntArray
|
||||
@@ -35,16 +34,18 @@ object FoodModule {
|
||||
val NO_STD_RGB = floatArrayOf(1.0f, 1.0f, 1.0f)
|
||||
val DEFAULT_FOOD_INDEX = -1
|
||||
|
||||
fun init(context: Context) {
|
||||
fun init(context: Context, block: () -> Unit = {}) {
|
||||
Thread {
|
||||
module_mobile = Module.load(copyAssetToCache(context, "best_embedding_model_mobile.pt"))
|
||||
box = ObjectBox.boxStore.boxFor(Food::class)
|
||||
//if (box.all.isNotEmpty()) {
|
||||
// box.removeAll()
|
||||
//}
|
||||
//初始化默认重新拉取数据,先清空本地数据
|
||||
if (box.all.isNotEmpty()) {
|
||||
box.removeAll()
|
||||
}
|
||||
//if (box.all.isEmpty()) {
|
||||
// initDefFoodData(context)
|
||||
//}
|
||||
block()
|
||||
}.start()
|
||||
}
|
||||
|
||||
@@ -100,16 +101,25 @@ object FoodModule {
|
||||
val idScoreList = query.findIdsWithScores()
|
||||
val nameScoreList = mutableListOf<IdNameScore>()
|
||||
idScoreList.forEach {
|
||||
nameScoreList.add(IdNameScore(id = it.id, name = box.get(it.id).name?:"", score = it.score))
|
||||
nameScoreList.add(
|
||||
IdNameScore(
|
||||
id = it.id,
|
||||
name = box.get(it.id).foodName ?: "",
|
||||
score = it.score
|
||||
)
|
||||
)
|
||||
}
|
||||
val nameScoreData = GsonUtils.toJson(nameScoreList)
|
||||
Timber.tag("FoodModule").d("registerDataChange,queryFood,耗时:${System.currentTimeMillis()-startTime},数据:$nameScoreData")
|
||||
Timber.tag("FoodModule")
|
||||
.d("registerDataChange,queryFood,耗时:${System.currentTimeMillis() - startTime},数据:$nameScoreData")
|
||||
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("FoodModule")
|
||||
.d("registerDataChange,bitmap2FloatArray,耗时:${System.currentTimeMillis() - startTime}")
|
||||
val nameScoreList = queryFoodNameScore(floatArray, queryCount)
|
||||
if (nameScoreList.isEmpty()) {
|
||||
return emptyList()
|
||||
@@ -136,6 +146,7 @@ object FoodModule {
|
||||
//Timber.tag("FoodModule").d("getFoodScoreList数据:${sortedScoreList.toJsonString()}")
|
||||
return sortedScoreList
|
||||
}
|
||||
|
||||
fun queryFood(floatArray: FloatArray, queryCount: Int = 15): List<String> {
|
||||
val query: Query<Food> =
|
||||
box.query(Food_.foodVector.nearestNeighbors(floatArray, queryCount)).build()
|
||||
@@ -144,10 +155,10 @@ object FoodModule {
|
||||
val map = mutableMapOf<String, Int>()
|
||||
query.findIdsWithScores().forEach {
|
||||
val food = box.get(it.id)
|
||||
Timber.d("${food.name}|${food.foodIdx}|${it.score}")
|
||||
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.name?.let { key ->
|
||||
food.foodName?.let { key ->
|
||||
val count = map[key] ?: 0
|
||||
map.put(key, count + 1)
|
||||
}
|
||||
@@ -167,16 +178,16 @@ object FoodModule {
|
||||
}
|
||||
|
||||
data class IdNameScore(
|
||||
val id:Long,
|
||||
var name:String,
|
||||
val id: Long,
|
||||
var name: String,
|
||||
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")
|
||||
@@ -192,7 +203,13 @@ object FoodModule {
|
||||
val classIdx = labelsList[index]
|
||||
val foodName = foodMap["$classIdx"]
|
||||
val array = floatList.toFloatArray()
|
||||
box.put(Food(name = foodName, foodVector = array, foodIdx = DEFAULT_FOOD_INDEX))
|
||||
box.put(
|
||||
Food(
|
||||
foodName = foodName,
|
||||
foodVector = array,
|
||||
//foodIdx = DEFAULT_FOOD_INDEX
|
||||
)
|
||||
)
|
||||
}
|
||||
action()
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import io.objectbox.android.ObjectBoxLiveData
|
||||
import io.objectbox.exception.DbException
|
||||
import io.objectbox.exception.FileCorruptException
|
||||
import io.objectbox.sync.Sync
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.util.Date
|
||||
import java.util.zip.GZIPOutputStream
|
||||
@@ -65,7 +66,7 @@ object ObjectBox {
|
||||
.androidContext(context.applicationContext)
|
||||
.build()
|
||||
} catch (e: DbException) {
|
||||
if (e.javaClass.equals(DbException::class.java) || e is FileCorruptException) {
|
||||
if (e.javaClass == DbException::class.java || e is FileCorruptException) {
|
||||
// Failed to build BoxStore due to database file issue, store message;
|
||||
// checked in NoteListActivity to notify user.
|
||||
dbExceptionMessage = e.toString()
|
||||
@@ -77,8 +78,9 @@ object ObjectBox {
|
||||
}
|
||||
|
||||
// if (BuildConfig.DEBUG) {
|
||||
var syncAvailable = if (Sync.isAvailable()) "available" else "unavailable"
|
||||
Log.d(TAG,"Using ObjectBox ${BoxStore.getVersion()} (${BoxStore.getVersionNative()}, sync $syncAvailable)")
|
||||
val syncAvailable = if (Sync.isAvailable()) "available" else "unavailable"
|
||||
Timber.tag(TAG)
|
||||
.d("Using ObjectBox ${BoxStore.getVersion()} (${BoxStore.getVersionNative()}, sync $syncAvailable)")
|
||||
// Enable ObjectBox Admin on debug builds.
|
||||
// https://docs.objectbox.io/data-browser
|
||||
Admin(boxStore).start(context.applicationContext)
|
||||
@@ -94,7 +96,7 @@ object ObjectBox {
|
||||
// Do not copy if database file is still in use.
|
||||
// If it would be open, the copy will likely get corrupted
|
||||
// as BoxStore may currently write data to the file.
|
||||
Log.e(TAG, "Database file is still in use, can not copy.")
|
||||
Timber.tag(TAG).e("Database file is still in use, can not copy.")
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.sw.dualscreen.model.response.FaceData
|
||||
import com.sw.dualscreen.model.response.FoodInfo
|
||||
import com.sw.dualscreen.model.response.FoodSearchReq
|
||||
import com.sw.dualscreen.model.response.FoodOrder
|
||||
import com.sw.dualscreen.model.response.FoodVector
|
||||
import com.sw.dualscreen.model.response.MemberInfo
|
||||
import com.sw.dualscreen.model.response.UserFaceModel
|
||||
import com.sw.dualscreen.model.response.UserNutrition
|
||||
@@ -281,7 +282,7 @@ class RemoteRepository constructor(
|
||||
suspend fun uploadCollectFoodPics(
|
||||
fileList: List<File>,
|
||||
params: HashMap<String, RequestBody>
|
||||
): ApiResponse<String> {
|
||||
): ApiResponse<List<String>?> {
|
||||
// 准备文件参数
|
||||
val fileParts = mutableListOf<MultipartBody.Part>()
|
||||
fileList.forEachIndexed { index, file ->
|
||||
@@ -302,4 +303,19 @@ class RemoteRepository constructor(
|
||||
suspend fun getDeviceConfig(): ApiResponse<DeviceConfig?> {
|
||||
return safeApiCall { apiService.getDeviceConfig() }
|
||||
}
|
||||
|
||||
suspend fun getCollectedFoodVector(param: MutableMap<String, String>): ApiResponse<List<FoodVector>?> {
|
||||
return safeApiCall {
|
||||
apiService.getCollectedFoodVector(param = param)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun deleteCollectFood(foodId:String?, version: String?): ApiResponse<Any?> {
|
||||
return safeApiCall {
|
||||
apiService.deleteCollectFood(param = mutableMapOf(
|
||||
"foodId" to foodId,
|
||||
"version" to version
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@ import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class ImageUploader(
|
||||
private val totalList: List<FoodCollectionBean>,
|
||||
private val uploadImage: suspend (List<FoodCollectionBean>)-> Boolean,
|
||||
private val onProgress: (Int, List<FoodCollectionBean>) -> Unit,
|
||||
private val uploadImage: suspend (List<FoodCollectionBean>)-> List<String>?,
|
||||
private val onProgress: (Int, List<FoodCollectionBean>, List<String>) -> Unit,
|
||||
private val onError:(List<FoodCollectionBean>) -> Unit,
|
||||
private val onComplete:() -> Unit
|
||||
) {
|
||||
@@ -16,18 +16,18 @@ class ImageUploader(
|
||||
private val uploadedCount = AtomicInteger(0)
|
||||
|
||||
suspend fun processUploads() {
|
||||
val batches = totalList.chunked(BATCH_SIZE)
|
||||
val batches = totalList.filter { it.imageVector!=null }.chunked(BATCH_SIZE)
|
||||
|
||||
for (batch in batches) {
|
||||
val success = uploadImage(batch)
|
||||
if (!success) {
|
||||
val idList = uploadImage(batch)
|
||||
if (idList.isNullOrEmpty()) {
|
||||
//println("上传失败,终止流程")
|
||||
onError(batch)
|
||||
return
|
||||
}
|
||||
val fileCount = batch.count { it.imageFile!=null }
|
||||
uploadedCount.addAndGet(fileCount)
|
||||
onProgress(uploadedCount.get(), batch)
|
||||
onProgress(uploadedCount.get(), batch, idList)
|
||||
//println("已上传 ${uploadedCount.get()}/$totalImages")
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,14 @@
|
||||
package com.sw.dualscreen.viewmodel
|
||||
|
||||
import com.sw.dualscreen.GlobalData
|
||||
import com.sw.dualscreen.GlobalKey
|
||||
import com.sw.dualscreen.model.response.EquipmentInfo
|
||||
import com.sw.dualscreen.utils.GsonUtils
|
||||
import com.sw.dualscreen.utils.SPUtil
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* 初始化的viewmodel
|
||||
*/
|
||||
class DeviceViewModel : BaseViewModel() {
|
||||
private val _deviceInfoResult = MutableStateFlow<Boolean?>(null)
|
||||
val deviceInfoResult: StateFlow<Boolean?> = _deviceInfoResult
|
||||
// private val _deviceInfoResult = MutableStateFlow<Boolean?>(null)
|
||||
// val deviceInfoResult: StateFlow<Boolean?> = _deviceInfoResult
|
||||
|
||||
/**
|
||||
* 获取token
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.sw.dualscreen.model.response.DeviceConfig
|
||||
import com.sw.dualscreen.model.response.DinnerType
|
||||
import com.sw.dualscreen.model.response.FoodInfo
|
||||
import com.sw.dualscreen.model.response.FoodOrder
|
||||
import com.sw.dualscreen.model.response.FoodVector
|
||||
import com.sw.dualscreen.model.response.MemberInfo
|
||||
import com.sw.dualscreen.model.response.UserFaceModel
|
||||
import com.sw.dualscreen.model.response.UserFaceModel2
|
||||
@@ -539,17 +540,17 @@ class UserViewModel : BaseViewModel() {
|
||||
suspend fun uploadCollectFoodPics(
|
||||
files: List<File?>,
|
||||
params: HashMap<String, RequestBody>
|
||||
): Boolean {
|
||||
): List<String>? {
|
||||
Timber.tag(TAG).d("uploadCollectFoodPics")
|
||||
val fileList = files.filterNotNull()
|
||||
if (fileList.isEmpty()) {
|
||||
return true
|
||||
return null
|
||||
}
|
||||
val resp = repository.uploadCollectFoodPics(fileList, params)
|
||||
// if (!parseResponse(resp)) {
|
||||
// return false
|
||||
// }
|
||||
return resp.code == "00000"
|
||||
if (resp.code != "00000") {
|
||||
return null
|
||||
}
|
||||
return resp.data
|
||||
}
|
||||
|
||||
fun getDeviceConfig(block: (DeviceConfig?) -> Unit) {
|
||||
@@ -563,4 +564,28 @@ class UserViewModel : BaseViewModel() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getCollectedFoodVector(param: MutableMap<String, String>, block: (List<FoodVector>?) -> Unit) {
|
||||
Timber.tag(TAG).d("getCollectedFoodVector")
|
||||
launchWithLoading {
|
||||
val response = repository.getCollectedFoodVector(param)
|
||||
if (parseResponse(response)) {
|
||||
block(response.data)
|
||||
} else {
|
||||
block(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteCollectFood(foodId:String?, version: String?, block: (Boolean) -> Unit) {
|
||||
Timber.tag(TAG).d("deleteCollectFood")
|
||||
launchWithLoading {
|
||||
val response = repository.deleteCollectFood(foodId, version)
|
||||
if (parseResponse(response)) {
|
||||
block(true)
|
||||
} else {
|
||||
block(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,22 +13,23 @@
|
||||
<RadioGroup
|
||||
android:id="@+id/rgSetting"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="130dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_height="90dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbBusiness"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="50dp"
|
||||
android:layout_marginEnd="25dp"
|
||||
android:layout_weight="1"
|
||||
android:button="@null"
|
||||
tools:checked="true"
|
||||
android:gravity="center"
|
||||
android:gravity="center_horizontal"
|
||||
android:maxLines="1"
|
||||
android:text="经营设置"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingBottom="32dp"
|
||||
android:textColor="@color/setting_radio"
|
||||
android:textSize="30sp"
|
||||
android:drawableBottom="@drawable/line_shape_horizontal"
|
||||
@@ -37,14 +38,16 @@
|
||||
<RadioButton
|
||||
android:id="@+id/rbCollect"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="25dp"
|
||||
android:layout_marginEnd="50dp"
|
||||
android:layout_weight="1"
|
||||
android:button="@null"
|
||||
android:gravity="center"
|
||||
android:gravity="center_horizontal"
|
||||
android:maxLines="1"
|
||||
android:text="餐品采集"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingBottom="32dp"
|
||||
android:textColor="@color/setting_radio"
|
||||
android:textSize="30sp"
|
||||
android:drawableBottom="@drawable/line_shape_horizontal"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?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="400dp"
|
||||
android:layout_height="260dp"
|
||||
android:layout_width="500dp"
|
||||
android:layout_height="300dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/bg_white_radius12"
|
||||
@@ -23,7 +23,7 @@
|
||||
android:gravity="center"
|
||||
tools:text="存在未收货的物品,请确认是否放弃收货,若放弃则数据不会保存?"
|
||||
android:textColor="#ff141428"
|
||||
android:textSize="20sp" />
|
||||
android:textSize="22sp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
@@ -31,26 +31,26 @@
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnCancel"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_width="130dp"
|
||||
android:layout_height="60dp"
|
||||
android:background="@drawable/bg_white_stroke_blue_ripple"
|
||||
android:text="取消"
|
||||
android:gravity="center"
|
||||
android:textColor="#FF0033CC"
|
||||
android:textSize="20sp" />
|
||||
android:textSize="22sp" />
|
||||
|
||||
<TextView
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnConfirm"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginStart="30dp"
|
||||
android:layout_width="130dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_marginStart="40dp"
|
||||
android:background="@drawable/bg_blue_ripple"
|
||||
android:text="确认"
|
||||
android:gravity="center"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="20sp" />
|
||||
android:textSize="22sp" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -39,12 +39,12 @@
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivDeleteFood"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_marginEnd="75dp"
|
||||
android:paddingHorizontal="10dp"
|
||||
android:paddingHorizontal="13dp"
|
||||
android:src="@drawable/ic_close2"
|
||||
tools:ignore="ContentDescription"
|
||||
android:visibility="gone"/>
|
||||
android:visibility="visible"/>
|
||||
|
||||
</LinearLayout>
|
||||
Reference in New Issue
Block a user