Loading packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +8 −0 Original line number Original line Diff line number Diff line Loading @@ -136,6 +136,14 @@ public class LogModule { return factory.create("NotifRemoteInputLog", 50 /* maxSize */, false /* systrace */); return factory.create("NotifRemoteInputLog", 50 /* maxSize */, false /* systrace */); } } /** Provides a logging buffer for all logs related to unseen notifications. */ @Provides @SysUISingleton @UnseenNotificationLog public static LogBuffer provideUnseenNotificationLogBuffer(LogBufferFactory factory) { return factory.create("UnseenNotifLog", 20 /* maxSize */, false /* systrace */); } /** Provides a logging buffer for all logs related to the data layer of notifications. */ /** Provides a logging buffer for all logs related to the data layer of notifications. */ @Provides @Provides @SysUISingleton @SysUISingleton Loading packages/SystemUI/src/com/android/systemui/log/dagger/UnseenNotificationLog.java 0 → 100644 +33 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; import com.android.systemui.plugins.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import javax.inject.Qualifier; /** A {@link LogBuffer} for unseen notification related messages. */ @Qualifier @Documented @Retention(RUNTIME) public @interface UnseenNotificationLog { } packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt +45 −9 Original line number Original line Diff line number Diff line Loading @@ -21,8 +21,10 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.os.UserHandle import android.os.UserHandle import android.provider.Settings import android.provider.Settings import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting import com.android.systemui.Dumpable import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState Loading @@ -42,8 +44,14 @@ import com.android.systemui.statusbar.notification.collection.provider.SeenNotif import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.headsUpEvents import com.android.systemui.statusbar.policy.headsUpEvents import com.android.systemui.util.asIndenting import com.android.systemui.util.indentIfPossible import com.android.systemui.util.settings.SecureSettings import com.android.systemui.util.settings.SecureSettings import com.android.systemui.util.settings.SettingsProxyExt.observerFlow import com.android.systemui.util.settings.SettingsProxyExt.observerFlow import java.io.PrintWriter import javax.inject.Inject import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi Loading @@ -57,13 +65,11 @@ import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.launch import kotlinx.coroutines.launch import kotlinx.coroutines.yield import kotlinx.coroutines.yield import javax.inject.Inject import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds /** /** * Filters low priority and privacy-sensitive notifications from the lockscreen, and hides section * Filters low priority and privacy-sensitive notifications from the lockscreen, and hides section Loading @@ -74,17 +80,19 @@ class KeyguardCoordinator @Inject @Inject constructor( constructor( @Background private val bgDispatcher: CoroutineDispatcher, @Background private val bgDispatcher: CoroutineDispatcher, private val dumpManager: DumpManager, private val headsUpManager: HeadsUpManager, private val headsUpManager: HeadsUpManager, private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider, private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider, private val keyguardRepository: KeyguardRepository, private val keyguardRepository: KeyguardRepository, private val keyguardTransitionRepository: KeyguardTransitionRepository, private val keyguardTransitionRepository: KeyguardTransitionRepository, private val logger: KeyguardCoordinatorLogger, private val notifPipelineFlags: NotifPipelineFlags, private val notifPipelineFlags: NotifPipelineFlags, @Application private val scope: CoroutineScope, @Application private val scope: CoroutineScope, private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider, private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider, private val secureSettings: SecureSettings, private val secureSettings: SecureSettings, private val seenNotifsProvider: SeenNotificationsProviderImpl, private val seenNotifsProvider: SeenNotificationsProviderImpl, private val statusBarStateController: StatusBarStateController, private val statusBarStateController: StatusBarStateController, ) : Coordinator { ) : Coordinator, Dumpable { private val unseenNotifications = mutableSetOf<NotificationEntry>() private val unseenNotifications = mutableSetOf<NotificationEntry>() private var unseenFilterEnabled = false private var unseenFilterEnabled = false Loading @@ -103,6 +111,7 @@ constructor( pipeline.addCollectionListener(collectionListener) pipeline.addCollectionListener(collectionListener) scope.launch { trackUnseenNotificationsWhileUnlocked() } scope.launch { trackUnseenNotificationsWhileUnlocked() } scope.launch { invalidateWhenUnseenSettingChanges() } scope.launch { invalidateWhenUnseenSettingChanges() } dumpManager.registerDumpable(this) } } private suspend fun trackUnseenNotificationsWhileUnlocked() { private suspend fun trackUnseenNotificationsWhileUnlocked() { Loading @@ -122,14 +131,16 @@ constructor( // If the screen is turning off, stop tracking, but if that transition is // If the screen is turning off, stop tracking, but if that transition is // cancelled, then start again. // cancelled, then start again. emitAll( emitAll( keyguardTransitionRepository.transitions keyguardTransitionRepository.transitions.map { step -> .map { step -> !step.isScreenTurningOff } !step.isScreenTurningOff } ) ) } } } } // Prevent double emit of `false` caused by transition to AOD, followed by keyguard // Prevent double emit of `false` caused by transition to AOD, followed by keyguard // showing // showing .distinctUntilChanged() .distinctUntilChanged() .onEach { trackingUnseen -> logger.logTrackingUnseen(trackingUnseen) } // Use collectLatest so that trackUnseenNotifications() is cancelled when the keyguard is // Use collectLatest so that trackUnseenNotifications() is cancelled when the keyguard is // showing again // showing again Loading @@ -140,9 +151,11 @@ constructor( // set when unlocked // set when unlocked awaitTimeSpentNotDozing(SEEN_TIMEOUT) awaitTimeSpentNotDozing(SEEN_TIMEOUT) clearUnseenOnBeginTracking = true clearUnseenOnBeginTracking = true logger.logSeenOnLockscreen() } else { } else { if (clearUnseenOnBeginTracking) { if (clearUnseenOnBeginTracking) { clearUnseenOnBeginTracking = false clearUnseenOnBeginTracking = false logger.logAllMarkedSeenOnUnlock() unseenNotifications.clear() unseenNotifications.clear() } } unseenNotifFilter.invalidateList("keyguard no longer showing") unseenNotifFilter.invalidateList("keyguard no longer showing") Loading @@ -166,6 +179,8 @@ constructor( .first() .first() } } // Track "unseen" notifications, marking them as seen when either shade is expanded or the // notification becomes heads up. private suspend fun trackUnseenNotifications() { private suspend fun trackUnseenNotifications() { coroutineScope { coroutineScope { launch { clearUnseenNotificationsWhenShadeIsExpanded() } launch { clearUnseenNotificationsWhenShadeIsExpanded() } Loading @@ -179,6 +194,7 @@ constructor( // keyguard transition and not the user expanding the shade // keyguard transition and not the user expanding the shade yield() yield() if (isExpanded) { if (isExpanded) { logger.logShadeExpanded() unseenNotifications.clear() unseenNotifications.clear() } } } } Loading @@ -190,6 +206,7 @@ constructor( .forEach { unseenNotifications.remove(it) } .forEach { unseenNotifications.remove(it) } headsUpManager.headsUpEvents.collect { (entry, isHun) -> headsUpManager.headsUpEvents.collect { (entry, isHun) -> if (isHun) { if (isHun) { logger.logUnseenHun(entry.key) unseenNotifications.remove(entry) unseenNotifications.remove(entry) } } } } Loading Loading @@ -231,6 +248,7 @@ constructor( if ( if ( keyguardRepository.isKeyguardShowing() || !statusBarStateController.isExpanded keyguardRepository.isKeyguardShowing() || !statusBarStateController.isExpanded ) { ) { logger.logUnseenAdded(entry.key) unseenNotifications.add(entry) unseenNotifications.add(entry) } } } } Loading @@ -239,12 +257,15 @@ constructor( if ( if ( keyguardRepository.isKeyguardShowing() || !statusBarStateController.isExpanded keyguardRepository.isKeyguardShowing() || !statusBarStateController.isExpanded ) { ) { logger.logUnseenUpdated(entry.key) unseenNotifications.add(entry) unseenNotifications.add(entry) } } } } override fun onEntryRemoved(entry: NotificationEntry, reason: Int) { override fun onEntryRemoved(entry: NotificationEntry, reason: Int) { unseenNotifications.remove(entry) if (unseenNotifications.remove(entry)) { logger.logUnseenRemoved(entry.key) } } } } } Loading Loading @@ -272,6 +293,7 @@ constructor( }.also { hasFiltered -> hasFilteredAnyNotifs = hasFilteredAnyNotifs || hasFiltered } }.also { hasFiltered -> hasFilteredAnyNotifs = hasFilteredAnyNotifs || hasFiltered } override fun onCleanup() { override fun onCleanup() { logger.logProviderHasFilteredOutSeenNotifs(hasFilteredAnyNotifs) seenNotifsProvider.hasFilteredOutSeenNotifications = hasFilteredAnyNotifs seenNotifsProvider.hasFilteredOutSeenNotifications = hasFilteredAnyNotifs hasFilteredAnyNotifs = false hasFilteredAnyNotifs = false } } Loading Loading @@ -306,11 +328,25 @@ constructor( sectionHeaderVisibilityProvider.sectionHeadersVisible = showSections sectionHeaderVisibilityProvider.sectionHeadersVisible = showSections } } override fun dump(pw: PrintWriter, args: Array<out String>) = with(pw.asIndenting()) { println( "seenNotifsProvider.hasFilteredOutSeenNotifications=" + seenNotifsProvider.hasFilteredOutSeenNotifications ) println("unseen notifications:") indentIfPossible { for (notification in unseenNotifications) { println(notification.key) } } } companion object { companion object { private const val TAG = "KeyguardCoordinator" private const val TAG = "KeyguardCoordinator" private val SEEN_TIMEOUT = 5.seconds private val SEEN_TIMEOUT = 5.seconds } } } } private val TransitionStep.isScreenTurningOff: Boolean get() = private val TransitionStep.isScreenTurningOff: Boolean transitionState == TransitionState.STARTED && to != KeyguardState.GONE get() = transitionState == TransitionState.STARTED && to != KeyguardState.GONE No newline at end of file packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt 0 → 100644 +99 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.statusbar.notification.collection.coordinator import com.android.systemui.log.dagger.UnseenNotificationLog import com.android.systemui.plugins.log.LogBuffer import com.android.systemui.plugins.log.LogLevel import javax.inject.Inject private const val TAG = "KeyguardCoordinator" class KeyguardCoordinatorLogger @Inject constructor( @UnseenNotificationLog private val buffer: LogBuffer, ) { fun logSeenOnLockscreen() = buffer.log( TAG, LogLevel.DEBUG, "Notifications on lockscreen will be marked as seen when unlocked." ) fun logTrackingUnseen(trackingUnseen: Boolean) = buffer.log( TAG, LogLevel.DEBUG, messageInitializer = { bool1 = trackingUnseen }, messagePrinter = { "${if (bool1) "Start" else "Stop"} tracking unseen notifications." }, ) fun logAllMarkedSeenOnUnlock() = buffer.log( TAG, LogLevel.DEBUG, "Notifications have been marked as seen now that device is unlocked." ) fun logShadeExpanded() = buffer.log( TAG, LogLevel.DEBUG, "Notifications have been marked as seen due to shade expansion." ) fun logUnseenAdded(key: String) = buffer.log( TAG, LogLevel.DEBUG, messageInitializer = { str1 = key }, messagePrinter = { "Unseen notif added: $str1" }, ) fun logUnseenUpdated(key: String) = buffer.log( TAG, LogLevel.DEBUG, messageInitializer = { str1 = key }, messagePrinter = { "Unseen notif updated: $str1" }, ) fun logUnseenRemoved(key: String) = buffer.log( TAG, LogLevel.DEBUG, messageInitializer = { str1 = key }, messagePrinter = { "Unseen notif removed: $str1" }, ) fun logProviderHasFilteredOutSeenNotifs(hasFilteredAnyNotifs: Boolean) = buffer.log( TAG, LogLevel.DEBUG, messageInitializer = { bool1 = hasFilteredAnyNotifs }, messagePrinter = { "UI showing unseen filter treatment: $bool1" }, ) fun logUnseenHun(key: String) = buffer.log( TAG, LogLevel.DEBUG, messageInitializer = { str1 = key }, messagePrinter = { "Unseen notif has become heads up: $str1" }, ) } packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt +3 −0 Original line number Original line Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.advanceTimeBy import com.android.systemui.coroutines.advanceTimeBy import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState Loading Loading @@ -380,10 +381,12 @@ class KeyguardCoordinatorTest : SysuiTestCase() { val keyguardCoordinator = val keyguardCoordinator = KeyguardCoordinator( KeyguardCoordinator( testDispatcher, testDispatcher, mock<DumpManager>(), headsUpManager, headsUpManager, keyguardNotifVisibilityProvider, keyguardNotifVisibilityProvider, keyguardRepository, keyguardRepository, keyguardTransitionRepository, keyguardTransitionRepository, mock<KeyguardCoordinatorLogger>(), notifPipelineFlags, notifPipelineFlags, testScope.backgroundScope, testScope.backgroundScope, sectionHeaderVisibilityProvider, sectionHeaderVisibilityProvider, Loading Loading
packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +8 −0 Original line number Original line Diff line number Diff line Loading @@ -136,6 +136,14 @@ public class LogModule { return factory.create("NotifRemoteInputLog", 50 /* maxSize */, false /* systrace */); return factory.create("NotifRemoteInputLog", 50 /* maxSize */, false /* systrace */); } } /** Provides a logging buffer for all logs related to unseen notifications. */ @Provides @SysUISingleton @UnseenNotificationLog public static LogBuffer provideUnseenNotificationLogBuffer(LogBufferFactory factory) { return factory.create("UnseenNotifLog", 20 /* maxSize */, false /* systrace */); } /** Provides a logging buffer for all logs related to the data layer of notifications. */ /** Provides a logging buffer for all logs related to the data layer of notifications. */ @Provides @Provides @SysUISingleton @SysUISingleton Loading
packages/SystemUI/src/com/android/systemui/log/dagger/UnseenNotificationLog.java 0 → 100644 +33 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.log.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; import com.android.systemui.plugins.log.LogBuffer; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import javax.inject.Qualifier; /** A {@link LogBuffer} for unseen notification related messages. */ @Qualifier @Documented @Retention(RUNTIME) public @interface UnseenNotificationLog { }
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt +45 −9 Original line number Original line Diff line number Diff line Loading @@ -21,8 +21,10 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.os.UserHandle import android.os.UserHandle import android.provider.Settings import android.provider.Settings import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting import com.android.systemui.Dumpable import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState Loading @@ -42,8 +44,14 @@ import com.android.systemui.statusbar.notification.collection.provider.SeenNotif import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.headsUpEvents import com.android.systemui.statusbar.policy.headsUpEvents import com.android.systemui.util.asIndenting import com.android.systemui.util.indentIfPossible import com.android.systemui.util.settings.SecureSettings import com.android.systemui.util.settings.SecureSettings import com.android.systemui.util.settings.SettingsProxyExt.observerFlow import com.android.systemui.util.settings.SettingsProxyExt.observerFlow import java.io.PrintWriter import javax.inject.Inject import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi Loading @@ -57,13 +65,11 @@ import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.launch import kotlinx.coroutines.launch import kotlinx.coroutines.yield import kotlinx.coroutines.yield import javax.inject.Inject import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds /** /** * Filters low priority and privacy-sensitive notifications from the lockscreen, and hides section * Filters low priority and privacy-sensitive notifications from the lockscreen, and hides section Loading @@ -74,17 +80,19 @@ class KeyguardCoordinator @Inject @Inject constructor( constructor( @Background private val bgDispatcher: CoroutineDispatcher, @Background private val bgDispatcher: CoroutineDispatcher, private val dumpManager: DumpManager, private val headsUpManager: HeadsUpManager, private val headsUpManager: HeadsUpManager, private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider, private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider, private val keyguardRepository: KeyguardRepository, private val keyguardRepository: KeyguardRepository, private val keyguardTransitionRepository: KeyguardTransitionRepository, private val keyguardTransitionRepository: KeyguardTransitionRepository, private val logger: KeyguardCoordinatorLogger, private val notifPipelineFlags: NotifPipelineFlags, private val notifPipelineFlags: NotifPipelineFlags, @Application private val scope: CoroutineScope, @Application private val scope: CoroutineScope, private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider, private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider, private val secureSettings: SecureSettings, private val secureSettings: SecureSettings, private val seenNotifsProvider: SeenNotificationsProviderImpl, private val seenNotifsProvider: SeenNotificationsProviderImpl, private val statusBarStateController: StatusBarStateController, private val statusBarStateController: StatusBarStateController, ) : Coordinator { ) : Coordinator, Dumpable { private val unseenNotifications = mutableSetOf<NotificationEntry>() private val unseenNotifications = mutableSetOf<NotificationEntry>() private var unseenFilterEnabled = false private var unseenFilterEnabled = false Loading @@ -103,6 +111,7 @@ constructor( pipeline.addCollectionListener(collectionListener) pipeline.addCollectionListener(collectionListener) scope.launch { trackUnseenNotificationsWhileUnlocked() } scope.launch { trackUnseenNotificationsWhileUnlocked() } scope.launch { invalidateWhenUnseenSettingChanges() } scope.launch { invalidateWhenUnseenSettingChanges() } dumpManager.registerDumpable(this) } } private suspend fun trackUnseenNotificationsWhileUnlocked() { private suspend fun trackUnseenNotificationsWhileUnlocked() { Loading @@ -122,14 +131,16 @@ constructor( // If the screen is turning off, stop tracking, but if that transition is // If the screen is turning off, stop tracking, but if that transition is // cancelled, then start again. // cancelled, then start again. emitAll( emitAll( keyguardTransitionRepository.transitions keyguardTransitionRepository.transitions.map { step -> .map { step -> !step.isScreenTurningOff } !step.isScreenTurningOff } ) ) } } } } // Prevent double emit of `false` caused by transition to AOD, followed by keyguard // Prevent double emit of `false` caused by transition to AOD, followed by keyguard // showing // showing .distinctUntilChanged() .distinctUntilChanged() .onEach { trackingUnseen -> logger.logTrackingUnseen(trackingUnseen) } // Use collectLatest so that trackUnseenNotifications() is cancelled when the keyguard is // Use collectLatest so that trackUnseenNotifications() is cancelled when the keyguard is // showing again // showing again Loading @@ -140,9 +151,11 @@ constructor( // set when unlocked // set when unlocked awaitTimeSpentNotDozing(SEEN_TIMEOUT) awaitTimeSpentNotDozing(SEEN_TIMEOUT) clearUnseenOnBeginTracking = true clearUnseenOnBeginTracking = true logger.logSeenOnLockscreen() } else { } else { if (clearUnseenOnBeginTracking) { if (clearUnseenOnBeginTracking) { clearUnseenOnBeginTracking = false clearUnseenOnBeginTracking = false logger.logAllMarkedSeenOnUnlock() unseenNotifications.clear() unseenNotifications.clear() } } unseenNotifFilter.invalidateList("keyguard no longer showing") unseenNotifFilter.invalidateList("keyguard no longer showing") Loading @@ -166,6 +179,8 @@ constructor( .first() .first() } } // Track "unseen" notifications, marking them as seen when either shade is expanded or the // notification becomes heads up. private suspend fun trackUnseenNotifications() { private suspend fun trackUnseenNotifications() { coroutineScope { coroutineScope { launch { clearUnseenNotificationsWhenShadeIsExpanded() } launch { clearUnseenNotificationsWhenShadeIsExpanded() } Loading @@ -179,6 +194,7 @@ constructor( // keyguard transition and not the user expanding the shade // keyguard transition and not the user expanding the shade yield() yield() if (isExpanded) { if (isExpanded) { logger.logShadeExpanded() unseenNotifications.clear() unseenNotifications.clear() } } } } Loading @@ -190,6 +206,7 @@ constructor( .forEach { unseenNotifications.remove(it) } .forEach { unseenNotifications.remove(it) } headsUpManager.headsUpEvents.collect { (entry, isHun) -> headsUpManager.headsUpEvents.collect { (entry, isHun) -> if (isHun) { if (isHun) { logger.logUnseenHun(entry.key) unseenNotifications.remove(entry) unseenNotifications.remove(entry) } } } } Loading Loading @@ -231,6 +248,7 @@ constructor( if ( if ( keyguardRepository.isKeyguardShowing() || !statusBarStateController.isExpanded keyguardRepository.isKeyguardShowing() || !statusBarStateController.isExpanded ) { ) { logger.logUnseenAdded(entry.key) unseenNotifications.add(entry) unseenNotifications.add(entry) } } } } Loading @@ -239,12 +257,15 @@ constructor( if ( if ( keyguardRepository.isKeyguardShowing() || !statusBarStateController.isExpanded keyguardRepository.isKeyguardShowing() || !statusBarStateController.isExpanded ) { ) { logger.logUnseenUpdated(entry.key) unseenNotifications.add(entry) unseenNotifications.add(entry) } } } } override fun onEntryRemoved(entry: NotificationEntry, reason: Int) { override fun onEntryRemoved(entry: NotificationEntry, reason: Int) { unseenNotifications.remove(entry) if (unseenNotifications.remove(entry)) { logger.logUnseenRemoved(entry.key) } } } } } Loading Loading @@ -272,6 +293,7 @@ constructor( }.also { hasFiltered -> hasFilteredAnyNotifs = hasFilteredAnyNotifs || hasFiltered } }.also { hasFiltered -> hasFilteredAnyNotifs = hasFilteredAnyNotifs || hasFiltered } override fun onCleanup() { override fun onCleanup() { logger.logProviderHasFilteredOutSeenNotifs(hasFilteredAnyNotifs) seenNotifsProvider.hasFilteredOutSeenNotifications = hasFilteredAnyNotifs seenNotifsProvider.hasFilteredOutSeenNotifications = hasFilteredAnyNotifs hasFilteredAnyNotifs = false hasFilteredAnyNotifs = false } } Loading Loading @@ -306,11 +328,25 @@ constructor( sectionHeaderVisibilityProvider.sectionHeadersVisible = showSections sectionHeaderVisibilityProvider.sectionHeadersVisible = showSections } } override fun dump(pw: PrintWriter, args: Array<out String>) = with(pw.asIndenting()) { println( "seenNotifsProvider.hasFilteredOutSeenNotifications=" + seenNotifsProvider.hasFilteredOutSeenNotifications ) println("unseen notifications:") indentIfPossible { for (notification in unseenNotifications) { println(notification.key) } } } companion object { companion object { private const val TAG = "KeyguardCoordinator" private const val TAG = "KeyguardCoordinator" private val SEEN_TIMEOUT = 5.seconds private val SEEN_TIMEOUT = 5.seconds } } } } private val TransitionStep.isScreenTurningOff: Boolean get() = private val TransitionStep.isScreenTurningOff: Boolean transitionState == TransitionState.STARTED && to != KeyguardState.GONE get() = transitionState == TransitionState.STARTED && to != KeyguardState.GONE No newline at end of file
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt 0 → 100644 +99 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.statusbar.notification.collection.coordinator import com.android.systemui.log.dagger.UnseenNotificationLog import com.android.systemui.plugins.log.LogBuffer import com.android.systemui.plugins.log.LogLevel import javax.inject.Inject private const val TAG = "KeyguardCoordinator" class KeyguardCoordinatorLogger @Inject constructor( @UnseenNotificationLog private val buffer: LogBuffer, ) { fun logSeenOnLockscreen() = buffer.log( TAG, LogLevel.DEBUG, "Notifications on lockscreen will be marked as seen when unlocked." ) fun logTrackingUnseen(trackingUnseen: Boolean) = buffer.log( TAG, LogLevel.DEBUG, messageInitializer = { bool1 = trackingUnseen }, messagePrinter = { "${if (bool1) "Start" else "Stop"} tracking unseen notifications." }, ) fun logAllMarkedSeenOnUnlock() = buffer.log( TAG, LogLevel.DEBUG, "Notifications have been marked as seen now that device is unlocked." ) fun logShadeExpanded() = buffer.log( TAG, LogLevel.DEBUG, "Notifications have been marked as seen due to shade expansion." ) fun logUnseenAdded(key: String) = buffer.log( TAG, LogLevel.DEBUG, messageInitializer = { str1 = key }, messagePrinter = { "Unseen notif added: $str1" }, ) fun logUnseenUpdated(key: String) = buffer.log( TAG, LogLevel.DEBUG, messageInitializer = { str1 = key }, messagePrinter = { "Unseen notif updated: $str1" }, ) fun logUnseenRemoved(key: String) = buffer.log( TAG, LogLevel.DEBUG, messageInitializer = { str1 = key }, messagePrinter = { "Unseen notif removed: $str1" }, ) fun logProviderHasFilteredOutSeenNotifs(hasFilteredAnyNotifs: Boolean) = buffer.log( TAG, LogLevel.DEBUG, messageInitializer = { bool1 = hasFilteredAnyNotifs }, messagePrinter = { "UI showing unseen filter treatment: $bool1" }, ) fun logUnseenHun(key: String) = buffer.log( TAG, LogLevel.DEBUG, messageInitializer = { str1 = key }, messagePrinter = { "Unseen notif has become heads up: $str1" }, ) }
packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt +3 −0 Original line number Original line Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.advanceTimeBy import com.android.systemui.coroutines.advanceTimeBy import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState Loading Loading @@ -380,10 +381,12 @@ class KeyguardCoordinatorTest : SysuiTestCase() { val keyguardCoordinator = val keyguardCoordinator = KeyguardCoordinator( KeyguardCoordinator( testDispatcher, testDispatcher, mock<DumpManager>(), headsUpManager, headsUpManager, keyguardNotifVisibilityProvider, keyguardNotifVisibilityProvider, keyguardRepository, keyguardRepository, keyguardTransitionRepository, keyguardTransitionRepository, mock<KeyguardCoordinatorLogger>(), notifPipelineFlags, notifPipelineFlags, testScope.backgroundScope, testScope.backgroundScope, sectionHeaderVisibilityProvider, sectionHeaderVisibilityProvider, Loading