Loading packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt +27 −6 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background Loading @@ -32,14 +33,21 @@ import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch @ExperimentalCoroutinesApi @SysUISingleton class FromAlternateBouncerTransitionInteractor @Inject Loading @@ -53,6 +61,7 @@ constructor( private val communalInteractor: CommunalInteractor, powerInteractor: PowerInteractor, keyguardOcclusionInteractor: KeyguardOcclusionInteractor, private val primaryBouncerInteractor: PrimaryBouncerInteractor, ) : TransitionInteractor( fromState = KeyguardState.ALTERNATE_BOUNCER, Loading Loading @@ -102,13 +111,14 @@ constructor( keyguardInteractor.primaryBouncerShowing, powerInteractor.isAwake, keyguardInteractor.isAodAvailable, communalInteractor.isIdleOnCommunal communalInteractor.isIdleOnCommunal, keyguardInteractor.isKeyguardOccluded, ) .filterRelevantKeyguardStateAnd { (isAlternateBouncerShowing, isPrimaryBouncerShowing, _, _, _) -> !isAlternateBouncerShowing && !isPrimaryBouncerShowing } .collect { (_, _, isAwake, isAodAvailable, isIdleOnCommunal) -> .collect { (_, _, isAwake, isAodAvailable, isIdleOnCommunal, isOccluded) -> val to = if (!isAwake) { if (isAodAvailable) { Loading @@ -119,6 +129,8 @@ constructor( } else { if (isIdleOnCommunal) { KeyguardState.GLANCEABLE_HUB } else if (isOccluded) { KeyguardState.OCCLUDED } else { KeyguardState.LOCKSCREEN } Loading @@ -135,10 +147,19 @@ constructor( } scope.launch { keyguardInteractor.isKeyguardGoingAway .sampleUtil(finishedKeyguardState, ::Pair) .collect { (isKeyguardGoingAway, keyguardState) -> if (isKeyguardGoingAway && keyguardState == KeyguardState.ALTERNATE_BOUNCER) { merge( keyguardInteractor.isKeyguardGoingAway.filter { it }.map {}, // map to Unit keyguardInteractor.isKeyguardOccluded.flatMapLatest { keyguardOccluded -> if (keyguardOccluded) { primaryBouncerInteractor.keyguardAuthenticatedBiometricsHandled } else { emptyFlow() } } ) .sampleUtil(finishedKeyguardState) .collect { keyguardState -> if (keyguardState == KeyguardState.ALTERNATE_BOUNCER) { startTransitionTo(KeyguardState.GONE) } } Loading packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt 0 → 100644 +144 −0 Original line number Diff line number Diff line /* * 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. */ /* * 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.keyguard.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat import com.android.systemui.kosmos.testScope import com.android.systemui.power.data.repository.fakePowerRepository import com.android.systemui.power.shared.model.WakeSleepReason import com.android.systemui.power.shared.model.WakefulnessState import com.android.systemui.testKosmos 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.runner.RunWith import org.mockito.Mockito import org.mockito.Mockito.reset @ExperimentalCoroutinesApi @SmallTest @RunWith(AndroidJUnit4::class) class FromAlternateBouncerTransitionInteractorTest : SysuiTestCase() { private val kosmos = testKosmos().apply { this.fakeKeyguardTransitionRepository = Mockito.spy(FakeKeyguardTransitionRepository()) } private val testScope = kosmos.testScope private lateinit var underTest: FromAlternateBouncerTransitionInteractor private lateinit var transitionRepository: FakeKeyguardTransitionRepository @Before fun setup() { transitionRepository = kosmos.fakeKeyguardTransitionRepository underTest = kosmos.fromAlternateBouncerTransitionInteractor underTest.start() } @Test fun transitionToGone_keyguardOccluded_biometricAuthenticated() = testScope.runTest { transitionRepository.sendTransitionSteps( from = KeyguardState.OCCLUDED, to = KeyguardState.ALTERNATE_BOUNCER, testScope ) reset(transitionRepository) kosmos.fakeKeyguardRepository.setKeyguardOccluded(true) kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(true) runCurrent() kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(null) runCurrent() assertThat(transitionRepository) .startedTransition(from = KeyguardState.ALTERNATE_BOUNCER, to = KeyguardState.GONE) } @Test fun noTransition_keyguardNotOccluded_biometricAuthenticated() = testScope.runTest { transitionRepository.sendTransitionSteps( from = KeyguardState.OCCLUDED, to = KeyguardState.ALTERNATE_BOUNCER, testScope ) reset(transitionRepository) kosmos.fakeKeyguardRepository.setKeyguardOccluded(false) kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(true) runCurrent() kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(null) runCurrent() assertThat(transitionRepository).noTransitionsStarted() } @Test fun transitionToOccluded() = testScope.runTest { kosmos.fakePowerRepository.updateWakefulness( WakefulnessState.AWAKE, WakeSleepReason.POWER_BUTTON, WakeSleepReason.POWER_BUTTON, false, ) kosmos.fakeKeyguardRepository.setKeyguardOccluded(true) kosmos.fakeKeyguardBouncerRepository.setAlternateVisible(true) transitionRepository.sendTransitionSteps( from = KeyguardState.OCCLUDED, to = KeyguardState.ALTERNATE_BOUNCER, testScope ) reset(transitionRepository) kosmos.fakeKeyguardBouncerRepository.setAlternateVisible(false) runCurrent() testScope.testScheduler.advanceTimeBy(200) // advance past delay assertThat(transitionRepository) .startedTransition( from = KeyguardState.ALTERNATE_BOUNCER, to = KeyguardState.OCCLUDED ) } } packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorKosmos.kt +4 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.keyguard.domain.interactor import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor import com.android.systemui.communal.domain.interactor.communalInteractor import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository import com.android.systemui.kosmos.Kosmos Loading @@ -23,7 +24,9 @@ import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor import kotlinx.coroutines.ExperimentalCoroutinesApi @ExperimentalCoroutinesApi val Kosmos.fromAlternateBouncerTransitionInteractor by Kosmos.Fixture { FromAlternateBouncerTransitionInteractor( Loading @@ -36,5 +39,6 @@ val Kosmos.fromAlternateBouncerTransitionInteractor by communalInteractor = communalInteractor, powerInteractor = powerInteractor, keyguardOcclusionInteractor = keyguardOcclusionInteractor, primaryBouncerInteractor = primaryBouncerInteractor, ) } Loading
packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt +27 −6 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background Loading @@ -32,14 +33,21 @@ import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch @ExperimentalCoroutinesApi @SysUISingleton class FromAlternateBouncerTransitionInteractor @Inject Loading @@ -53,6 +61,7 @@ constructor( private val communalInteractor: CommunalInteractor, powerInteractor: PowerInteractor, keyguardOcclusionInteractor: KeyguardOcclusionInteractor, private val primaryBouncerInteractor: PrimaryBouncerInteractor, ) : TransitionInteractor( fromState = KeyguardState.ALTERNATE_BOUNCER, Loading Loading @@ -102,13 +111,14 @@ constructor( keyguardInteractor.primaryBouncerShowing, powerInteractor.isAwake, keyguardInteractor.isAodAvailable, communalInteractor.isIdleOnCommunal communalInteractor.isIdleOnCommunal, keyguardInteractor.isKeyguardOccluded, ) .filterRelevantKeyguardStateAnd { (isAlternateBouncerShowing, isPrimaryBouncerShowing, _, _, _) -> !isAlternateBouncerShowing && !isPrimaryBouncerShowing } .collect { (_, _, isAwake, isAodAvailable, isIdleOnCommunal) -> .collect { (_, _, isAwake, isAodAvailable, isIdleOnCommunal, isOccluded) -> val to = if (!isAwake) { if (isAodAvailable) { Loading @@ -119,6 +129,8 @@ constructor( } else { if (isIdleOnCommunal) { KeyguardState.GLANCEABLE_HUB } else if (isOccluded) { KeyguardState.OCCLUDED } else { KeyguardState.LOCKSCREEN } Loading @@ -135,10 +147,19 @@ constructor( } scope.launch { keyguardInteractor.isKeyguardGoingAway .sampleUtil(finishedKeyguardState, ::Pair) .collect { (isKeyguardGoingAway, keyguardState) -> if (isKeyguardGoingAway && keyguardState == KeyguardState.ALTERNATE_BOUNCER) { merge( keyguardInteractor.isKeyguardGoingAway.filter { it }.map {}, // map to Unit keyguardInteractor.isKeyguardOccluded.flatMapLatest { keyguardOccluded -> if (keyguardOccluded) { primaryBouncerInteractor.keyguardAuthenticatedBiometricsHandled } else { emptyFlow() } } ) .sampleUtil(finishedKeyguardState) .collect { keyguardState -> if (keyguardState == KeyguardState.ALTERNATE_BOUNCER) { startTransitionTo(KeyguardState.GONE) } } Loading
packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt 0 → 100644 +144 −0 Original line number Diff line number Diff line /* * 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. */ /* * 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.keyguard.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat import com.android.systemui.kosmos.testScope import com.android.systemui.power.data.repository.fakePowerRepository import com.android.systemui.power.shared.model.WakeSleepReason import com.android.systemui.power.shared.model.WakefulnessState import com.android.systemui.testKosmos 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.runner.RunWith import org.mockito.Mockito import org.mockito.Mockito.reset @ExperimentalCoroutinesApi @SmallTest @RunWith(AndroidJUnit4::class) class FromAlternateBouncerTransitionInteractorTest : SysuiTestCase() { private val kosmos = testKosmos().apply { this.fakeKeyguardTransitionRepository = Mockito.spy(FakeKeyguardTransitionRepository()) } private val testScope = kosmos.testScope private lateinit var underTest: FromAlternateBouncerTransitionInteractor private lateinit var transitionRepository: FakeKeyguardTransitionRepository @Before fun setup() { transitionRepository = kosmos.fakeKeyguardTransitionRepository underTest = kosmos.fromAlternateBouncerTransitionInteractor underTest.start() } @Test fun transitionToGone_keyguardOccluded_biometricAuthenticated() = testScope.runTest { transitionRepository.sendTransitionSteps( from = KeyguardState.OCCLUDED, to = KeyguardState.ALTERNATE_BOUNCER, testScope ) reset(transitionRepository) kosmos.fakeKeyguardRepository.setKeyguardOccluded(true) kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(true) runCurrent() kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(null) runCurrent() assertThat(transitionRepository) .startedTransition(from = KeyguardState.ALTERNATE_BOUNCER, to = KeyguardState.GONE) } @Test fun noTransition_keyguardNotOccluded_biometricAuthenticated() = testScope.runTest { transitionRepository.sendTransitionSteps( from = KeyguardState.OCCLUDED, to = KeyguardState.ALTERNATE_BOUNCER, testScope ) reset(transitionRepository) kosmos.fakeKeyguardRepository.setKeyguardOccluded(false) kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(true) runCurrent() kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(null) runCurrent() assertThat(transitionRepository).noTransitionsStarted() } @Test fun transitionToOccluded() = testScope.runTest { kosmos.fakePowerRepository.updateWakefulness( WakefulnessState.AWAKE, WakeSleepReason.POWER_BUTTON, WakeSleepReason.POWER_BUTTON, false, ) kosmos.fakeKeyguardRepository.setKeyguardOccluded(true) kosmos.fakeKeyguardBouncerRepository.setAlternateVisible(true) transitionRepository.sendTransitionSteps( from = KeyguardState.OCCLUDED, to = KeyguardState.ALTERNATE_BOUNCER, testScope ) reset(transitionRepository) kosmos.fakeKeyguardBouncerRepository.setAlternateVisible(false) runCurrent() testScope.testScheduler.advanceTimeBy(200) // advance past delay assertThat(transitionRepository) .startedTransition( from = KeyguardState.ALTERNATE_BOUNCER, to = KeyguardState.OCCLUDED ) } }
packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorKosmos.kt +4 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.keyguard.domain.interactor import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor import com.android.systemui.communal.domain.interactor.communalInteractor import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository import com.android.systemui.kosmos.Kosmos Loading @@ -23,7 +24,9 @@ import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor import kotlinx.coroutines.ExperimentalCoroutinesApi @ExperimentalCoroutinesApi val Kosmos.fromAlternateBouncerTransitionInteractor by Kosmos.Fixture { FromAlternateBouncerTransitionInteractor( Loading @@ -36,5 +39,6 @@ val Kosmos.fromAlternateBouncerTransitionInteractor by communalInteractor = communalInteractor, powerInteractor = powerInteractor, keyguardOcclusionInteractor = keyguardOcclusionInteractor, primaryBouncerInteractor = primaryBouncerInteractor, ) }