summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Treehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com> 2024-03-28 03:48:49 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-03-28 03:48:49 +0000
commit80b546e60bc9ad69c596de1836daac878d3fb921 (patch)
treee93f4be85666edcd7cecaea387c081bb7e082485
parent280dc5486760ea1e226776bc8ee592b1dd533615 (diff)
parent75584ff6da827aa0ed13d1da2b3b3d44d12da977 (diff)
Merge "[flexiglass] Occlusion support." into main
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt)57
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractorTest.kt270
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt86
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt50
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt11
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractorKosmos.kt30
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardOcclusionInteractorKosmos.kt4
11 files changed, 525 insertions, 47 deletions
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt
index 8a77ed2130a9..056a401a2644 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt
@@ -30,17 +30,24 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.keyguard.domain.interactor
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.powerInteractor
@@ -49,22 +56,33 @@ import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
-import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class KeyguardOcclusionInteractorTest : SysuiTestCase() {
+
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val underTest = kosmos.keyguardOcclusionInteractor
- private val powerInteractor = kosmos.powerInteractor
- private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
+
+ private lateinit var underTest: KeyguardOcclusionInteractor
+ private lateinit var powerInteractor: PowerInteractor
+ private lateinit var transitionRepository: FakeKeyguardTransitionRepository
+
+ @Before
+ fun setUp() {
+ powerInteractor = kosmos.powerInteractor
+ transitionRepository = kosmos.fakeKeyguardTransitionRepository
+ underTest = kosmos.keyguardOcclusionInteractor
+ }
@Test
- fun testTransitionFromPowerGesture_whileGoingToSleep_isTrue() =
+ fun transitionFromPowerGesture_whileGoingToSleep_isTrue() =
testScope.runTest {
powerInteractor.setAwakeForTest()
transitionRepository.sendTransitionSteps(
@@ -81,7 +99,7 @@ class KeyguardOcclusionInteractorTest : SysuiTestCase() {
}
@Test
- fun testTransitionFromPowerGesture_whileAsleep_isTrue() =
+ fun transitionFromPowerGesture_whileAsleep_isTrue() =
testScope.runTest {
powerInteractor.setAwakeForTest()
transitionRepository.sendTransitionSteps(
@@ -97,7 +115,7 @@ class KeyguardOcclusionInteractorTest : SysuiTestCase() {
}
@Test
- fun testTransitionFromPowerGesture_whileWaking_isFalse() =
+ fun transitionFromPowerGesture_whileWaking_isFalse() =
testScope.runTest {
powerInteractor.setAwakeForTest()
transitionRepository.sendTransitionSteps(
@@ -119,7 +137,7 @@ class KeyguardOcclusionInteractorTest : SysuiTestCase() {
}
@Test
- fun testTransitionFromPowerGesture_whileAwake_isFalse() =
+ fun transitionFromPowerGesture_whileAwake_isFalse() =
testScope.runTest {
powerInteractor.setAwakeForTest()
transitionRepository.sendTransitionSteps(
@@ -140,7 +158,7 @@ class KeyguardOcclusionInteractorTest : SysuiTestCase() {
}
@Test
- fun testShowWhenLockedActivityLaunchedFromPowerGesture_notTrueSecondTime() =
+ fun showWhenLockedActivityLaunchedFromPowerGesture_notTrueSecondTime() =
testScope.runTest {
val values by collectValues(underTest.showWhenLockedActivityLaunchedFromPowerGesture)
powerInteractor.setAsleepForTest()
@@ -187,7 +205,7 @@ class KeyguardOcclusionInteractorTest : SysuiTestCase() {
}
@Test
- fun testShowWhenLockedActivityLaunchedFromPowerGesture_falseIfReturningToGone() =
+ fun showWhenLockedActivityLaunchedFromPowerGesture_falseIfReturningToGone() =
testScope.runTest {
val values by collectValues(underTest.showWhenLockedActivityLaunchedFromPowerGesture)
powerInteractor.setAwakeForTest()
@@ -221,4 +239,23 @@ class KeyguardOcclusionInteractorTest : SysuiTestCase() {
false,
)
}
+
+ @Test
+ @EnableSceneContainer
+ fun occludingActivityWillDismissKeyguard() =
+ testScope.runTest {
+ val occludingActivityWillDismissKeyguard by
+ collectLastValue(underTest.occludingActivityWillDismissKeyguard)
+ assertThat(occludingActivityWillDismissKeyguard).isFalse()
+
+ // Unlock device:
+ kosmos.fakeDeviceEntryRepository.setUnlocked(true)
+ runCurrent()
+ assertThat(occludingActivityWillDismissKeyguard).isTrue()
+
+ // Re-lock device:
+ kosmos.fakeDeviceEntryRepository.setUnlocked(false)
+ runCurrent()
+ assertThat(occludingActivityWillDismissKeyguard).isFalse()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index c80835d9b2b3..efbdb7d466d1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -64,6 +64,7 @@ import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.qs.footerActionsController
import com.android.systemui.qs.footerActionsViewModelFactory
import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
+import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.startable.SceneContainerStartable
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
@@ -286,6 +287,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
deviceProvisioningInteractor = kosmos.deviceProvisioningInteractor,
centralSurfaces = mock(),
headsUpInteractor = kosmos.headsUpNotificationInteractor,
+ occlusionInteractor = kosmos.sceneContainerOcclusionInteractor,
)
startable.start()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractorTest.kt
new file mode 100644
index 000000000000..c3366ad21e2a
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractorTest.kt
@@ -0,0 +1,270 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.scene.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.shared.model.sceneDataSource
+import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SceneContainerOcclusionInteractorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val keyguardOcclusionInteractor = kosmos.keyguardOcclusionInteractor
+ private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+ private val keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor
+ private val mutableTransitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(Scenes.Lockscreen)
+ )
+ private val sceneInteractor =
+ kosmos.sceneInteractor.apply { setTransitionState(mutableTransitionState) }
+ private val sceneDataSource =
+ kosmos.sceneDataSource.apply { changeScene(toScene = Scenes.Lockscreen) }
+
+ private val underTest = kosmos.sceneContainerOcclusionInteractor
+
+ @Test
+ fun invisibleDueToOcclusion() =
+ testScope.runTest {
+ val invisibleDueToOcclusion by collectLastValue(underTest.invisibleDueToOcclusion)
+ val keyguardState by collectLastValue(keyguardTransitionInteractor.currentKeyguardState)
+
+ // Assert that we have the desired preconditions:
+ assertThat(keyguardState).isEqualTo(KeyguardState.LOCKSCREEN)
+ assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Lockscreen)
+ assertThat(sceneInteractor.transitionState.value)
+ .isEqualTo(ObservableTransitionState.Idle(Scenes.Lockscreen))
+ assertWithMessage("Should start unoccluded").that(invisibleDueToOcclusion).isFalse()
+
+ // Actual testing starts here:
+ showOccludingActivity()
+ assertWithMessage("Should become occluded when occluding activity is shown")
+ .that(invisibleDueToOcclusion)
+ .isTrue()
+
+ transitionIntoAod {
+ assertWithMessage("Should become unoccluded when transitioning into AOD")
+ .that(invisibleDueToOcclusion)
+ .isFalse()
+ }
+ assertWithMessage("Should stay unoccluded when in AOD")
+ .that(invisibleDueToOcclusion)
+ .isFalse()
+
+ transitionOutOfAod {
+ assertWithMessage("Should remain unoccluded while transitioning away from AOD")
+ .that(invisibleDueToOcclusion)
+ .isFalse()
+ }
+ assertWithMessage("Should become occluded now that no longer in AOD")
+ .that(invisibleDueToOcclusion)
+ .isTrue()
+
+ expandShade {
+ assertWithMessage("Should become unoccluded once shade begins to expand")
+ .that(invisibleDueToOcclusion)
+ .isFalse()
+ }
+ assertWithMessage("Should be unoccluded when shade is fully expanded")
+ .that(invisibleDueToOcclusion)
+ .isFalse()
+
+ collapseShade {
+ assertWithMessage("Should remain unoccluded while shade is collapsing")
+ .that(invisibleDueToOcclusion)
+ .isFalse()
+ }
+ assertWithMessage("Should become occluded now that shade is fully collapsed")
+ .that(invisibleDueToOcclusion)
+ .isTrue()
+
+ hideOccludingActivity()
+ assertWithMessage("Should become unoccluded once the occluding activity is hidden")
+ .that(invisibleDueToOcclusion)
+ .isFalse()
+ }
+
+ /** Simulates the appearance of a show-when-locked `Activity` in the foreground. */
+ private fun TestScope.showOccludingActivity() {
+ keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(
+ showWhenLockedActivityOnTop = true,
+ taskInfo = mock(),
+ )
+ runCurrent()
+ }
+
+ /** Simulates the disappearance of a show-when-locked `Activity` from the foreground. */
+ private fun TestScope.hideOccludingActivity() {
+ keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(
+ showWhenLockedActivityOnTop = false,
+ )
+ runCurrent()
+ }
+
+ /** Simulates a user-driven gradual expansion of the shade. */
+ private fun TestScope.expandShade(
+ assertMidTransition: () -> Unit = {},
+ ) {
+ val progress = MutableStateFlow(0f)
+ mutableTransitionState.value =
+ ObservableTransitionState.Transition(
+ fromScene = sceneDataSource.currentScene.value,
+ toScene = Scenes.Shade,
+ progress = progress,
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(true),
+ )
+ runCurrent()
+
+ progress.value = 0.5f
+ runCurrent()
+ assertMidTransition()
+
+ progress.value = 1f
+ runCurrent()
+
+ mutableTransitionState.value = ObservableTransitionState.Idle(Scenes.Shade)
+ runCurrent()
+ }
+
+ /** Simulates a user-driven gradual collapse of the shade. */
+ private fun TestScope.collapseShade(
+ assertMidTransition: () -> Unit = {},
+ ) {
+ val progress = MutableStateFlow(0f)
+ mutableTransitionState.value =
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Shade,
+ toScene = Scenes.Lockscreen,
+ progress = progress,
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(true),
+ )
+ runCurrent()
+
+ progress.value = 0.5f
+ runCurrent()
+ assertMidTransition()
+
+ progress.value = 1f
+ runCurrent()
+
+ mutableTransitionState.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
+ runCurrent()
+ }
+
+ /** Simulates a transition into AOD. */
+ private suspend fun TestScope.transitionIntoAod(
+ assertMidTransition: () -> Unit = {},
+ ) {
+ val currentKeyguardState = keyguardTransitionInteractor.getCurrentState()
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = currentKeyguardState,
+ to = KeyguardState.AOD,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ runCurrent()
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = currentKeyguardState,
+ to = KeyguardState.AOD,
+ value = 0.5f,
+ transitionState = TransitionState.RUNNING,
+ )
+ )
+ runCurrent()
+ assertMidTransition()
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = currentKeyguardState,
+ to = KeyguardState.AOD,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ )
+ )
+ runCurrent()
+ }
+
+ /** Simulates a transition away from AOD. */
+ private suspend fun TestScope.transitionOutOfAod(
+ assertMidTransition: () -> Unit = {},
+ ) {
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ runCurrent()
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ value = 0.5f,
+ transitionState = TransitionState.RUNNING,
+ )
+ )
+ runCurrent()
+ assertMidTransition()
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ )
+ )
+ runCurrent()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 594543b88b17..605e5c067eab 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -46,11 +46,13 @@ import com.android.systemui.model.SysUiState
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
+import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
import com.android.systemui.statusbar.notification.data.repository.HeadsUpRowRepository
import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
@@ -128,6 +130,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
deviceProvisioningInteractor = kosmos.deviceProvisioningInteractor,
centralSurfaces = centralSurfaces,
headsUpInteractor = kosmos.headsUpNotificationInteractor,
+ occlusionInteractor = kosmos.sceneContainerOcclusionInteractor,
)
}
@@ -204,6 +207,28 @@ class SceneContainerStartableTest : SysuiTestCase() {
}
@Test
+ fun hydrateVisibility_basedOnOcclusion() =
+ testScope.runTest {
+ val isVisible by collectLastValue(sceneInteractor.isVisible)
+ prepareState(
+ isDeviceUnlocked = true,
+ initialSceneKey = Scenes.Lockscreen,
+ )
+
+ underTest.start()
+ assertThat(isVisible).isTrue()
+
+ kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(
+ true,
+ mock()
+ )
+ assertThat(isVisible).isFalse()
+
+ kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(false)
+ assertThat(isVisible).isTrue()
+ }
+
+ @Test
fun startsInLockscreenScene() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt
index 9aa2202b4100..03ed5675a77a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt
@@ -19,13 +19,17 @@ package com.android.systemui.keyguard.domain.interactor
import android.app.ActivityManager.RunningTaskInfo
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
import com.android.systemui.keyguard.data.repository.KeyguardOcclusionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.util.kotlin.sample
+import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
@@ -45,11 +49,12 @@ import kotlinx.coroutines.flow.stateIn
class KeyguardOcclusionInteractor
@Inject
constructor(
- @Application scope: CoroutineScope,
- val repository: KeyguardOcclusionRepository,
- val powerInteractor: PowerInteractor,
- val transitionInteractor: KeyguardTransitionInteractor,
- val keyguardInteractor: KeyguardInteractor,
+ @Application applicationScope: CoroutineScope,
+ private val repository: KeyguardOcclusionRepository,
+ private val powerInteractor: PowerInteractor,
+ private val transitionInteractor: KeyguardTransitionInteractor,
+ keyguardInteractor: KeyguardInteractor,
+ deviceUnlockedInteractor: Lazy<DeviceUnlockedInteractor>,
) {
val showWhenLockedActivityInfo = repository.showWhenLockedActivityInfo.asStateFlow()
@@ -94,14 +99,19 @@ constructor(
// Emit false once that activity goes away.
isShowWhenLockedActivityOnTop.filter { !it }.map { false }
)
- .stateIn(scope, SharingStarted.Eagerly, false)
+ .stateIn(applicationScope, SharingStarted.Eagerly, false)
/**
* Whether launching an occluding activity will automatically dismiss keyguard. This happens if
* the keyguard is dismissable.
*/
- val occludingActivityWillDismissKeyguard =
- keyguardInteractor.isKeyguardDismissible.stateIn(scope, SharingStarted.Eagerly, false)
+ val occludingActivityWillDismissKeyguard: StateFlow<Boolean> =
+ if (SceneContainerFlag.isEnabled) {
+ deviceUnlockedInteractor.get().isDeviceUnlocked
+ } else {
+ keyguardInteractor.isKeyguardDismissible
+ }
+ .stateIn(scope = applicationScope, SharingStarted.Eagerly, false)
/**
* Called to let System UI know that WM says a SHOW_WHEN_LOCKED activity is on top (or no longer
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt
new file mode 100644
index 000000000000..a82391643e11
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt
@@ -0,0 +1,86 @@
+/*
+ * 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.scene.domain.interactor
+
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardOcclusionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.scene.shared.model.Scenes
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+
+/** Encapsulates logic regarding the occlusion state of the scene container. */
+@SysUISingleton
+class SceneContainerOcclusionInteractor
+@Inject
+constructor(
+ keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
+ sceneInteractor: SceneInteractor,
+ keyguardTransitionInteractor: KeyguardTransitionInteractor,
+) {
+ /**
+ * Whether the scene container should become invisible due to "occlusion" by an in-foreground
+ * "show when locked" activity.
+ */
+ val invisibleDueToOcclusion: Flow<Boolean> =
+ combine(
+ keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop,
+ sceneInteractor.transitionState,
+ keyguardTransitionInteractor
+ .transitionValue(KeyguardState.AOD)
+ .onStart { emit(0f) }
+ .map { it > 0 }
+ .distinctUntilChanged(),
+ ) { isOccludingActivityShown, sceneTransitionState, isAodFullyOrPartiallyShown ->
+ isOccludingActivityShown &&
+ !isAodFullyOrPartiallyShown &&
+ sceneTransitionState.canBeOccluded
+ }
+ .distinctUntilChanged()
+
+ private val ObservableTransitionState.canBeOccluded: Boolean
+ get() =
+ when (this) {
+ is ObservableTransitionState.Idle -> scene.canBeOccluded
+ is ObservableTransitionState.Transition ->
+ fromScene.canBeOccluded && toScene.canBeOccluded
+ }
+
+ /**
+ * Whether the scene can be occluded by a "show when locked" activity. Some scenes should, on
+ * principle not be occlude-able because they render as if they are expanding on top of the
+ * occluding activity.
+ */
+ private val SceneKey.canBeOccluded: Boolean
+ get() =
+ when (this) {
+ Scenes.Bouncer -> true
+ Scenes.Communal -> true
+ Scenes.Gone -> true
+ Scenes.Lockscreen -> true
+ Scenes.QuickSettings -> false
+ Scenes.Shade -> false
+ else -> error("SceneKey \"$this\" doesn't have a mapping for canBeOccluded!")
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 42b41f88e155..0e4049bbd21e 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -40,6 +40,7 @@ import com.android.systemui.model.updateFlags
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.FalsingManager.FalsingBeliefListener
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.scene.shared.logger.SceneLogger
@@ -94,6 +95,7 @@ constructor(
private val deviceProvisioningInteractor: DeviceProvisioningInteractor,
private val centralSurfaces: CentralSurfaces,
private val headsUpInteractor: HeadsUpNotificationInteractor,
+ private val occlusionInteractor: SceneContainerOcclusionInteractor,
) : CoreStartable {
override fun start() {
@@ -130,32 +132,36 @@ constructor(
.distinctUntilChanged()
.flatMapLatest { isAllowedToBeVisible ->
if (isAllowedToBeVisible) {
- sceneInteractor.transitionState
- .mapNotNull { state ->
- when (state) {
- is ObservableTransitionState.Idle -> {
- if (state.scene != Scenes.Gone) {
- true to "scene is not Gone"
- } else {
- false to "scene is Gone"
+ combine(
+ sceneInteractor.transitionState.mapNotNull { state ->
+ when (state) {
+ is ObservableTransitionState.Idle -> {
+ if (state.scene != Scenes.Gone) {
+ true to "scene is not Gone"
+ } else {
+ false to "scene is Gone"
+ }
}
- }
- is ObservableTransitionState.Transition -> {
- if (state.fromScene == Scenes.Gone) {
- true to "scene transitioning away from Gone"
- } else {
- null
+ is ObservableTransitionState.Transition -> {
+ if (state.fromScene == Scenes.Gone) {
+ true to "scene transitioning away from Gone"
+ } else {
+ null
+ }
}
}
- }
- }
- .combine(headsUpInteractor.isHeadsUpOrAnimatingAway) {
+ },
+ headsUpInteractor.isHeadsUpOrAnimatingAway,
+ occlusionInteractor.invisibleDueToOcclusion,
+ ) {
visibilityForTransitionState,
- isHeadsUpOrAnimatingAway ->
- if (isHeadsUpOrAnimatingAway) {
- true to "showing a HUN"
- } else {
- visibilityForTransitionState
+ isHeadsUpOrAnimatingAway,
+ invisibleDueToOcclusion,
+ ->
+ when {
+ isHeadsUpOrAnimatingAway -> true to "showing a HUN"
+ invisibleDueToOcclusion -> false to "invisible due to occlusion"
+ else -> visibilityForTransitionState
}
}
.distinctUntilChanged()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
index 7bef01a7a5ce..3926f92b44e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
@@ -48,6 +48,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat
import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.powerInteractor
@@ -73,13 +74,17 @@ class FromAodTransitionInteractorTest : SysuiTestCase() {
}
private val testScope = kosmos.testScope
- private val underTest = kosmos.fromAodTransitionInteractor
+ private lateinit var underTest: FromAodTransitionInteractor
- private val powerInteractor = kosmos.powerInteractor
- private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
+ private lateinit var powerInteractor: PowerInteractor
+ private lateinit var transitionRepository: FakeKeyguardTransitionRepository
@Before
fun setup() {
+ powerInteractor = kosmos.powerInteractor
+ transitionRepository = kosmos.fakeKeyguardTransitionRepository
+ underTest = kosmos.fromAodTransitionInteractor
+
underTest.start()
// Transition to AOD and set the power interactor asleep.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
index 258dbf3efbae..cded2a4ec27c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
@@ -51,6 +51,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat
import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.powerInteractor
@@ -77,13 +78,17 @@ class FromDozingTransitionInteractorTest : SysuiTestCase() {
}
private val testScope = kosmos.testScope
- private val underTest = kosmos.fromDozingTransitionInteractor
+ private lateinit var underTest: FromDozingTransitionInteractor
- private val powerInteractor = kosmos.powerInteractor
- private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
+ private lateinit var powerInteractor: PowerInteractor
+ private lateinit var transitionRepository: FakeKeyguardTransitionRepository
@Before
fun setup() {
+ powerInteractor = kosmos.powerInteractor
+ transitionRepository = kosmos.fakeKeyguardTransitionRepository
+ underTest = kosmos.fromDozingTransitionInteractor
+
underTest.start()
// Transition to DOZING and set the power interactor asleep.
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractorKosmos.kt
new file mode 100644
index 000000000000..b32960a73178
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractorKosmos.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.scene.domain.interactor
+
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
+
+val Kosmos.sceneContainerOcclusionInteractor by Fixture {
+ SceneContainerOcclusionInteractor(
+ keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+ sceneInteractor = sceneInteractor,
+ keyguardTransitionInteractor = keyguardTransitionInteractor,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardOcclusionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardOcclusionInteractorKosmos.kt
index d79374021968..a90a9ffc8a33 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardOcclusionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardOcclusionInteractorKosmos.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.domain.interactor
+import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardOcclusionInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
@@ -27,10 +28,11 @@ import com.android.systemui.power.domain.interactor.powerInteractor
val Kosmos.keyguardOcclusionInteractor by
Kosmos.Fixture {
KeyguardOcclusionInteractor(
- scope = testScope.backgroundScope,
+ applicationScope = testScope.backgroundScope,
repository = keyguardOcclusionRepository,
powerInteractor = powerInteractor,
transitionInteractor = keyguardTransitionInteractor,
keyguardInteractor = keyguardInteractor,
+ deviceUnlockedInteractor = { deviceUnlockedInteractor },
)
}