feat(fragment): 实现调料秤重量增加高亮提醒功能
主设备订阅 allScales StateFlow 检测所有子设备秤重量变化, 超过10g且秤稳定时高亮对应格子,15秒后自动恢复
This commit is contained in:
@@ -1,7 +1,11 @@
|
||||
package com.shuwei.dish.match.ui.fragment
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.google.android.flexbox.AlignItems
|
||||
import com.google.android.flexbox.FlexDirection
|
||||
@@ -10,13 +14,28 @@ import com.google.android.flexbox.FlexboxLayoutManager
|
||||
import com.shuwei.dish.match.adapter.Seasoning18GridAdapter
|
||||
import com.shuwei.dish.match.adapter.Seasoning22GridAdapter
|
||||
import com.shuwei.dish.match.base.BaseFragment
|
||||
import com.shuwei.dish.match.base.DeviceRole
|
||||
import com.shuwei.dish.match.base.GlobalData
|
||||
import com.shuwei.dish.match.databinding.FragmentSeasoningConfigBinding
|
||||
import com.shuwei.dish.match.dialog.SeasoningSelectDialog
|
||||
import com.shuwei.dish.match.scale.ScaleDeviceConfig
|
||||
import com.shuwei.dish.match.scale.ScaleEvent
|
||||
import com.shuwei.dish.match.scale.ScaleServiceManager
|
||||
import com.shuwei.dish.match.ui.SettingActivity
|
||||
import com.shuwei.dish.match.utils.WeightUtil
|
||||
import com.shuwei.dish.match.utils.ext.dp
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "SeasoningConfigFragment"
|
||||
/** 重量增加阈值(克),超过此值才触发高亮 */
|
||||
private const val WEIGHT_DELTA_THRESHOLD = 10.0
|
||||
/** 高亮自动恢复时间(毫秒) */
|
||||
private const val HIGHLIGHT_DURATION_MS = 15_000L
|
||||
}
|
||||
|
||||
override fun inflateBinding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?
|
||||
@@ -28,11 +47,19 @@ class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
||||
private val adapter22 by lazy { buildAdapter22() }
|
||||
private val adapter18 by lazy { Seasoning18GridAdapter() }
|
||||
|
||||
/** 各秤地址的上一次重量快照,用于计算 delta */
|
||||
private val weightSnapshot = mutableMapOf<Int, Double>()
|
||||
/** 主线程 Handler,用于延迟清除高亮 */
|
||||
private val mainHandler = Handler(Looper.getMainLooper())
|
||||
/** 当前高亮清除任务,新高亮触发时取消旧任务 */
|
||||
private var clearHighlightRunnable: Runnable? = null
|
||||
|
||||
override fun initialize() {
|
||||
settingActivity = activity as SettingActivity
|
||||
// initViewModel()
|
||||
setupRecyclerViews()
|
||||
bindAdapterClicks()
|
||||
addWeightListener()
|
||||
loadData()
|
||||
}
|
||||
|
||||
@@ -108,4 +135,144 @@ class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
||||
// adapter18.update(list ?: emptyList())
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册重量监听,检测重量增加超过阈值的秤,高亮对应格子并通知主设备
|
||||
*
|
||||
* 策略:
|
||||
* - MASTER 设备:订阅 ScaleServiceManager.allScales StateFlow,覆盖本机+远端所有秤数据
|
||||
* - SLAVE 设备:注册 WeightUtil 监听(仅本机秤)并向主设备广播事件
|
||||
*
|
||||
* 基准更新策略:
|
||||
* - 首次收到数据:直接记录基准,不触发高亮
|
||||
* - delta > 阈值:触发高亮,同时更新基准防止重复触发
|
||||
* - STATE_STABLE:无条件更新基准(覆盖取走调料/归零等场景)
|
||||
*/
|
||||
private fun addWeightListener() {
|
||||
Log.d(TAG, "addWeightListener: 注册监听, deviceId=${GlobalData.deviceId}, role=${GlobalData.deviceRole}")
|
||||
|
||||
if (GlobalData.deviceRole == DeviceRole.MASTER) {
|
||||
// 主设备:通过 allScales StateFlow 观测所有秤(含远端子设备秤)
|
||||
val allScalesFlow = ScaleServiceManager.allScales
|
||||
if (allScalesFlow == null) {
|
||||
Log.w(TAG, "allScales 为 null,主设备聚合器未启动")
|
||||
return
|
||||
}
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
allScalesFlow.collect { scaleMap ->
|
||||
scaleMap.forEach { (key, data) ->
|
||||
val snapshotKey = data.address + data.deviceId.hashCode() * 1000
|
||||
val baseline = weightSnapshot[snapshotKey]
|
||||
if (baseline == null) {
|
||||
// 首次收到:记录基准
|
||||
weightSnapshot[snapshotKey] = data.weight
|
||||
return@forEach
|
||||
}
|
||||
val delta = data.weight - baseline
|
||||
Log.d(TAG, "allScales: key=$key address=${data.address} state=${data.state} weight=${data.weight} baseline=$baseline delta=$delta")
|
||||
when {
|
||||
// 仅在秤稳定后判断增量,避免取走物品时手部冲力造成误触发
|
||||
data.state == WeightUtil.STATE_STABLE && delta > WEIGHT_DELTA_THRESHOLD -> {
|
||||
weightSnapshot[snapshotKey] = data.weight
|
||||
Log.d(TAG, "allScales: 触发高亮 key=$key address=${data.address} delta=$delta")
|
||||
triggerHighlight(data.deviceId, data.address, delta)
|
||||
}
|
||||
data.state == WeightUtil.STATE_STABLE -> {
|
||||
weightSnapshot[snapshotKey] = data.weight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 子设备:注册本机 WeightUtil 监听,触发后通过 broadcastEvent 通知主设备
|
||||
WeightUtil.addWeightListener(TAG) { address, state, weight ->
|
||||
val baseline = weightSnapshot[address]
|
||||
Log.d(TAG, "onWeight: address=$address state=$state weight=$weight baseline=$baseline")
|
||||
|
||||
if (baseline == null) {
|
||||
weightSnapshot[address] = weight
|
||||
return@addWeightListener
|
||||
}
|
||||
|
||||
val delta = weight - baseline
|
||||
Log.d(TAG, "onWeight: address=$address state=$state weight=$weight baseline=$baseline delta=$delta")
|
||||
|
||||
when {
|
||||
// 仅在秤稳定后判断增量,避免取走物品时手部冲力造成误触发
|
||||
state == WeightUtil.STATE_STABLE && delta > WEIGHT_DELTA_THRESHOLD -> {
|
||||
weightSnapshot[address] = weight
|
||||
Log.d(TAG, "onWeight: 触发高亮 address=$address delta=$delta")
|
||||
triggerHighlight(GlobalData.deviceId, address, delta)
|
||||
}
|
||||
state == WeightUtil.STATE_STABLE -> {
|
||||
weightSnapshot[address] = weight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据来源设备ID和秤地址触发对应格子高亮,15 秒后自动恢复
|
||||
* 通过 sourceDeviceId 精确匹配 SCALE_ORDER,解决 18格/22格 地址重叠问题
|
||||
* @param sourceDeviceId 产生重量变化的设备 ID
|
||||
* @param address 秤硬件地址
|
||||
* @param delta 重量变化量(克)
|
||||
*/
|
||||
private fun triggerHighlight(sourceDeviceId: String, address: Int, delta: Double) {
|
||||
// 根据来源设备决定查哪个列表,而非查本机设备ID
|
||||
val pos22 = if (sourceDeviceId != ScaleDeviceConfig.DEVICE_ID_18)
|
||||
ScaleDeviceConfig.SCALE_ORDER_22.indexOf(address) else -1
|
||||
val pos18 = if (sourceDeviceId != ScaleDeviceConfig.DEVICE_ID_22)
|
||||
ScaleDeviceConfig.SCALE_ORDER_18.indexOf(address) else -1
|
||||
|
||||
Log.d(TAG, "triggerHighlight: sourceDeviceId=$sourceDeviceId address=$address pos22=$pos22 pos18=$pos18")
|
||||
|
||||
// 两个列表都不包含该地址,忽略
|
||||
if (pos22 < 0 && pos18 < 0) {
|
||||
Log.d(TAG, "triggerHighlight: address=$address 不在任何 SCALE_ORDER 中,忽略")
|
||||
return
|
||||
}
|
||||
|
||||
mainHandler.post {
|
||||
clearHighlightRunnable?.let { mainHandler.removeCallbacks(it) }
|
||||
|
||||
when {
|
||||
pos22 >= 0 -> {
|
||||
adapter22.highlightPosition(pos22)
|
||||
adapter18.clearHighlight()
|
||||
}
|
||||
else -> {
|
||||
adapter18.highlightPosition(pos18)
|
||||
adapter22.clearHighlight()
|
||||
}
|
||||
}
|
||||
|
||||
clearHighlightRunnable = Runnable {
|
||||
adapter22.clearHighlight()
|
||||
adapter18.clearHighlight()
|
||||
}.also { mainHandler.postDelayed(it, HIGHLIGHT_DURATION_MS) }
|
||||
|
||||
// 子设备:通知主设备(携带本机 deviceId 和地址)
|
||||
if (GlobalData.deviceRole == DeviceRole.SLAVE) {
|
||||
ScaleServiceManager.broadcastEvent(
|
||||
ScaleEvent(
|
||||
type = ScaleEvent.TYPE_SEASONING_ADDED,
|
||||
deviceId = GlobalData.deviceId,
|
||||
address = address,
|
||||
delta = delta
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
// 子设备才注册了 WeightUtil 监听,移除时安全无副作用
|
||||
WeightUtil.removeWeightListener(TAG)
|
||||
ScaleServiceManager.onScaleEvent = null
|
||||
clearHighlightRunnable?.let { mainHandler.removeCallbacks(it) }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user