Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71ce668e3c | ||
|
|
561a2fc9b2 | ||
|
|
08611349aa | ||
|
|
bbf2e605b5 |
Generated
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
<selectionStates>
|
<selectionStates>
|
||||||
<SelectionState runConfigName="app">
|
<SelectionState runConfigName="app">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
<DropdownSelection timestamp="2025-07-31T00:35:57.879961400Z">
|
<DropdownSelection timestamp="2025-08-05T03:50:06.075639Z">
|
||||||
<Target type="DEFAULT_BOOT">
|
<Target type="DEFAULT_BOOT">
|
||||||
<handle>
|
<handle>
|
||||||
<DeviceId pluginId="PhysicalDevice" identifier="serial=2FD2511004054749" />
|
<DeviceId pluginId="PhysicalDevice" identifier="serial=2FD2511004054749" />
|
||||||
|
|||||||
@@ -0,0 +1,271 @@
|
|||||||
|
{
|
||||||
|
"info": {
|
||||||
|
"_postman_id": "adfceb8d-2c35-41bf-82d9-40b87e10524d",
|
||||||
|
"name": "档口机",
|
||||||
|
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
|
||||||
|
"_exporter_id": "751386"
|
||||||
|
},
|
||||||
|
"item": [{
|
||||||
|
"name": "device获取token",
|
||||||
|
"request": {
|
||||||
|
"auth": {
|
||||||
|
"type": "noauth"
|
||||||
|
},
|
||||||
|
"method": "GET",
|
||||||
|
"header": [],
|
||||||
|
"url": {
|
||||||
|
"
|
||||||
|
raw": "http://device.shuziweidao.com:8889/sys/getEquipmentToken?qrcodeId=3ea47dc0-3cf0-3c2f-909c-265a9a65572e",
|
||||||
|
"protocol": "http",
|
||||||
|
"host": [
|
||||||
|
"device",
|
||||||
|
"shuziweidao",
|
||||||
|
"com"
|
||||||
|
],
|
||||||
|
"port": "8889",
|
||||||
|
"path": [
|
||||||
|
"sys",
|
||||||
|
"getEquipmentToken"
|
||||||
|
],
|
||||||
|
"query": [{
|
||||||
|
"key": "qrcodeId",
|
||||||
|
"value": "3ea47dc0-3cf0-3c2f-909c-265a9a65572e"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
}, {
|
||||||
|
"name": "获取配置信息",
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"header": [{
|
||||||
|
"key": "X-Access-Token",
|
||||||
|
"value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3NTM4NDIzNDEsInVzZXJuYW1lIjoiYWRtaW4ifQ.gcKT7BeMWNve8NydRmTTrdXW8wdS7W_Ot9kVdtllyuc",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"url": {
|
||||||
|
"
|
||||||
|
raw": "http://device.shuziweidao.com:8889/equipment/stEquipment/queryByEquipmentCode?equipmentCode=3ea47dc0-3cf0-3c2f-909c-265a9a65572e",
|
||||||
|
"protocol": "http",
|
||||||
|
"host": [
|
||||||
|
"device",
|
||||||
|
"shuziweidao",
|
||||||
|
"com"
|
||||||
|
],
|
||||||
|
"port": "8889",
|
||||||
|
"path": [
|
||||||
|
"equipment",
|
||||||
|
"stEquipment",
|
||||||
|
"queryByEquipmentCode"
|
||||||
|
],
|
||||||
|
"query": [{
|
||||||
|
"key": "equipmentCode",
|
||||||
|
"value": "3ea47dc0-3cf0-3c2f-909c-265a9a65572e"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
}, {
|
||||||
|
"name": "获取业务服务器token",
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"header": [],
|
||||||
|
"url": {
|
||||||
|
"
|
||||||
|
raw": "http://192.168.1.207/gateway/local/sys/getEquipmentToken?qrcodeId=3ea47dc0-3cf0-3c2f-909c-265a9a65572e",
|
||||||
|
"protocol": "http",
|
||||||
|
"host": [
|
||||||
|
"192",
|
||||||
|
"168",
|
||||||
|
"1",
|
||||||
|
"207"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"gateway",
|
||||||
|
"local",
|
||||||
|
"sys",
|
||||||
|
"getEquipmentToken"
|
||||||
|
],
|
||||||
|
"query": [{
|
||||||
|
"key": "qrcodeId",
|
||||||
|
"value": "3ea47dc0-3cf0-3c2f-909c-265a9a65572e"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
}, {
|
||||||
|
"name": "获取人脸数据",
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"header": [{
|
||||||
|
"key": "X-Access-Token",
|
||||||
|
"value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjYW50ZWVuSWQiOiIyOCIsInR5cGUiOiI0IiwidXNlcklkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMSJ9.Jy7Dc2k5FlqUAmGReRn-uEg3Du5gtlyUhXKUMZLlO-E",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"url": {
|
||||||
|
"raw": "http://192.168.1.207/gateway/local/stapi/cquser/getUserFaceCache/v2?pageIndex=0",
|
||||||
|
"protocol": "http",
|
||||||
|
"host": [
|
||||||
|
"192",
|
||||||
|
"168",
|
||||||
|
"1",
|
||||||
|
"207"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"gateway",
|
||||||
|
"local",
|
||||||
|
"stapi",
|
||||||
|
"cquser",
|
||||||
|
"getUserFaceCache",
|
||||||
|
"v2"
|
||||||
|
],
|
||||||
|
"query": [{
|
||||||
|
"key": "pageIndex",
|
||||||
|
"value": "0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
}, {
|
||||||
|
"name": "获取档口菜品信息",
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"header": [{
|
||||||
|
"key": "X-Access-Token",
|
||||||
|
"value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjYW50ZWVuSWQiOiIyOCIsInR5cGUiOiI0IiwidXNlcklkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMSJ9.Jy7Dc2k5FlqUAmGReRn-uEg3Du5gtlyUhXKUMZLlO-E",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"url": {
|
||||||
|
"
|
||||||
|
raw": "http://192.168.1.207/gateway/local/zhstapi/zhst/getRestInfoFoodsByType/stall?restId=1678234139391512577&type=0&foodName=西红柿",
|
||||||
|
"protocol": "http",
|
||||||
|
"host": [
|
||||||
|
"192",
|
||||||
|
"168",
|
||||||
|
"1",
|
||||||
|
"207"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"gateway",
|
||||||
|
"local",
|
||||||
|
"zhstapi",
|
||||||
|
"zhst",
|
||||||
|
"getRestInfoFoodsByType",
|
||||||
|
"stall"
|
||||||
|
],
|
||||||
|
"query": [{
|
||||||
|
"key": "restId",
|
||||||
|
"value": "1678234139391512577"
|
||||||
|
}, {
|
||||||
|
"key": "type",
|
||||||
|
"value": "0",
|
||||||
|
"description": "0全部 1餐次"
|
||||||
|
}, {
|
||||||
|
"key": "foodName",
|
||||||
|
"value": "西红柿"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
}, {
|
||||||
|
"name": "通过用户信息获取就餐数据",
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"header": [{
|
||||||
|
"key": "X-Access-Token",
|
||||||
|
"value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjYW50ZWVuSWQiOiIyOCIsInR5cGUiOiI0IiwidXNlcklkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMSJ9.Jy7Dc2k5FlqUAmGReRn-uEg3Du5gtlyUhXKUMZLlO-E",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"url": {
|
||||||
|
"
|
||||||
|
raw": "http://192.168.1.207/gateway/local/zhstapi/zhst/getUserNutritionData/face?restId=1678234139391512577&userId=4958698b725b4e89a514fa8552aa5fa3&foodId=1701417944860119041",
|
||||||
|
"protocol": "http",
|
||||||
|
"host": [
|
||||||
|
"192",
|
||||||
|
"168",
|
||||||
|
"1",
|
||||||
|
"207"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"gateway",
|
||||||
|
"local",
|
||||||
|
"zhstapi",
|
||||||
|
"zhst",
|
||||||
|
"getUserNutritionData",
|
||||||
|
"face"
|
||||||
|
],
|
||||||
|
"query": [{
|
||||||
|
"key": "restId",
|
||||||
|
"value": "1678234139391512577"
|
||||||
|
}, {
|
||||||
|
"key": "userId",
|
||||||
|
"value": "4958698b725b4e89a514fa8552aa5fa3"
|
||||||
|
}, {
|
||||||
|
"key": "foodId",
|
||||||
|
"value": "1701417944860119041"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
}, {
|
||||||
|
"name": "提交就餐数据",
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"header": [],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "",
|
||||||
|
"options": {
|
||||||
|
"raw": {
|
||||||
|
"language": "json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"
|
||||||
|
raw": "http://192.168.1.207/gateway/local/zhstapi/zhst/userEatFood/doubleBalance/face/stall?restId=1678234139391512577&userId=4958698b725b4e89a514fa8552aa5fa3&foodId=1701417944860119041&faceTime&faceEndTime&eatWeight&foodWeight&deviceId",
|
||||||
|
"protocol": "http",
|
||||||
|
"host": [
|
||||||
|
"192",
|
||||||
|
"168",
|
||||||
|
"1",
|
||||||
|
"207"
|
||||||
|
],
|
||||||
|
"path": [
|
||||||
|
"gateway",
|
||||||
|
"local",
|
||||||
|
"zhstapi",
|
||||||
|
"zhst",
|
||||||
|
"userEatFood",
|
||||||
|
"doubleBalance",
|
||||||
|
"face",
|
||||||
|
"stall"
|
||||||
|
],
|
||||||
|
"body": [{
|
||||||
|
"appVersion": "1",
|
||||||
|
"deviceId": "abe60247-99d1-3c2a-9f10-c0b76f4b3f3b",
|
||||||
|
"eatWeight": 0.278,
|
||||||
|
"faceEndTime": 1754385701611,
|
||||||
|
"faceTime": 1754385694475,
|
||||||
|
"foodId": "1701417944860119041",
|
||||||
|
"foodWeight": 0.278,
|
||||||
|
"restId": "1678234139391512577",
|
||||||
|
"userId": "1937692681894932481"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -7,6 +7,10 @@ object GlobalData {
|
|||||||
// var globalEquipmentCode: String = "202501171634"
|
// var globalEquipmentCode: String = "202501171634"
|
||||||
var restId: String = ""
|
var restId: String = ""
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备id
|
||||||
|
*/
|
||||||
|
var deviceId: String = ""
|
||||||
/**
|
/**
|
||||||
* app版本号
|
* app版本号
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.sw.dualscreen
|
package com.sw.dualscreen
|
||||||
|
|
||||||
import com.sw.plate.App
|
import com.sw.plate.App
|
||||||
|
import com.sw.plate.utils.AppUtil
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
class MyApp : App() {
|
class MyApp : App() {
|
||||||
@@ -11,5 +12,8 @@ class MyApp : App() {
|
|||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
Timber.plant(Timber.DebugTree())
|
Timber.plant(Timber.DebugTree())
|
||||||
|
val deviceId = AppUtil.getUDID(this)
|
||||||
|
Timber.d("UDID = ${AppUtil.getUDID(this)}")
|
||||||
|
GlobalData.deviceId = deviceId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,9 @@ import android.content.Context
|
|||||||
import android.graphics.Outline
|
import android.graphics.Outline
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
import android.hardware.display.DisplayManager
|
import android.hardware.display.DisplayManager
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.text.TextUtils
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewOutlineProvider
|
import android.view.ViewOutlineProvider
|
||||||
import android.widget.AdapterView
|
import android.widget.AdapterView
|
||||||
@@ -35,7 +37,6 @@ import com.sw.dualscreen.sdk.SensorScaleUtils
|
|||||||
import com.sw.dualscreen.utils.GlideUtils
|
import com.sw.dualscreen.utils.GlideUtils
|
||||||
import com.sw.dualscreen.view.CustomBottomSheetDialog
|
import com.sw.dualscreen.view.CustomBottomSheetDialog
|
||||||
import com.sw.dualscreen.viewmodel.UserViewModel
|
import com.sw.dualscreen.viewmodel.UserViewModel
|
||||||
import com.sw.plate.utils.AppUtil
|
|
||||||
import com.sw.plate.utils.ToastUtils
|
import com.sw.plate.utils.ToastUtils
|
||||||
import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel
|
import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -54,7 +55,9 @@ class MainActivity : AppCompatActivity() {
|
|||||||
private var checkedItem: FoodInfo? = null
|
private var checkedItem: FoodInfo? = null
|
||||||
private var imageAnalysis: ImageAnalysis? = null
|
private var imageAnalysis: ImageAnalysis? = null
|
||||||
private var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>? = null
|
private var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>? = null
|
||||||
private var canIdentify: Boolean = true
|
private var canIdentify: Boolean = false
|
||||||
|
private var bottomSheetDialog: CustomBottomSheetDialog? = null
|
||||||
|
private var lastWeight: Double = 0.0
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@@ -62,7 +65,6 @@ class MainActivity : AppCompatActivity() {
|
|||||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.main)
|
setContentView(binding.main)
|
||||||
|
|
||||||
Timber.d("UDID = ${AppUtil.getUDID(this)}")
|
|
||||||
initView()
|
initView()
|
||||||
setupSecondaryDisplay()
|
setupSecondaryDisplay()
|
||||||
setupCamera()
|
setupCamera()
|
||||||
@@ -83,10 +85,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
binding.previewView.clipToOutline = true
|
binding.previewView.clipToOutline = true
|
||||||
binding.tvToSearch.setOnClickListener {
|
binding.tvToSearch.setOnClickListener {
|
||||||
val bottomSheetDialog = CustomBottomSheetDialog.newInstance(viewModel) {
|
bottomSheetDialog = CustomBottomSheetDialog.newInstance(viewModel) {
|
||||||
updateCurrentFood(it)
|
updateCurrentFood(it)
|
||||||
}
|
}
|
||||||
bottomSheetDialog.show(supportFragmentManager, "CustomBottomSheetDialog")
|
bottomSheetDialog!!.show(supportFragmentManager, "CustomBottomSheetDialog")
|
||||||
}
|
}
|
||||||
|
|
||||||
val spinnerAdapter = ArrayAdapter<String>(
|
val spinnerAdapter = ArrayAdapter<String>(
|
||||||
@@ -122,22 +124,26 @@ class MainActivity : AppCompatActivity() {
|
|||||||
)
|
)
|
||||||
binding.recyclerview.adapter = adapter
|
binding.recyclerview.adapter = adapter
|
||||||
binding.tvFoodName.setOnClickListener {
|
binding.tvFoodName.setOnClickListener {
|
||||||
if (presentation.currentStep == 2) {
|
SensorScaleUtils.zero()
|
||||||
presentation.step3()
|
|
||||||
} else {
|
|
||||||
presentation.step1()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
registerDataChange()
|
registerDataChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateCurrentFood(foodInfo: FoodInfo?) {
|
fun updateCurrentFood(foodInfo: FoodInfo?) {
|
||||||
presentation.updateFood(foodInfo)
|
|
||||||
if (foodInfo != null) {
|
if (foodInfo != null) {
|
||||||
|
presentation.updateFood(foodInfo)
|
||||||
binding.tvFoodName.text = foodInfo.foodName
|
binding.tvFoodName.text = foodInfo.foodName
|
||||||
binding.previewView.visibility = View.GONE
|
binding.previewView.visibility = View.GONE
|
||||||
binding.ivImg.visibility = View.VISIBLE
|
binding.ivImg.visibility = View.VISIBLE
|
||||||
GlideUtils.loadRoundCornerImage(this, foodInfo.imgUrl, binding.ivImg, 12)
|
val imgUrl = foodInfo.imgUrl
|
||||||
|
if (TextUtils.isEmpty(imgUrl)) {
|
||||||
|
takePhoto { imgUri ->
|
||||||
|
GlideUtils.loadRoundCornerImage(this, imgUri, binding.ivImg, 12)
|
||||||
|
presentation.updateFood(foodInfo.copy(imgUrl = imgUri.toString()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GlideUtils.loadRoundCornerImage(this, foodInfo.imgUrl, binding.ivImg, 12)
|
||||||
|
}
|
||||||
pauseAnalysis()
|
pauseAnalysis()
|
||||||
} else {
|
} else {
|
||||||
binding.tvFoodName.text = "-"
|
binding.tvFoodName.text = "-"
|
||||||
@@ -156,9 +162,9 @@ class MainActivity : AppCompatActivity() {
|
|||||||
// presentation.step2()
|
// presentation.step2()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
viewModel.getIdentifiedFoodList()
|
|
||||||
SensorScaleUtils.startScale { weight ->
|
SensorScaleUtils.startScale { weight ->
|
||||||
// Timber.d("weight = $weight")
|
Timber.d("registerDataChange weight = $weight")
|
||||||
|
presentation.updateWeight(weight)
|
||||||
if (weight > 0.0) {
|
if (weight > 0.0) {
|
||||||
if (canIdentify) {
|
if (canIdentify) {
|
||||||
canIdentify = false
|
canIdentify = false
|
||||||
@@ -289,24 +295,35 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 拍照功能
|
// 拍照功能
|
||||||
private fun takePhoto() {
|
private fun takePhoto(callback: (Uri) -> Unit) {
|
||||||
|
Timber.d("takePhoto")
|
||||||
// 临时停止分析以避免干扰
|
// 临时停止分析以避免干扰
|
||||||
isAnalyzing = false
|
isAnalyzing = false
|
||||||
|
|
||||||
// 创建输出选项
|
// 创建输出选项
|
||||||
val outputFileOptions = ImageCapture.OutputFileOptions.Builder(
|
val executor = ContextCompat.getMainExecutor(this)
|
||||||
File(
|
val cacheDir = cacheDir
|
||||||
cacheDir.path, "IMG_${System.currentTimeMillis()}.jpg"
|
val photoFile = File.createTempFile(
|
||||||
)
|
"IMG_${System.currentTimeMillis()}",
|
||||||
).build()
|
".jpg",
|
||||||
|
cacheDir
|
||||||
|
)
|
||||||
|
|
||||||
|
val cacheOutputOptions =
|
||||||
|
ImageCapture.OutputFileOptions.Builder(photoFile).build()
|
||||||
// 执行拍照
|
// 执行拍照
|
||||||
imageCapture?.takePicture(
|
imageCapture?.takePicture(
|
||||||
outputFileOptions,
|
cacheOutputOptions,
|
||||||
executor,
|
executor,
|
||||||
object : ImageCapture.OnImageSavedCallback {
|
object : ImageCapture.OnImageSavedCallback {
|
||||||
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
|
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
ToastUtils.showToast("照片保存成功: ${outputFileResults.savedUri}")
|
val savedUri = outputFileResults.savedUri
|
||||||
|
Timber.d("takePhoto savedUri = $savedUri")
|
||||||
|
ToastUtils.showToast("照片保存成功: $savedUri")
|
||||||
|
if (savedUri != null) {
|
||||||
|
callback(savedUri)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 恢复图像分析
|
// 恢复图像分析
|
||||||
isAnalyzing = true
|
isAnalyzing = true
|
||||||
@@ -343,6 +360,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
presentation.setStepChangeCallback { step ->
|
presentation.setStepChangeCallback { step ->
|
||||||
if (step == 1) {
|
if (step == 1) {
|
||||||
updateCurrentFood(null)
|
updateCurrentFood(null)
|
||||||
|
bottomSheetDialog?.dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
presentation.show()
|
presentation.show()
|
||||||
|
|||||||
@@ -34,4 +34,13 @@ fun String?.maskPhone(): String {
|
|||||||
sb.setCharAt(i, '*')
|
sb.setCharAt(i, '*')
|
||||||
}
|
}
|
||||||
return sb.toString()
|
return sb.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String?.toSafeDouble(): Double {
|
||||||
|
if (this == null) return 0.0
|
||||||
|
return try {
|
||||||
|
toDouble()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -14,5 +14,5 @@ data class UserNutritionParam(
|
|||||||
val faceEndTime: Long,
|
val faceEndTime: Long,
|
||||||
val eatWeight: Double,
|
val eatWeight: Double,
|
||||||
val foodWeight: Double,
|
val foodWeight: Double,
|
||||||
val deviceId: String = ""
|
val deviceId: String = GlobalData.deviceId
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ data class UserNutritionData(
|
|||||||
* 食物类型和实际摄入量清单
|
* 食物类型和实际摄入量清单
|
||||||
*/
|
*/
|
||||||
@SerializedName("foodTypeAndRealIntakeVoList")
|
@SerializedName("foodTypeAndRealIntakeVoList")
|
||||||
val foodTypeAndRealIntakeVoList: List<String?>? = listOf(),
|
val foodTypeAndRealIntakeVoList: List<FoodTypeAndRealIntakeVo?>? = listOf(),
|
||||||
@SerializedName("foodWeight")
|
@SerializedName("foodWeight")
|
||||||
val foodWeight: String? = "",
|
val foodWeight: String? = "",
|
||||||
@SerializedName("message")
|
@SerializedName("message")
|
||||||
@@ -66,7 +66,7 @@ data class UserNutritionData(
|
|||||||
@SerializedName("stFoodInfoPagoda")
|
@SerializedName("stFoodInfoPagoda")
|
||||||
val stFoodInfoPagoda: StFoodInfoPagoda? = StFoodInfoPagoda(),
|
val stFoodInfoPagoda: StFoodInfoPagoda? = StFoodInfoPagoda(),
|
||||||
@SerializedName("stUserFoodInfoList")
|
@SerializedName("stUserFoodInfoList")
|
||||||
val stUserFoodInfoList: List<String?>? = listOf(),
|
val stUserFoodInfoList: List<StUserFoodInfo?>? = listOf(),
|
||||||
/**
|
/**
|
||||||
* 每日总热量
|
* 每日总热量
|
||||||
*/
|
*/
|
||||||
@@ -104,6 +104,26 @@ data class UserNutritionData(
|
|||||||
if (TextUtils.isEmpty(totalEnergyCalculateScore)) 0.0
|
if (TextUtils.isEmpty(totalEnergyCalculateScore)) 0.0
|
||||||
else totalEnergyCalculateScore!!.toDouble()
|
else totalEnergyCalculateScore!!.toDouble()
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data class FoodTypeAndRealIntakeVo(
|
||||||
|
@SerializedName("childMaterClassName")
|
||||||
|
val childMaterClassName: String? = "",
|
||||||
|
@SerializedName("foodId")
|
||||||
|
val foodId: String? = "",
|
||||||
|
@SerializedName("goodsId")
|
||||||
|
val goodsId: String? = "",
|
||||||
|
@SerializedName("goodsName")
|
||||||
|
val goodsName: String? = "",
|
||||||
|
@SerializedName("materClassName")
|
||||||
|
val materClassName: String? = "",
|
||||||
|
@SerializedName("materId")
|
||||||
|
val materId: String? = "",
|
||||||
|
@SerializedName("materialType")
|
||||||
|
val materialType: String? = "",
|
||||||
|
@SerializedName("realityIntake")
|
||||||
|
val realityIntake: String? = ""
|
||||||
|
) : Parcelable
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class StFoodInfoPagoda(
|
data class StFoodInfoPagoda(
|
||||||
/**
|
/**
|
||||||
@@ -178,4 +198,56 @@ data class UserNutritionData(
|
|||||||
else meat!!.toDouble()
|
else meat!!.toDouble()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data class StUserFoodInfo(
|
||||||
|
@SerializedName("canteenId")
|
||||||
|
val canteenId: String? = "",
|
||||||
|
@SerializedName("createBy")
|
||||||
|
val createBy: String? = "",
|
||||||
|
@SerializedName("createTime")
|
||||||
|
val createTime: String? = "",
|
||||||
|
@SerializedName("dataSource")
|
||||||
|
val dataSource: String? = "",
|
||||||
|
@SerializedName("dataType")
|
||||||
|
val dataType: String? = "",
|
||||||
|
@SerializedName("delFlag")
|
||||||
|
val delFlag: String? = "",
|
||||||
|
@SerializedName("deviceId")
|
||||||
|
val deviceId: String? = "",
|
||||||
|
@SerializedName("dinnerType")
|
||||||
|
val dinnerType: String? = "",
|
||||||
|
@SerializedName("eatDay")
|
||||||
|
val eatDay: String? = "",
|
||||||
|
@SerializedName("eatNum")
|
||||||
|
val eatNum: String? = "",
|
||||||
|
@SerializedName("eatWeight")
|
||||||
|
val eatWeight: Double? = 0.0,
|
||||||
|
@SerializedName("foodId")
|
||||||
|
val foodId: String? = "",
|
||||||
|
@SerializedName("foodImg")
|
||||||
|
val foodImg: String? = "",
|
||||||
|
@SerializedName("foodMaterialId")
|
||||||
|
val foodMaterialId: String? = "",
|
||||||
|
@SerializedName("foodName")
|
||||||
|
val foodName: String? = "",
|
||||||
|
@SerializedName("foodWeight")
|
||||||
|
val foodWeight: String? = "",
|
||||||
|
@SerializedName("id")
|
||||||
|
val id: String? = "",
|
||||||
|
@SerializedName("isSync")
|
||||||
|
val isSync: String? = "",
|
||||||
|
@SerializedName("isSyncCopy")
|
||||||
|
val isSyncCopy: String? = "",
|
||||||
|
@SerializedName("specId")
|
||||||
|
val specId: String? = "",
|
||||||
|
@SerializedName("stBasicDiningInformationUserESVo")
|
||||||
|
val stBasicDiningInformationUserESVo: String? = "",
|
||||||
|
@SerializedName("stallType")
|
||||||
|
val stallType: String? = "",
|
||||||
|
@SerializedName("type")
|
||||||
|
val type: String? = "",
|
||||||
|
@SerializedName("userId")
|
||||||
|
val userId: String? = ""
|
||||||
|
) : Parcelable
|
||||||
}
|
}
|
||||||
@@ -97,6 +97,6 @@ interface ApiService {
|
|||||||
@POST
|
@POST
|
||||||
suspend fun postUserNutritionData(
|
suspend fun postUserNutritionData(
|
||||||
@Url url: String = "${GlobalData.appBaseUrl}/zhstapi/zhst/userEatFood/doubleBalance/face/stall",
|
@Url url: String = "${GlobalData.appBaseUrl}/zhstapi/zhst/userEatFood/doubleBalance/face/stall",
|
||||||
@Body param: UserNutritionParam
|
@Body param: List<UserNutritionParam>
|
||||||
): ApiResponse<String>
|
): ApiResponse<String>
|
||||||
}
|
}
|
||||||
@@ -8,8 +8,6 @@ import android.graphics.Bitmap
|
|||||||
import android.graphics.Outline
|
import android.graphics.Outline
|
||||||
import android.graphics.Point
|
import android.graphics.Point
|
||||||
import android.hardware.Camera
|
import android.hardware.Camera
|
||||||
import android.hardware.camera2.CameraDevice
|
|
||||||
import android.hardware.camera2.CameraManager
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
||||||
@@ -19,7 +17,6 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.ViewOutlineProvider
|
import android.view.ViewOutlineProvider
|
||||||
import android.view.ViewTreeObserver
|
import android.view.ViewTreeObserver
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
@@ -32,10 +29,10 @@ import com.sw.dualscreen.databinding.PresentationSecondaryScreenBinding
|
|||||||
import com.sw.dualscreen.ext.dp
|
import com.sw.dualscreen.ext.dp
|
||||||
import com.sw.dualscreen.ext.format2String
|
import com.sw.dualscreen.ext.format2String
|
||||||
import com.sw.dualscreen.ext.maskName
|
import com.sw.dualscreen.ext.maskName
|
||||||
|
import com.sw.dualscreen.model.request.UserNutritionParam
|
||||||
import com.sw.dualscreen.model.response.DinnerTypeInfo
|
import com.sw.dualscreen.model.response.DinnerTypeInfo
|
||||||
import com.sw.dualscreen.model.response.FoodInfo
|
import com.sw.dualscreen.model.response.FoodInfo
|
||||||
import com.sw.dualscreen.model.response.UserNutritionData
|
import com.sw.dualscreen.model.response.UserNutritionData
|
||||||
import com.sw.dualscreen.sdk.SensorScaleUtils
|
|
||||||
import com.sw.dualscreen.utils.Debouncer
|
import com.sw.dualscreen.utils.Debouncer
|
||||||
import com.sw.dualscreen.utils.GlideUtils
|
import com.sw.dualscreen.utils.GlideUtils
|
||||||
import com.sw.dualscreen.viewmodel.UserViewModel
|
import com.sw.dualscreen.viewmodel.UserViewModel
|
||||||
@@ -49,9 +46,11 @@ import com.sw.plate.utils.arcface.PreviewConfig
|
|||||||
import com.sw.plate.utils.arcface.camera.CameraListener
|
import com.sw.plate.utils.arcface.camera.CameraListener
|
||||||
import com.sw.plate.utils.arcface.camera.DualCameraHelper
|
import com.sw.plate.utils.arcface.camera.DualCameraHelper
|
||||||
import com.sw.plate.utils.arcface.face.constants.LivenessType
|
import com.sw.plate.utils.arcface.face.constants.LivenessType
|
||||||
|
import com.sw.plate.utils.arcface.face.model.CompareResult
|
||||||
import com.sw.plate.utils.arcface.face.model.FacePreviewInfo
|
import com.sw.plate.utils.arcface.face.model.FacePreviewInfo
|
||||||
import com.sw.plate.utils.arcface.face.model.RecognizeConfiguration
|
import com.sw.plate.utils.arcface.face.model.RecognizeConfiguration
|
||||||
import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel
|
import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel
|
||||||
|
import kotlinx.coroutines.flow.drop
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
@@ -67,19 +66,23 @@ class SecondaryScreenPresentation(
|
|||||||
val recognizeViewModel: RecognizeViewModel
|
val recognizeViewModel: RecognizeViewModel
|
||||||
) : Presentation(context, display), ViewTreeObserver.OnGlobalLayoutListener {
|
) : Presentation(context, display), ViewTreeObserver.OnGlobalLayoutListener {
|
||||||
private lateinit var binding: PresentationSecondaryScreenBinding
|
private lateinit var binding: PresentationSecondaryScreenBinding
|
||||||
private var cameraDevice: CameraDevice? = null
|
|
||||||
|
|
||||||
// private var cameraCaptureSession: CameraCaptureSession? = null
|
|
||||||
private lateinit var cameraManager: CameraManager
|
|
||||||
private var cameraIdList: Array<String> = arrayOf()
|
|
||||||
|
|
||||||
// 当前食物信息
|
// 当前食物信息
|
||||||
private var currentFood: FoodInfo? = null
|
private var currentFood: FoodInfo? = null
|
||||||
|
private var lastFood: FoodInfo? = null
|
||||||
private var userNutritionData: UserNutritionData? = null
|
private var userNutritionData: UserNutritionData? = null
|
||||||
private var dinnerType: DinnerTypeInfo? = null
|
private var dinnerTypeInfo: DinnerTypeInfo? = null
|
||||||
private lateinit var itemUserNutritionBinding: ItemUserNutritionBinding
|
private lateinit var itemUserNutritionBinding: ItemUserNutritionBinding
|
||||||
private lateinit var stepChangeCallback: (Int) -> Unit
|
private lateinit var stepChangeCallback: (Int) -> Unit
|
||||||
|
|
||||||
|
// 上一次的计算热量结果
|
||||||
|
private var lastCalcResultInfo: UserNutritionUtils.CalcResultInfo? = null
|
||||||
|
private var lastWeight = 0.0
|
||||||
|
private val debouncer = Debouncer(500)
|
||||||
|
private var faceTime: Long = 0L // 人脸识别时的时间
|
||||||
|
private var recognitionWeight: Double = 0.0 // 人脸识别时的重量
|
||||||
|
private var lastFaceTrackId: Int = 0 // 上一次的人脸信息
|
||||||
|
|
||||||
// 当前步骤
|
// 当前步骤
|
||||||
var currentStep: Int = 0
|
var currentStep: Int = 0
|
||||||
|
|
||||||
@@ -90,6 +93,7 @@ class SecondaryScreenPresentation(
|
|||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
window?.setBackgroundDrawableResource(android.R.color.transparent)
|
window?.setBackgroundDrawableResource(android.R.color.transparent)
|
||||||
initView()
|
initView()
|
||||||
|
setupArcCamera()
|
||||||
// binding.includeUserHeat.ivHeat.showText("1070千卡")
|
// binding.includeUserHeat.ivHeat.showText("1070千卡")
|
||||||
registerDataChange()
|
registerDataChange()
|
||||||
}
|
}
|
||||||
@@ -98,17 +102,20 @@ class SecondaryScreenPresentation(
|
|||||||
activity.lifecycleScope.launch {
|
activity.lifecycleScope.launch {
|
||||||
viewModel.nutritionData.collect {
|
viewModel.nutritionData.collect {
|
||||||
Timber.d("registerDataChange nutritionData = $it")
|
Timber.d("registerDataChange nutritionData = $it")
|
||||||
if (it == null) return@collect
|
|
||||||
userNutritionData = it
|
userNutritionData = it
|
||||||
|
if (it == null) return@collect
|
||||||
step3()
|
step3()
|
||||||
itemUserNutritionBinding.tvUserName.text = it.userName.maskName()
|
itemUserNutritionBinding.tvUserName.text = it.userName.maskName()
|
||||||
itemUserNutritionBinding.tvRecommendHeat.text =
|
itemUserNutritionBinding.tvRecommendHeat.text =
|
||||||
"推荐热量:${it.recommendMin}-${it.recommendMax}"
|
"推荐热量:${it.recommendMin}-${it.recommendMax}"
|
||||||
|
|
||||||
|
updateWeight(lastWeight / 1000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
activity.lifecycleScope.launch {
|
activity.lifecycleScope.launch {
|
||||||
viewModel.dinnerTypeInfo.collect {
|
viewModel.dinnerTypeInfo.drop(1).collect {
|
||||||
dinnerType = it
|
Timber.d("registerDataChange dinnerTypeInfo = $it")
|
||||||
|
dinnerTypeInfo = it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,22 +145,9 @@ class SecondaryScreenPresentation(
|
|||||||
* 启用虹软人脸失败
|
* 启用虹软人脸失败
|
||||||
*/
|
*/
|
||||||
private fun setupArcCamera() {
|
private fun setupArcCamera() {
|
||||||
cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
|
|
||||||
// 获取摄像头列表
|
|
||||||
cameraIdList = cameraManager.cameraIdList
|
|
||||||
if (cameraIdList.isEmpty()) {
|
|
||||||
Toast.makeText(context, "没有可用的摄像头", Toast.LENGTH_SHORT).show()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cameraIdList.forEach {
|
|
||||||
Timber.d("摄像头id列表 = ${it}")
|
|
||||||
}
|
|
||||||
|
|
||||||
initArcViewModel()
|
initArcViewModel()
|
||||||
initArcView()
|
initArcView()
|
||||||
openRectInfoDraw = true
|
openRectInfoDraw = true
|
||||||
openCamera(cameraIdList.last())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -162,18 +156,21 @@ class SecondaryScreenPresentation(
|
|||||||
fun step1() {
|
fun step1() {
|
||||||
Timber.d("step1")
|
Timber.d("step1")
|
||||||
currentStep = 1
|
currentStep = 1
|
||||||
|
//回复上一次的设置
|
||||||
|
lastWeight = 0.0
|
||||||
|
lastFood = null
|
||||||
|
lastCalcResultInfo = null
|
||||||
|
recognitionWeight = 0.0
|
||||||
|
|
||||||
stepChangeCallback(currentStep)
|
stepChangeCallback(currentStep)
|
||||||
binding.tvFoodInfo.text = "菜品识别中"
|
binding.tvFoodName.text = "菜品识别中"
|
||||||
binding.ivFaceBg.visibility = View.GONE
|
binding.ivFaceBg.visibility = View.GONE
|
||||||
binding.flPreview.visibility = View.VISIBLE
|
binding.flPreview.visibility = View.VISIBLE
|
||||||
binding.ivImg.visibility = View.VISIBLE
|
binding.ivImg.visibility = View.VISIBLE
|
||||||
binding.flFace.visibility = View.GONE
|
binding.flFace.visibility = View.INVISIBLE
|
||||||
pauseCamera()
|
pauseCamera()
|
||||||
|
|
||||||
binding.tvBottomTip.visibility = View.VISIBLE
|
binding.tvBottomTip.visibility = View.VISIBLE
|
||||||
itemUserNutritionBinding.clNutritionData.visibility = View.GONE
|
itemUserNutritionBinding.clNutritionData.visibility = View.GONE
|
||||||
stopSensorScale()
|
|
||||||
viewModel.getDinnerType()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -184,27 +181,34 @@ class SecondaryScreenPresentation(
|
|||||||
currentStep = 2
|
currentStep = 2
|
||||||
stepChangeCallback(currentStep)
|
stepChangeCallback(currentStep)
|
||||||
currentFood = foodInfo
|
currentFood = foodInfo
|
||||||
|
viewModel.getDinnerType()
|
||||||
resumeCamera()
|
resumeCamera()
|
||||||
binding.tvFoodInfo.text = foodInfo.foodName
|
binding.tvFoodName.text = foodInfo.foodName
|
||||||
|
binding.ivImg.visibility = View.GONE
|
||||||
|
|
||||||
binding.ivFaceBg.visibility = View.VISIBLE
|
|
||||||
binding.flPreview.visibility = View.GONE
|
binding.flPreview.visibility = View.GONE
|
||||||
|
|
||||||
binding.flFace.visibility = View.VISIBLE
|
binding.flFace.visibility = View.VISIBLE
|
||||||
binding.ivFaceBg.visibility = View.VISIBLE
|
binding.ivFaceBg.visibility = View.GONE
|
||||||
binding.tvFaceTip.visibility = View.VISIBLE
|
binding.tvFaceTip.visibility = View.VISIBLE
|
||||||
|
binding.tvBottomTip.visibility = View.VISIBLE
|
||||||
setupArcCamera()
|
itemUserNutritionBinding.clNutritionData.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateFood(foodInfo: FoodInfo?) {
|
fun updateFood(foodInfo: FoodInfo?) {
|
||||||
Timber.d("step2 updateFood")
|
Timber.d("updateFood")
|
||||||
currentFood = foodInfo
|
currentFood = foodInfo
|
||||||
if (foodInfo != null) {
|
if (foodInfo != null) {
|
||||||
if (currentStep != 2) {
|
if (currentStep == 1) {
|
||||||
step2(foodInfo)
|
step2(foodInfo)
|
||||||
} else {
|
} else {
|
||||||
binding.tvFoodInfo.text = foodInfo.foodName
|
binding.tvFoodName.text = foodInfo.foodName
|
||||||
|
GlideUtils.loadRoundCornerImage(
|
||||||
|
context,
|
||||||
|
url = currentFood!!.imgUrl,
|
||||||
|
imageView = binding.ivImg,
|
||||||
|
radius = 12
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -220,10 +224,7 @@ class SecondaryScreenPresentation(
|
|||||||
ToastUtils.showToast("选中物品为空3")
|
ToastUtils.showToast("选中物品为空3")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
binding.tvFoodInfo.text = currentFood!!.foodName
|
binding.tvFoodName.text = currentFood!!.foodName
|
||||||
|
|
||||||
startSensorScale()
|
|
||||||
|
|
||||||
GlideUtils.loadRoundCornerImage(
|
GlideUtils.loadRoundCornerImage(
|
||||||
context,
|
context,
|
||||||
url = currentFood!!.imgUrl,
|
url = currentFood!!.imgUrl,
|
||||||
@@ -231,124 +232,132 @@ class SecondaryScreenPresentation(
|
|||||||
radius = 12
|
radius = 12
|
||||||
)
|
)
|
||||||
binding.flPreview.visibility = View.VISIBLE
|
binding.flPreview.visibility = View.VISIBLE
|
||||||
// binding.previewView.visibility = View.GONE
|
|
||||||
binding.ivImg.visibility = View.VISIBLE
|
binding.ivImg.visibility = View.VISIBLE
|
||||||
binding.flFace.visibility = View.GONE
|
binding.flFace.visibility = View.INVISIBLE
|
||||||
binding.ivFaceBg.visibility = View.GONE
|
binding.ivFaceBg.visibility = View.GONE
|
||||||
binding.tvFaceTip.visibility = View.GONE
|
binding.tvFaceTip.visibility = View.GONE
|
||||||
|
|
||||||
binding.tvBottomTip.visibility = View.GONE
|
binding.tvBottomTip.visibility = View.GONE
|
||||||
itemUserNutritionBinding.clNutritionData.visibility = View.VISIBLE
|
itemUserNutritionBinding.clNutritionData.visibility = View.VISIBLE
|
||||||
|
|
||||||
viewModel.getUserNutritionData(
|
|
||||||
userId = userNutritionData!!.userId!!,
|
|
||||||
foodId = currentFood!!.id!!
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startSensorScale() {
|
fun updateWeight(weight: Double) {
|
||||||
var lastWeight = 0.0
|
lastWeight = weight * 1000 // 将千克转成克
|
||||||
val debouncer = Debouncer(1000)
|
Timber.d("updateWeight lastWeight = $weight")
|
||||||
SensorScaleUtils.startScale { weight ->
|
if (currentFood == null) {
|
||||||
if (weight == lastWeight) return@startScale
|
Timber.e("updateWeight currentFood is null")
|
||||||
Timber.d("registerDataChange weight = $weight")
|
return
|
||||||
lastWeight = weight
|
}
|
||||||
if (currentFood == null) return@startScale
|
if (userNutritionData == null) {
|
||||||
if (userNutritionData == null) return@startScale
|
Timber.e("updateWeight userNutritionData is null")
|
||||||
if (dinnerType == null) return@startScale
|
return
|
||||||
debouncer.debounce {
|
}
|
||||||
val calcResultInfo = UserNutritionUtils.calculateNutrition(
|
if (dinnerTypeInfo == null) {
|
||||||
currentFood!!,
|
Timber.e("updateWeight dinnerType is null")
|
||||||
userNutritionData!!,
|
return
|
||||||
weight,
|
}
|
||||||
dinnerType = dinnerType!!.dinnerType!!.dinnerType!!
|
calculateNutrition(recognitionWeight - lastWeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun calculateNutrition(weight: Double) {
|
||||||
|
debouncer.debounce {
|
||||||
|
Timber.d("calculateNutrition weight = $weight, recognitionWeight = $recognitionWeight")
|
||||||
|
val dinnerType = dinnerTypeInfo!!.dinnerType!!.dinnerType!!
|
||||||
|
Timber.d("calculateNutrition foodName = ${currentFood!!.foodName}, dinnerType = $dinnerType, userId = ${userNutritionData!!.userId}")
|
||||||
|
val calcResultInfo = UserNutritionUtils.calculateNutrition(
|
||||||
|
currentFood!!,
|
||||||
|
userNutritionData!!,
|
||||||
|
weight,
|
||||||
|
dinnerType = dinnerType
|
||||||
|
)
|
||||||
|
Timber.d("calculateNutrition calcResultInfo = $calcResultInfo")
|
||||||
|
if (lastCalcResultInfo == calcResultInfo) {
|
||||||
|
Timber.d("calculateNutrition 与上次计算结果无变化")
|
||||||
|
return@debounce
|
||||||
|
}
|
||||||
|
lastCalcResultInfo = calcResultInfo
|
||||||
|
val columnMax = UserNutritionUtils.getMaxInt(calcResultInfo)
|
||||||
|
|
||||||
|
activity.runOnUiThread {
|
||||||
|
val totalKcal = calcResultInfo.totalKcal
|
||||||
|
val grain = calcResultInfo.grain
|
||||||
|
val fruits = calcResultInfo.fruits
|
||||||
|
val meat = calcResultInfo.meat
|
||||||
|
// 设置热量
|
||||||
|
itemUserNutritionBinding.customKcalColumn.setImageDrawable(
|
||||||
|
true,
|
||||||
|
totalKcal.max < totalKcal.current
|
||||||
)
|
)
|
||||||
Timber.d("calcResultInfo = $calcResultInfo")
|
var maxKcal = totalKcal.max
|
||||||
activity.runOnUiThread {
|
Timber.d("maxKcal = $maxKcal")
|
||||||
val totalKcal = calcResultInfo.totalKcal
|
if (maxKcal <= 0.0) {
|
||||||
val grain = calcResultInfo.grain
|
maxKcal = 100.0
|
||||||
val fruits = calcResultInfo.fruits
|
|
||||||
val meat = calcResultInfo.meat
|
|
||||||
itemUserNutritionBinding.customKcalColumn.setImageDrawable(
|
|
||||||
true,
|
|
||||||
totalKcal.first < totalKcal.second
|
|
||||||
)
|
|
||||||
itemUserNutritionBinding.totalKcalTv.text = totalKcal.second.format2String()
|
|
||||||
itemUserNutritionBinding.customKcalColumn.setCustomHeightPercent(
|
|
||||||
(totalKcal.second / 100).toFloat(),
|
|
||||||
true
|
|
||||||
)
|
|
||||||
|
|
||||||
itemUserNutritionBinding.customFoodColumn.setImageDrawable(
|
|
||||||
false,
|
|
||||||
grain.first < grain.second
|
|
||||||
)
|
|
||||||
itemUserNutritionBinding.totalFoodTv.text = grain.second.format2String()
|
|
||||||
itemUserNutritionBinding.customFoodColumn.setCustomHeightPercent(
|
|
||||||
(grain.second / 100).toFloat(),
|
|
||||||
true
|
|
||||||
)
|
|
||||||
|
|
||||||
itemUserNutritionBinding.customVegetableColumn.setImageDrawable(
|
|
||||||
false,
|
|
||||||
fruits.first < fruits.second
|
|
||||||
)
|
|
||||||
itemUserNutritionBinding.totalVegetableTv.text = fruits.second.format2String()
|
|
||||||
itemUserNutritionBinding.customVegetableColumn.setCustomHeightPercent(
|
|
||||||
(fruits.second / 100).toFloat(),
|
|
||||||
true
|
|
||||||
)
|
|
||||||
|
|
||||||
itemUserNutritionBinding.customMeatColumn.setImageDrawable(
|
|
||||||
false,
|
|
||||||
meat.first < meat.second
|
|
||||||
)
|
|
||||||
itemUserNutritionBinding.totalMeatTv.text = meat.second.format2String()
|
|
||||||
itemUserNutritionBinding.customMeatColumn.setCustomHeightPercent(
|
|
||||||
(meat.second / 100).toFloat(),
|
|
||||||
true
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
Timber.d("maxKcal 1 = $maxKcal")
|
||||||
|
itemUserNutritionBinding.totalKcalTv.text = totalKcal.current.format2String()
|
||||||
|
itemUserNutritionBinding.customKcalColumn.setCustomHeightPercent(
|
||||||
|
(totalKcal.current / maxKcal).toFloat(),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
// 设置主食
|
||||||
|
itemUserNutritionBinding.customFoodColumn.setImageDrawable(
|
||||||
|
false,
|
||||||
|
grain.max < grain.current
|
||||||
|
)
|
||||||
|
itemUserNutritionBinding.totalFoodTv.text = grain.current.format2String()
|
||||||
|
itemUserNutritionBinding.customFoodColumn.setCustomHeightPercent(
|
||||||
|
(grain.current / columnMax).toFloat(),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
// 设置果蔬
|
||||||
|
itemUserNutritionBinding.customVegetableColumn.setImageDrawable(
|
||||||
|
false,
|
||||||
|
fruits.max < fruits.current
|
||||||
|
)
|
||||||
|
itemUserNutritionBinding.totalVegetableTv.text = fruits.current.format2String()
|
||||||
|
itemUserNutritionBinding.customVegetableColumn.setCustomHeightPercent(
|
||||||
|
(fruits.current / columnMax).toFloat(),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
// 设置肉蛋
|
||||||
|
itemUserNutritionBinding.customMeatColumn.setImageDrawable(
|
||||||
|
false,
|
||||||
|
meat.max < meat.current
|
||||||
|
)
|
||||||
|
itemUserNutritionBinding.totalMeatTv.text = meat.current.format2String()
|
||||||
|
itemUserNutritionBinding.customMeatColumn.setCustomHeightPercent(
|
||||||
|
(meat.current / columnMax).toFloat(),
|
||||||
|
true
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun stopSensorScale() {
|
private fun openCamera() {
|
||||||
SensorScaleUtils.closeScale()
|
Timber.d("openCamera")
|
||||||
}
|
|
||||||
|
|
||||||
private fun openCamera(cameraId: String) {
|
|
||||||
if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) !=
|
if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) !=
|
||||||
PackageManager.PERMISSION_GRANTED
|
PackageManager.PERMISSION_GRANTED
|
||||||
) {
|
) {
|
||||||
|
ToastUtils.showToast("无摄像头权限")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
cameraManager.openCamera(cameraId, object : CameraDevice.StateCallback() {
|
val cameraCount = Camera.getNumberOfCameras()
|
||||||
override fun onOpened(device: CameraDevice) {
|
if (cameraCount < 3){
|
||||||
cameraDevice = device
|
ToastUtils.showToast("摄像头数量异常")
|
||||||
// createCameraPreviewSession()
|
return
|
||||||
recognizeViewModel.init(
|
}
|
||||||
PreviewConfig(
|
recognizeViewModel.init(
|
||||||
2, 4, 0, 0
|
PreviewConfig(
|
||||||
)
|
2, 1, 90, 90
|
||||||
)
|
)
|
||||||
initRgbCamera()
|
)
|
||||||
resumeCamera()
|
initRgbCamera()
|
||||||
}
|
if (DualCameraHelper.hasDualCamera() && livenessType === LivenessType.IR) {
|
||||||
|
initIrCamera()
|
||||||
override fun onDisconnected(device: CameraDevice) {
|
}
|
||||||
device.close()
|
|
||||||
cameraDevice = null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(device: CameraDevice, error: Int) {
|
|
||||||
device.close()
|
|
||||||
cameraDevice = null
|
|
||||||
}
|
|
||||||
}, null)
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("Camera2", "打开摄像头失败", e)
|
Timber.e(e, "打开摄像头失败")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,9 +369,12 @@ class SecondaryScreenPresentation(
|
|||||||
private var isRecognition = false
|
private var isRecognition = false
|
||||||
private var rgbCameraHelper: DualCameraHelper? = null
|
private var rgbCameraHelper: DualCameraHelper? = null
|
||||||
private var rgbFaceRectTransformer: FaceRectTransformer? = null
|
private var rgbFaceRectTransformer: FaceRectTransformer? = null
|
||||||
private val livenessType = LivenessType.RGB
|
private val livenessType = LivenessType.IR
|
||||||
private var openRectInfoDraw = false
|
private var openRectInfoDraw = false
|
||||||
|
|
||||||
|
private var irCameraHelper: DualCameraHelper? = null
|
||||||
|
private var irFaceRectTransformer: FaceRectTransformer? = null
|
||||||
|
|
||||||
private fun initArcViewModel() {
|
private fun initArcViewModel() {
|
||||||
|
|
||||||
recognizeViewModel.setLiveType(livenessType)
|
recognizeViewModel.setLiveType(livenessType)
|
||||||
@@ -403,17 +415,24 @@ class SecondaryScreenPresentation(
|
|||||||
Timber.i("recognizeConfiguration: observe = ${recognizeConfiguration.toString()}")
|
Timber.i("recognizeConfiguration: observe = ${recognizeConfiguration.toString()}")
|
||||||
})
|
})
|
||||||
recognizeViewModel.recognizeNotice.observe(activity, Observer { notice: String? ->
|
recognizeViewModel.recognizeNotice.observe(activity, Observer { notice: String? ->
|
||||||
Timber.i("recognizeNotice observe notice = $notice")
|
// Timber.i("recognizeNotice observe notice = $notice")
|
||||||
if (currentStep == 3 && notice == "leave") {
|
|
||||||
// step1()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
recognizeViewModel.recognizeUserId.observe(activity, Observer { userId: String? ->
|
recognizeViewModel.recognizeUserId.observe(
|
||||||
Timber.i("recognizeUserId observe userId = $userId")
|
activity,
|
||||||
if (userId == null) return@Observer
|
Observer { compareResult: CompareResult ->
|
||||||
viewModel.getUserNutritionData(userId = userId, foodId = currentFood!!.id!!)
|
Timber.i("recognizeUserId observe compareResult = ${compareResult.trackId}, userId = ${compareResult.faceEntity.userName}")
|
||||||
})
|
faceTime = System.currentTimeMillis()
|
||||||
|
recognitionWeight = lastWeight
|
||||||
|
lastFaceTrackId = compareResult.trackId
|
||||||
|
val faceEntity = compareResult.faceEntity
|
||||||
|
val userId = faceEntity.userName
|
||||||
|
if (userId == null) return@Observer
|
||||||
|
viewModel.getUserNutritionData(
|
||||||
|
userId = userId,
|
||||||
|
foodId = currentFood!!.id!!
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
recognizeViewModel.drawRectInfoText.observe(activity, Observer { info ->
|
recognizeViewModel.drawRectInfoText.observe(activity, Observer { info ->
|
||||||
Timber.i("drawRectInfoText observe info = $info")
|
Timber.i("drawRectInfoText observe info = $info")
|
||||||
@@ -495,12 +514,13 @@ class SecondaryScreenPresentation(
|
|||||||
displayOrientation: Int,
|
displayOrientation: Int,
|
||||||
isMirror: Boolean
|
isMirror: Boolean
|
||||||
) {
|
) {
|
||||||
|
Timber.d("Rgb onCameraOpened: cameraId = $cameraId, displayOrientation = $displayOrientation")
|
||||||
activity.runOnUiThread({
|
activity.runOnUiThread({
|
||||||
val previewSizeRgb = camera.getParameters().getPreviewSize()
|
val previewSizeRgb = camera.getParameters().getPreviewSize()
|
||||||
val layoutParams = adjustPreviewViewSize(
|
val layoutParams = adjustPreviewViewSize(
|
||||||
binding.dualCameraTexturePreviewRgb,
|
binding.dualCameraTexturePreviewRgb,
|
||||||
binding.dualCameraTexturePreviewRgb, binding.dualCameraFaceRectView,
|
binding.dualCameraTexturePreviewRgb, binding.dualCameraFaceRectView,
|
||||||
previewSizeRgb, 90, 1f
|
previewSizeRgb, displayOrientation, 1f
|
||||||
)
|
)
|
||||||
Timber.d("initRgbCamera previewSizeRgb = ${previewSizeRgb.width}-${previewSizeRgb.height}")
|
Timber.d("initRgbCamera previewSizeRgb = ${previewSizeRgb.width}-${previewSizeRgb.height}")
|
||||||
Timber.d("initRgbCamera layoutParams = ${layoutParams.width}-${layoutParams.height}")
|
Timber.d("initRgbCamera layoutParams = ${layoutParams.width}-${layoutParams.height}")
|
||||||
@@ -562,7 +582,7 @@ class SecondaryScreenPresentation(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
.rotation(activity.windowManager.defaultDisplay.rotation)
|
.rotation(activity.windowManager.defaultDisplay.rotation)
|
||||||
.additionalRotation(90) // 角度
|
.additionalRotation(previewConfig.rgbAdditionalDisplayOrientation) // 角度
|
||||||
.previewSize(recognizeViewModel.loadPreviewSize())
|
.previewSize(recognizeViewModel.loadPreviewSize())
|
||||||
.specificCameraId(previewConfig.rgbCameraId)
|
.specificCameraId(previewConfig.rgbCameraId)
|
||||||
.isMirror(true)
|
.isMirror(true)
|
||||||
@@ -573,12 +593,96 @@ class SecondaryScreenPresentation(
|
|||||||
rgbCameraHelper!!.start()
|
rgbCameraHelper!!.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化红外相机,若活体检测类型是可见光活体检测或不启用活体,则不需要启用
|
||||||
|
*/
|
||||||
|
private fun initIrCamera() {
|
||||||
|
Timber.d("initIrCamera: livenessType = $livenessType")
|
||||||
|
if (livenessType === LivenessType.RGB) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val irCameraListener: CameraListener = object : CameraListener {
|
||||||
|
override fun onCameraOpened(
|
||||||
|
camera: Camera,
|
||||||
|
cameraId: Int,
|
||||||
|
displayOrientation: Int,
|
||||||
|
isMirror: Boolean
|
||||||
|
) {
|
||||||
|
Timber.d("IR onCameraOpened: cameraId = $cameraId, displayOrientation = $displayOrientation")
|
||||||
|
val previewSizeIr = camera.getParameters().getPreviewSize()
|
||||||
|
val layoutParams = adjustPreviewViewSize(
|
||||||
|
binding.dualCameraTexturePreviewRgb,
|
||||||
|
binding.dualCameraTexturePreviewIr, binding.dualCameraFaceRectViewIr,
|
||||||
|
previewSizeIr, displayOrientation, 0.25f
|
||||||
|
)
|
||||||
|
|
||||||
|
irFaceRectTransformer = FaceRectTransformer(
|
||||||
|
previewSizeIr.width, previewSizeIr.height,
|
||||||
|
layoutParams.width, layoutParams.height, displayOrientation, cameraId, isMirror,
|
||||||
|
ConfigUtil.isDrawIrRectHorizontalMirror(context),
|
||||||
|
ConfigUtil.isDrawIrRectVerticalMirror(context)
|
||||||
|
)
|
||||||
|
|
||||||
|
recognizeViewModel.onIrCameraOpened(camera)
|
||||||
|
recognizeViewModel.setIrFaceRectTransformer(irFaceRectTransformer)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onPreview(nv21: ByteArray?, camera: Camera?) {
|
||||||
|
recognizeViewModel.refreshIrPreviewData(nv21)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCameraClosed() {
|
||||||
|
Timber.i("onCameraClosed: ")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCameraError(e: java.lang.Exception) {
|
||||||
|
Timber.i("onCameraError: ${e.message}")
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCameraConfigurationChanged(
|
||||||
|
cameraID: Int,
|
||||||
|
displayOrientation: Int
|
||||||
|
) {
|
||||||
|
if (irFaceRectTransformer != null) {
|
||||||
|
irFaceRectTransformer!!.cameraDisplayOrientation = displayOrientation
|
||||||
|
}
|
||||||
|
Timber.i("onCameraConfigurationChanged: cameraID = $cameraID, displayOrientation = $displayOrientation")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val previewConfig = recognizeViewModel.previewConfig
|
||||||
|
irCameraHelper = DualCameraHelper.Builder()
|
||||||
|
.previewViewSize(
|
||||||
|
Point(
|
||||||
|
binding.dualCameraTexturePreviewIr.measuredWidth,
|
||||||
|
binding.dualCameraTexturePreviewIr.measuredHeight
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.rotation(activity.windowManager.defaultDisplay.rotation)
|
||||||
|
.specificCameraId(previewConfig.irCameraId)
|
||||||
|
.previewOn(binding.dualCameraTexturePreviewIr)
|
||||||
|
.cameraListener(irCameraListener)
|
||||||
|
.isMirror(true)
|
||||||
|
.previewSize(recognizeViewModel.loadPreviewSize()) //相机预览大小设置,RGB与IR需使用相同大小
|
||||||
|
.additionalRotation(previewConfig.irAdditionalDisplayOrientation) //额外旋转角度
|
||||||
|
.build()
|
||||||
|
irCameraHelper!!.init()
|
||||||
|
try {
|
||||||
|
irCameraHelper!!.start()
|
||||||
|
} catch (e: RuntimeException) {
|
||||||
|
ToastUtils.showToast(e.message + context.getString(R.string.camera_error_notice))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 绘制RGB、IR画面的实时人脸信息
|
* 绘制RGB、IR画面的实时人脸信息
|
||||||
*
|
*
|
||||||
* @param facePreviewInfoList RGB画面的实时人脸信息
|
* @param facePreviewInfoList RGB画面的实时人脸信息
|
||||||
*/
|
*/
|
||||||
private fun drawPreviewInfo(facePreviewInfoList: MutableList<FacePreviewInfo?>) {
|
private fun drawPreviewInfo(facePreviewInfoList: MutableList<FacePreviewInfo?>) {
|
||||||
|
// Timber.d("drawPreviewInfo facePreviewInfoList = ${facePreviewInfoList.size}, rgbFaceRectTransformer = ${rgbFaceRectTransformer != null}")
|
||||||
if (rgbFaceRectTransformer != null) {
|
if (rgbFaceRectTransformer != null) {
|
||||||
val rgbDrawInfoList: MutableList<DrawInfo?>? = recognizeViewModel.getDrawInfo(
|
val rgbDrawInfoList: MutableList<DrawInfo?>? = recognizeViewModel.getDrawInfo(
|
||||||
facePreviewInfoList,
|
facePreviewInfoList,
|
||||||
@@ -588,6 +692,32 @@ class SecondaryScreenPresentation(
|
|||||||
// 识别成功
|
// 识别成功
|
||||||
binding.dualCameraFaceRectView.drawRealtimeFaceInfo(rgbDrawInfoList)
|
binding.dualCameraFaceRectView.drawRealtimeFaceInfo(rgbDrawInfoList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (facePreviewInfoList.isEmpty() || (lastFaceTrackId != facePreviewInfoList[0]!!.trackId)) {
|
||||||
|
if (lastFaceTrackId != 0) {
|
||||||
|
Timber.i("$lastFaceTrackId 用户离开")
|
||||||
|
lastFaceTrackId = 0
|
||||||
|
postUserData()
|
||||||
|
step1()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun postUserData() {
|
||||||
|
Timber.d("postUserData")
|
||||||
|
if (userNutritionData == null || currentFood == null) {
|
||||||
|
Timber.d("postUserData userNutritionData = ${userNutritionData == null}, currentFood = ${currentFood == null}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val userNutritionParam = UserNutritionParam(
|
||||||
|
userId = userNutritionData?.userId!!,
|
||||||
|
foodId = currentFood?.id!!,
|
||||||
|
faceTime = faceTime,
|
||||||
|
faceEndTime = System.currentTimeMillis(),
|
||||||
|
eatWeight = recognitionWeight - lastWeight,
|
||||||
|
foodWeight = lastWeight
|
||||||
|
)
|
||||||
|
viewModel.postUserNutritionData(listOf<UserNutritionParam>(userNutritionParam))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun openRectInfoDraw(view: View?) {
|
fun openRectInfoDraw(view: View?) {
|
||||||
@@ -599,6 +729,7 @@ class SecondaryScreenPresentation(
|
|||||||
override fun onGlobalLayout() {
|
override fun onGlobalLayout() {
|
||||||
Timber.d("onGlobalLayout")
|
Timber.d("onGlobalLayout")
|
||||||
binding.dualCameraTexturePreviewRgb.getViewTreeObserver().removeOnGlobalLayoutListener(this)
|
binding.dualCameraTexturePreviewRgb.getViewTreeObserver().removeOnGlobalLayoutListener(this)
|
||||||
|
openCamera()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -618,13 +749,16 @@ class SecondaryScreenPresentation(
|
|||||||
rgbCameraHelper!!.release()
|
rgbCameraHelper!!.release()
|
||||||
rgbCameraHelper = null
|
rgbCameraHelper = null
|
||||||
}
|
}
|
||||||
SensorScaleUtils.closeScale()
|
if (irCameraHelper != null) {
|
||||||
|
irCameraHelper!!.release()
|
||||||
|
irCameraHelper = null
|
||||||
|
}
|
||||||
recognizeViewModel.destroy()
|
recognizeViewModel.destroy()
|
||||||
super.onStop()
|
super.onStop()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resumeCamera() {
|
fun resumeCamera() {
|
||||||
Timber.d("resumeCamera")
|
Timber.d("resumeCamera isRecognition = $isRecognition")
|
||||||
isRecognition = true
|
isRecognition = true
|
||||||
if (rgbCameraHelper != null && rgbCameraHelper!!.isStopped) {
|
if (rgbCameraHelper != null && rgbCameraHelper!!.isStopped) {
|
||||||
rgbCameraHelper!!.start()
|
rgbCameraHelper!!.start()
|
||||||
@@ -632,7 +766,7 @@ class SecondaryScreenPresentation(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun pauseCamera() {
|
fun pauseCamera() {
|
||||||
Timber.d("pauseCamera")
|
Timber.d("pauseCamera isRecognition = $isRecognition")
|
||||||
isRecognition = false
|
isRecognition = false
|
||||||
|
|
||||||
recognizeViewModel.onPreviewFrame(ByteArray(1382400), true)
|
recognizeViewModel.onPreviewFrame(ByteArray(1382400), true)
|
||||||
|
|||||||
@@ -2,8 +2,12 @@ package com.sw.dualscreen.presentation
|
|||||||
|
|
||||||
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
|
import com.sw.dualscreen.ext.toSafeDouble
|
||||||
import com.sw.dualscreen.model.response.FoodInfo
|
import com.sw.dualscreen.model.response.FoodInfo
|
||||||
import com.sw.dualscreen.model.response.UserNutritionData
|
import com.sw.dualscreen.model.response.UserNutritionData
|
||||||
|
import timber.log.Timber
|
||||||
|
import kotlin.math.max
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
object UserNutritionUtils {
|
object UserNutritionUtils {
|
||||||
|
|
||||||
@@ -16,12 +20,12 @@ object UserNutritionUtils {
|
|||||||
weight: Double,
|
weight: Double,
|
||||||
dinnerType: String
|
dinnerType: String
|
||||||
): CalcResultInfo {
|
): CalcResultInfo {
|
||||||
// 1. 初始化变量
|
// 初始化变量
|
||||||
var foodKcal = 0.0
|
var foodKcal = 0.0
|
||||||
var totalKcal = 0.0
|
var totalKcal = 0.0
|
||||||
var (vegetable, meat, fruits, grain) = List(4) { 0.0 }
|
var (vegetable, meat, fruits, grain) = List(4) { 0.0 }
|
||||||
|
|
||||||
// 2. 处理食物信息
|
// 处理食物信息
|
||||||
foodKcal = calculateValue(foodInfo.stFoodInfoMaterial?.energyKcal, weight)
|
foodKcal = calculateValue(foodInfo.stFoodInfoMaterial?.energyKcal, weight)
|
||||||
|
|
||||||
grain = calculateValue(foodInfo.stFoodInfoPagodaAPPVO?.grainValue(), weight)
|
grain = calculateValue(foodInfo.stFoodInfoPagodaAPPVO?.grainValue(), weight)
|
||||||
@@ -29,33 +33,52 @@ object UserNutritionUtils {
|
|||||||
vegetable = calculateValue(foodInfo.stFoodInfoPagodaAPPVO?.vegetableValue(), weight)
|
vegetable = calculateValue(foodInfo.stFoodInfoPagodaAPPVO?.vegetableValue(), weight)
|
||||||
meat = calculateValue(foodInfo.stFoodInfoPagodaAPPVO?.meatValue(), weight)
|
meat = calculateValue(foodInfo.stFoodInfoPagodaAPPVO?.meatValue(), weight)
|
||||||
|
|
||||||
// 3. 合并用户数据
|
// 合并用户数据
|
||||||
grain += userModel.stFoodInfoPagoda?.grainValue() ?: 0.0
|
grain += userModel.stFoodInfoPagoda?.grainValue() ?: 0.0
|
||||||
fruits += userModel.stFoodInfoPagoda?.fruitsValue() ?: 0.0
|
fruits += userModel.stFoodInfoPagoda?.fruitsValue() ?: 0.0
|
||||||
vegetable = vegetable.plus(userModel.stFoodInfoPagoda?.vegetableValue() ?: 0.0)
|
vegetable = vegetable.plus(userModel.stFoodInfoPagoda?.vegetableValue() ?: 0.0)
|
||||||
meat += userModel.stFoodInfoPagoda?.meatValue() ?: 0.0
|
meat += userModel.stFoodInfoPagoda?.meatValue() ?: 0.0
|
||||||
|
|
||||||
totalKcal = foodKcal + userModel.energyValue()
|
totalKcal = foodKcal + userModel.energyValue()
|
||||||
val fruitsAndVeg = fruits + vegetable
|
if (totalKcal < 0){
|
||||||
|
totalKcal = 0.0
|
||||||
// 4. 判断当餐最大热量
|
}
|
||||||
|
// 判断当餐最大热量
|
||||||
val maxKcal =
|
val maxKcal =
|
||||||
userModel.totalEnergyCalculateScoreValue() * calculateDinnerTypeRatio(dinnerType) / 10
|
userModel.totalEnergyCalculateScoreValue() * calculateDinnerTypeRatio(dinnerType) / 10
|
||||||
|
|
||||||
val pagoda = userModel.stFoodInfoPagoda
|
val pagoda = userModel.stFoodInfoPagoda
|
||||||
|
val fruitsInfo = parseRecommend(pagoda?.fruitsRecommend, fruits)
|
||||||
|
val vegetableInfo = parseRecommend(pagoda?.vegetableRecommend, vegetable)
|
||||||
val calcResult = CalcResultInfo(
|
val calcResult = CalcResultInfo(
|
||||||
totalKcal = maxKcal to totalKcal,
|
totalKcal = CalcInfo(
|
||||||
grain = parseMax(pagoda?.grainRecommend) to grain,
|
max = maxKcal,
|
||||||
fruits = parseMax(pagoda?.fruitsRecommend) + parseMax(pagoda?.vegetableRecommend) to fruitsAndVeg,
|
min = 0.0,
|
||||||
meat = parseMax(pagoda?.meatRecommend) to meat
|
current = totalKcal
|
||||||
|
),
|
||||||
|
grain = parseRecommend(pagoda?.grainRecommend, grain),
|
||||||
|
fruits = CalcInfo( // 果蔬 = 水果+蔬菜
|
||||||
|
max = fruitsInfo.max + vegetableInfo.max,
|
||||||
|
min = fruitsInfo.min + vegetableInfo.min,
|
||||||
|
current = fruitsInfo.current + vegetableInfo.current
|
||||||
|
),
|
||||||
|
meat = parseRecommend(pagoda?.meatRecommend, meat)
|
||||||
)
|
)
|
||||||
return calcResult
|
return calcResult
|
||||||
}
|
}
|
||||||
|
|
||||||
fun parseMax(recommend: String?): Double {
|
fun parseRecommend(recommend: String?, current: Double): CalcInfo {
|
||||||
if (TextUtils.isEmpty(recommend) || !recommend!!.contains("-")) return 0.0
|
var newCurrent = if (current < 0) 0.0 else current
|
||||||
|
if (TextUtils.isEmpty(recommend) || !recommend!!.contains("-")) {
|
||||||
|
Timber.e("parseRecommend recommend 格式错误")
|
||||||
|
return CalcInfo(
|
||||||
|
newCurrent,
|
||||||
|
0.0,
|
||||||
|
newCurrent
|
||||||
|
)
|
||||||
|
}
|
||||||
val (min, max) = recommend.split("-")
|
val (min, max) = recommend.split("-")
|
||||||
return max.toDouble()
|
return CalcInfo(max = max.toSafeDouble(), min = min.toSafeDouble(), current = newCurrent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun calculateDinnerTypeRatio(dinnerType: String): Int {
|
private fun calculateDinnerTypeRatio(dinnerType: String): Int {
|
||||||
@@ -66,14 +89,28 @@ object UserNutritionUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算每百克含量
|
||||||
|
*/
|
||||||
private fun calculateValue(nutrient: Double?, weight: Double): Double {
|
private fun calculateValue(nutrient: Double?, weight: Double): Double {
|
||||||
return weight * (nutrient ?: 1.0)
|
return weight * (nutrient ?: 1.0) / 100
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getMaxInt(calcResultInfo: CalcResultInfo): Int {
|
||||||
|
val max1 = max(calcResultInfo.grain.max, calcResultInfo.fruits.max)
|
||||||
|
return max(max1, calcResultInfo.meat.max).toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
data class CalcInfo(
|
||||||
|
var max: Double,
|
||||||
|
var min: Double,
|
||||||
|
var current: Double,
|
||||||
|
)
|
||||||
|
|
||||||
data class CalcResultInfo(
|
data class CalcResultInfo(
|
||||||
var totalKcal: Pair<Double, Double>,
|
var totalKcal: CalcInfo,
|
||||||
var grain: Pair<Double, Double>,
|
var grain: CalcInfo,
|
||||||
var fruits: Pair<Double, Double>,
|
var fruits: CalcInfo,
|
||||||
var meat: Pair<Double, Double>,
|
var meat: CalcInfo,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -99,7 +99,7 @@ class RemoteRepository constructor(
|
|||||||
* 提交就餐数据
|
* 提交就餐数据
|
||||||
*/
|
*/
|
||||||
suspend fun postUserNutritionData(
|
suspend fun postUserNutritionData(
|
||||||
param: UserNutritionParam
|
param: List<UserNutritionParam>
|
||||||
): ApiResponse<String> {
|
): ApiResponse<String> {
|
||||||
return safeApiCall { apiService.postUserNutritionData(param = param) }
|
return safeApiCall { apiService.postUserNutritionData(param = param) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.sw.dualscreen.sdk
|
package com.sw.dualscreen.sdk
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import com.sw.plate.utils.ToastUtils
|
import com.sw.plate.utils.ToastUtils
|
||||||
import com.wabon.wbintelligenthardwaresdk.api.SensorScale
|
import com.wabon.wbintelligenthardwaresdk.api.SensorScale
|
||||||
import com.wabon.wbintelligenthardwaresdk.api.SensorScale.OnScaleResult
|
import com.wabon.wbintelligenthardwaresdk.api.SensorScale.OnScaleResult
|
||||||
@@ -19,7 +18,10 @@ object SensorScaleUtils {
|
|||||||
const val baudRate = 115200
|
const val baudRate = 115200
|
||||||
private var mSensorScale: SensorScale? = null
|
private var mSensorScale: SensorScale? = null
|
||||||
private var isOpened: Boolean = false
|
private var isOpened: Boolean = false
|
||||||
|
private var isRead: Boolean = false
|
||||||
|
private var isZero = false
|
||||||
private var callback: Callback? = {}
|
private var callback: Callback? = {}
|
||||||
|
private var lastWeight: Double? = null
|
||||||
|
|
||||||
private fun init() {
|
private fun init() {
|
||||||
mSensorScale = SensorScale(object : OnScaleResult {
|
mSensorScale = SensorScale(object : OnScaleResult {
|
||||||
@@ -35,7 +37,8 @@ object SensorScaleUtils {
|
|||||||
}
|
}
|
||||||
// Log.d(TAG, "readWeight state = ${stateStr}, weight = $value")
|
// Log.d(TAG, "readWeight state = ${stateStr}, weight = $value")
|
||||||
// 只使用稳定值
|
// 只使用稳定值
|
||||||
if (state == SensorScale.STATE_STABLE) {
|
if (state == SensorScale.STATE_STABLE && lastWeight != value) {
|
||||||
|
lastWeight = value
|
||||||
callback?.invoke(value)
|
callback?.invoke(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,11 +47,11 @@ object SensorScaleUtils {
|
|||||||
* 读取鉴别率
|
* 读取鉴别率
|
||||||
*/
|
*/
|
||||||
override fun readIdentify(rate: Int) {
|
override fun readIdentify(rate: Int) {
|
||||||
Log.e(TAG, "readIdentify rate = $rate")
|
Timber.e("readIdentify rate = $rate")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun fail(errCode: Int) {
|
override fun fail(errCode: Int) {
|
||||||
Log.e(TAG, "fail code = $errCode")
|
Timber.e("fail code = $errCode")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -58,24 +61,24 @@ object SensorScaleUtils {
|
|||||||
* @param autoScale 是否开启自动读取
|
* @param autoScale 是否开启自动读取
|
||||||
*/
|
*/
|
||||||
fun startScale(autoScale: Boolean = true, callback: Callback?) {
|
fun startScale(autoScale: Boolean = true, callback: Callback?) {
|
||||||
|
Timber.d("startScale isOpened = $isOpened")
|
||||||
if (isOpened) {
|
if (isOpened) {
|
||||||
startContinuousRead(callback = callback)
|
startContinuousRead(callback = callback)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.callback = callback
|
this.callback = callback
|
||||||
init()
|
init()
|
||||||
mSensorScale?.zero {
|
|
||||||
Timber.d("startScale: zero")
|
|
||||||
}
|
|
||||||
mSensorScale?.openScale(serialPort, baudRate) { open ->
|
mSensorScale?.openScale(serialPort, baudRate) { open ->
|
||||||
isOpened = open
|
isOpened = open
|
||||||
Timber.d("isOpened = $isOpened")
|
Timber.d("isOpened = $isOpened")
|
||||||
if (open) {
|
if (open) {
|
||||||
|
// zero()
|
||||||
if (autoScale) {
|
if (autoScale) {
|
||||||
Thread({
|
Thread({
|
||||||
Thread.sleep(1000)
|
Thread.sleep(1000)
|
||||||
// 打开后需要等待后才能调用,否则会 1001 SDK未初始化
|
// 打开后需要等待后才能调用,否则会 1001 SDK未初始化
|
||||||
mSensorScale?.startContinuousRead()
|
mSensorScale?.startContinuousRead()
|
||||||
|
|
||||||
}).start()
|
}).start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,23 +89,24 @@ object SensorScaleUtils {
|
|||||||
* 开启自动读取重量
|
* 开启自动读取重量
|
||||||
*/
|
*/
|
||||||
fun startContinuousRead(callback: Callback?) {
|
fun startContinuousRead(callback: Callback?) {
|
||||||
this.callback = callback
|
Timber.d("开启自动读取 = $isRead")
|
||||||
Log.d(TAG, "开启自动读取 = $isOpened")
|
if (isRead) {
|
||||||
if (!isOpened) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
this.callback = callback
|
||||||
mSensorScale?.startContinuousRead()
|
mSensorScale?.startContinuousRead()
|
||||||
|
isRead = true
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 手动读取重量
|
* 手动读取重量
|
||||||
*/
|
*/
|
||||||
fun readWeight(callback: Callback?) {
|
fun readWeight(callback: Callback?) {
|
||||||
this.callback = callback
|
Timber.d("isOpened = $isOpened")
|
||||||
Log.d(TAG, "isOpened = $isOpened")
|
|
||||||
if (!isOpened) {
|
if (!isOpened) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
this.callback = callback
|
||||||
mSensorScale?.readWeight()
|
mSensorScale?.readWeight()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,9 +114,11 @@ object SensorScaleUtils {
|
|||||||
* 零位标定
|
* 零位标定
|
||||||
*/
|
*/
|
||||||
fun zero() {
|
fun zero() {
|
||||||
if (!isOpened) return
|
Timber.d("zero isZero = $isZero")
|
||||||
|
if (isZero) return
|
||||||
|
isZero = true
|
||||||
mSensorScale?.zero {
|
mSensorScale?.zero {
|
||||||
Log.d(TAG, "零位标定操作成功")
|
Timber.d("零位标定操作成功")
|
||||||
ToastUtils.showToast("零位标定操作成功")
|
ToastUtils.showToast("零位标定操作成功")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,7 +129,7 @@ object SensorScaleUtils {
|
|||||||
fun tare() {
|
fun tare() {
|
||||||
if (!isOpened) return
|
if (!isOpened) return
|
||||||
mSensorScale?.tare {
|
mSensorScale?.tare {
|
||||||
Log.d(TAG, "去皮置零操作成功")
|
Timber.d("去皮置零操作成功")
|
||||||
ToastUtils.showToast("去皮置零操作成功")
|
ToastUtils.showToast("去皮置零操作成功")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,18 +138,19 @@ object SensorScaleUtils {
|
|||||||
* 停止自动读取重量
|
* 停止自动读取重量
|
||||||
*/
|
*/
|
||||||
fun stopContinuousRead() {
|
fun stopContinuousRead() {
|
||||||
Log.d(TAG, "isOpened = $isOpened")
|
Timber.d("isOpened = $isOpened")
|
||||||
if (!isOpened) {
|
if (!isOpened) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
mSensorScale?.stopContinuousRead()
|
mSensorScale?.stopContinuousRead()
|
||||||
|
isRead = false
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 关闭称重
|
* 关闭称重
|
||||||
*/
|
*/
|
||||||
fun closeScale() {
|
fun closeScale() {
|
||||||
Log.d(TAG, "closeScale")
|
Timber.d("closeScale")
|
||||||
isOpened = false
|
isOpened = false
|
||||||
mSensorScale?.closeScale()
|
mSensorScale?.closeScale()
|
||||||
mSensorScale == null
|
mSensorScale == null
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.sw.dualscreen.view
|
package com.sw.dualscreen.view
|
||||||
|
|
||||||
|
import android.content.DialogInterface
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
import android.graphics.drawable.ColorDrawable
|
import android.graphics.drawable.ColorDrawable
|
||||||
@@ -7,6 +8,7 @@ import android.os.Bundle
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.view.inputmethod.EditorInfo
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
@@ -29,6 +31,7 @@ class CustomBottomSheetDialog(
|
|||||||
private lateinit var binding: BottomSheetDialogBinding
|
private lateinit var binding: BottomSheetDialogBinding
|
||||||
private lateinit var adapter: GenericItemAdapter<FoodInfo, ItemSearchFoodInfoBinding>
|
private lateinit var adapter: GenericItemAdapter<FoodInfo, ItemSearchFoodInfoBinding>
|
||||||
private var checkedItem: FoodInfo? = null
|
private var checkedItem: FoodInfo? = null
|
||||||
|
private val debouncer = Debouncer(2000)
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
@@ -41,13 +44,8 @@ class CustomBottomSheetDialog(
|
|||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
val debouncer = Debouncer(3000)
|
|
||||||
initView()
|
initView()
|
||||||
binding.ivSearch.setOnClickListener {
|
|
||||||
debouncer.debounce {
|
|
||||||
viewModel.searchByFoodName(binding.etSearch.text.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@@ -82,6 +80,7 @@ class CustomBottomSheetDialog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun initView() {
|
private fun initView() {
|
||||||
|
binding.viewClose.setOnClickListener { dismiss() }
|
||||||
adapter = createAdapter()
|
adapter = createAdapter()
|
||||||
binding.recyclerview.layoutManager = GridLayoutManager(context, 2)
|
binding.recyclerview.layoutManager = GridLayoutManager(context, 2)
|
||||||
// 添加间距装饰(12dp)
|
// 添加间距装饰(12dp)
|
||||||
@@ -93,9 +92,26 @@ class CustomBottomSheetDialog(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
binding.recyclerview.adapter = adapter
|
binding.recyclerview.adapter = adapter
|
||||||
|
binding.ivSearch.setOnClickListener {
|
||||||
|
searchInfo()
|
||||||
|
}
|
||||||
|
binding.etSearch.setOnEditorActionListener { _, actionId, _ ->
|
||||||
|
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
|
||||||
|
searchInfo()
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
registerDataChange()
|
registerDataChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun searchInfo(){
|
||||||
|
debouncer.debounce {
|
||||||
|
viewModel.searchByFoodName(binding.etSearch.text.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun registerDataChange() {
|
private fun registerDataChange() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
viewModel.searchFoodInfoList.collect {
|
viewModel.searchFoodInfoList.collect {
|
||||||
@@ -131,7 +147,14 @@ class CustomBottomSheetDialog(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCancel(dialog: DialogInterface) {
|
||||||
|
Timber.d("onCancel")
|
||||||
|
viewModel.cleanSearchFoodInfoList()
|
||||||
|
super.onCancel(dialog)
|
||||||
|
}
|
||||||
|
|
||||||
override fun dismiss() {
|
override fun dismiss() {
|
||||||
|
Timber.d("dismiss")
|
||||||
viewModel.cleanSearchFoodInfoList()
|
viewModel.cleanSearchFoodInfoList()
|
||||||
super.dismiss()
|
super.dismiss()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import android.view.animation.AlphaAnimation;
|
|||||||
import com.sw.dualscreen.R;
|
import com.sw.dualscreen.R;
|
||||||
import com.sw.plate.utils.AppUtil;
|
import com.sw.plate.utils.AppUtil;
|
||||||
|
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class CustomImageView extends androidx.appcompat.widget.AppCompatImageView {
|
public class CustomImageView extends androidx.appcompat.widget.AppCompatImageView {
|
||||||
private AlphaAnimation alphaAnimation;
|
private AlphaAnimation alphaAnimation;
|
||||||
private String textToDraw;
|
private String textToDraw;
|
||||||
@@ -95,11 +97,22 @@ public class CustomImageView extends androidx.appcompat.widget.AppCompatImageVie
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setCustomHeightPercent(float percent, boolean isAnimation) {
|
public void setCustomHeightPercent(float percent, boolean isAnimation) {
|
||||||
setCustomHeight((int) (percent * MAX_HEIGHT), isAnimation);
|
Timber.d("setCustomHeightPercent percent = %s", percent);
|
||||||
|
if (percent<= 0){
|
||||||
|
percent = 0f;
|
||||||
|
}
|
||||||
|
setCustomHeight((int) (percent * MAX_HEIGHT), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用于设置自定义高度的方法
|
// 用于设置自定义高度的方法
|
||||||
public void setCustomHeight(int height, boolean isAnimation) {
|
public void setCustomHeight(int height, boolean isAnimation) {
|
||||||
|
Timber.d("setCustomHeight height = %s", height);
|
||||||
|
if (height > 0) {
|
||||||
|
height += 30;
|
||||||
|
}
|
||||||
|
if (height > MAX_HEIGHT){
|
||||||
|
height = MAX_HEIGHT;
|
||||||
|
}
|
||||||
ViewGroup.LayoutParams layoutParams = getLayoutParams();
|
ViewGroup.LayoutParams layoutParams = getLayoutParams();
|
||||||
int height1 = layoutParams.height;
|
int height1 = layoutParams.height;
|
||||||
if (height1 != height) {
|
if (height1 != height) {
|
||||||
|
|||||||
@@ -68,7 +68,6 @@ abstract class BaseViewModel() : ViewModel() {
|
|||||||
GlobalData.restId = equipmentInfo.canteenId!!
|
GlobalData.restId = equipmentInfo.canteenId!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun handleError(exception: Exception) {
|
fun handleError(exception: Exception) {
|
||||||
Timber.d("handleError ${exception.message}")
|
Timber.d("handleError ${exception.message}")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class UserViewModel : BaseViewModel() {
|
|||||||
val nutritionData: StateFlow<UserNutritionData?> = _nutritionData
|
val nutritionData: StateFlow<UserNutritionData?> = _nutritionData
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 饭点类型
|
* 饭点类型 早餐/午餐/晚餐
|
||||||
*/
|
*/
|
||||||
private val _dinnerTypeInfo = MutableStateFlow<DinnerTypeInfo?>(null)
|
private val _dinnerTypeInfo = MutableStateFlow<DinnerTypeInfo?>(null)
|
||||||
val dinnerTypeInfo: StateFlow<DinnerTypeInfo?> = _dinnerTypeInfo
|
val dinnerTypeInfo: StateFlow<DinnerTypeInfo?> = _dinnerTypeInfo
|
||||||
@@ -179,6 +179,7 @@ class UserViewModel : BaseViewModel() {
|
|||||||
*/
|
*/
|
||||||
fun getUserNutritionData(userId: String, foodId: String) {
|
fun getUserNutritionData(userId: String, foodId: String) {
|
||||||
Timber.d("getUserNutritionData userId = ${userId}, foodId = $foodId")
|
Timber.d("getUserNutritionData userId = ${userId}, foodId = $foodId")
|
||||||
|
_nutritionData.value = null
|
||||||
launchWithLoading {
|
launchWithLoading {
|
||||||
val response = repository.getUserNutritionData(userId = userId, foodId = foodId)
|
val response = repository.getUserNutritionData(userId = userId, foodId = foodId)
|
||||||
if (parseResponse(response)) {
|
if (parseResponse(response)) {
|
||||||
@@ -200,9 +201,9 @@ class UserViewModel : BaseViewModel() {
|
|||||||
/**
|
/**
|
||||||
* 提交就餐数据
|
* 提交就餐数据
|
||||||
*/
|
*/
|
||||||
fun postUserNutritionData(param: UserNutritionParam) {
|
fun postUserNutritionData(param: List<UserNutritionParam>) {
|
||||||
Timber.d("postUserNutritionData param = $param")
|
Timber.d("postUserNutritionData param = $param")
|
||||||
launchWithLoading {
|
launch {
|
||||||
val response = repository.postUserNutritionData(param)
|
val response = repository.postUserNutritionData(param)
|
||||||
if (parseResponse(response)) {
|
if (parseResponse(response)) {
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
android:paddingBottom="0dp">
|
android:paddingBottom="0dp">
|
||||||
|
|
||||||
<View
|
<View
|
||||||
|
android:id="@+id/view_close"
|
||||||
android:layout_width="90dp"
|
android:layout_width="90dp"
|
||||||
android:layout_height="10dp"
|
android:layout_height="10dp"
|
||||||
android:background="@drawable/view_shape_point" />
|
android:background="@drawable/view_shape_point" />
|
||||||
@@ -41,6 +42,7 @@
|
|||||||
android:hint="输入菜品名称"
|
android:hint="输入菜品名称"
|
||||||
android:imeOptions="actionSearch"
|
android:imeOptions="actionSearch"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
|
android:inputType="text"
|
||||||
android:textColor="#333333"
|
android:textColor="#333333"
|
||||||
android:textColorHint="#ffc8c8c8"
|
android:textColorHint="#ffc8c8c8"
|
||||||
android:textSize="36sp" />
|
android:textSize="36sp" />
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:paddingTop="1dp"
|
android:paddingTop="1dp"
|
||||||
android:visibility="gone">
|
android:visibility="visible">
|
||||||
|
|
||||||
<TextureView
|
<TextureView
|
||||||
android:id="@+id/dual_camera_texture_preview_rgb"
|
android:id="@+id/dual_camera_texture_preview_rgb"
|
||||||
@@ -29,8 +29,25 @@
|
|||||||
android:id="@+id/dual_camera_face_rect_view"
|
android:id="@+id/dual_camera_face_rect_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent" />
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/fl_recognize_ir"
|
||||||
|
android:layout_width="150dp"
|
||||||
|
android:layout_height="200dp"
|
||||||
|
android:layout_gravity="bottom">
|
||||||
|
|
||||||
|
<TextureView
|
||||||
|
android:id="@+id/dual_camera_texture_preview_ir"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
<com.sw.plate.utils.arcface.FaceRectView
|
||||||
|
android:id="@+id/dual_camera_face_rect_view_ir"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
</FrameLayout>
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/iv_face_bg"
|
android:id="@+id/iv_face_bg"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -38,7 +55,7 @@
|
|||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:background="@drawable/bg_face"
|
android:background="@drawable/bg_face"
|
||||||
android:contentDescription="相机遮盖层"
|
android:contentDescription="相机遮盖层"
|
||||||
android:visibility="visible" />
|
android:visibility="gone" />
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -55,7 +72,7 @@
|
|||||||
android:visibility="visible">
|
android:visibility="visible">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tv_food_info"
|
android:id="@+id/tv_food_name"
|
||||||
style="@style/text_title_blob_2"
|
style="@style/text_title_blob_2"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<string name="app_name">档口双屏</string>
|
<string name="app_name">档口双屏</string>
|
||||||
<string name="specific_engine_init_failed">%s 初始化失败,错误码:%d\n错误码常量名:%s</string>
|
<string name="specific_engine_init_failed">%s 初始化失败,错误码:%d\n错误码常量名:%s</string>
|
||||||
<string name="permission_denied">权限被拒绝!</string>
|
<string name="permission_denied">权限被拒绝!</string>
|
||||||
|
<string name="camera_error_notice">\n可能的原因:该设备不支持同时打开两个摄像头</string>
|
||||||
|
|
||||||
<array name="detection_method">
|
<array name="detection_method">
|
||||||
<item>即放即取</item>
|
<item>即放即取</item>
|
||||||
|
|||||||
@@ -129,7 +129,8 @@ public class DualCameraHelper implements Camera.PreviewCallback {
|
|||||||
//相机数量为2则打开1,1则打开0,相机ID 1为前置,0为后置
|
//相机数量为2则打开1,1则打开0,相机ID 1为前置,0为后置
|
||||||
mCameraId = Camera.getNumberOfCameras() - 1;
|
mCameraId = Camera.getNumberOfCameras() - 1;
|
||||||
//若指定了相机ID且该相机存在,则打开指定的相机
|
//若指定了相机ID且该相机存在,则打开指定的相机
|
||||||
if (specificCameraId != null && specificCameraId <= mCameraId) {
|
// if (specificCameraId != null && specificCameraId <= mCameraId) {
|
||||||
|
if (specificCameraId != null) {
|
||||||
mCameraId = specificCameraId;
|
mCameraId = specificCameraId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
|
|||||||
|
|
||||||
private MutableLiveData<String> drawRectInfoText = new MutableLiveData<>();
|
private MutableLiveData<String> drawRectInfoText = new MutableLiveData<>();
|
||||||
|
|
||||||
private MutableLiveData<String> recognizeUserId = new MutableLiveData<>();
|
private MutableLiveData<CompareResult> recognizeUserId = new MutableLiveData<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检测ir活体前,是否需要更新faceData
|
* 检测ir活体前,是否需要更新faceData
|
||||||
@@ -475,7 +475,7 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
|
|||||||
Disposable disposable = Observable.just(true).observeOn(AndroidSchedulers.mainThread()).subscribe(aBoolean -> {
|
Disposable disposable = Observable.just(true).observeOn(AndroidSchedulers.mainThread()).subscribe(aBoolean -> {
|
||||||
if (similarPass) {
|
if (similarPass) {
|
||||||
if (recognizeUserId != null) {
|
if (recognizeUserId != null) {
|
||||||
recognizeUserId.postValue(compareResult.getFaceEntity().getUserName());
|
recognizeUserId.postValue(compareResult);
|
||||||
}
|
}
|
||||||
boolean isAdded = false;
|
boolean isAdded = false;
|
||||||
List<CompareResult> compareResults = compareResultList.getValue();
|
List<CompareResult> compareResults = compareResultList.getValue();
|
||||||
@@ -541,7 +541,7 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
|
|||||||
return recognizeNotice;
|
return recognizeNotice;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MutableLiveData<String> getRecognizeUserId() {
|
public MutableLiveData<CompareResult> getRecognizeUserId() {
|
||||||
return recognizeUserId;
|
return recognizeUserId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user