diff options
10 files changed, 112 insertions, 48 deletions
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt index be6f022d8d52..4c76fb41e26c 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt @@ -37,7 +37,6 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.ui.binder.DeviceEntryIconViewBinder import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines import com.android.systemui.keyguard.ui.view.DeviceEntryIconView import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel @@ -78,15 +77,16 @@ constructor( if (DeviceEntryUdfpsRefactor.isEnabled) { DeviceEntryIconView(context, null).apply { id = R.id.device_entry_icon_view - DeviceEntryIconViewBinder.bind( - applicationScope, - this, - deviceEntryIconViewModel.get(), - deviceEntryForegroundViewModel.get(), - deviceEntryBackgroundViewModel.get(), - falsingManager.get(), - vibratorHelper.get(), - ) + // TODO: b/326624996 Bind the DeviceEntryIcon + // DeviceEntryIconViewBinder.bind( + // applicationScope, + // this, + // deviceEntryIconViewModel.get(), + // deviceEntryForegroundViewModel.get(), + // deviceEntryBackgroundViewModel.get(), + // falsingManager.get(), + // vibratorHelper.get(), + // ) } } else { // keyguardBottomAreaRefactor() diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt index 0c0ed77a65f7..435df5fa1902 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt @@ -51,6 +51,8 @@ import kotlinx.coroutines.withContext * There is never more than one instance of the FingerprintProperty at any given time. */ interface FingerprintPropertyRepository { + /** Whether the fingerprint properties have been initialized yet. */ + val propertiesInitialized: StateFlow<Boolean> /** The id of fingerprint sensor. */ val sensorId: Flow<Int> @@ -105,7 +107,16 @@ constructor( .stateIn( applicationScope, started = SharingStarted.Eagerly, - initialValue = DEFAULT_PROPS, + initialValue = UNINITIALIZED_PROPS, + ) + + override val propertiesInitialized: StateFlow<Boolean> = + props + .map { it != UNINITIALIZED_PROPS } + .stateIn( + applicationScope, + started = SharingStarted.WhileSubscribed(), + initialValue = props.value != UNINITIALIZED_PROPS, ) override val sensorId: Flow<Int> = props.map { it.sensorId } @@ -124,6 +135,17 @@ constructor( companion object { private const val TAG = "FingerprintPropertyRepositoryImpl" + private val UNINITIALIZED_PROPS = + FingerprintSensorPropertiesInternal( + -2 /* sensorId */, + SensorProperties.STRENGTH_CONVENIENCE, + 0 /* maxEnrollmentsPerUser */, + listOf<ComponentInfoInternal>(), + FingerprintSensorProperties.TYPE_UNKNOWN, + false /* halControlsIllumination */, + true /* resetLockoutRequiresHardwareAuthToken */, + listOf<SensorLocationInternal>(SensorLocationInternal.DEFAULT) + ) private val DEFAULT_PROPS = FingerprintSensorPropertiesInternal( -1 /* sensorId */, diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt index ff9cdbd7dc24..8bc0c5456eec 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt @@ -25,6 +25,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import javax.inject.Inject import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filterNotNull @@ -39,6 +40,7 @@ constructor( configurationInteractor: ConfigurationInteractor, displayStateInteractor: DisplayStateInteractor, ) { + val propertiesInitialized: StateFlow<Boolean> = repository.propertiesInitialized val isUdfps: Flow<Boolean> = repository.sensorType.map { it.isUdfps() } /** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt index 873cc847fa60..d0821c989d81 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt @@ -22,11 +22,14 @@ import android.content.res.ColorStateList import android.util.StateSet import android.view.HapticFeedbackConstants import android.view.View +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.constraintlayout.widget.ConstraintSet import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.common.ui.view.LongPressHandlingView import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.keyguard.ui.view.DeviceEntryIconView +import com.android.systemui.keyguard.ui.view.layout.sections.DefaultDeviceEntrySection import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel @@ -51,6 +54,8 @@ object DeviceEntryIconViewBinder { @JvmStatic fun bind( applicationScope: CoroutineScope, + keyguardRootView: ConstraintLayout, + section: DefaultDeviceEntrySection, view: DeviceEntryIconView, viewModel: DeviceEntryIconViewModel, fgViewModel: DeviceEntryForegroundViewModel, @@ -76,6 +81,23 @@ object DeviceEntryIconViewBinder { } } + keyguardRootView.repeatWhenAttached { + repeatOnLifecycle(Lifecycle.State.STARTED) { + launch { + viewModel.positionAtUdfpsLocation.collect { supportsUdfps -> + section.removeViews(keyguardRootView) + keyguardRootView.addView(view) + val constraintSet = ConstraintSet().apply { clone(keyguardRootView) } + section.applyConstraintsAfterPropertiesInitialized( + constraintSet, + supportsUdfps, + ) + constraintSet.applyTo(keyguardRootView) + } + } + } + } + view.repeatWhenAttached { // Repeat on CREATED so that the view will always observe the entire // GONE => AOD transition (even though the view may not be visible until the middle diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt index 3fc9b4200f35..d70ecce90430 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt @@ -26,7 +26,6 @@ import android.view.WindowManager import androidx.annotation.VisibleForTesting import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet -import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.LockIconView import com.android.keyguard.LockIconViewController import com.android.systemui.Flags.keyguardBottomAreaRefactor @@ -56,7 +55,6 @@ class DefaultDeviceEntrySection @Inject constructor( @Application private val applicationScope: CoroutineScope, - private val keyguardUpdateMonitor: KeyguardUpdateMonitor, private val authController: AuthController, private val windowManager: WindowManager, private val context: Context, @@ -69,6 +67,7 @@ constructor( private val falsingManager: Lazy<FalsingManager>, private val vibratorHelper: Lazy<VibratorHelper>, ) : KeyguardSection() { + private var deviceEntryIconView: DeviceEntryIconView? = null private val deviceEntryIconViewId = R.id.device_entry_icon_view override fun addViews(constraintLayout: ConstraintLayout) { @@ -80,21 +79,22 @@ constructor( notificationPanelView.removeView(it) } - val view = - if (DeviceEntryUdfpsRefactor.isEnabled) { + if (DeviceEntryUdfpsRefactor.isEnabled) { + deviceEntryIconView = DeviceEntryIconView(context, null).apply { id = deviceEntryIconViewId } - } else { - // keyguardBottomAreaRefactor() - LockIconView(context, null).apply { id = R.id.lock_icon_view } - } - constraintLayout.addView(view) + } else { + // keyguardBottomAreaRefactor() + constraintLayout.addView(LockIconView(context, null).apply { id = R.id.lock_icon_view }) + } } override fun bindData(constraintLayout: ConstraintLayout) { if (DeviceEntryUdfpsRefactor.isEnabled) { - constraintLayout.findViewById<DeviceEntryIconView?>(deviceEntryIconViewId)?.let { + deviceEntryIconView?.let { DeviceEntryIconViewBinder.bind( applicationScope, + constraintLayout, + this, it, deviceEntryIconViewModel.get(), deviceEntryForegroundViewModel.get(), @@ -111,7 +111,22 @@ constructor( } override fun applyConstraints(constraintSet: ConstraintSet) { - val isUdfpsSupported = keyguardUpdateMonitor.isUdfpsSupported + if (!DeviceEntryUdfpsRefactor.isEnabled) { + applyConstraints(constraintSet, authController.isUdfpsSupported) + } + } + + fun applyConstraintsAfterPropertiesInitialized( + constraintSet: ConstraintSet, + isUdfpsSupported: Boolean + ) { + if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) { + return + } + applyConstraints(constraintSet, isUdfpsSupported) + } + + private fun applyConstraints(constraintSet: ConstraintSet, isUdfpsSupported: Boolean) { val scaleFactor: Float = authController.scaleFactor val mBottomPaddingPx = context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt index c9cf0c31a8fd..dc327fff0a1d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.ui.viewmodel import android.animation.FloatEvaluator import android.animation.IntEvaluator import com.android.keyguard.KeyguardViewController +import com.android.systemui.biometrics.domain.interactor.FingerprintPropertyInteractor import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.DeviceEntrySourceInteractor import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor @@ -34,11 +35,10 @@ import com.android.systemui.util.kotlin.sample import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge @@ -60,6 +60,7 @@ constructor( private val keyguardViewController: Lazy<KeyguardViewController>, private val deviceEntryInteractor: DeviceEntryInteractor, private val deviceEntrySourceInteractor: DeviceEntrySourceInteractor, + private val fingerprintPropertyInteractor: FingerprintPropertyInteractor, ) { private val intEvaluator = IntEvaluator() private val floatEvaluator = FloatEvaluator() @@ -178,21 +179,16 @@ constructor( } } - private val isUnlocked: Flow<Boolean> = - deviceEntryInteractor.isUnlocked.flatMapLatest { isUnlocked -> - if (!isUnlocked) { - flowOf(false) + /** Whether the device entry icon should be positioned at the location of UDFPS. */ + val positionAtUdfpsLocation: Flow<Boolean> = + fingerprintPropertyInteractor.propertiesInitialized.flatMapLatest { initialized -> + if (initialized) { + fingerprintPropertyInteractor.isUdfps } else { - flow { - // delay in case device ends up transitioning away from the lock screen; - // we don't want to animate to the unlocked icon and just let the - // icon fade with the transition to GONE - delay(UNLOCKED_DELAY_MS) - emit(true) - } + // Don't update the position of the icon until properties are initialized. + emptyFlow() } } - val iconType: Flow<DeviceEntryIconView.IconType> = combine( deviceEntryUdfpsInteractor.isListeningForUdfps, @@ -245,10 +241,6 @@ constructor( DeviceEntryIconView.AccessibilityHintType.NONE } } - - companion object { - const val UNLOCKED_DELAY_MS = 50L - } } data class BurnInOffsets( diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt index dc438d7d80f2..47dc70fcf919 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt @@ -83,11 +83,13 @@ class FingerprintRepositoryImplTest : SysuiTestCase() { val strength by collectLastValue(repository.strength) val sensorType by collectLastValue(repository.sensorType) val sensorLocations by collectLastValue(repository.sensorLocations) + val propertiesInitialized by collectLastValue(repository.propertiesInitialized) - // Assert default properties. - assertThat(sensorId).isEqualTo(-1) + // Assert uninitialized properties. + assertThat(sensorId).isEqualTo(-2) assertThat(strength).isEqualTo(SensorStrength.CONVENIENCE) assertThat(sensorType).isEqualTo(FingerprintSensorType.UNKNOWN) + assertThat(propertiesInitialized).isEqualTo(false) val fingerprintProps = listOf( @@ -129,6 +131,7 @@ class FingerprintRepositoryImplTest : SysuiTestCase() { assertThat(sensorId).isEqualTo(1) assertThat(strength).isEqualTo(SensorStrength.STRONG) assertThat(sensorType).isEqualTo(FingerprintSensorType.REAR) + assertThat(propertiesInitialized).isEqualTo(true) assertThat(sensorLocations?.size).isEqualTo(2) assertThat(sensorLocations).containsKey("display_id_1") diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt index 699284e29ce3..17d602c60379 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt @@ -18,15 +18,16 @@ package com.android.systemui.keyguard.ui.view.layout.sections import android.graphics.Point +import android.testing.TestableLooper import android.view.WindowManager import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.test.filters.SmallTest -import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.LockIconViewController import com.android.systemui.Flags as AConfigFlags import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.AuthController +import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.flags.Flags @@ -51,9 +52,9 @@ import org.mockito.MockitoAnnotations @ExperimentalCoroutinesApi @RunWith(JUnit4::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest class DefaultDeviceEntrySectionTest : SysuiTestCase() { - @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor @Mock private lateinit var authController: AuthController @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var windowManager: WindowManager @Mock private lateinit var notificationPanelView: NotificationPanelView @@ -61,11 +62,12 @@ class DefaultDeviceEntrySectionTest : SysuiTestCase() { @Mock private lateinit var lockIconViewController: LockIconViewController @Mock private lateinit var falsingManager: FalsingManager private lateinit var underTest: DefaultDeviceEntrySection + private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository @Before fun setup() { MockitoAnnotations.initMocks(this) - + fingerprintPropertyRepository = FakeFingerprintPropertyRepository() mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR) featureFlags = @@ -73,7 +75,6 @@ class DefaultDeviceEntrySectionTest : SysuiTestCase() { underTest = DefaultDeviceEntrySection( TestScope().backgroundScope, - keyguardUpdateMonitor, authController, windowManager, context, @@ -91,6 +92,7 @@ class DefaultDeviceEntrySectionTest : SysuiTestCase() { @Test fun addViewsConditionally_migrateFlagOn() { mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR) + mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) val constraintLayout = ConstraintLayout(context, null) underTest.addViews(constraintLayout) assertThat(constraintLayout.childCount).isGreaterThan(0) @@ -102,7 +104,9 @@ class DefaultDeviceEntrySectionTest : SysuiTestCase() { mSetFlagsRule.enableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) val constraintLayout = ConstraintLayout(context, null) underTest.addViews(constraintLayout) - assertThat(constraintLayout.childCount).isGreaterThan(0) + + // No views are added initially because fingerprint properties aren't initialized yet. + assertThat(constraintLayout.childCount).isEqualTo(0) } @Test @@ -130,7 +134,7 @@ class DefaultDeviceEntrySectionTest : SysuiTestCase() { fun applyConstraints_udfps_refactor_on() { mSetFlagsRule.enableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) val cs = ConstraintSet() - underTest.applyConstraints(cs) + underTest.applyConstraintsAfterPropertiesInitialized(cs, false) val constraint = cs.getConstraint(R.id.device_entry_icon_view) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt index 005cac490d89..f95c721759a9 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt @@ -28,6 +28,7 @@ import kotlinx.coroutines.flow.asStateFlow @SysUISingleton class FakeFingerprintPropertyRepository @Inject constructor() : FingerprintPropertyRepository { + override val propertiesInitialized: MutableStateFlow<Boolean> = MutableStateFlow(false) private val _sensorId: MutableStateFlow<Int> = MutableStateFlow(-1) override val sensorId = _sensorId.asStateFlow() @@ -50,6 +51,7 @@ class FakeFingerprintPropertyRepository @Inject constructor() : FingerprintPrope sensorType: FingerprintSensorType, sensorLocations: Map<String, SensorLocationInternal> ) { + propertiesInitialized.value = true _sensorId.value = sensorId _strength.value = strength _sensorType.value = sensorType diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt index 73fd9991945c..9fe65710bb9c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt @@ -16,6 +16,7 @@ package com.android.systemui.keyguard.ui.viewmodel +import com.android.systemui.biometrics.domain.interactor.fingerprintPropertyInteractor import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.deviceEntrySourceInteractor import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor @@ -50,5 +51,6 @@ val Kosmos.deviceEntryIconViewModel by Fixture { keyguardViewController = { statusBarKeyguardViewManager }, deviceEntryInteractor = deviceEntryInteractor, deviceEntrySourceInteractor = deviceEntrySourceInteractor, + fingerprintPropertyInteractor = fingerprintPropertyInteractor, ) } |