调整了个人营养数据计算逻辑,修复了摄像头逻辑
This commit is contained in:
@@ -124,11 +124,7 @@ 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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
@@ -68,8 +66,6 @@ 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 lateinit var cameraManager: CameraManager
|
|
||||||
private var cameraIdList: Array<String> = arrayOf()
|
|
||||||
|
|
||||||
// 当前食物信息
|
// 当前食物信息
|
||||||
private var currentFood: FoodInfo? = null
|
private var currentFood: FoodInfo? = null
|
||||||
@@ -82,7 +78,7 @@ class SecondaryScreenPresentation(
|
|||||||
// 上一次的计算热量结果
|
// 上一次的计算热量结果
|
||||||
private var lastCalcResultInfo: UserNutritionUtils.CalcResultInfo? = null
|
private var lastCalcResultInfo: UserNutritionUtils.CalcResultInfo? = null
|
||||||
private var lastWeight = 0.0
|
private var lastWeight = 0.0
|
||||||
private val debouncer = Debouncer(2000)
|
private val debouncer = Debouncer(500)
|
||||||
private var faceTime: Long = 0L // 人脸识别时的时间
|
private var faceTime: Long = 0L // 人脸识别时的时间
|
||||||
private var recognitionWeight: Double = 0.0 // 人脸识别时的重量
|
private var recognitionWeight: Double = 0.0 // 人脸识别时的重量
|
||||||
private var lastFaceTrackId: Int = 0 // 上一次的人脸信息
|
private var lastFaceTrackId: Int = 0 // 上一次的人脸信息
|
||||||
@@ -113,7 +109,7 @@ class SecondaryScreenPresentation(
|
|||||||
itemUserNutritionBinding.tvRecommendHeat.text =
|
itemUserNutritionBinding.tvRecommendHeat.text =
|
||||||
"推荐热量:${it.recommendMin}-${it.recommendMax}"
|
"推荐热量:${it.recommendMin}-${it.recommendMax}"
|
||||||
|
|
||||||
updateWeight(lastWeight)
|
updateWeight(lastWeight / 1000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
activity.lifecycleScope.launch {
|
activity.lifecycleScope.launch {
|
||||||
@@ -246,89 +242,95 @@ class SecondaryScreenPresentation(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun updateWeight(weight: Double) {
|
fun updateWeight(weight: Double) {
|
||||||
Timber.d("updateWeight weight = $weight")
|
lastWeight = weight * 1000 // 将千克转成克
|
||||||
lastWeight = weight
|
|
||||||
Timber.d("updateWeight lastWeight = $weight")
|
Timber.d("updateWeight lastWeight = $weight")
|
||||||
if (currentFood == null) {
|
if (currentFood == null) {
|
||||||
Timber.e("updateWeight currentFood is null")
|
Timber.e("updateWeight currentFood is null")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (userNutritionData == null) {
|
if (userNutritionData == null) {
|
||||||
Timber.e("updateWeight userNutritionData is null")
|
Timber.e("updateWeight userNutritionData is null")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (dinnerTypeInfo == null) {
|
if (dinnerTypeInfo == null) {
|
||||||
Timber.e("updateWeight dinnerType is null")
|
Timber.e("updateWeight dinnerType is null")
|
||||||
return
|
return
|
||||||
}
|
|
||||||
// 重量和 当前食物都没变,则不再计算
|
|
||||||
if (lastFood == currentFood) {
|
|
||||||
// Timber.e("updateWeight 物品未改变")
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
lastFood = currentFood
|
calculateNutrition(recognitionWeight - lastWeight)
|
||||||
debouncer.debounce {
|
}
|
||||||
val dinnerType = dinnerTypeInfo!!.dinnerType!!.dinnerType!!
|
|
||||||
Timber.d("updateWeight foodName = ${currentFood!!.foodName}, dinnerType = $dinnerType, userId = ${userNutritionData!!.userId}")
|
private fun calculateNutrition(weight: Double) {
|
||||||
val calcResultInfo = UserNutritionUtils.calculateNutrition(
|
debouncer.debounce {
|
||||||
currentFood!!,
|
Timber.d("calculateNutrition weight = $weight, recognitionWeight = $recognitionWeight")
|
||||||
userNutritionData!!,
|
val dinnerType = dinnerTypeInfo!!.dinnerType!!.dinnerType!!
|
||||||
weight,
|
Timber.d("calculateNutrition foodName = ${currentFood!!.foodName}, dinnerType = $dinnerType, userId = ${userNutritionData!!.userId}")
|
||||||
dinnerType = dinnerType
|
val calcResultInfo = UserNutritionUtils.calculateNutrition(
|
||||||
)
|
currentFood!!,
|
||||||
Timber.d("updateWeight calcResultInfo = $calcResultInfo")
|
userNutritionData!!,
|
||||||
if (lastCalcResultInfo == calcResultInfo) {
|
weight,
|
||||||
Timber.d("updateWeight 与上次计算结果无变化")
|
dinnerType = dinnerType
|
||||||
return@debounce
|
)
|
||||||
}
|
Timber.d("calculateNutrition calcResultInfo = $calcResultInfo")
|
||||||
lastCalcResultInfo = calcResultInfo
|
if (lastCalcResultInfo == calcResultInfo) {
|
||||||
activity.runOnUiThread {
|
Timber.d("calculateNutrition 与上次计算结果无变化")
|
||||||
val totalKcal = calcResultInfo.totalKcal
|
return@debounce
|
||||||
val grain = calcResultInfo.grain
|
|
||||||
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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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
|
||||||
|
)
|
||||||
|
var maxKcal = totalKcal.max
|
||||||
|
Timber.d("maxKcal = $maxKcal")
|
||||||
|
if (maxKcal <= 0.0) {
|
||||||
|
maxKcal = 100.0
|
||||||
|
}
|
||||||
|
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 openCamera() {
|
private fun openCamera() {
|
||||||
@@ -340,50 +342,22 @@ class SecondaryScreenPresentation(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
|
val cameraCount = Camera.getNumberOfCameras()
|
||||||
// 获取摄像头列表
|
if (cameraCount < 3){
|
||||||
cameraIdList = cameraManager.cameraIdList
|
|
||||||
if (cameraIdList.isEmpty()) {
|
|
||||||
ToastUtils.showToast("没有可用的摄像头")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (cameraIdList.size < 3) {
|
|
||||||
ToastUtils.showToast("摄像头数量异常")
|
ToastUtils.showToast("摄像头数量异常")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
recognizeViewModel.init(
|
||||||
cameraIdList.forEach {
|
PreviewConfig(
|
||||||
Timber.d("摄像头id列表 = $it")
|
2, 1, 90, 90
|
||||||
|
)
|
||||||
|
)
|
||||||
|
initRgbCamera()
|
||||||
|
if (DualCameraHelper.hasDualCamera() && livenessType === LivenessType.IR) {
|
||||||
|
initIrCamera()
|
||||||
}
|
}
|
||||||
val cameraId = cameraIdList.last()
|
|
||||||
Timber.d("openCamera cameraId = $cameraId")
|
|
||||||
val c1 = cameraIdList[0]
|
|
||||||
val c2 = cameraIdList[1]
|
|
||||||
val c3 = cameraIdList[2]
|
|
||||||
cameraManager.openCamera(c3, object : CameraDevice.StateCallback() {
|
|
||||||
override fun onOpened(device: CameraDevice) {
|
|
||||||
recognizeViewModel.init(
|
|
||||||
PreviewConfig(
|
|
||||||
c3.toInt(), c2.toInt(), 90, 90
|
|
||||||
)
|
|
||||||
)
|
|
||||||
initRgbCamera()
|
|
||||||
// resumeCamera()
|
|
||||||
if (DualCameraHelper.hasDualCamera() && livenessType === LivenessType.IR) {
|
|
||||||
initIrCamera()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDisconnected(device: CameraDevice) {
|
|
||||||
device.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(device: CameraDevice, error: Int) {
|
|
||||||
device.close()
|
|
||||||
}
|
|
||||||
}, null)
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("Camera2", "打开摄像头失败", e)
|
Timber.e(e, "打开摄像头失败")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -441,21 +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")
|
||||||
})
|
})
|
||||||
|
|
||||||
recognizeViewModel.recognizeUserId.observe(
|
recognizeViewModel.recognizeUserId.observe(
|
||||||
activity,
|
activity,
|
||||||
Observer { compareResult: CompareResult ->
|
Observer { compareResult: CompareResult ->
|
||||||
Timber.i("recognizeUserId observe compareResult = $compareResult")
|
Timber.i("recognizeUserId observe compareResult = ${compareResult.trackId}, userId = ${compareResult.faceEntity.userName}")
|
||||||
faceTime = System.currentTimeMillis()
|
faceTime = System.currentTimeMillis()
|
||||||
recognitionWeight = lastWeight
|
recognitionWeight = lastWeight
|
||||||
lastFaceTrackId = compareResult.trackId
|
lastFaceTrackId = compareResult.trackId
|
||||||
val faceEntity = compareResult.faceEntity
|
val faceEntity = compareResult.faceEntity
|
||||||
val userId = faceEntity.userName
|
val userId = faceEntity.userName
|
||||||
if (userId == null) return@Observer
|
if (userId == null) return@Observer
|
||||||
viewModel.getUserNutritionData(userId = userId, foodId = currentFood!!.id!!)
|
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")
|
||||||
@@ -727,6 +704,11 @@ class SecondaryScreenPresentation(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun postUserData() {
|
fun postUserData() {
|
||||||
|
Timber.d("postUserData")
|
||||||
|
if (userNutritionData == null || currentFood == null) {
|
||||||
|
Timber.d("postUserData userNutritionData = ${userNutritionData == null}, currentFood = ${currentFood == null}")
|
||||||
|
return
|
||||||
|
}
|
||||||
val userNutritionParam = UserNutritionParam(
|
val userNutritionParam = UserNutritionParam(
|
||||||
userId = userNutritionData?.userId!!,
|
userId = userNutritionData?.userId!!,
|
||||||
foodId = currentFood?.id!!,
|
foodId = currentFood?.id!!,
|
||||||
|
|||||||
@@ -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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -72,12 +72,13 @@ object SensorScaleUtils {
|
|||||||
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()
|
||||||
zero()
|
|
||||||
}).start()
|
}).start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,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
|
||||||
@@ -30,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,
|
||||||
@@ -42,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?) {
|
||||||
@@ -95,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 {
|
||||||
|
|||||||
@@ -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,16 +97,21 @@ public class CustomImageView extends androidx.appcompat.widget.AppCompatImageVie
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setCustomHeightPercent(float percent, boolean isAnimation) {
|
public void setCustomHeightPercent(float percent, boolean isAnimation) {
|
||||||
// if (percent<= 0){
|
Timber.d("setCustomHeightPercent percent = %s", percent);
|
||||||
// percent = 0.01f;
|
if (percent<= 0){
|
||||||
// }
|
percent = 0f;
|
||||||
setCustomHeight((int) (percent * MAX_HEIGHT), isAnimation);
|
}
|
||||||
|
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) {
|
if (height > 0) {
|
||||||
height += 40;
|
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;
|
||||||
|
|||||||
@@ -42,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" />
|
||||||
|
|||||||
Reference in New Issue
Block a user