fix: 用通知前快照精确显示红点(区分首页/非首页场景)

问题:非首页返回时,onViewCreated已覆盖lastStats,
导致diffStats对比不出差异,红点要么全显要么不显。

修复:
1. NotificationManager新增preNotificationStats
   - 首条通知到达时保存当前lastStats快照
   - consumeAll时清除
   - 不会被onViewCreated的fetchStatistics覆盖

2. HomeFragment.onResume用preNotificationStats做精确diffStats
   - 有快照→fetchStatisticsForDots(baseline, acknowledged)
   - 只给变化且未查看的分类显示红点
   - 无快照→降级为全显(未查看的)

场景验证:
- 首页收到通知→当场diffStats→红点精确 
- 首页点红点→对应消除,其他保留 
- 首页点通知→consumeAll→返回后全部消除 
- 非首页返回→preNotificationStats对比→红点精确 

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
dongliang
2026-04-29 16:59:56 +09:30
parent a00d5abd51
commit 58ef336df4
2 changed files with 58 additions and 8 deletions

View File

@@ -66,6 +66,10 @@ class NotificationManager @Inject constructor(
/** 上次统计数据(对比红点用) */ /** 上次统计数据(对比红点用) */
var lastStats: TaskStatistics? = null var lastStats: TaskStatistics? = null
/** 通知到达前的统计快照(用于非首页返回后精确 diffStats */
var preNotificationStats: TaskStatistics? = null
private set
/** 协程作用域 */ /** 协程作用域 */
private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob()) private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
@@ -111,6 +115,11 @@ class NotificationManager @Inject constructor(
val taskIds = parseTaskIds(rawJson) val taskIds = parseTaskIds(rawJson)
if (taskIds.isEmpty()) return if (taskIds.isEmpty()) return
// 保存通知到达前的统计快照(只在首次通知时保存,后续累积不覆盖)
if (preNotificationStats == null) {
preNotificationStats = lastStats?.copy()
}
addTaskIds(taskIds) addTaskIds(taskIds)
// 震动 + 亮屏(只在首条时触发,暂存的合并后不重复震动) // 震动 + 亮屏(只在首条时触发,暂存的合并后不重复震动)
@@ -152,6 +161,7 @@ class NotificationManager @Inject constructor(
_pendingTaskIds.clear() _pendingTaskIds.clear()
acknowledgedCards.clear() acknowledgedCards.clear()
_cardIncrements.clear() _cardIncrements.clear()
preNotificationStats = null
Timber.d("通知: 已清空全部未读") Timber.d("通知: 已清空全部未读")
} }

View File

@@ -130,19 +130,59 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
// 从其他页面返回时,只恢复未查看分类的红点 if (notificationManager.pendingCount <= 0) {
if (notificationManager.pendingCount > 0) {
val ack = notificationManager.acknowledgedCards
if (2 !in ack) dotPool.visibility = View.VISIBLE
if (3 !in ack) dotPunch.visibility = View.VISIBLE
if (4 !in ack) dotComplete.visibility = View.VISIBLE
updateNewTaskHint()
} else {
// 全部已查看 → 清除所有提示 // 全部已查看 → 清除所有提示
dotPool.visibility = View.GONE dotPool.visibility = View.GONE
dotPunch.visibility = View.GONE dotPunch.visibility = View.GONE
dotComplete.visibility = View.GONE dotComplete.visibility = View.GONE
tvNewTaskHint.visibility = View.GONE tvNewTaskHint.visibility = View.GONE
return
}
// 有未读通知 → 用 preNotificationStats 精确对比红点
// preNotificationStats 是通知到达前的快照,不会被 onViewCreated 的 fetchStatistics 覆盖)
val ack = notificationManager.acknowledgedCards
val baseline = notificationManager.preNotificationStats
if (baseline != null) {
// 精确模式:对比快照和当前统计,只给变化的分类显示红点
fetchStatisticsForDots(baseline, ack)
} else {
// 无快照(首次加载等):已查看的不显示,未查看的显示
if (2 !in ack) dotPool.visibility = View.VISIBLE
if (3 !in ack) dotPunch.visibility = View.VISIBLE
if (4 !in ack) dotComplete.visibility = View.VISIBLE
}
updateNewTaskHint()
}
/**
* 用通知前快照精确对比红点(非首页返回时调用)
* @param baseline 通知到达前的统计快照
* @param acknowledged 已查看的分类集合
*/
private fun fetchStatisticsForDots(baseline: com.xiaoqu.watch.data.task.TaskStatistics, acknowledged: Set<Int>) {
viewLifecycleOwner.lifecycleScope.launch {
val result = safeApiCall { taskApi.getStatistics() }
if (result is ApiResult.Success && result.data != null) {
val data = result.data
val changed = notificationManager.diffStats(baseline, data)
// 只给变化且未查看的分类显示红点
dotPool.visibility = if (2 in changed && 2 !in acknowledged) View.VISIBLE else View.GONE
dotPunch.visibility = if (3 in changed && 3 !in acknowledged) View.VISIBLE else View.GONE
dotComplete.visibility = if (4 in changed && 4 !in acknowledged) View.VISIBLE else View.GONE
// 记录增量(供 acknowledgeCard 扣减用)
if (2 in changed) notificationManager.recordCardIncrement(2, data.waitForTask - baseline.waitForTask)
if (3 in changed) notificationManager.recordCardIncrement(3, data.treatTask - baseline.treatTask)
if (4 in changed) notificationManager.recordCardIncrement(4, data.incompleteTask - baseline.incompleteTask)
// 更新数字
tvPoolNum.text = data.waitForTask.toString()
tvPunchNum.text = data.treatTask.toString()
tvCompleteNum.text = data.incompleteTask.toString()
notificationManager.lastStats = data
}
} }
} }