1 /* <lambda>null2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.systemui.statusbar.notification.collection.coordinator 17 18 import android.util.ArrayMap 19 import com.android.systemui.dagger.qualifiers.Main 20 import com.android.systemui.statusbar.notification.collection.GroupEntry 21 import com.android.systemui.statusbar.notification.collection.ListEntry 22 import com.android.systemui.statusbar.notification.collection.NotifPipeline 23 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope 24 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator 25 import com.android.systemui.statusbar.notification.collection.render.NotifGroupController 26 import com.android.systemui.util.concurrency.DelayableExecutor 27 import com.android.systemui.util.time.SystemClock 28 import javax.inject.Inject 29 import kotlin.math.max 30 import kotlin.math.min 31 32 /** A small coordinator which finds, stores, and applies the closest notification time. */ 33 @CoordinatorScope 34 class GroupWhenCoordinator 35 @Inject 36 constructor( 37 @Main private val delayableExecutor: DelayableExecutor, 38 private val systemClock: SystemClock 39 ) : Coordinator { 40 41 private val invalidator = object : Invalidator("GroupWhenCoordinator") {} 42 private val notificationGroupTimes = ArrayMap<GroupEntry, Long>() 43 private var cancelInvalidateListRunnable: Runnable? = null 44 45 private val invalidateListRunnable: Runnable = Runnable { 46 invalidator.invalidateList("future notification invalidation") 47 } 48 49 override fun attach(pipeline: NotifPipeline) { 50 pipeline.addOnBeforeFinalizeFilterListener(::onBeforeFinalizeFilterListener) 51 pipeline.addOnAfterRenderGroupListener(::onAfterRenderGroupListener) 52 pipeline.addPreRenderInvalidator(invalidator) 53 } 54 55 private fun onBeforeFinalizeFilterListener(entries: List<ListEntry>) { 56 cancelListInvalidation() 57 notificationGroupTimes.clear() 58 59 val now = systemClock.currentTimeMillis() 60 var closestFutureTime = Long.MAX_VALUE 61 entries.asSequence().filterIsInstance<GroupEntry>().forEach { groupEntry -> 62 val whenMillis = calculateGroupNotificationTime(groupEntry, now) 63 notificationGroupTimes[groupEntry] = whenMillis 64 if (whenMillis > now) { 65 closestFutureTime = min(closestFutureTime, whenMillis) 66 } 67 } 68 69 if (closestFutureTime != Long.MAX_VALUE) { 70 cancelInvalidateListRunnable = 71 delayableExecutor.executeDelayed(invalidateListRunnable, closestFutureTime - now) 72 } 73 } 74 75 private fun cancelListInvalidation() { 76 cancelInvalidateListRunnable?.run() 77 cancelInvalidateListRunnable = null 78 } 79 80 private fun onAfterRenderGroupListener(group: GroupEntry, controller: NotifGroupController) { 81 notificationGroupTimes[group]?.let(controller::setNotificationGroupWhen) 82 } 83 84 private fun calculateGroupNotificationTime( 85 groupEntry: GroupEntry, 86 currentTimeMillis: Long 87 ): Long { 88 var pastTime = Long.MIN_VALUE 89 var futureTime = Long.MAX_VALUE 90 groupEntry.children 91 .asSequence() 92 .mapNotNull { child -> child.sbn.notification.getWhen().takeIf { it > 0 } } 93 .forEach { time -> 94 val isInThePast = currentTimeMillis - time > 0 95 if (isInThePast) { 96 pastTime = max(pastTime, time) 97 } else { 98 futureTime = min(futureTime, time) 99 } 100 } 101 102 if (pastTime == Long.MIN_VALUE && futureTime == Long.MAX_VALUE) { 103 return checkNotNull(groupEntry.summary).creationTime 104 } 105 106 return if (futureTime != Long.MAX_VALUE) futureTime else pastTime 107 } 108 } 109