diff options
11 files changed, 992 insertions, 509 deletions
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt index 573e5ca5e2d5..6a9dfbd29092 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt @@ -187,6 +187,7 @@ import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.communal.ui.viewmodel.ResizeInfo import com.android.systemui.communal.ui.viewmodel.ResizeableItemFrameViewModel import com.android.systemui.communal.util.DensityUtils.Companion.adjustedDp +import com.android.systemui.communal.util.ResizeUtils.resizeOngoingItems import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView import com.android.systemui.communal.widgets.WidgetConfigurator import com.android.systemui.lifecycle.rememberViewModel @@ -834,8 +835,16 @@ private fun BoxScope.CommunalHubLazyGrid( gridState = gridState, contentPadding = contentPadding, ) { sizeInfo -> + /** Override spans based on the responsive grid size */ + val finalizedList = + if (sizeInfo != null) { + resizeOngoingItems(list, sizeInfo.gridSize.height) + } else { + list + } + itemsIndexed( - items = list, + items = finalizedList, key = { _, item -> item.key }, contentType = { _, item -> item.key }, span = { _, item -> GridItemSpan(item.getSpanOrMax(sizeInfo?.gridSize?.height)) }, @@ -1193,6 +1202,7 @@ private fun CommunalContent( is CommunalContentModel.Smartspace -> SmartspaceContent(interactionHandler, model, modifier) is CommunalContentModel.Tutorial -> TutorialContent(modifier) is CommunalContentModel.Umo -> Umo(viewModel, sceneScope, modifier) + is CommunalContentModel.Spacer -> Box(Modifier.fillMaxSize()) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/util/ResizeUtilsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/util/ResizeUtilsTest.kt new file mode 100644 index 000000000000..b0f48038877e --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/util/ResizeUtilsTest.kt @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2024 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.communal.util + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.communal.domain.model.CommunalContentModel +import com.android.systemui.communal.shared.model.CommunalContentSize +import com.android.systemui.communal.util.ResizeUtils.resizeOngoingItems +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock + +@SmallTest +@RunWith(AndroidJUnit4::class) +class ResizeUtilsTest : SysuiTestCase() { + private val mockWidget = + mock<CommunalContentModel.WidgetContent.Widget> { + on { size } doReturn CommunalContentSize.Responsive(1) + } + + @Test + fun noOngoingContent() { + val list = listOf(mockWidget) + val resized = resizeOngoingItems(list = list, numRows = 2) + + assertThat(resized).containsExactly(mockWidget) + } + + @Test + fun singleOngoingContent_singleRowGrid() { + val list = createOngoingListWithSize(1) + listOf(mockWidget) + val resized = resizeOngoingItems(list = list, numRows = 1) + + assertThat(resized.map { it.size }) + .containsExactly(CommunalContentSize.Responsive(1), mockWidget.size) + .inOrder() + } + + @Test + fun singleOngoingContent_twoRowGrid() { + val list = createOngoingListWithSize(1) + listOf(mockWidget) + val resized = resizeOngoingItems(list = list, numRows = 2) + + assertThat(resized.map { it.size }) + .containsExactly(CommunalContentSize.Responsive(2), mockWidget.size) + .inOrder() + } + + @Test + fun singleOngoingContent_threeRowGrid() { + val list = createOngoingListWithSize(1) + listOf(mockWidget) + val resized = resizeOngoingItems(list = list, numRows = 3) + + assertThat(resized.map { it.size }) + .containsExactly( + CommunalContentSize.Responsive(2), + CommunalContentSize.Responsive(1), + mockWidget.size, + ) + .inOrder() + // A spacer should be added as the second element to avoid mixing widget content + // with ongoing content. + assertThat(resized[1]).isInstanceOf(CommunalContentModel.Spacer::class.java) + } + + @Test + fun twoOngoingContent_singleRowGrid() { + val list = createOngoingListWithSize(2) + listOf(mockWidget) + val resized = resizeOngoingItems(list = list, numRows = 1) + + assertThat(resized.map { it.size }) + .containsExactly( + CommunalContentSize.Responsive(1), + CommunalContentSize.Responsive(1), + mockWidget.size, + ) + .inOrder() + } + + @Test + fun twoOngoingContent_twoRowGrid() { + val list = createOngoingListWithSize(2) + listOf(mockWidget) + val resized = resizeOngoingItems(list = list, numRows = 2) + + assertThat(resized.map { it.size }) + .containsExactly( + CommunalContentSize.Responsive(1), + CommunalContentSize.Responsive(1), + mockWidget.size, + ) + .inOrder() + } + + @Test + fun twoOngoingContent_threeRowGrid() { + val list = createOngoingListWithSize(2) + listOf(mockWidget) + val resized = resizeOngoingItems(list = list, numRows = 3) + + assertThat(resized.map { it.size }) + .containsExactly( + CommunalContentSize.Responsive(2), + CommunalContentSize.Responsive(1), + mockWidget.size, + ) + .inOrder() + } + + @Test + fun threeOngoingContent_singleRowGrid() { + val list = createOngoingListWithSize(3) + listOf(mockWidget) + val resized = resizeOngoingItems(list = list, numRows = 1) + + assertThat(resized.map { it.size }) + .containsExactly( + CommunalContentSize.Responsive(1), + CommunalContentSize.Responsive(1), + CommunalContentSize.Responsive(1), + mockWidget.size, + ) + .inOrder() + } + + @Test + fun threeOngoingContent_twoRowGrid() { + val list = createOngoingListWithSize(3) + listOf(mockWidget) + val resized = resizeOngoingItems(list = list, numRows = 2) + + assertThat(resized.map { it.size }) + .containsExactly( + CommunalContentSize.Responsive(2), + CommunalContentSize.Responsive(1), + CommunalContentSize.Responsive(1), + mockWidget.size, + ) + .inOrder() + } + + @Test + fun threeOngoingContent_threeRowGrid() { + val list = createOngoingListWithSize(3) + listOf(mockWidget) + val resized = resizeOngoingItems(list = list, numRows = 3) + + assertThat(resized.map { it.size }) + .containsExactly( + CommunalContentSize.Responsive(1), + CommunalContentSize.Responsive(1), + CommunalContentSize.Responsive(1), + mockWidget.size, + ) + .inOrder() + } + + @Test + fun fourOngoingContent_singleRowGrid() { + val list = createOngoingListWithSize(4) + listOf(mockWidget) + val resized = resizeOngoingItems(list = list, numRows = 1) + + assertThat(resized.map { it.size }) + .containsExactly( + CommunalContentSize.Responsive(1), + CommunalContentSize.Responsive(1), + CommunalContentSize.Responsive(1), + CommunalContentSize.Responsive(1), + mockWidget.size, + ) + .inOrder() + } + + @Test + fun fourOngoingContent_twoRowGrid() { + val list = createOngoingListWithSize(4) + listOf(mockWidget) + val resized = resizeOngoingItems(list = list, numRows = 2) + + assertThat(resized.map { it.size }) + .containsExactly( + CommunalContentSize.Responsive(1), + CommunalContentSize.Responsive(1), + CommunalContentSize.Responsive(1), + CommunalContentSize.Responsive(1), + mockWidget.size, + ) + .inOrder() + } + + @Test + fun fourOngoingContent_threeRowGrid() { + val list = createOngoingListWithSize(4) + listOf(mockWidget) + val resized = resizeOngoingItems(list = list, numRows = 3) + + assertThat(resized.map { it.size }) + .containsExactly( + CommunalContentSize.Responsive(2), + CommunalContentSize.Responsive(1), + CommunalContentSize.Responsive(2), + CommunalContentSize.Responsive(1), + mockWidget.size, + ) + .inOrder() + } + + private fun createOngoingListWithSize(size: Int): List<CommunalContentModel.Ongoing> { + return List(size) { CommunalContentModel.Umo(createdTimestampMillis = 100) } + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt index 68798a88eecc..9e7befd3cf92 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt @@ -27,15 +27,28 @@ import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.FlagsParameterization import android.service.notification.StatusBarNotification import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.keyguardUpdateMonitor import com.android.server.notification.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING import com.android.systemui.SysuiTestCase +import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository +import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor +import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor +import com.android.systemui.flags.DisableSceneContainer +import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.flags.andSceneContainer +import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository +import com.android.systemui.kosmos.testScope import com.android.systemui.plugins.statusbar.fakeStatusBarStateController import com.android.systemui.plugins.statusbar.statusBarStateController import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.RankingBuilder import com.android.systemui.statusbar.StatusBarState @@ -60,11 +73,16 @@ import com.android.systemui.statusbar.policy.sensitiveNotificationProtectionCont import com.android.systemui.testKosmos import com.android.systemui.util.mockito.withArgCaptor import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.any +import org.mockito.kotlin.clearInvocations import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.never @@ -86,6 +104,8 @@ class SensitiveContentCoordinatorTest(flags: FlagsParameterization) : SysuiTestC statusBarStateController = fakeStatusBarStateController } + val testScope = kosmos.testScope + val dynamicPrivacyController: DynamicPrivacyController = kosmos.mockDynamicPrivacyController val lockscreenUserManager: NotificationLockscreenUserManager = kosmos.notificationLockscreenUserManager @@ -95,7 +115,12 @@ class SensitiveContentCoordinatorTest(flags: FlagsParameterization) : SysuiTestC kosmos.fakeStatusBarStateController val sensitiveNotificationProtectionController: SensitiveNotificationProtectionController = kosmos.mockSensitiveNotificationProtectionController - val sceneInteractor: SceneInteractor = kosmos.sceneInteractor + val deviceEntryInteractor: DeviceEntryInteractor + get() = kosmos.deviceEntryInteractor + + val faceAuthRepository = kosmos.fakeDeviceEntryFaceAuthRepository + val sceneInteractor: SceneInteractor + get() = kosmos.sceneInteractor val coordinator: SensitiveContentCoordinator by lazy { kosmos.sensitiveContentCoordinator } @@ -112,592 +137,696 @@ class SensitiveContentCoordinatorTest(flags: FlagsParameterization) : SysuiTestC } @Test - fun onDynamicPrivacyChanged_invokeInvalidationListener() { - coordinator.attach(pipeline) - val invalidator = - withArgCaptor<Invalidator> { verify(pipeline).addPreRenderInvalidator(capture()) } - val dynamicPrivacyListener = - withArgCaptor<DynamicPrivacyController.Listener> { - verify(dynamicPrivacyController).addListener(capture()) - } - - val invalidationListener = mock<Pluggable.PluggableListener<Invalidator>>() - invalidator.setInvalidationListener(invalidationListener) - - dynamicPrivacyListener.onDynamicPrivacyChanged() + @EnableSceneContainer + fun onLockscreenDynamicallyUnlocked_invokeInvalidationListener() = + testScope.runTest { + // Setup + coordinator.attach(pipeline) + val invalidator = + withArgCaptor<Invalidator> { verify(pipeline).addPreRenderInvalidator(capture()) } + val invalidationListener = mock<Pluggable.PluggableListener<Invalidator>>() + invalidator.setInvalidationListener(invalidationListener) + + // Given the lockscreen is shown + setupLockScreen(canSwipeUp = false) + clearInvocations(invalidationListener) + + // When the device gets unlocked + faceAuthRepository.isAuthenticated.value = true + runCurrent() + + // Then the invalidationListener is called + verify(invalidationListener).onPluggableInvalidated(eq(invalidator), any()) + } - verify(invalidationListener).onPluggableInvalidated(eq(invalidator), any()) - } + @Test + @DisableSceneContainer + fun onDynamicPrivacyChanged_invokeInvalidationListener() = + testScope.runTest { + coordinator.attach(pipeline) + val invalidator = + withArgCaptor<Invalidator> { verify(pipeline).addPreRenderInvalidator(capture()) } + val dynamicPrivacyListener = + withArgCaptor<DynamicPrivacyController.Listener> { + verify(dynamicPrivacyController).addListener(capture()) + } + + val invalidationListener = mock<Pluggable.PluggableListener<Invalidator>>() + invalidator.setInvalidationListener(invalidationListener) + + dynamicPrivacyListener.onDynamicPrivacyChanged() + + verify(invalidationListener).onPluggableInvalidated(eq(invalidator), any()) + } @Test @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING) - fun onSensitiveStateChanged_invokeInvalidationListener() { - coordinator.attach(pipeline) - val invalidator = - withArgCaptor<Invalidator> { verify(pipeline).addPreRenderInvalidator(capture()) } - val onSensitiveStateChangedListener = - withArgCaptor<Runnable> { - verify(sensitiveNotificationProtectionController) - .registerSensitiveStateListener(capture()) - } - - val invalidationListener = mock<Pluggable.PluggableListener<Invalidator>>() - invalidator.setInvalidationListener(invalidationListener) - - onSensitiveStateChangedListener.run() - - verify(invalidationListener).onPluggableInvalidated(eq(invalidator), any()) - } + fun onSensitiveStateChanged_invokeInvalidationListener() = + testScope.runTest { + coordinator.attach(pipeline) + val invalidator = + withArgCaptor<Invalidator> { verify(pipeline).addPreRenderInvalidator(capture()) } + val onSensitiveStateChangedListener = + withArgCaptor<Runnable> { + verify(sensitiveNotificationProtectionController) + .registerSensitiveStateListener(capture()) + } + + val invalidationListener = mock<Pluggable.PluggableListener<Invalidator>>() + invalidator.setInvalidationListener(invalidationListener) + + onSensitiveStateChangedListener.run() + + verify(invalidationListener).onPluggableInvalidated(eq(invalidator), any()) + } @Test @DisableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING) - fun screenshareSecretFilter_flagDisabled_filterNoAdded() { - coordinator.attach(pipeline) + fun screenshareSecretFilter_flagDisabled_filterNoAdded() = + testScope.runTest { + coordinator.attach(pipeline) - verify(pipeline, never()).addFinalizeFilter(any()) - } + verify(pipeline, never()).addFinalizeFilter(any()) + } @Test @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING) - fun screenshareSecretFilter_sensitiveInctive_noFiltersSecret() { - whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(false) - - coordinator.attach(pipeline) - val filter = withArgCaptor<NotifFilter> { verify(pipeline).addFinalizeFilter(capture()) } - - val defaultNotification = createNotificationEntry("test", false, false) - val notificationWithSecretVisibility = createNotificationEntry("test", true, false) - val notificationOnSecretChannel = createNotificationEntry("test", false, true) - - assertFalse(filter.shouldFilterOut(defaultNotification, 0)) - assertFalse(filter.shouldFilterOut(notificationWithSecretVisibility, 0)) - assertFalse(filter.shouldFilterOut(notificationOnSecretChannel, 0)) - } + fun screenshareSecretFilter_sensitiveInctive_noFiltersSecret() = + testScope.runTest { + whenever(sensitiveNotificationProtectionController.isSensitiveStateActive) + .thenReturn(false) + + coordinator.attach(pipeline) + val filter = + withArgCaptor<NotifFilter> { verify(pipeline).addFinalizeFilter(capture()) } + + val defaultNotification = createNotificationEntry("test", false, false) + val notificationWithSecretVisibility = createNotificationEntry("test", true, false) + val notificationOnSecretChannel = createNotificationEntry("test", false, true) + + assertFalse(filter.shouldFilterOut(defaultNotification, 0)) + assertFalse(filter.shouldFilterOut(notificationWithSecretVisibility, 0)) + assertFalse(filter.shouldFilterOut(notificationOnSecretChannel, 0)) + } @Test @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING) - fun screenshareSecretFilter_sensitiveActive_filtersSecret() { - whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true) - - coordinator.attach(pipeline) - val filter = withArgCaptor<NotifFilter> { verify(pipeline).addFinalizeFilter(capture()) } - - val defaultNotification = createNotificationEntry("test", false, false) - val notificationWithSecretVisibility = createNotificationEntry("test", true, false) - val notificationOnSecretChannel = createNotificationEntry("test", false, true) - - assertFalse(filter.shouldFilterOut(defaultNotification, 0)) - assertTrue(filter.shouldFilterOut(notificationWithSecretVisibility, 0)) - assertTrue(filter.shouldFilterOut(notificationOnSecretChannel, 0)) - } + fun screenshareSecretFilter_sensitiveActive_filtersSecret() = + testScope.runTest { + whenever(sensitiveNotificationProtectionController.isSensitiveStateActive) + .thenReturn(true) + + coordinator.attach(pipeline) + val filter = + withArgCaptor<NotifFilter> { verify(pipeline).addFinalizeFilter(capture()) } + + val defaultNotification = createNotificationEntry("test", false, false) + val notificationWithSecretVisibility = createNotificationEntry("test", true, false) + val notificationOnSecretChannel = createNotificationEntry("test", false, true) + + assertFalse(filter.shouldFilterOut(defaultNotification, 0)) + assertTrue(filter.shouldFilterOut(notificationWithSecretVisibility, 0)) + assertTrue(filter.shouldFilterOut(notificationOnSecretChannel, 0)) + } @Test - fun onBeforeRenderList_deviceUnlocked_notifDoesNotNeedRedaction() { - coordinator.attach(pipeline) - val onBeforeRenderListListener = - withArgCaptor<OnBeforeRenderListListener> { - verify(pipeline).addOnBeforeRenderListListener(capture()) - } - - whenever(lockscreenUserManager.currentUserId).thenReturn(1) - whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false) - whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true) - whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false) - val entry = fakeNotification(1, false) - - onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) - - verify(entry.representativeEntry!!).setSensitive(false, false) - } + fun onBeforeRenderList_deviceUnlocked_notifDoesNotNeedRedaction() = + testScope.runTest { + coordinator.attach(pipeline) + val onBeforeRenderListListener = + withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)) + .thenReturn(true) + setupLockScreen(canSwipeUp = false) + val entry = fakeNotification(1, false) + + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + + verify(entry.representativeEntry!!).setSensitive(false, false) + } @Test @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING) - fun onBeforeRenderList_deviceUnlocked_notifDoesNotNeedRedaction_sensitiveActive() { - coordinator.attach(pipeline) - val onBeforeRenderListListener = - withArgCaptor<OnBeforeRenderListListener> { - verify(pipeline).addOnBeforeRenderListListener(capture()) - } - - whenever(lockscreenUserManager.currentUserId).thenReturn(1) - whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false) - whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true) - whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false) - val entry = fakeNotification(1, false) - whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true) - - onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) - - verify(entry.representativeEntry!!).setSensitive(false, true) - verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true) - } + fun onBeforeRenderList_deviceUnlocked_notifDoesNotNeedRedaction_sensitiveActive() = + testScope.runTest { + coordinator.attach(pipeline) + val onBeforeRenderListListener = + withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)) + .thenReturn(true) + setupLockScreen(canSwipeUp = false) + val entry = fakeNotification(1, false) + whenever(sensitiveNotificationProtectionController.isSensitiveStateActive) + .thenReturn(true) + + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + + verify(entry.representativeEntry!!).setSensitive(false, true) + verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true) + } @Test @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING) - fun onBeforeRenderList_deviceUnlocked_notifDoesNotNeedRedaction_shouldProtectNotification() { - coordinator.attach(pipeline) - val onBeforeRenderListListener = - withArgCaptor<OnBeforeRenderListListener> { - verify(pipeline).addOnBeforeRenderListListener(capture()) - } - - whenever(lockscreenUserManager.currentUserId).thenReturn(1) - whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false) - whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true) - whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false) - val entry = fakeNotification(1, false) - whenever( - sensitiveNotificationProtectionController.shouldProtectNotification( - entry.getRepresentativeEntry() + fun onBeforeRenderList_deviceUnlocked_notifDoesNotNeedRedaction_shouldProtectNotification() = + testScope.runTest { + coordinator.attach(pipeline) + val onBeforeRenderListListener = + withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)) + .thenReturn(true) + setupLockScreen(canSwipeUp = false) + val entry = fakeNotification(1, false) + whenever( + sensitiveNotificationProtectionController.shouldProtectNotification( + entry.getRepresentativeEntry() + ) ) - ) - .thenReturn(true) + .thenReturn(true) - onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) - verify(entry.representativeEntry!!).setSensitive(true, false) - verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false) - } + verify(entry.representativeEntry!!).setSensitive(true, false) + verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false) + } @Test - fun onBeforeRenderList_deviceUnlocked_notifWouldNeedRedaction() { - coordinator.attach(pipeline) - val onBeforeRenderListListener = - withArgCaptor<OnBeforeRenderListListener> { - verify(pipeline).addOnBeforeRenderListListener(capture()) - } - - whenever(lockscreenUserManager.currentUserId).thenReturn(1) - whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false) - whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true) - whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false) - val entry = fakeNotification(1, true) - - onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) - - verify(entry.representativeEntry!!).setSensitive(false, false) - } + fun onBeforeRenderList_deviceUnlocked_notifWouldNeedRedaction() = + testScope.runTest { + coordinator.attach(pipeline) + val onBeforeRenderListListener = + withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)) + .thenReturn(true) + setupLockScreen(canSwipeUp = false) + val entry = fakeNotification(1, true) + + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + + verify(entry.representativeEntry!!).setSensitive(false, false) + } @Test @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING) - fun onBeforeRenderList_deviceUnlocked_notifWouldNeedRedaction_sensitiveActive() { - coordinator.attach(pipeline) - val onBeforeRenderListListener = - withArgCaptor<OnBeforeRenderListListener> { - verify(pipeline).addOnBeforeRenderListListener(capture()) - } - - whenever(lockscreenUserManager.currentUserId).thenReturn(1) - whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false) - whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true) - whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false) - val entry = fakeNotification(1, true) - whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true) - - onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) - - verify(entry.representativeEntry!!).setSensitive(false, true) - verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true) - } + fun onBeforeRenderList_deviceUnlocked_notifWouldNeedRedaction_sensitiveActive() = + testScope.runTest { + coordinator.attach(pipeline) + val onBeforeRenderListListener = + withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)) + .thenReturn(true) + setupLockScreen(canSwipeUp = false) + val entry = fakeNotification(1, true) + whenever(sensitiveNotificationProtectionController.isSensitiveStateActive) + .thenReturn(true) + + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + + verify(entry.representativeEntry!!).setSensitive(false, true) + verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true) + } @Test @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING) - fun onBeforeRenderList_deviceUnlocked_notifWouldNeedRedaction_shouldProtectNotification() { - coordinator.attach(pipeline) - val onBeforeRenderListListener = - withArgCaptor<OnBeforeRenderListListener> { - verify(pipeline).addOnBeforeRenderListListener(capture()) - } - - whenever(lockscreenUserManager.currentUserId).thenReturn(1) - whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false) - whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true) - whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false) - val entry = fakeNotification(1, true) - whenever( - sensitiveNotificationProtectionController.shouldProtectNotification( - entry.getRepresentativeEntry() + fun onBeforeRenderList_deviceUnlocked_notifWouldNeedRedaction_shouldProtectNotification() = + testScope.runTest { + coordinator.attach(pipeline) + val onBeforeRenderListListener = + withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)) + .thenReturn(true) + setupLockScreen(canSwipeUp = false) + val entry = fakeNotification(1, true) + whenever( + sensitiveNotificationProtectionController.shouldProtectNotification( + entry.getRepresentativeEntry() + ) ) - ) - .thenReturn(true) + .thenReturn(true) - onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) - verify(entry.representativeEntry!!).setSensitive(true, false) - verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false) - } + verify(entry.representativeEntry!!).setSensitive(true, false) + verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false) + } @Test - fun onBeforeRenderList_deviceLocked_userAllowsPublicNotifs() { - coordinator.attach(pipeline) - val onBeforeRenderListListener = - withArgCaptor<OnBeforeRenderListListener> { - verify(pipeline).addOnBeforeRenderListListener(capture()) - } - - whenever(lockscreenUserManager.currentUserId).thenReturn(1) - whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) - whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true) - whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false) - val entry = fakeNotification(1, false) - - onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) - - verify(entry.representativeEntry!!).setSensitive(false, false) - } + fun onBeforeRenderList_deviceLocked_userAllowsPublicNotifs() = + testScope.runTest { + coordinator.attach(pipeline) + val onBeforeRenderListListener = + withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)) + .thenReturn(true) + setupLockScreen(canSwipeUp = false) + val entry = fakeNotification(1, false) + + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + + verify(entry.representativeEntry!!).setSensitive(false, false) + } @Test @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING) - fun onBeforeRenderList_deviceLocked_userAllowsPublicNotifs_sensitiveActive() { - coordinator.attach(pipeline) - val onBeforeRenderListListener = - withArgCaptor<OnBeforeRenderListListener> { - verify(pipeline).addOnBeforeRenderListListener(capture()) - } - - whenever(lockscreenUserManager.currentUserId).thenReturn(1) - whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) - whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true) - whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false) - val entry = fakeNotification(1, false) - whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true) - - onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) - - verify(entry.representativeEntry!!).setSensitive(false, true) - verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true) - } + fun onBeforeRenderList_deviceLocked_userAllowsPublicNotifs_sensitiveActive() = + testScope.runTest { + coordinator.attach(pipeline) + val onBeforeRenderListListener = + withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)) + .thenReturn(true) + setupLockScreen(canSwipeUp = false) + val entry = fakeNotification(1, false) + whenever(sensitiveNotificationProtectionController.isSensitiveStateActive) + .thenReturn(true) + + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + + verify(entry.representativeEntry!!).setSensitive(false, true) + verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true) + } @Test @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING) - fun onBeforeRenderList_deviceLocked_userAllowsPublicNotifs_shouldProtectNotification() { - coordinator.attach(pipeline) - val onBeforeRenderListListener = - withArgCaptor<OnBeforeRenderListListener> { - verify(pipeline).addOnBeforeRenderListListener(capture()) - } - - whenever(lockscreenUserManager.currentUserId).thenReturn(1) - whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) - whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true) - whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false) - val entry = fakeNotification(1, false) - whenever( - sensitiveNotificationProtectionController.shouldProtectNotification( - entry.getRepresentativeEntry() + fun onBeforeRenderList_deviceLocked_userAllowsPublicNotifs_shouldProtectNotification() = + testScope.runTest { + coordinator.attach(pipeline) + val onBeforeRenderListListener = + withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)) + .thenReturn(true) + setupLockScreen(canSwipeUp = false) + val entry = fakeNotification(1, false) + whenever( + sensitiveNotificationProtectionController.shouldProtectNotification( + entry.getRepresentativeEntry() + ) ) - ) - .thenReturn(true) + .thenReturn(true) - onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) - verify(entry.representativeEntry!!).setSensitive(true, false) - verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false) - } + verify(entry.representativeEntry!!).setSensitive(true, false) + verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false) + } @Test - fun onBeforeRenderList_deviceLocked_userDisallowsPublicNotifs_notifDoesNotNeedRedaction() { - coordinator.attach(pipeline) - val onBeforeRenderListListener = - withArgCaptor<OnBeforeRenderListListener> { - verify(pipeline).addOnBeforeRenderListListener(capture()) - } - - whenever(lockscreenUserManager.currentUserId).thenReturn(1) - whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) - whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false) - whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false) - val entry = fakeNotification(1, false) - - onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) - - verify(entry.representativeEntry!!).setSensitive(false, true) - } + fun onBeforeRenderList_deviceLocked_userDisallowsPublicNotifs_notifDoesNotNeedRedaction() = + testScope.runTest { + coordinator.attach(pipeline) + val onBeforeRenderListListener = + withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)) + .thenReturn(false) + setupLockScreen(canSwipeUp = false) + val entry = fakeNotification(1, false) + + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + + verify(entry.representativeEntry!!).setSensitive(false, true) + } @Test @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING) @Suppress("ktlint:standard:max-line-length") - fun onBeforeRenderList_deviceLocked_userDisallowsPublicNotifs_notifDoesNotNeedRedaction_sensitiveActive() { - coordinator.attach(pipeline) - val onBeforeRenderListListener = - withArgCaptor<OnBeforeRenderListListener> { - verify(pipeline).addOnBeforeRenderListListener(capture()) - } - - whenever(lockscreenUserManager.currentUserId).thenReturn(1) - whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) - whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false) - whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false) - val entry = fakeNotification(1, false) - whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true) - - onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) - - verify(entry.representativeEntry!!).setSensitive(false, true) - verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true) - } + fun onBeforeRenderList_deviceLocked_userDisallowsPublicNotifs_notifDoesNotNeedRedaction_sensitiveActive() = + testScope.runTest { + coordinator.attach(pipeline) + val onBeforeRenderListListener = + withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)) + .thenReturn(false) + setupLockScreen(canSwipeUp = false) + val entry = fakeNotification(1, false) + whenever(sensitiveNotificationProtectionController.isSensitiveStateActive) + .thenReturn(true) + + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + + verify(entry.representativeEntry!!).setSensitive(false, true) + verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true) + } @Test @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING) @Suppress("ktlint:standard:max-line-length") - fun onBeforeRenderList_deviceLocked_userDisallowsPublicNotifs_notifDoesNotNeedRedaction_shouldProtectNotification() { - coordinator.attach(pipeline) - val onBeforeRenderListListener = - withArgCaptor<OnBeforeRenderListListener> { - verify(pipeline).addOnBeforeRenderListListener(capture()) - } - - whenever(lockscreenUserManager.currentUserId).thenReturn(1) - whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) - whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false) - whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false) - val entry = fakeNotification(1, false) - whenever( - sensitiveNotificationProtectionController.shouldProtectNotification( - entry.getRepresentativeEntry() + fun onBeforeRenderList_deviceLocked_userDisallowsPublicNotifs_notifDoesNotNeedRedaction_shouldProtectNotification() = + testScope.runTest { + coordinator.attach(pipeline) + val onBeforeRenderListListener = + withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)) + .thenReturn(false) + setupLockScreen(canSwipeUp = false) + val entry = fakeNotification(1, false) + whenever( + sensitiveNotificationProtectionController.shouldProtectNotification( + entry.getRepresentativeEntry() + ) ) - ) - .thenReturn(true) + .thenReturn(true) - onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) - verify(entry.representativeEntry!!).setSensitive(true, true) - verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false) - } + verify(entry.representativeEntry!!).setSensitive(true, true) + verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false) + } @Test - fun onBeforeRenderList_deviceLocked_notifNeedsRedaction() { - coordinator.attach(pipeline) - val onBeforeRenderListListener = - withArgCaptor<OnBeforeRenderListListener> { - verify(pipeline).addOnBeforeRenderListListener(capture()) - } - - whenever(lockscreenUserManager.currentUserId).thenReturn(1) - whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) - whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false) - whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false) - val entry = fakeNotification(1, true) - - onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) - - verify(entry.representativeEntry!!).setSensitive(true, true) - } + fun onBeforeRenderList_deviceLocked_notifNeedsRedaction() = + testScope.runTest { + coordinator.attach(pipeline) + val onBeforeRenderListListener = + withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)) + .thenReturn(false) + setupLockScreen(canSwipeUp = false) + val entry = fakeNotification(1, true) + + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + + verify(entry.representativeEntry!!).setSensitive(true, true) + } @Test @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING) - fun onBeforeRenderList_deviceLocked_notifNeedsRedaction_sensitiveActive() { - coordinator.attach(pipeline) - val onBeforeRenderListListener = - withArgCaptor<OnBeforeRenderListListener> { - verify(pipeline).addOnBeforeRenderListListener(capture()) - } - - whenever(lockscreenUserManager.currentUserId).thenReturn(1) - whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) - whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false) - whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false) - val entry = fakeNotification(1, true) - whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true) - - onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) - - verify(entry.representativeEntry!!).setSensitive(true, true) - verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true) - } + fun onBeforeRenderList_deviceLocked_notifNeedsRedaction_sensitiveActive() = + testScope.runTest { + coordinator.attach(pipeline) + val onBeforeRenderListListener = + withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)) + .thenReturn(false) + setupLockScreen(canSwipeUp = false) + val entry = fakeNotification(1, true) + whenever(sensitiveNotificationProtectionController.isSensitiveStateActive) + .thenReturn(true) + + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + + verify(entry.representativeEntry!!).setSensitive(true, true) + verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true) + } @Test @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING) - fun onBeforeRenderList_deviceLocked_notifNeedsRedaction_shouldProtectNotification() { - coordinator.attach(pipeline) - val onBeforeRenderListListener = - withArgCaptor<OnBeforeRenderListListener> { - verify(pipeline).addOnBeforeRenderListListener(capture()) - } - - whenever(lockscreenUserManager.currentUserId).thenReturn(1) - whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) - whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false) - whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false) - val entry = fakeNotification(1, true) - whenever( - sensitiveNotificationProtectionController.shouldProtectNotification( - entry.getRepresentativeEntry() + fun onBeforeRenderList_deviceLocked_notifNeedsRedaction_shouldProtectNotification() = + testScope.runTest { + coordinator.attach(pipeline) + val onBeforeRenderListListener = + withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)) + .thenReturn(false) + setupLockScreen(canSwipeUp = false) + val entry = fakeNotification(1, true) + whenever( + sensitiveNotificationProtectionController.shouldProtectNotification( + entry.getRepresentativeEntry() + ) ) - ) - .thenReturn(true) + .thenReturn(true) - onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) - verify(entry.representativeEntry!!).setSensitive(true, true) - verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false) - } + verify(entry.representativeEntry!!).setSensitive(true, true) + verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false) + } @Test - fun onBeforeRenderList_deviceDynamicallyUnlocked_notifNeedsRedaction() { - coordinator.attach(pipeline) - val onBeforeRenderListListener = - withArgCaptor<OnBeforeRenderListListener> { - verify(pipeline).addOnBeforeRenderListListener(capture()) - } - - whenever(lockscreenUserManager.currentUserId).thenReturn(1) - whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) - whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false) - whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true) - val entry = fakeNotification(1, true) - - onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) - - verify(entry.representativeEntry!!).setSensitive(false, true) - } + fun onBeforeRenderList_deviceDynamicallyUnlocked_notifNeedsRedaction() = + testScope.runTest { + coordinator.attach(pipeline) + val onBeforeRenderListListener = + withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)) + .thenReturn(false) + setupLockScreen(canSwipeUp = true) + val entry = fakeNotification(1, true) + + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + + verify(entry.representativeEntry!!).setSensitive(false, true) + } @Test @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING) - fun onBeforeRenderList_deviceDynamicallyUnlocked_notifNeedsRedaction_sensitiveActive() { - coordinator.attach(pipeline) - val onBeforeRenderListListener = - withArgCaptor<OnBeforeRenderListListener> { - verify(pipeline).addOnBeforeRenderListListener(capture()) - } - - whenever(lockscreenUserManager.currentUserId).thenReturn(1) - whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) - whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false) - whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true) - val entry = fakeNotification(1, true) - whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true) - - onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) - - verify(entry.representativeEntry!!).setSensitive(false, true) - verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true) - } + fun onBeforeRenderList_deviceDynamicallyUnlocked_notifNeedsRedaction_sensitiveActive() = + testScope.runTest { + coordinator.attach(pipeline) + val onBeforeRenderListListener = + withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)) + .thenReturn(false) + setupLockScreen(canSwipeUp = true) + val entry = fakeNotification(1, true) + whenever(sensitiveNotificationProtectionController.isSensitiveStateActive) + .thenReturn(true) + + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + + verify(entry.representativeEntry!!).setSensitive(false, true) + verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true) + } @Test @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING) @Suppress("ktlint:standard:max-line-length") - fun onBeforeRenderList_deviceDynamicallyUnlocked_notifNeedsRedaction_shouldProtectNotification() { - coordinator.attach(pipeline) - val onBeforeRenderListListener = - withArgCaptor<OnBeforeRenderListListener> { - verify(pipeline).addOnBeforeRenderListListener(capture()) - } - - whenever(lockscreenUserManager.currentUserId).thenReturn(1) - whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) - whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false) - whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true) - val entry = fakeNotification(1, true) - whenever( - sensitiveNotificationProtectionController.shouldProtectNotification( - entry.getRepresentativeEntry() + fun onBeforeRenderList_deviceDynamicallyUnlocked_notifNeedsRedaction_shouldProtectNotification() = + testScope.runTest { + coordinator.attach(pipeline) + val onBeforeRenderListListener = + withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)) + .thenReturn(false) + setupLockScreen(canSwipeUp = true) + val entry = fakeNotification(1, true) + whenever( + sensitiveNotificationProtectionController.shouldProtectNotification( + entry.getRepresentativeEntry() + ) ) - ) - .thenReturn(true) + .thenReturn(true) - onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) - verify(entry.representativeEntry!!).setSensitive(true, true) - verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false) - } + verify(entry.representativeEntry!!).setSensitive(true, true) + verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false) + } @Test - fun onBeforeRenderList_deviceDynamicallyUnlocked_notifUserNeedsWorkChallenge() { - coordinator.attach(pipeline) - val onBeforeRenderListListener = - withArgCaptor<OnBeforeRenderListListener> { - verify(pipeline).addOnBeforeRenderListListener(capture()) - } - - whenever(lockscreenUserManager.currentUserId).thenReturn(1) - whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) - whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false) - whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true) - whenever(lockscreenUserManager.needsSeparateWorkChallenge(2)).thenReturn(true) - val entry = fakeNotification(2, true) - - onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) - - verify(entry.representativeEntry!!).setSensitive(true, true) - } + fun onBeforeRenderList_deviceDynamicallyUnlocked_notifUserNeedsWorkChallenge() = + testScope.runTest { + coordinator.attach(pipeline) + val onBeforeRenderListListener = + withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)) + .thenReturn(false) + setupLockScreen(canSwipeUp = true) + whenever(lockscreenUserManager.needsSeparateWorkChallenge(2)).thenReturn(true) + val entry = fakeNotification(2, true) + + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + + verify(entry.representativeEntry!!).setSensitive(true, true) + } @Test @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING) - fun onBeforeRenderList_deviceDynamicallyUnlocked_notifUserNeedsWorkChallenge_sensitiveActive() { - coordinator.attach(pipeline) - val onBeforeRenderListListener = - withArgCaptor<OnBeforeRenderListListener> { - verify(pipeline).addOnBeforeRenderListListener(capture()) - } - - whenever(lockscreenUserManager.currentUserId).thenReturn(1) - whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) - whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false) - whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true) - whenever(lockscreenUserManager.needsSeparateWorkChallenge(2)).thenReturn(true) - val entry = fakeNotification(2, true) - whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true) - - onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) - - verify(entry.representativeEntry!!).setSensitive(true, true) - verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true) - } + fun onBeforeRenderList_deviceDynamicallyUnlocked_notifUserNeedsWorkChallenge_sensitiveActive() = + testScope.runTest { + coordinator.attach(pipeline) + val onBeforeRenderListListener = + withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)) + .thenReturn(false) + setupLockScreen(canSwipeUp = true) + whenever(lockscreenUserManager.needsSeparateWorkChallenge(2)).thenReturn(true) + val entry = fakeNotification(2, true) + whenever(sensitiveNotificationProtectionController.isSensitiveStateActive) + .thenReturn(true) + + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + + verify(entry.representativeEntry!!).setSensitive(true, true) + verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(true) + } @Test @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING) @Suppress("ktlint:standard:max-line-length") - fun onBeforeRenderList_deviceDynamicallyUnlocked_notifUserNeedsWorkChallenge_shouldProtectNotification() { - coordinator.attach(pipeline) - val onBeforeRenderListListener = - withArgCaptor<OnBeforeRenderListListener> { - verify(pipeline).addOnBeforeRenderListListener(capture()) - } - - whenever(lockscreenUserManager.currentUserId).thenReturn(1) - whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) - whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false) - whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true) - whenever(lockscreenUserManager.needsSeparateWorkChallenge(2)).thenReturn(true) - val entry = fakeNotification(2, true) - whenever( - sensitiveNotificationProtectionController.shouldProtectNotification( - entry.getRepresentativeEntry() + fun onBeforeRenderList_deviceDynamicallyUnlocked_notifUserNeedsWorkChallenge_shouldProtectNotification() = + testScope.runTest { + coordinator.attach(pipeline) + val onBeforeRenderListListener = + withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)) + .thenReturn(false) + setupLockScreen(canSwipeUp = true) + whenever(lockscreenUserManager.needsSeparateWorkChallenge(2)).thenReturn(true) + val entry = fakeNotification(2, true) + whenever( + sensitiveNotificationProtectionController.shouldProtectNotification( + entry.getRepresentativeEntry() + ) ) - ) - .thenReturn(true) + .thenReturn(true) - onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) - verify(entry.representativeEntry!!).setSensitive(true, true) - verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false) - } + verify(entry.representativeEntry!!).setSensitive(true, true) + verify(entry.representativeEntry!!.row!!).setPublicExpanderVisible(false) + } @Test - fun onBeforeRenderList_deviceDynamicallyUnlocked_deviceBiometricBypassingLockScreen() { - coordinator.attach(pipeline) - val onBeforeRenderListListener = - withArgCaptor<OnBeforeRenderListListener> { - verify(pipeline).addOnBeforeRenderListListener(capture()) - } + fun onBeforeRenderList_deviceDynamicallyUnlocked_deviceBiometricBypassingLockScreen() = + testScope.runTest { + coordinator.attach(pipeline) + val onBeforeRenderListListener = + withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)) + .thenReturn(false) + setupLockScreen(canSwipeUp = true) + whenever(keyguardUpdateMonitor.getUserUnlockedWithBiometricAndIsBypassing(any())) + .thenReturn(true) + val entry = fakeNotification(2, true) + whenever(sensitiveNotificationProtectionController.isSensitiveStateActive) + .thenReturn(true) + whenever(sensitiveNotificationProtectionController.shouldProtectNotification(any())) + .thenReturn(true) + statusBarStateController.state = StatusBarState.KEYGUARD + + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + + verify(entry.representativeEntry!!, never()).setSensitive(any(), any()) + verify(entry.representativeEntry!!.row!!, never()).setPublicExpanderVisible(any()) + } + + private fun TestScope.setupLockScreen(canSwipeUp: Boolean) { + if (SceneContainerFlag.isEnabled) { + val authMethod = + if (canSwipeUp) AuthenticationMethodModel.None + else AuthenticationMethodModel.Password + kosmos.fakeAuthenticationRepository.setAuthenticationMethod(authMethod) + kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true) + switchToScene(Scenes.Lockscreen) + } else { + whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(canSwipeUp) + } + } - whenever(lockscreenUserManager.currentUserId).thenReturn(1) - whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) - whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false) - whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true) - whenever(keyguardUpdateMonitor.getUserUnlockedWithBiometricAndIsBypassing(any())) - .thenReturn(true) - val entry = fakeNotification(2, true) - whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true) - whenever(sensitiveNotificationProtectionController.shouldProtectNotification(any())) - .thenReturn(true) - statusBarStateController.state = StatusBarState.KEYGUARD - - onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) - - verify(entry.representativeEntry!!, never()).setSensitive(any(), any()) - verify(entry.representativeEntry!!.row!!, never()).setPublicExpanderVisible(any()) + private fun TestScope.switchToScene(sceneKey: SceneKey) { + sceneInteractor.changeScene(sceneKey, "reason") + sceneInteractor.setTransitionState(flowOf(ObservableTransitionState.Idle(sceneKey))) + runCurrent() } private fun fakeNotification(notifUserId: Int, needsRedaction: Boolean): ListEntry { diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt index f9b30c6c2ba1..ea428698e476 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt @@ -27,6 +27,7 @@ import com.android.app.tracing.coroutines.launchTraced as launch import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.TransitionKey +import com.android.systemui.Flags.communalResponsiveGrid import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.communal.data.repository.CommunalMediaRepository import com.android.systemui.communal.data.repository.CommunalSmartspaceRepository @@ -535,7 +536,9 @@ constructor( // Order by creation time descending. ongoingContent.sortByDescending { it.createdTimestampMillis } // Resize the items. - ongoingContent.resizeItems() + if (!communalResponsiveGrid()) { + ongoingContent.resizeItems() + } // Return the sorted and resized items. ongoingContent diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt index da613f58dce8..c0456d5bb46e 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt @@ -110,6 +110,11 @@ sealed interface CommunalContentModel { get() = fixedHalfOrResponsiveSize() } + /** An empty spacer to reserve space in the grid. */ + data class Spacer(override val size: CommunalContentSize) : CommunalContentModel { + override val key: String = KEY.spacer() + } + /** A CTA tile in the glanceable hub view mode which can be dismissed. */ class CtaTileInViewMode : CommunalContentModel { override val key: String = KEY.CTA_TILE_IN_VIEW_MODE_KEY @@ -171,6 +176,10 @@ sealed interface CommunalContentModel { fun umo(): String { return "umo" } + + fun spacer(): String { + return "spacer_${UUID.randomUUID()}" + } } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/ResizeUtils.kt b/packages/SystemUI/src/com/android/systemui/communal/util/ResizeUtils.kt new file mode 100644 index 000000000000..36cc1c051336 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/util/ResizeUtils.kt @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2024 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.communal.util + +import com.android.systemui.communal.domain.model.CommunalContentModel +import com.android.systemui.communal.shared.model.CommunalContentSize + +object ResizeUtils { + /** + * Resizes ongoing items such that we don't mix regular content with ongoing content. + * + * NOTE: This is *NOT* a pure function, as it modifies items in the input list. + * + * Assumptions: + * 1. Ongoing content is always at the start of the list. + * 2. The maximum size of ongoing content is 2 rows. + */ + fun resizeOngoingItems( + list: List<CommunalContentModel>, + numRows: Int, + ): List<CommunalContentModel> { + val finalizedList = mutableListOf<CommunalContentModel>() + val numOngoing = list.count { it is CommunalContentModel.Ongoing } + // Calculate the number of extra rows we have if each ongoing item were to take up a single + // row. This is the number of rows we have to distribute across items. + var extraRows = + if (numOngoing % numRows == 0) { + 0 + } else { + numRows - (numOngoing % numRows) + } + var remainingRows = numRows + + for (item in list) { + if (item is CommunalContentModel.Ongoing) { + if (remainingRows == 0) { + // Start a new column. + remainingRows = numRows + } + val newSize = if (extraRows > 0 && remainingRows > 1) 2 else 1 + item.size = CommunalContentSize.Responsive(newSize) + finalizedList.add(item) + extraRows -= (newSize - 1) + remainingRows -= newSize + } else { + if (numOngoing > 0 && remainingRows > 0) { + finalizedList.add( + CommunalContentModel.Spacer(CommunalContentSize.Responsive(remainingRows)) + ) + } + remainingRows = -1 + finalizedList.add(item) + } + } + return finalizedList + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt index ef7b1c3d562e..04458f3a7168 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt @@ -14,10 +14,13 @@ * limitations under the License. */ +@file:OptIn(ExperimentalCoroutinesApi::class) + package com.android.systemui.statusbar.notification.collection.coordinator import android.app.Notification import android.os.UserHandle +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.keyguard.KeyguardUpdateMonitor import com.android.server.notification.Flags.screenshareNotificationHiding import com.android.systemui.dagger.qualifiers.Application @@ -44,9 +47,9 @@ import dagger.Binds import dagger.Module import javax.inject.Inject import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.mapNotNull -import com.android.app.tracing.coroutines.launchTraced as launch @Module(includes = [PrivateSensitiveContentCoordinatorModule::class]) interface SensitiveContentCoordinatorModule @@ -80,6 +83,7 @@ constructor( DynamicPrivacyController.Listener, OnBeforeRenderListListener { private var inTransitionFromLockedToGone = false + private var canSwipeToEnter = false private val onSensitiveStateChanged = Runnable() { invalidateList("onSensitiveStateChanged") } @@ -98,7 +102,9 @@ constructor( } override fun attach(pipeline: NotifPipeline) { - dynamicPrivacyController.addListener(this) + if (!SceneContainerFlag.isEnabled) { + dynamicPrivacyController.addListener(this) + } if (screenshareNotificationHiding()) { sensitiveNotificationProtectionController.registerSensitiveStateListener( onSensitiveStateChanged @@ -128,6 +134,15 @@ constructor( invalidateList("inTransitionFromLockedToGoneChanged") } } + scope.launch { + deviceEntryInteractor.canSwipeToEnter.collect { + val canSwipeToEnter = it ?: false + if (this@SensitiveContentCoordinatorImpl.canSwipeToEnter != canSwipeToEnter) { + this@SensitiveContentCoordinatorImpl.canSwipeToEnter = canSwipeToEnter + invalidateList("canSwipeToEnterChanged") + } + } + } } } @@ -168,7 +183,9 @@ constructor( (devicePublic && !lockscreenUserManager.userAllowsPrivateNotificationsInPublic(currentUserId)) || isSensitiveContentProtectionActive - val dynamicallyUnlocked = dynamicPrivacyController.isDynamicallyUnlocked + val dynamicallyUnlocked = + if (SceneContainerFlag.isEnabled) canSwipeToEnter + else dynamicPrivacyController.isDynamicallyUnlocked for (entry in extractAllRepresentativeEntries(entries).filter { it.rowExists() }) { val notifUserId = entry.sbn.user.identifier val userLockscreen = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index ba707a5cd0b1..245b1d29fb79 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -429,9 +429,10 @@ public class NotificationStackScrollLayoutController implements Dumpable { }; /** - * Recalculate sensitiveness without animation; called when waking up while keyguard occluded. + * Recalculate sensitiveness without animation; called when waking up while keyguard occluded, + * or whenever we update the Lockscreen public mode. */ - public void updateSensitivenessForOccludedWakeup() { + public void updateSensitivenessWithoutAnimation() { updateSensitivenessWithAnimation(false); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index c6af3280eef1..7bea4800f7fc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -2234,6 +2234,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { // If the state didn't change, we may still need to update public mode mLockscreenUserManager.updatePublicMode(); + if (SceneContainerFlag.isEnabled()) { + mStackScrollerController.updateSensitivenessWithoutAnimation(); + } } if (mStatusBarStateController.leaveOpenOnKeyguardHide()) { if (!mStatusBarStateController.isKeyguardRequested()) { @@ -2681,7 +2684,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { // So if AOD is off or unsupported we need to trigger these updates at screen on // when the keyguard is occluded. mLockscreenUserManager.updatePublicMode(); - mStackScrollerController.updateSensitivenessForOccludedWakeup(); + mStackScrollerController.updateSensitivenessWithoutAnimation(); } if (mLaunchCameraWhenFinishedWaking) { startLaunchTransitionTimeout(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index 6912eda3c3d4..d157cf2d51e0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -36,8 +36,6 @@ import static com.android.systemui.statusbar.phone.CentralSurfaces.MSG_DISMISS_K import static com.google.common.truth.Truth.assertThat; -import static kotlinx.coroutines.flow.FlowKt.flowOf; - import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -56,6 +54,8 @@ import static org.mockito.Mockito.when; import static java.util.Collections.emptySet; +import static kotlinx.coroutines.flow.FlowKt.flowOf; + import android.app.ActivityManager; import android.app.IWallpaperManager; import android.app.NotificationManager; @@ -138,6 +138,7 @@ import com.android.systemui.plugins.PluginDependencyProvider; import com.android.systemui.plugins.PluginManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.power.domain.interactor.PowerInteractor; +import com.android.systemui.qs.flags.QSComposeFragment; import com.android.systemui.res.R; import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor; import com.android.systemui.scene.domain.startable.ScrimStartable; @@ -181,6 +182,7 @@ import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.interruption.AvalancheProvider; import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider; @@ -198,7 +200,6 @@ import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.ExtensionController; -import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.window.StatusBarWindowController; @@ -217,10 +218,6 @@ import com.android.systemui.volume.VolumeComponent; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.startingsurface.StartingSurface; -import dagger.Lazy; - -import kotlinx.coroutines.test.TestScope; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -237,6 +234,9 @@ import java.util.Set; import javax.inject.Provider; +import dagger.Lazy; +import kotlinx.coroutines.test.TestScope; + @SmallTest @RunWith(AndroidJUnit4.class) @RunWithLooper(setAsMainLooper = true) @@ -667,6 +667,9 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mCentralSurfaces.startKeyguard(); mInitController.executePostInitTasks(); mCentralSurfaces.registerCallbacks(); + // Clear first invocations caused by registering flows with JavaAdapter + mTestScope.getTestScheduler().runCurrent(); + clearInvocations(mScrimController); } @Test @@ -1159,8 +1162,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Test @EnableSceneContainer - public void brightnesShowingChanged_flagEnabled_ScrimControllerNotified() { - mCentralSurfaces.registerCallbacks(); + public void brightnesShowingChanged_sceneContainerFlagEnabled_ScrimControllerNotified() { final ScrimStartable scrimStartable = mKosmos.getScrimStartable(); scrimStartable.start(); @@ -1178,9 +1180,25 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Test @DisableSceneContainer - public void brightnesShowingChanged_flagDisabled_ScrimControllerNotified() { - mCentralSurfaces.registerCallbacks(); + @EnableFlags(QSComposeFragment.FLAG_NAME) + public void brightnesShowingChanged_qsUiRefactorFlagEnabled_ScrimControllerNotified() { + mBrightnessMirrorShowingInteractor.setMirrorShowing(true); + mTestScope.getTestScheduler().runCurrent(); + verify(mScrimController, atLeastOnce()).legacyTransitionTo(ScrimState.BRIGHTNESS_MIRROR); + clearInvocations(mScrimController); + + mBrightnessMirrorShowingInteractor.setMirrorShowing(false); + mTestScope.getTestScheduler().runCurrent(); + ArgumentCaptor<ScrimState> captor = ArgumentCaptor.forClass(ScrimState.class); + // The default is to call the one with the callback argument + verify(mScrimController, atLeastOnce()).legacyTransitionTo(captor.capture(), any()); + assertThat(captor.getValue()).isNotEqualTo(ScrimState.BRIGHTNESS_MIRROR); + } + @Test + @DisableSceneContainer + @DisableFlags(QSComposeFragment.FLAG_NAME) + public void brightnesShowingChanged_flagsDisabled_ScrimControllerNotified() { mBrightnessMirrorShowingInteractor.setMirrorShowing(true); mTestScope.getTestScheduler().runCurrent(); verify(mScrimController, never()).legacyTransitionTo(ScrimState.BRIGHTNESS_MIRROR); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorKosmos.kt index 4a249a8591e9..88bf9a5f2d5b 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorKosmos.kt @@ -42,6 +42,6 @@ var Kosmos.sensitiveContentCoordinator: SensitiveContentCoordinator by sensitiveNotificationProtectionController, deviceEntryInteractor, sceneInteractor, - testScope, + testScope.backgroundScope, ) } |