Merge "Fade out UDFPS on shadeExpand & dialogProgress" into udc-qpr-dev

This commit is contained in:
Treehugger Robot
2023-07-20 20:02:50 +00:00
committed by Android (Google) Code Review
7 changed files with 454 additions and 60 deletions

View File

@@ -21,10 +21,16 @@ import android.animation.FloatEvaluator
import android.animation.IntEvaluator import android.animation.IntEvaluator
import com.android.systemui.common.ui.data.repository.ConfigurationRepository import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.statusbar.phone.hideAffordancesRequest
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
/** Encapsulates business logic for transitions between UDFPS states on the keyguard. */ /** Encapsulates business logic for transitions between UDFPS states on the keyguard. */
@ExperimentalCoroutinesApi @ExperimentalCoroutinesApi
@@ -35,6 +41,8 @@ constructor(
configRepo: ConfigurationRepository, configRepo: ConfigurationRepository,
burnInInteractor: BurnInInteractor, burnInInteractor: BurnInInteractor,
keyguardInteractor: KeyguardInteractor, keyguardInteractor: KeyguardInteractor,
shadeRepository: ShadeRepository,
dialogManager: SystemUIDialogManager,
) { ) {
private val intEvaluator = IntEvaluator() private val intEvaluator = IntEvaluator()
private val floatEvaluator = FloatEvaluator() private val floatEvaluator = FloatEvaluator()
@@ -56,6 +64,26 @@ constructor(
floatEvaluator.evaluate(dozeAmount, 0, fullyDozingBurnInProgress), floatEvaluator.evaluate(dozeAmount, 0, fullyDozingBurnInProgress),
) )
} }
val dialogHideAffordancesRequest: Flow<Boolean> = dialogManager.hideAffordancesRequest
val qsProgress: Flow<Float> =
shadeRepository.qsExpansion // swipe from top of LS
.map { (it * 2).coerceIn(0f, 1f) }
.onStart { emit(0f) }
val shadeExpansion: Flow<Float> =
combine(
shadeRepository.udfpsTransitionToFullShadeProgress, // swipe from middle of LS
keyguardInteractor.statusBarState, // quick swipe from middle of LS
) { shadeProgress, statusBarState ->
if (statusBarState == StatusBarState.SHADE_LOCKED) {
1f
} else {
shadeProgress
}
}
.onStart { emit(0f) }
} }
data class BurnInOffsets( data class BurnInOffsets(

View File

@@ -21,13 +21,17 @@ import androidx.annotation.ColorInt
import com.android.settingslib.Utils.getColorAttrDefaultColor import com.android.settingslib.Utils.getColorAttrDefaultColor
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.keyguard.domain.interactor.BurnInOffsets import com.android.systemui.keyguard.domain.interactor.BurnInOffsets
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.roundToInt import kotlin.math.roundToInt
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.merge
@@ -38,6 +42,8 @@ open class UdfpsLockscreenViewModel(
lockscreenColorResId: Int, lockscreenColorResId: Int,
alternateBouncerColorResId: Int, alternateBouncerColorResId: Int,
transitionInteractor: KeyguardTransitionInteractor, transitionInteractor: KeyguardTransitionInteractor,
udfpsKeyguardInteractor: UdfpsKeyguardInteractor,
keyguardInteractor: KeyguardInteractor,
) { ) {
private val toLockscreen: Flow<TransitionViewModel> = private val toLockscreen: Flow<TransitionViewModel> =
transitionInteractor.anyStateToLockscreenTransition.map { transitionInteractor.anyStateToLockscreenTransition.map {
@@ -54,46 +60,53 @@ open class UdfpsLockscreenViewModel(
} }
private val toAlternateBouncer: Flow<TransitionViewModel> = private val toAlternateBouncer: Flow<TransitionViewModel> =
transitionInteractor.anyStateToAlternateBouncerTransition.map { keyguardInteractor.statusBarState.flatMapLatest { statusBarState ->
TransitionViewModel( transitionInteractor.anyStateToAlternateBouncerTransition.map {
alpha = 1f, TransitionViewModel(
scale = alpha = 1f,
if (visibleInKeyguardState(it.from)) { scale =
1f if (visibleInKeyguardState(it.from, statusBarState)) {
} else { 1f
it.value } else {
}, it.value
color = getColorAttrDefaultColor(context, alternateBouncerColorResId), },
) color = getColorAttrDefaultColor(context, alternateBouncerColorResId),
)
}
} }
private val fadeOut: Flow<TransitionViewModel> = private val fadeOut: Flow<TransitionViewModel> =
merge( keyguardInteractor.statusBarState.flatMapLatest { statusBarState ->
transitionInteractor.anyStateToGoneTransition, merge(
transitionInteractor.anyStateToAodTransition, transitionInteractor.anyStateToGoneTransition,
transitionInteractor.anyStateToOccludedTransition, transitionInteractor.anyStateToAodTransition,
transitionInteractor.anyStateToPrimaryBouncerTransition, transitionInteractor.anyStateToOccludedTransition,
transitionInteractor.anyStateToDreamingTransition, transitionInteractor.anyStateToPrimaryBouncerTransition,
) transitionInteractor.anyStateToDreamingTransition,
.map {
TransitionViewModel(
alpha =
if (visibleInKeyguardState(it.from)) {
1f - it.value
} else {
0f
},
scale = 1f,
color =
if (it.from == KeyguardState.ALTERNATE_BOUNCER) {
getColorAttrDefaultColor(context, alternateBouncerColorResId)
} else {
getColorAttrDefaultColor(context, lockscreenColorResId)
},
) )
} .map {
TransitionViewModel(
alpha =
if (visibleInKeyguardState(it.from, statusBarState)) {
1f - it.value
} else {
0f
},
scale = 1f,
color =
if (it.from == KeyguardState.ALTERNATE_BOUNCER) {
getColorAttrDefaultColor(context, alternateBouncerColorResId)
} else {
getColorAttrDefaultColor(context, lockscreenColorResId)
},
)
}
}
private fun visibleInKeyguardState(state: KeyguardState): Boolean { private fun visibleInKeyguardState(
state: KeyguardState,
statusBarState: StatusBarState
): Boolean {
return when (state) { return when (state) {
KeyguardState.OFF, KeyguardState.OFF,
KeyguardState.DOZING, KeyguardState.DOZING,
@@ -102,17 +115,53 @@ open class UdfpsLockscreenViewModel(
KeyguardState.PRIMARY_BOUNCER, KeyguardState.PRIMARY_BOUNCER,
KeyguardState.GONE, KeyguardState.GONE,
KeyguardState.OCCLUDED -> false KeyguardState.OCCLUDED -> false
KeyguardState.LOCKSCREEN, KeyguardState.LOCKSCREEN -> statusBarState == StatusBarState.KEYGUARD
KeyguardState.ALTERNATE_BOUNCER -> true KeyguardState.ALTERNATE_BOUNCER -> true
} }
} }
val transition: Flow<TransitionViewModel> = private val keyguardStateTransition =
merge( merge(
toAlternateBouncer, toAlternateBouncer,
toLockscreen, toLockscreen,
fadeOut, fadeOut,
) )
private val dialogHideAffordancesAlphaMultiplier: Flow<Float> =
udfpsKeyguardInteractor.dialogHideAffordancesRequest.map { hideAffordances ->
if (hideAffordances) {
0f
} else {
1f
}
}
private val alphaMultiplier: Flow<Float> =
combine(
transitionInteractor.startedKeyguardState,
dialogHideAffordancesAlphaMultiplier,
udfpsKeyguardInteractor.shadeExpansion,
udfpsKeyguardInteractor.qsProgress,
) { startedKeyguardState, dialogHideAffordancesAlphaMultiplier, shadeExpansion, qsProgress
->
if (startedKeyguardState == KeyguardState.ALTERNATE_BOUNCER) {
1f
} else {
dialogHideAffordancesAlphaMultiplier * (1f - shadeExpansion) * (1f - qsProgress)
}
}
val transition: Flow<TransitionViewModel> =
combine(
alphaMultiplier,
keyguardStateTransition,
) { alphaMultiplier, keyguardStateTransition ->
TransitionViewModel(
alpha = keyguardStateTransition.alpha * alphaMultiplier,
scale = keyguardStateTransition.scale,
color = keyguardStateTransition.color,
)
}
val visible: Flow<Boolean> = transition.map { it.alpha != 0f } val visible: Flow<Boolean> = transition.map { it.alpha != 0f }
} }
@@ -123,12 +172,15 @@ constructor(
val context: Context, val context: Context,
transitionInteractor: KeyguardTransitionInteractor, transitionInteractor: KeyguardTransitionInteractor,
interactor: UdfpsKeyguardInteractor, interactor: UdfpsKeyguardInteractor,
keyguardInteractor: KeyguardInteractor,
) : ) :
UdfpsLockscreenViewModel( UdfpsLockscreenViewModel(
context, context,
android.R.attr.textColorPrimary, android.R.attr.textColorPrimary,
com.android.internal.R.attr.materialColorOnPrimaryFixed, com.android.internal.R.attr.materialColorOnPrimaryFixed,
transitionInteractor, transitionInteractor,
interactor,
keyguardInteractor,
) { ) {
val dozeAmount: Flow<Float> = interactor.dozeAmount val dozeAmount: Flow<Float> = interactor.dozeAmount
val burnInOffsets: Flow<BurnInOffsets> = interactor.burnInOffsets val burnInOffsets: Flow<BurnInOffsets> = interactor.burnInOffsets
@@ -147,12 +199,16 @@ class BackgroundViewModel
constructor( constructor(
val context: Context, val context: Context,
transitionInteractor: KeyguardTransitionInteractor, transitionInteractor: KeyguardTransitionInteractor,
interactor: UdfpsKeyguardInteractor,
keyguardInteractor: KeyguardInteractor,
) : ) :
UdfpsLockscreenViewModel( UdfpsLockscreenViewModel(
context, context,
com.android.internal.R.attr.colorSurface, com.android.internal.R.attr.colorSurface,
com.android.internal.R.attr.materialColorPrimaryFixed, com.android.internal.R.attr.materialColorPrimaryFixed,
transitionInteractor, transitionInteractor,
interactor,
keyguardInteractor,
) )
data class TransitionViewModel( data class TransitionViewModel(

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.phone
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
/** Whether dialogs are requesting for affordances to be hidden or not. */
val SystemUIDialogManager.hideAffordancesRequest: Flow<Boolean>
get() = conflatedCallbackFlow {
val callback =
SystemUIDialogManager.Listener { hideAffordance ->
trySendWithFailureLogging(hideAffordance, "dialogHideAffordancesRequest")
}
registerListener(callback)
trySendWithFailureLogging(shouldHideAffordance(), "dialogHideAffordancesRequestInitial")
awaitClose { unregisterListener(callback) }
}

View File

@@ -33,6 +33,9 @@ import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakeSleepReason import com.android.systemui.keyguard.shared.model.WakeSleepReason
import com.android.systemui.keyguard.shared.model.WakefulnessModel import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.util.time.FakeSystemClock
@@ -46,6 +49,7 @@ import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
@ExperimentalCoroutinesApi @ExperimentalCoroutinesApi
@@ -63,19 +67,21 @@ class UdfpsKeyguardInteractorTest : SysuiTestCase() {
private lateinit var fakeCommandQueue: FakeCommandQueue private lateinit var fakeCommandQueue: FakeCommandQueue
private lateinit var featureFlags: FakeFeatureFlags private lateinit var featureFlags: FakeFeatureFlags
private lateinit var burnInInteractor: BurnInInteractor private lateinit var burnInInteractor: BurnInInteractor
private lateinit var shadeRepository: FakeShadeRepository
@Mock private lateinit var burnInHelper: BurnInHelperWrapper @Mock private lateinit var burnInHelper: BurnInHelperWrapper
@Mock private lateinit var dialogManager: SystemUIDialogManager
private lateinit var underTest: UdfpsKeyguardInteractor private lateinit var underTest: UdfpsKeyguardInteractor
@Before @Before
fun setUp() { fun setUp() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
testScope = TestScope() testScope = TestScope()
configRepository = FakeConfigurationRepository() configRepository = FakeConfigurationRepository()
keyguardRepository = FakeKeyguardRepository() keyguardRepository = FakeKeyguardRepository()
bouncerRepository = FakeKeyguardBouncerRepository() bouncerRepository = FakeKeyguardBouncerRepository()
shadeRepository = FakeShadeRepository()
fakeCommandQueue = FakeCommandQueue() fakeCommandQueue = FakeCommandQueue()
featureFlags = featureFlags =
FakeFeatureFlags().apply { FakeFeatureFlags().apply {
@@ -102,6 +108,8 @@ class UdfpsKeyguardInteractorTest : SysuiTestCase() {
bouncerRepository, bouncerRepository,
configRepository, configRepository,
), ),
shadeRepository,
dialogManager,
) )
} }
@@ -142,6 +150,61 @@ class UdfpsKeyguardInteractorTest : SysuiTestCase() {
assertThat(burnInOffsets?.burnInXOffset).isEqualTo(burnInXOffset) assertThat(burnInOffsets?.burnInXOffset).isEqualTo(burnInXOffset)
} }
@Test
fun dialogHideAffordances() =
testScope.runTest {
val dialogHideAffordancesRequest by
collectLastValue(underTest.dialogHideAffordancesRequest)
runCurrent()
val captor = argumentCaptor<SystemUIDialogManager.Listener>()
verify(dialogManager).registerListener(captor.capture())
captor.value.shouldHideAffordances(false)
assertThat(dialogHideAffordancesRequest).isEqualTo(false)
captor.value.shouldHideAffordances(true)
assertThat(dialogHideAffordancesRequest).isEqualTo(true)
captor.value.shouldHideAffordances(false)
assertThat(dialogHideAffordancesRequest).isEqualTo(false)
}
@Test
fun shadeExpansion_updates() =
testScope.runTest {
keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
val shadeExpansion by collectLastValue(underTest.shadeExpansion)
assertThat(shadeExpansion).isEqualTo(0f)
shadeRepository.setUdfpsTransitionToFullShadeProgress(.5f)
assertThat(shadeExpansion).isEqualTo(.5f)
shadeRepository.setUdfpsTransitionToFullShadeProgress(.7f)
assertThat(shadeExpansion).isEqualTo(.7f)
shadeRepository.setUdfpsTransitionToFullShadeProgress(.22f)
assertThat(shadeExpansion).isEqualTo(.22f)
keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
assertThat(shadeExpansion).isEqualTo(1f)
}
@Test
fun qsProgress_updates() =
testScope.runTest {
val qsProgress by collectLastValue(underTest.qsProgress)
assertThat(qsProgress).isEqualTo(0f)
shadeRepository.setQsExpansion(.22f)
assertThat(qsProgress).isEqualTo(.44f)
shadeRepository.setQsExpansion(.5f)
assertThat(qsProgress).isEqualTo(1f)
shadeRepository.setQsExpansion(.7f)
assertThat(qsProgress).isEqualTo(1f)
}
private fun initializeBurnInOffsets() { private fun initializeBurnInOffsets() {
whenever(burnInHelper.burnInProgressOffset()).thenReturn(burnInProgress) whenever(burnInHelper.burnInProgressOffset()).thenReturn(burnInProgress)
whenever(burnInHelper.burnInOffset(anyInt(), /* xAxis */ eq(true))) whenever(burnInHelper.burnInOffset(anyInt(), /* xAxis */ eq(true)))

View File

@@ -32,6 +32,8 @@ import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.BurnInInteractor import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -58,7 +60,9 @@ class UdfpsAodViewModelTest : SysuiTestCase() {
private lateinit var keyguardRepository: FakeKeyguardRepository private lateinit var keyguardRepository: FakeKeyguardRepository
private lateinit var fakeCommandQueue: FakeCommandQueue private lateinit var fakeCommandQueue: FakeCommandQueue
private lateinit var featureFlags: FakeFeatureFlags private lateinit var featureFlags: FakeFeatureFlags
private lateinit var shadeRepository: FakeShadeRepository
@Mock private lateinit var dialogManager: SystemUIDialogManager
@Mock private lateinit var burnInHelper: BurnInHelperWrapper @Mock private lateinit var burnInHelper: BurnInHelperWrapper
@Before @Before
@@ -70,6 +74,7 @@ class UdfpsAodViewModelTest : SysuiTestCase() {
keyguardRepository = FakeKeyguardRepository() keyguardRepository = FakeKeyguardRepository()
bouncerRepository = FakeKeyguardBouncerRepository() bouncerRepository = FakeKeyguardBouncerRepository()
fakeCommandQueue = FakeCommandQueue() fakeCommandQueue = FakeCommandQueue()
shadeRepository = FakeShadeRepository()
featureFlags = featureFlags =
FakeFeatureFlags().apply { FakeFeatureFlags().apply {
set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true) set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
@@ -93,6 +98,8 @@ class UdfpsAodViewModelTest : SysuiTestCase() {
bouncerRepository, bouncerRepository,
configRepository, configRepository,
), ),
shadeRepository,
dialogManager,
) )
underTest = underTest =

View File

@@ -33,6 +33,8 @@ import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -60,8 +62,10 @@ class UdfpsFingerprintViewModelTest : SysuiTestCase() {
private lateinit var fakeCommandQueue: FakeCommandQueue private lateinit var fakeCommandQueue: FakeCommandQueue
private lateinit var featureFlags: FakeFeatureFlags private lateinit var featureFlags: FakeFeatureFlags
private lateinit var transitionRepository: FakeKeyguardTransitionRepository private lateinit var transitionRepository: FakeKeyguardTransitionRepository
private lateinit var shadeRepository: FakeShadeRepository
@Mock private lateinit var burnInHelper: BurnInHelperWrapper @Mock private lateinit var burnInHelper: BurnInHelperWrapper
@Mock private lateinit var dialogManager: SystemUIDialogManager
@Before @Before
fun setUp() { fun setUp() {
@@ -79,35 +83,39 @@ class UdfpsFingerprintViewModelTest : SysuiTestCase() {
} }
bouncerRepository = FakeKeyguardBouncerRepository() bouncerRepository = FakeKeyguardBouncerRepository()
transitionRepository = FakeKeyguardTransitionRepository() transitionRepository = FakeKeyguardTransitionRepository()
shadeRepository = FakeShadeRepository()
val transitionInteractor = val transitionInteractor =
KeyguardTransitionInteractor( KeyguardTransitionInteractor(
transitionRepository, transitionRepository,
testScope.backgroundScope, testScope.backgroundScope,
) )
val udfpsKeyguardInteractor = val keyguardInteractor =
UdfpsKeyguardInteractor( KeyguardInteractor(
keyguardRepository,
fakeCommandQueue,
featureFlags,
bouncerRepository,
configRepository, configRepository,
BurnInInteractor(
context,
burnInHelper,
testScope.backgroundScope,
configRepository,
FakeSystemClock(),
),
KeyguardInteractor(
keyguardRepository,
fakeCommandQueue,
featureFlags,
bouncerRepository,
configRepository,
),
) )
underTest = underTest =
FingerprintViewModel( FingerprintViewModel(
context, context,
transitionInteractor, transitionInteractor,
udfpsKeyguardInteractor, UdfpsKeyguardInteractor(
configRepository,
BurnInInteractor(
context,
burnInHelper,
testScope.backgroundScope,
configRepository,
FakeSystemClock(),
),
keyguardInteractor,
shadeRepository,
dialogManager,
),
keyguardInteractor,
) )
} }

View File

@@ -20,12 +20,26 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.settingslib.Utils import com.android.settingslib.Utils
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.collect.Range import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -35,6 +49,8 @@ import kotlinx.coroutines.test.runTest
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
/** Tests UDFPS lockscreen view model transitions. */ /** Tests UDFPS lockscreen view model transitions. */
@@ -48,26 +64,63 @@ class UdfpsLockscreenViewModelTest : SysuiTestCase() {
private val alternateBouncerColor = private val alternateBouncerColor =
Utils.getColorAttrDefaultColor(context, alternateBouncerResId) Utils.getColorAttrDefaultColor(context, alternateBouncerResId)
@Mock private lateinit var dialogManager: SystemUIDialogManager
private lateinit var underTest: UdfpsLockscreenViewModel private lateinit var underTest: UdfpsLockscreenViewModel
private lateinit var testScope: TestScope private lateinit var testScope: TestScope
private lateinit var transitionRepository: FakeKeyguardTransitionRepository private lateinit var transitionRepository: FakeKeyguardTransitionRepository
private lateinit var configRepository: FakeConfigurationRepository
private lateinit var keyguardRepository: FakeKeyguardRepository
private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
private lateinit var shadeRepository: FakeShadeRepository
private lateinit var featureFlags: FakeFeatureFlags
@Before @Before
fun setUp() { fun setUp() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
testScope = TestScope() testScope = TestScope()
transitionRepository = FakeKeyguardTransitionRepository() transitionRepository = FakeKeyguardTransitionRepository()
val transitionInteractor = configRepository = FakeConfigurationRepository()
KeyguardTransitionInteractor( keyguardRepository = FakeKeyguardRepository()
transitionRepository, bouncerRepository = FakeKeyguardBouncerRepository()
testScope.backgroundScope, shadeRepository = FakeShadeRepository()
featureFlags =
FakeFeatureFlags().apply {
set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
set(Flags.FACE_AUTH_REFACTOR, false)
}
val keyguardInteractor =
KeyguardInteractor(
keyguardRepository,
commandQueue = mock(),
featureFlags,
bouncerRepository,
configRepository,
) )
underTest = underTest =
UdfpsLockscreenViewModel( UdfpsLockscreenViewModel(
context, context,
lockscreenColorResId, lockscreenColorResId,
alternateBouncerResId, alternateBouncerResId,
transitionInteractor, KeyguardTransitionInteractor(
transitionRepository,
testScope.backgroundScope,
),
UdfpsKeyguardInteractor(
configRepository,
BurnInInteractor(
context,
burnInHelperWrapper = mock(),
testScope.backgroundScope,
configRepository,
FakeSystemClock(),
),
keyguardInteractor,
shadeRepository,
dialogManager,
),
keyguardInteractor,
) )
} }
@@ -125,6 +178,7 @@ class UdfpsLockscreenViewModelTest : SysuiTestCase() {
testScope.runTest { testScope.runTest {
val transition by collectLastValue(underTest.transition) val transition by collectLastValue(underTest.transition)
val visible by collectLastValue(underTest.visible) val visible by collectLastValue(underTest.visible)
keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
// TransitionState.STARTED: lockscreen -> AOD // TransitionState.STARTED: lockscreen -> AOD
transitionRepository.sendTransitionStep( transitionRepository.sendTransitionStep(
@@ -175,6 +229,56 @@ class UdfpsLockscreenViewModelTest : SysuiTestCase() {
assertThat(visible).isFalse() assertThat(visible).isFalse()
} }
@Test
fun lockscreenShadeLockedToAod() =
testScope.runTest {
val transition by collectLastValue(underTest.transition)
val visible by collectLastValue(underTest.visible)
keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
// TransitionState.STARTED: lockscreen -> AOD
transitionRepository.sendTransitionStep(
TransitionStep(
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.AOD,
value = 0f,
transitionState = TransitionState.STARTED,
ownerName = "lockscreenToAod",
)
)
runCurrent()
assertThat(transition?.alpha).isEqualTo(0f)
assertThat(visible).isFalse()
// TransitionState.RUNNING: lockscreen -> AOD
transitionRepository.sendTransitionStep(
TransitionStep(
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.AOD,
value = .6f,
transitionState = TransitionState.RUNNING,
ownerName = "lockscreenToAod",
)
)
runCurrent()
assertThat(transition?.alpha).isEqualTo(0f)
assertThat(visible).isFalse()
// TransitionState.FINISHED: lockscreen -> AOD
transitionRepository.sendTransitionStep(
TransitionStep(
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.AOD,
value = 1f,
transitionState = TransitionState.FINISHED,
ownerName = "lockscreenToAod",
)
)
runCurrent()
assertThat(transition?.alpha).isEqualTo(0f)
assertThat(visible).isFalse()
}
@Test @Test
fun aodToLockscreen() = fun aodToLockscreen() =
testScope.runTest { testScope.runTest {
@@ -235,6 +339,7 @@ class UdfpsLockscreenViewModelTest : SysuiTestCase() {
testScope.runTest { testScope.runTest {
val transition by collectLastValue(underTest.transition) val transition by collectLastValue(underTest.transition)
val visible by collectLastValue(underTest.visible) val visible by collectLastValue(underTest.visible)
keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
// TransitionState.STARTED: lockscreen -> alternate bouncer // TransitionState.STARTED: lockscreen -> alternate bouncer
transitionRepository.sendTransitionStep( transitionRepository.sendTransitionStep(
@@ -398,6 +503,7 @@ class UdfpsLockscreenViewModelTest : SysuiTestCase() {
testScope.runTest { testScope.runTest {
val transition by collectLastValue(underTest.transition) val transition by collectLastValue(underTest.transition)
val visible by collectLastValue(underTest.visible) val visible by collectLastValue(underTest.visible)
keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
// TransitionState.STARTED: lockscreen -> occluded // TransitionState.STARTED: lockscreen -> occluded
transitionRepository.sendTransitionStep( transitionRepository.sendTransitionStep(
@@ -502,4 +608,96 @@ class UdfpsLockscreenViewModelTest : SysuiTestCase() {
assertThat(transition?.color).isEqualTo(lockscreenColor) assertThat(transition?.color).isEqualTo(lockscreenColor)
assertThat(visible).isTrue() assertThat(visible).isTrue()
} }
@Test
fun qsProgressChange() =
testScope.runTest {
val transition by collectLastValue(underTest.transition)
val visible by collectLastValue(underTest.visible)
givenTransitionToLockscreenFinished()
// qsExpansion = 0f
shadeRepository.setQsExpansion(0f)
runCurrent()
assertThat(transition?.alpha).isEqualTo(1f)
assertThat(visible).isEqualTo(true)
// qsExpansion = .25
shadeRepository.setQsExpansion(.2f)
runCurrent()
assertThat(transition?.alpha).isEqualTo(.6f)
assertThat(visible).isEqualTo(true)
// qsExpansion = .5
shadeRepository.setQsExpansion(.5f)
runCurrent()
assertThat(transition?.alpha).isEqualTo(0f)
assertThat(visible).isEqualTo(false)
// qsExpansion = 1
shadeRepository.setQsExpansion(1f)
runCurrent()
assertThat(transition?.alpha).isEqualTo(0f)
assertThat(visible).isEqualTo(false)
}
@Test
fun shadeExpansionChanged() =
testScope.runTest {
val transition by collectLastValue(underTest.transition)
val visible by collectLastValue(underTest.visible)
givenTransitionToLockscreenFinished()
// shadeExpansion = 0f
shadeRepository.setUdfpsTransitionToFullShadeProgress(0f)
runCurrent()
assertThat(transition?.alpha).isEqualTo(1f)
assertThat(visible).isEqualTo(true)
// shadeExpansion = .2
shadeRepository.setUdfpsTransitionToFullShadeProgress(.2f)
runCurrent()
assertThat(transition?.alpha).isEqualTo(.8f)
assertThat(visible).isEqualTo(true)
// shadeExpansion = .5
shadeRepository.setUdfpsTransitionToFullShadeProgress(.5f)
runCurrent()
assertThat(transition?.alpha).isEqualTo(.5f)
assertThat(visible).isEqualTo(true)
// shadeExpansion = 1
shadeRepository.setUdfpsTransitionToFullShadeProgress(1f)
runCurrent()
assertThat(transition?.alpha).isEqualTo(0f)
assertThat(visible).isEqualTo(false)
}
@Test
fun dialogHideAffordancesRequestChanged() =
testScope.runTest {
val transition by collectLastValue(underTest.transition)
givenTransitionToLockscreenFinished()
runCurrent()
val captor = argumentCaptor<SystemUIDialogManager.Listener>()
Mockito.verify(dialogManager).registerListener(captor.capture())
captor.value.shouldHideAffordances(true)
assertThat(transition?.alpha).isEqualTo(0f)
captor.value.shouldHideAffordances(false)
assertThat(transition?.alpha).isEqualTo(1f)
}
private suspend fun givenTransitionToLockscreenFinished() {
transitionRepository.sendTransitionStep(
TransitionStep(
from = KeyguardState.AOD,
to = KeyguardState.LOCKSCREEN,
value = 1f,
transitionState = TransitionState.FINISHED,
ownerName = "givenTransitionToLockscreenFinished",
)
)
}
} }