Loading packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt +11 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.keyguard.ui.composable import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue Loading Loading @@ -48,6 +49,16 @@ class LockscreenContent( fun SceneScope.Content( modifier: Modifier = Modifier, ) { val isContentVisible: Boolean by viewModel.isContentVisible.collectAsStateWithLifecycle() if (!isContentVisible) { // If the content isn't supposed to be visible, show a large empty box as it's needed // for scene transition animations (can't just skip rendering everything or shared // elements won't have correct final/initial bounds from animating in and out of the // lockscreen scene). Box(modifier) return } val coroutineScope = rememberCoroutineScope() val blueprintId by viewModel.blueprintId(coroutineScope).collectAsStateWithLifecycle() val view = LocalView.current Loading packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt +45 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,8 @@ * limitations under the License. */ @file:OptIn(ExperimentalCoroutinesApi::class) package com.android.systemui.keyguard.ui.viewmodel import android.platform.test.flag.junit.FlagsParameterization Loading @@ -27,10 +29,13 @@ import com.android.systemui.flags.Flags import com.android.systemui.flags.andSceneContainer import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository import com.android.systemui.keyguard.shared.model.ClockSize import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.data.repository.shadeRepository import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.testKosmos Loading @@ -38,6 +43,8 @@ import com.android.systemui.unfold.fakeUnfoldTransitionProgressProvider import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import java.util.Locale import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test Loading Loading @@ -201,6 +208,44 @@ class LockscreenContentViewModelTest(flags: FlagsParameterization) : SysuiTestCa } } @Test fun isContentVisible_whenNotOccluded_visible() = with(kosmos) { testScope.runTest { val isContentVisible by collectLastValue(underTest.isContentVisible) keyguardOcclusionRepository.setShowWhenLockedActivityInfo(false, null) runCurrent() assertThat(isContentVisible).isTrue() } } @Test fun isContentVisible_whenOccluded_notVisible() = with(kosmos) { testScope.runTest { val isContentVisible by collectLastValue(underTest.isContentVisible) keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, null) runCurrent() assertThat(isContentVisible).isFalse() } } @Test fun isContentVisible_whenOccluded_notVisible_evenIfShadeShown() = with(kosmos) { testScope.runTest { val isContentVisible by collectLastValue(underTest.isContentVisible) keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, null) runCurrent() sceneInteractor.snapToScene(Scenes.Shade, "") runCurrent() assertThat(isContentVisible).isFalse() } } private fun prepareConfiguration(): Int { val configuration = context.resources.configuration configuration.setLayoutDirection(Locale.US) Loading packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt +12 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteract import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor import com.android.systemui.keyguard.shared.model.ClockSize import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor Loading @@ -48,6 +49,7 @@ constructor( val shadeInteractor: ShadeInteractor, @Application private val applicationScope: CoroutineScope, unfoldTransitionInteractor: UnfoldTransitionInteractor, occlusionInteractor: SceneContainerOcclusionInteractor, ) { @VisibleForTesting val clockSize = clockInteractor.clockSize Loading Loading @@ -93,6 +95,16 @@ constructor( initialValue = UnfoldTranslations(), ) /** Whether the content of the scene UI should be shown. */ val isContentVisible: StateFlow<Boolean> = occlusionInteractor.isOccludingActivityShown .map { !it } .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), initialValue = true, ) fun getSmartSpacePaddingTop(resources: Resources): Int { return if (clockSize.value == ClockSize.LARGE) { resources.getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset) + Loading packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt +11 −2 Original line number Diff line number Diff line Loading @@ -43,8 +43,14 @@ constructor( sceneInteractor: SceneInteractor, keyguardTransitionInteractor: KeyguardTransitionInteractor, ) { /** Whether a show-when-locked activity is at the top of the current activity stack. */ private val isOccludingActivityShown: StateFlow<Boolean> = /** * Whether a show-when-locked activity is at the top of the current activity stack. * * Note: this isn't enough to figure out whether the scene container UI should be invisible as * that also depends on the things like the state of AOD and the current scene. If the code * needs that, [invisibleDueToOcclusion] should be collected instead. */ val isOccludingActivityShown: StateFlow<Boolean> = keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop.stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), Loading @@ -69,6 +75,9 @@ constructor( /** * Whether the scene container should become invisible due to "occlusion" by an in-foreground * "show when locked" activity. * * Note: this returns `false` when an overlaid scene (like shade or QS) is shown above the * occluding activity. */ val invisibleDueToOcclusion: StateFlow<Boolean> = combine( Loading packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt +2 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import com.android.systemui.keyguard.domain.interactor.keyguardBlueprintInteract import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.unfold.domain.interactor.unfoldTransitionInteractor Loading @@ -34,5 +35,6 @@ val Kosmos.lockscreenContentViewModel by shadeInteractor = shadeInteractor, applicationScope = applicationCoroutineScope, unfoldTransitionInteractor = unfoldTransitionInteractor, occlusionInteractor = sceneContainerOcclusionInteractor, ) } Loading
packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt +11 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.keyguard.ui.composable import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue Loading Loading @@ -48,6 +49,16 @@ class LockscreenContent( fun SceneScope.Content( modifier: Modifier = Modifier, ) { val isContentVisible: Boolean by viewModel.isContentVisible.collectAsStateWithLifecycle() if (!isContentVisible) { // If the content isn't supposed to be visible, show a large empty box as it's needed // for scene transition animations (can't just skip rendering everything or shared // elements won't have correct final/initial bounds from animating in and out of the // lockscreen scene). Box(modifier) return } val coroutineScope = rememberCoroutineScope() val blueprintId by viewModel.blueprintId(coroutineScope).collectAsStateWithLifecycle() val view = LocalView.current Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt +45 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,8 @@ * limitations under the License. */ @file:OptIn(ExperimentalCoroutinesApi::class) package com.android.systemui.keyguard.ui.viewmodel import android.platform.test.flag.junit.FlagsParameterization Loading @@ -27,10 +29,13 @@ import com.android.systemui.flags.Flags import com.android.systemui.flags.andSceneContainer import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository import com.android.systemui.keyguard.shared.model.ClockSize import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.data.repository.shadeRepository import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.testKosmos Loading @@ -38,6 +43,8 @@ import com.android.systemui.unfold.fakeUnfoldTransitionProgressProvider import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import java.util.Locale import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test Loading Loading @@ -201,6 +208,44 @@ class LockscreenContentViewModelTest(flags: FlagsParameterization) : SysuiTestCa } } @Test fun isContentVisible_whenNotOccluded_visible() = with(kosmos) { testScope.runTest { val isContentVisible by collectLastValue(underTest.isContentVisible) keyguardOcclusionRepository.setShowWhenLockedActivityInfo(false, null) runCurrent() assertThat(isContentVisible).isTrue() } } @Test fun isContentVisible_whenOccluded_notVisible() = with(kosmos) { testScope.runTest { val isContentVisible by collectLastValue(underTest.isContentVisible) keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, null) runCurrent() assertThat(isContentVisible).isFalse() } } @Test fun isContentVisible_whenOccluded_notVisible_evenIfShadeShown() = with(kosmos) { testScope.runTest { val isContentVisible by collectLastValue(underTest.isContentVisible) keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, null) runCurrent() sceneInteractor.snapToScene(Scenes.Shade, "") runCurrent() assertThat(isContentVisible).isFalse() } } private fun prepareConfiguration(): Int { val configuration = context.resources.configuration configuration.setLayoutDirection(Locale.US) Loading
packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt +12 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteract import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor import com.android.systemui.keyguard.shared.model.ClockSize import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor Loading @@ -48,6 +49,7 @@ constructor( val shadeInteractor: ShadeInteractor, @Application private val applicationScope: CoroutineScope, unfoldTransitionInteractor: UnfoldTransitionInteractor, occlusionInteractor: SceneContainerOcclusionInteractor, ) { @VisibleForTesting val clockSize = clockInteractor.clockSize Loading Loading @@ -93,6 +95,16 @@ constructor( initialValue = UnfoldTranslations(), ) /** Whether the content of the scene UI should be shown. */ val isContentVisible: StateFlow<Boolean> = occlusionInteractor.isOccludingActivityShown .map { !it } .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), initialValue = true, ) fun getSmartSpacePaddingTop(resources: Resources): Int { return if (clockSize.value == ClockSize.LARGE) { resources.getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset) + Loading
packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt +11 −2 Original line number Diff line number Diff line Loading @@ -43,8 +43,14 @@ constructor( sceneInteractor: SceneInteractor, keyguardTransitionInteractor: KeyguardTransitionInteractor, ) { /** Whether a show-when-locked activity is at the top of the current activity stack. */ private val isOccludingActivityShown: StateFlow<Boolean> = /** * Whether a show-when-locked activity is at the top of the current activity stack. * * Note: this isn't enough to figure out whether the scene container UI should be invisible as * that also depends on the things like the state of AOD and the current scene. If the code * needs that, [invisibleDueToOcclusion] should be collected instead. */ val isOccludingActivityShown: StateFlow<Boolean> = keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop.stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), Loading @@ -69,6 +75,9 @@ constructor( /** * Whether the scene container should become invisible due to "occlusion" by an in-foreground * "show when locked" activity. * * Note: this returns `false` when an overlaid scene (like shade or QS) is shown above the * occluding activity. */ val invisibleDueToOcclusion: StateFlow<Boolean> = combine( Loading
packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt +2 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import com.android.systemui.keyguard.domain.interactor.keyguardBlueprintInteract import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.unfold.domain.interactor.unfoldTransitionInteractor Loading @@ -34,5 +35,6 @@ val Kosmos.lockscreenContentViewModel by shadeInteractor = shadeInteractor, applicationScope = applicationCoroutineScope, unfoldTransitionInteractor = unfoldTransitionInteractor, occlusionInteractor = sceneContainerOcclusionInteractor, ) }