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