Fade out UDFPS on shadeExpand & dialogProgress
When the UDFPS view refactor is enabled, fade out UDFPS views when the shade expands to full shade and to QS. Also, don't show the UDFPS view when a dialog shows. Test: atest UdfpsKeyguardInteractorTest UdfpsLockscreenViewModelTest Test: manually enable udfps view refactor and pull down notification shade (from the middle of LS and from the top). Launch dialogs such as the power menu. See UDFPS lockscreen view hides + shows as expected Bug: 278719514 Change-Id: I5cbcb217b7bd5df7a7f19cb118b843c3de0db744
This commit is contained in:
@@ -21,10 +21,16 @@ import android.animation.FloatEvaluator
|
||||
import android.animation.IntEvaluator
|
||||
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
|
||||
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 kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
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. */
|
||||
@ExperimentalCoroutinesApi
|
||||
@@ -35,6 +41,8 @@ constructor(
|
||||
configRepo: ConfigurationRepository,
|
||||
burnInInteractor: BurnInInteractor,
|
||||
keyguardInteractor: KeyguardInteractor,
|
||||
shadeRepository: ShadeRepository,
|
||||
dialogManager: SystemUIDialogManager,
|
||||
) {
|
||||
private val intEvaluator = IntEvaluator()
|
||||
private val floatEvaluator = FloatEvaluator()
|
||||
@@ -56,6 +64,26 @@ constructor(
|
||||
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(
|
||||
|
||||
@@ -21,13 +21,17 @@ import androidx.annotation.ColorInt
|
||||
import com.android.settingslib.Utils.getColorAttrDefaultColor
|
||||
import com.android.systemui.R
|
||||
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.UdfpsKeyguardInteractor
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||
import com.android.systemui.keyguard.shared.model.StatusBarState
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.roundToInt
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.merge
|
||||
|
||||
@@ -38,6 +42,8 @@ open class UdfpsLockscreenViewModel(
|
||||
lockscreenColorResId: Int,
|
||||
alternateBouncerColorResId: Int,
|
||||
transitionInteractor: KeyguardTransitionInteractor,
|
||||
udfpsKeyguardInteractor: UdfpsKeyguardInteractor,
|
||||
keyguardInteractor: KeyguardInteractor,
|
||||
) {
|
||||
private val toLockscreen: Flow<TransitionViewModel> =
|
||||
transitionInteractor.anyStateToLockscreenTransition.map {
|
||||
@@ -54,46 +60,53 @@ open class UdfpsLockscreenViewModel(
|
||||
}
|
||||
|
||||
private val toAlternateBouncer: Flow<TransitionViewModel> =
|
||||
transitionInteractor.anyStateToAlternateBouncerTransition.map {
|
||||
TransitionViewModel(
|
||||
alpha = 1f,
|
||||
scale =
|
||||
if (visibleInKeyguardState(it.from)) {
|
||||
1f
|
||||
} else {
|
||||
it.value
|
||||
},
|
||||
color = getColorAttrDefaultColor(context, alternateBouncerColorResId),
|
||||
)
|
||||
keyguardInteractor.statusBarState.flatMapLatest { statusBarState ->
|
||||
transitionInteractor.anyStateToAlternateBouncerTransition.map {
|
||||
TransitionViewModel(
|
||||
alpha = 1f,
|
||||
scale =
|
||||
if (visibleInKeyguardState(it.from, statusBarState)) {
|
||||
1f
|
||||
} else {
|
||||
it.value
|
||||
},
|
||||
color = getColorAttrDefaultColor(context, alternateBouncerColorResId),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val fadeOut: Flow<TransitionViewModel> =
|
||||
merge(
|
||||
transitionInteractor.anyStateToGoneTransition,
|
||||
transitionInteractor.anyStateToAodTransition,
|
||||
transitionInteractor.anyStateToOccludedTransition,
|
||||
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)
|
||||
},
|
||||
keyguardInteractor.statusBarState.flatMapLatest { statusBarState ->
|
||||
merge(
|
||||
transitionInteractor.anyStateToGoneTransition,
|
||||
transitionInteractor.anyStateToAodTransition,
|
||||
transitionInteractor.anyStateToOccludedTransition,
|
||||
transitionInteractor.anyStateToPrimaryBouncerTransition,
|
||||
transitionInteractor.anyStateToDreamingTransition,
|
||||
)
|
||||
}
|
||||
.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) {
|
||||
KeyguardState.OFF,
|
||||
KeyguardState.DOZING,
|
||||
@@ -102,17 +115,53 @@ open class UdfpsLockscreenViewModel(
|
||||
KeyguardState.PRIMARY_BOUNCER,
|
||||
KeyguardState.GONE,
|
||||
KeyguardState.OCCLUDED -> false
|
||||
KeyguardState.LOCKSCREEN,
|
||||
KeyguardState.LOCKSCREEN -> statusBarState == StatusBarState.KEYGUARD
|
||||
KeyguardState.ALTERNATE_BOUNCER -> true
|
||||
}
|
||||
}
|
||||
|
||||
val transition: Flow<TransitionViewModel> =
|
||||
private val keyguardStateTransition =
|
||||
merge(
|
||||
toAlternateBouncer,
|
||||
toLockscreen,
|
||||
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 }
|
||||
}
|
||||
|
||||
@@ -123,12 +172,15 @@ constructor(
|
||||
val context: Context,
|
||||
transitionInteractor: KeyguardTransitionInteractor,
|
||||
interactor: UdfpsKeyguardInteractor,
|
||||
keyguardInteractor: KeyguardInteractor,
|
||||
) :
|
||||
UdfpsLockscreenViewModel(
|
||||
context,
|
||||
android.R.attr.textColorPrimary,
|
||||
com.android.internal.R.attr.materialColorOnPrimaryFixed,
|
||||
transitionInteractor,
|
||||
interactor,
|
||||
keyguardInteractor,
|
||||
) {
|
||||
val dozeAmount: Flow<Float> = interactor.dozeAmount
|
||||
val burnInOffsets: Flow<BurnInOffsets> = interactor.burnInOffsets
|
||||
@@ -147,12 +199,16 @@ class BackgroundViewModel
|
||||
constructor(
|
||||
val context: Context,
|
||||
transitionInteractor: KeyguardTransitionInteractor,
|
||||
interactor: UdfpsKeyguardInteractor,
|
||||
keyguardInteractor: KeyguardInteractor,
|
||||
) :
|
||||
UdfpsLockscreenViewModel(
|
||||
context,
|
||||
com.android.internal.R.attr.colorSurface,
|
||||
com.android.internal.R.attr.materialColorPrimaryFixed,
|
||||
transitionInteractor,
|
||||
interactor,
|
||||
keyguardInteractor,
|
||||
)
|
||||
|
||||
data class TransitionViewModel(
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
@@ -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.WakefulnessModel
|
||||
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.whenever
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
@@ -46,6 +49,7 @@ import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentMatchers.anyInt
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
@@ -63,19 +67,21 @@ class UdfpsKeyguardInteractorTest : SysuiTestCase() {
|
||||
private lateinit var fakeCommandQueue: FakeCommandQueue
|
||||
private lateinit var featureFlags: FakeFeatureFlags
|
||||
private lateinit var burnInInteractor: BurnInInteractor
|
||||
private lateinit var shadeRepository: FakeShadeRepository
|
||||
|
||||
@Mock private lateinit var burnInHelper: BurnInHelperWrapper
|
||||
@Mock private lateinit var dialogManager: SystemUIDialogManager
|
||||
|
||||
private lateinit var underTest: UdfpsKeyguardInteractor
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
testScope = TestScope()
|
||||
configRepository = FakeConfigurationRepository()
|
||||
keyguardRepository = FakeKeyguardRepository()
|
||||
bouncerRepository = FakeKeyguardBouncerRepository()
|
||||
shadeRepository = FakeShadeRepository()
|
||||
fakeCommandQueue = FakeCommandQueue()
|
||||
featureFlags =
|
||||
FakeFeatureFlags().apply {
|
||||
@@ -102,6 +108,8 @@ class UdfpsKeyguardInteractorTest : SysuiTestCase() {
|
||||
bouncerRepository,
|
||||
configRepository,
|
||||
),
|
||||
shadeRepository,
|
||||
dialogManager,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -142,6 +150,61 @@ class UdfpsKeyguardInteractorTest : SysuiTestCase() {
|
||||
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() {
|
||||
whenever(burnInHelper.burnInProgressOffset()).thenReturn(burnInProgress)
|
||||
whenever(burnInHelper.burnInOffset(anyInt(), /* xAxis */ eq(true)))
|
||||
|
||||
@@ -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.KeyguardInteractor
|
||||
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.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@@ -58,7 +60,9 @@ class UdfpsAodViewModelTest : SysuiTestCase() {
|
||||
private lateinit var keyguardRepository: FakeKeyguardRepository
|
||||
private lateinit var fakeCommandQueue: FakeCommandQueue
|
||||
private lateinit var featureFlags: FakeFeatureFlags
|
||||
private lateinit var shadeRepository: FakeShadeRepository
|
||||
|
||||
@Mock private lateinit var dialogManager: SystemUIDialogManager
|
||||
@Mock private lateinit var burnInHelper: BurnInHelperWrapper
|
||||
|
||||
@Before
|
||||
@@ -70,6 +74,7 @@ class UdfpsAodViewModelTest : SysuiTestCase() {
|
||||
keyguardRepository = FakeKeyguardRepository()
|
||||
bouncerRepository = FakeKeyguardBouncerRepository()
|
||||
fakeCommandQueue = FakeCommandQueue()
|
||||
shadeRepository = FakeShadeRepository()
|
||||
featureFlags =
|
||||
FakeFeatureFlags().apply {
|
||||
set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
|
||||
@@ -93,6 +98,8 @@ class UdfpsAodViewModelTest : SysuiTestCase() {
|
||||
bouncerRepository,
|
||||
configRepository,
|
||||
),
|
||||
shadeRepository,
|
||||
dialogManager,
|
||||
)
|
||||
|
||||
underTest =
|
||||
|
||||
@@ -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.KeyguardTransitionInteractor
|
||||
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.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@@ -60,8 +62,10 @@ class UdfpsFingerprintViewModelTest : SysuiTestCase() {
|
||||
private lateinit var fakeCommandQueue: FakeCommandQueue
|
||||
private lateinit var featureFlags: FakeFeatureFlags
|
||||
private lateinit var transitionRepository: FakeKeyguardTransitionRepository
|
||||
private lateinit var shadeRepository: FakeShadeRepository
|
||||
|
||||
@Mock private lateinit var burnInHelper: BurnInHelperWrapper
|
||||
@Mock private lateinit var dialogManager: SystemUIDialogManager
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
@@ -79,35 +83,39 @@ class UdfpsFingerprintViewModelTest : SysuiTestCase() {
|
||||
}
|
||||
bouncerRepository = FakeKeyguardBouncerRepository()
|
||||
transitionRepository = FakeKeyguardTransitionRepository()
|
||||
shadeRepository = FakeShadeRepository()
|
||||
val transitionInteractor =
|
||||
KeyguardTransitionInteractor(
|
||||
transitionRepository,
|
||||
testScope.backgroundScope,
|
||||
)
|
||||
val udfpsKeyguardInteractor =
|
||||
UdfpsKeyguardInteractor(
|
||||
val keyguardInteractor =
|
||||
KeyguardInteractor(
|
||||
keyguardRepository,
|
||||
fakeCommandQueue,
|
||||
featureFlags,
|
||||
bouncerRepository,
|
||||
configRepository,
|
||||
BurnInInteractor(
|
||||
context,
|
||||
burnInHelper,
|
||||
testScope.backgroundScope,
|
||||
configRepository,
|
||||
FakeSystemClock(),
|
||||
),
|
||||
KeyguardInteractor(
|
||||
keyguardRepository,
|
||||
fakeCommandQueue,
|
||||
featureFlags,
|
||||
bouncerRepository,
|
||||
configRepository,
|
||||
),
|
||||
)
|
||||
|
||||
underTest =
|
||||
FingerprintViewModel(
|
||||
context,
|
||||
transitionInteractor,
|
||||
udfpsKeyguardInteractor,
|
||||
UdfpsKeyguardInteractor(
|
||||
configRepository,
|
||||
BurnInInteractor(
|
||||
context,
|
||||
burnInHelper,
|
||||
testScope.backgroundScope,
|
||||
configRepository,
|
||||
FakeSystemClock(),
|
||||
),
|
||||
keyguardInteractor,
|
||||
shadeRepository,
|
||||
dialogManager,
|
||||
),
|
||||
keyguardInteractor,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -20,12 +20,26 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.settingslib.Utils
|
||||
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.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.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.UdfpsKeyguardInteractor
|
||||
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.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.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@@ -35,6 +49,8 @@ import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
/** Tests UDFPS lockscreen view model transitions. */
|
||||
@@ -48,26 +64,63 @@ class UdfpsLockscreenViewModelTest : SysuiTestCase() {
|
||||
private val alternateBouncerColor =
|
||||
Utils.getColorAttrDefaultColor(context, alternateBouncerResId)
|
||||
|
||||
@Mock private lateinit var dialogManager: SystemUIDialogManager
|
||||
|
||||
private lateinit var underTest: UdfpsLockscreenViewModel
|
||||
private lateinit var testScope: TestScope
|
||||
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
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
testScope = TestScope()
|
||||
transitionRepository = FakeKeyguardTransitionRepository()
|
||||
val transitionInteractor =
|
||||
KeyguardTransitionInteractor(
|
||||
transitionRepository,
|
||||
testScope.backgroundScope,
|
||||
configRepository = FakeConfigurationRepository()
|
||||
keyguardRepository = FakeKeyguardRepository()
|
||||
bouncerRepository = FakeKeyguardBouncerRepository()
|
||||
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 =
|
||||
UdfpsLockscreenViewModel(
|
||||
context,
|
||||
lockscreenColorResId,
|
||||
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 {
|
||||
val transition by collectLastValue(underTest.transition)
|
||||
val visible by collectLastValue(underTest.visible)
|
||||
keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
|
||||
|
||||
// TransitionState.STARTED: lockscreen -> AOD
|
||||
transitionRepository.sendTransitionStep(
|
||||
@@ -175,6 +229,56 @@ class UdfpsLockscreenViewModelTest : SysuiTestCase() {
|
||||
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
|
||||
fun aodToLockscreen() =
|
||||
testScope.runTest {
|
||||
@@ -235,6 +339,7 @@ class UdfpsLockscreenViewModelTest : SysuiTestCase() {
|
||||
testScope.runTest {
|
||||
val transition by collectLastValue(underTest.transition)
|
||||
val visible by collectLastValue(underTest.visible)
|
||||
keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
|
||||
|
||||
// TransitionState.STARTED: lockscreen -> alternate bouncer
|
||||
transitionRepository.sendTransitionStep(
|
||||
@@ -398,6 +503,7 @@ class UdfpsLockscreenViewModelTest : SysuiTestCase() {
|
||||
testScope.runTest {
|
||||
val transition by collectLastValue(underTest.transition)
|
||||
val visible by collectLastValue(underTest.visible)
|
||||
keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
|
||||
|
||||
// TransitionState.STARTED: lockscreen -> occluded
|
||||
transitionRepository.sendTransitionStep(
|
||||
@@ -502,4 +608,96 @@ class UdfpsLockscreenViewModelTest : SysuiTestCase() {
|
||||
assertThat(transition?.color).isEqualTo(lockscreenColor)
|
||||
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",
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user