Merge "Base "seen" logic on shade expansion and heads up" into tm-qpr-dev am: 156fdcab1b

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21322087

Change-Id: I34a866c5639e860fd498a455599ec44890e80b69
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Steve Elliott
2023-02-09 17:48:53 +00:00
committed by Automerger Merge Worker
4 changed files with 183 additions and 45 deletions

View File

@@ -0,0 +1,36 @@
/*
* 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
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.plugins.statusbar.StatusBarStateController
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
/** Returns a [Flow] that emits whenever [StatusBarStateController.isExpanded] changes value. */
val StatusBarStateController.expansionChanges: Flow<Boolean>
get() = conflatedCallbackFlow {
val listener =
object : StatusBarStateController.StateListener {
override fun onExpandedChanged(isExpanded: Boolean) {
trySend(isExpanded)
}
}
trySend(isExpanded)
addCallback(listener)
awaitClose { removeCallback(listener) }
}

View File

@@ -16,16 +16,15 @@
package com.android.systemui.statusbar.notification.collection.coordinator package com.android.systemui.statusbar.notification.collection.coordinator
import android.database.ContentObserver
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.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
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.keyguard.data.repository.KeyguardRepository import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.expansionChanges
import com.android.systemui.statusbar.notification.NotifPipelineFlags import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -35,15 +34,14 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No
import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl
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.headsUpEvents
import com.android.systemui.util.settings.SecureSettings import com.android.systemui.util.settings.SecureSettings
import com.android.systemui.util.settings.SettingsProxy import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
import javax.inject.Inject import javax.inject.Inject
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.channels.awaitClose import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
@@ -60,6 +58,7 @@ class KeyguardCoordinator
@Inject @Inject
constructor( constructor(
@Background private val bgDispatcher: CoroutineDispatcher, @Background private val bgDispatcher: CoroutineDispatcher,
private val headsUpManager: HeadsUpManager,
private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider, private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider,
private val keyguardRepository: KeyguardRepository, private val keyguardRepository: KeyguardRepository,
private val notifPipelineFlags: NotifPipelineFlags, private val notifPipelineFlags: NotifPipelineFlags,
@@ -87,28 +86,53 @@ constructor(
private fun attachUnseenFilter(pipeline: NotifPipeline) { private fun attachUnseenFilter(pipeline: NotifPipeline) {
pipeline.addFinalizeFilter(unseenNotifFilter) pipeline.addFinalizeFilter(unseenNotifFilter)
pipeline.addCollectionListener(collectionListener) pipeline.addCollectionListener(collectionListener)
scope.launch { clearUnseenWhenKeyguardIsDismissed() } scope.launch { trackUnseenNotificationsWhileUnlocked() }
scope.launch { invalidateWhenUnseenSettingChanges() } scope.launch { invalidateWhenUnseenSettingChanges() }
} }
private suspend fun clearUnseenWhenKeyguardIsDismissed() { private suspend fun trackUnseenNotificationsWhileUnlocked() {
// Use collectLatest so that the suspending block is cancelled if isKeyguardShowing changes // Use collectLatest so that trackUnseenNotifications() is cancelled when the keyguard is
// during the timeout period // showing again
keyguardRepository.isKeyguardShowing.collectLatest { isKeyguardShowing -> keyguardRepository.isKeyguardShowing.collectLatest { isKeyguardShowing ->
if (!isKeyguardShowing) { if (!isKeyguardShowing) {
unseenNotifFilter.invalidateList("keyguard no longer showing") unseenNotifFilter.invalidateList("keyguard no longer showing")
delay(SEEN_TIMEOUT) trackUnseenNotifications()
}
}
}
private suspend fun trackUnseenNotifications() {
coroutineScope {
launch { clearUnseenNotificationsWhenShadeIsExpanded() }
launch { markHeadsUpNotificationsAsSeen() }
}
}
private suspend fun clearUnseenNotificationsWhenShadeIsExpanded() {
statusBarStateController.expansionChanges.collect { isExpanded ->
if (isExpanded) {
unseenNotifications.clear() unseenNotifications.clear()
} }
} }
} }
private suspend fun markHeadsUpNotificationsAsSeen() {
headsUpManager.allEntries
.filter { it.isRowPinned }
.forEach { unseenNotifications.remove(it) }
headsUpManager.headsUpEvents.collect { (entry, isHun) ->
if (isHun) {
unseenNotifications.remove(entry)
}
}
}
private suspend fun invalidateWhenUnseenSettingChanges() { private suspend fun invalidateWhenUnseenSettingChanges() {
secureSettings secureSettings
// emit whenever the setting has changed // emit whenever the setting has changed
.settingChangesForUser( .observerFlow(
Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
UserHandle.USER_ALL, UserHandle.USER_ALL,
Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
) )
// perform a query immediately // perform a query immediately
.onStart { emit(Unit) } .onStart { emit(Unit) }
@@ -136,13 +160,17 @@ constructor(
private val collectionListener = private val collectionListener =
object : NotifCollectionListener { object : NotifCollectionListener {
override fun onEntryAdded(entry: NotificationEntry) { override fun onEntryAdded(entry: NotificationEntry) {
if (keyguardRepository.isKeyguardShowing()) { if (
keyguardRepository.isKeyguardShowing() || !statusBarStateController.isExpanded
) {
unseenNotifications.add(entry) unseenNotifications.add(entry)
} }
} }
override fun onEntryUpdated(entry: NotificationEntry) { override fun onEntryUpdated(entry: NotificationEntry) {
if (keyguardRepository.isKeyguardShowing()) { if (
keyguardRepository.isKeyguardShowing() || !statusBarStateController.isExpanded
) {
unseenNotifications.add(entry) unseenNotifications.add(entry)
} }
} }
@@ -212,18 +240,5 @@ constructor(
companion object { companion object {
private const val TAG = "KeyguardCoordinator" private const val TAG = "KeyguardCoordinator"
private val SEEN_TIMEOUT = 5.seconds
} }
} }
private fun SettingsProxy.settingChangesForUser(name: String, userHandle: Int): Flow<Unit> =
conflatedCallbackFlow {
val observer =
object : ContentObserver(null) {
override fun onChange(selfChange: Boolean) {
trySend(Unit)
}
}
registerContentObserverForUser(name, observer, userHandle)
awaitClose { unregisterContentObserver(observer) }
}

View File

@@ -0,0 +1,38 @@
/*
* 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.policy
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
/**
* Returns a [Flow] that emits events whenever a [NotificationEntry] enters or exists the "heads up"
* state.
*/
val HeadsUpManager.headsUpEvents: Flow<Pair<NotificationEntry, Boolean>>
get() = conflatedCallbackFlow {
val listener =
object : OnHeadsUpChangedListener {
override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) {
trySend(entry to isHeadsUp)
}
}
addListener(listener)
awaitClose { removeListener(listener) }
}

View File

@@ -38,6 +38,8 @@ import com.android.systemui.statusbar.notification.collection.provider.SeenNotif
import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.withArgCaptor import com.android.systemui.util.mockito.withArgCaptor
@@ -63,6 +65,7 @@ import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidTestingRunner::class) @RunWith(AndroidTestingRunner::class)
class KeyguardCoordinatorTest : SysuiTestCase() { class KeyguardCoordinatorTest : SysuiTestCase() {
private val headsUpManager: HeadsUpManager = mock()
private val keyguardNotifVisibilityProvider: KeyguardNotificationVisibilityProvider = mock() private val keyguardNotifVisibilityProvider: KeyguardNotificationVisibilityProvider = mock()
private val keyguardRepository = FakeKeyguardRepository() private val keyguardRepository = FakeKeyguardRepository()
private val notifPipelineFlags: NotifPipelineFlags = mock() private val notifPipelineFlags: NotifPipelineFlags = mock()
@@ -90,8 +93,9 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
fun unseenFilterSuppressesSeenNotifWhileKeyguardShowing() { fun unseenFilterSuppressesSeenNotifWhileKeyguardShowing() {
whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true) whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
// GIVEN: Keyguard is not showing, and a notification is present // GIVEN: Keyguard is not showing, shade is expanded, and a notification is present
keyguardRepository.setKeyguardShowing(false) keyguardRepository.setKeyguardShowing(false)
whenever(statusBarStateController.isExpanded).thenReturn(true)
runKeyguardCoordinatorTest { runKeyguardCoordinatorTest {
val fakeEntry = NotificationEntryBuilder().build() val fakeEntry = NotificationEntryBuilder().build()
collectionListener.onEntryAdded(fakeEntry) collectionListener.onEntryAdded(fakeEntry)
@@ -112,12 +116,45 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
} }
} }
@Test
fun unseenFilter_headsUpMarkedAsSeen() {
whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
// GIVEN: Keyguard is not showing, shade is not expanded
keyguardRepository.setKeyguardShowing(false)
whenever(statusBarStateController.isExpanded).thenReturn(false)
runKeyguardCoordinatorTest {
// WHEN: A notification is posted
val fakeEntry = NotificationEntryBuilder().build()
collectionListener.onEntryAdded(fakeEntry)
// WHEN: That notification is heads up
onHeadsUpChangedListener.onHeadsUpStateChanged(fakeEntry, /* isHeadsUp= */ true)
testScheduler.runCurrent()
// WHEN: The keyguard is now showing
keyguardRepository.setKeyguardShowing(true)
testScheduler.runCurrent()
// THEN: The notification is recognized as "seen" and is filtered out.
assertThat(unseenFilter.shouldFilterOut(fakeEntry, 0L)).isTrue()
// WHEN: The keyguard goes away
keyguardRepository.setKeyguardShowing(false)
testScheduler.runCurrent()
// THEN: The notification is shown regardless
assertThat(unseenFilter.shouldFilterOut(fakeEntry, 0L)).isFalse()
}
}
@Test @Test
fun unseenFilterDoesNotSuppressSeenOngoingNotifWhileKeyguardShowing() { fun unseenFilterDoesNotSuppressSeenOngoingNotifWhileKeyguardShowing() {
whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true) whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
// GIVEN: Keyguard is not showing, and an ongoing notification is present // GIVEN: Keyguard is not showing, shade is expanded, and an ongoing notification is present
keyguardRepository.setKeyguardShowing(false) keyguardRepository.setKeyguardShowing(false)
whenever(statusBarStateController.isExpanded).thenReturn(true)
runKeyguardCoordinatorTest { runKeyguardCoordinatorTest {
val fakeEntry = NotificationEntryBuilder() val fakeEntry = NotificationEntryBuilder()
.setNotification(Notification.Builder(mContext).setOngoing(true).build()) .setNotification(Notification.Builder(mContext).setOngoing(true).build())
@@ -137,8 +174,9 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
fun unseenFilterDoesNotSuppressSeenMediaNotifWhileKeyguardShowing() { fun unseenFilterDoesNotSuppressSeenMediaNotifWhileKeyguardShowing() {
whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true) whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
// GIVEN: Keyguard is not showing, and a media notification is present // GIVEN: Keyguard is not showing, shade is expanded, and a media notification is present
keyguardRepository.setKeyguardShowing(false) keyguardRepository.setKeyguardShowing(false)
whenever(statusBarStateController.isExpanded).thenReturn(true)
runKeyguardCoordinatorTest { runKeyguardCoordinatorTest {
val fakeEntry = NotificationEntryBuilder().build().apply { val fakeEntry = NotificationEntryBuilder().build().apply {
row = mock<ExpandableNotificationRow>().apply { row = mock<ExpandableNotificationRow>().apply {
@@ -160,8 +198,9 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
fun unseenFilterUpdatesSeenProviderWhenSuppressing() { fun unseenFilterUpdatesSeenProviderWhenSuppressing() {
whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true) whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
// GIVEN: Keyguard is not showing, and a notification is present // GIVEN: Keyguard is not showing, shade is expanded, and a notification is present
keyguardRepository.setKeyguardShowing(false) keyguardRepository.setKeyguardShowing(false)
whenever(statusBarStateController.isExpanded).thenReturn(true)
runKeyguardCoordinatorTest { runKeyguardCoordinatorTest {
val fakeEntry = NotificationEntryBuilder().build() val fakeEntry = NotificationEntryBuilder().build()
collectionListener.onEntryAdded(fakeEntry) collectionListener.onEntryAdded(fakeEntry)
@@ -185,8 +224,9 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
fun unseenFilterInvalidatesWhenSettingChanges() { fun unseenFilterInvalidatesWhenSettingChanges() {
whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true) whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
// GIVEN: Keyguard is not showing // GIVEN: Keyguard is not showing, and shade is expanded
keyguardRepository.setKeyguardShowing(false) keyguardRepository.setKeyguardShowing(false)
whenever(statusBarStateController.isExpanded).thenReturn(true)
runKeyguardCoordinatorTest { runKeyguardCoordinatorTest {
// GIVEN: A notification is present // GIVEN: A notification is present
val fakeEntry = NotificationEntryBuilder().build() val fakeEntry = NotificationEntryBuilder().build()
@@ -237,8 +277,9 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
fun unseenFilterSeenGroupSummaryWithUnseenChild() { fun unseenFilterSeenGroupSummaryWithUnseenChild() {
whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true) whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
// GIVEN: Keyguard is not showing, and a notification is present // GIVEN: Keyguard is not showing, shade is expanded, and a notification is present
keyguardRepository.setKeyguardShowing(false) keyguardRepository.setKeyguardShowing(false)
whenever(statusBarStateController.isExpanded).thenReturn(true)
runKeyguardCoordinatorTest { runKeyguardCoordinatorTest {
// WHEN: A new notification is posted // WHEN: A new notification is posted
val fakeSummary = NotificationEntryBuilder().build() val fakeSummary = NotificationEntryBuilder().build()
@@ -276,11 +317,11 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
val fakeEntry = NotificationEntryBuilder().build() val fakeEntry = NotificationEntryBuilder().build()
collectionListener.onEntryAdded(fakeEntry) collectionListener.onEntryAdded(fakeEntry)
// WHEN: Keyguard is no longer showing for 5 seconds // WHEN: Keyguard is no longer showing
keyguardRepository.setKeyguardShowing(false) keyguardRepository.setKeyguardShowing(false)
testScheduler.runCurrent()
testScheduler.advanceTimeBy(5.seconds.inWholeMilliseconds) // When: Shade is expanded
testScheduler.runCurrent() statusBarStateListener.onExpandedChanged(true)
// WHEN: Keyguard is shown again // WHEN: Keyguard is shown again
keyguardRepository.setKeyguardShowing(true) keyguardRepository.setKeyguardShowing(true)
@@ -292,7 +333,7 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
} }
@Test @Test
fun unseenNotificationIsNotMarkedAsSeenIfTimeThresholdNotMet() { fun unseenNotificationIsNotMarkedAsSeenIfShadeNotExpanded() {
whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true) whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
// GIVEN: Keyguard is showing, unseen notification is present // GIVEN: Keyguard is showing, unseen notification is present
@@ -301,10 +342,8 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
val fakeEntry = NotificationEntryBuilder().build() val fakeEntry = NotificationEntryBuilder().build()
collectionListener.onEntryAdded(fakeEntry) collectionListener.onEntryAdded(fakeEntry)
// WHEN: Keyguard is no longer showing for <5 seconds // WHEN: Keyguard is no longer showing
keyguardRepository.setKeyguardShowing(false) keyguardRepository.setKeyguardShowing(false)
testScheduler.runCurrent()
testScheduler.advanceTimeBy(1.seconds.inWholeMilliseconds)
// WHEN: Keyguard is shown again // WHEN: Keyguard is shown again
keyguardRepository.setKeyguardShowing(true) keyguardRepository.setKeyguardShowing(true)
@@ -327,6 +366,7 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
val keyguardCoordinator = val keyguardCoordinator =
KeyguardCoordinator( KeyguardCoordinator(
testDispatcher, testDispatcher,
headsUpManager,
keyguardNotifVisibilityProvider, keyguardNotifVisibilityProvider,
keyguardRepository, keyguardRepository,
notifPipelineFlags, notifPipelineFlags,
@@ -364,12 +404,21 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
val unseenFilter: NotifFilter val unseenFilter: NotifFilter
get() = keyguardCoordinator.unseenNotifFilter get() = keyguardCoordinator.unseenNotifFilter
// TODO(254647461): Remove lazy once Flags.FILTER_UNSEEN_NOTIFS_ON_KEYGUARD is enabled and // TODO(254647461): Remove lazy from these properties once
// removed // Flags.FILTER_UNSEEN_NOTIFS_ON_KEYGUARD is enabled and removed
val collectionListener: NotifCollectionListener by lazy { val collectionListener: NotifCollectionListener by lazy {
withArgCaptor { verify(notifPipeline).addCollectionListener(capture()) } withArgCaptor { verify(notifPipeline).addCollectionListener(capture()) }
} }
val onHeadsUpChangedListener: OnHeadsUpChangedListener by lazy {
withArgCaptor { verify(headsUpManager).addListener(capture()) }
}
val statusBarStateListener: StatusBarStateController.StateListener by lazy {
withArgCaptor { verify(statusBarStateController).addCallback(capture()) }
}
var showOnlyUnseenNotifsOnKeyguardSetting: Boolean var showOnlyUnseenNotifsOnKeyguardSetting: Boolean
get() = get() =
fakeSettings.getIntForUser( fakeSettings.getIntForUser(