Loading packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt +79 −2 Original line number Diff line number Diff line Loading @@ -16,13 +16,17 @@ package com.android.systemui.deviceentry.domain.ui.viewmodel import android.graphics.Point import android.platform.test.flag.junit.FlagsParameterization import android.view.MotionEvent import android.view.View import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.accessibility.data.repository.fakeAccessibilityRepository import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository import com.android.systemui.biometrics.udfpsUtils import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository import com.android.systemui.deviceentry.data.ui.viewmodel.alternateBouncerUdfpsAccessibilityOverlayViewModel import com.android.systemui.deviceentry.data.ui.viewmodel.deviceEntryUdfpsAccessibilityOverlayViewModel import com.android.systemui.deviceentry.ui.viewmodel.DeviceEntryUdfpsAccessibilityOverlayViewModel import com.android.systemui.flags.Flags.FULL_SCREEN_USER_SWITCHER Loading @@ -34,6 +38,7 @@ import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepos import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.keyguard.ui.viewmodel.accessibilityActionsViewModelKosmos import com.android.systemui.keyguard.ui.viewmodel.fakeDeviceEntryIconViewModelTransition import com.android.systemui.kosmos.testScope import com.android.systemui.res.R Loading @@ -41,14 +46,22 @@ import com.android.systemui.shade.shadeTestUtil import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlin.test.Test import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Before import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyInt import org.mockito.kotlin.any import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.whenever import platform.test.runner.parameterized.ParameterizedAndroidJunit4 import platform.test.runner.parameterized.Parameters @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(ParameterizedAndroidJunit4::class) class UdfpsAccessibilityOverlayViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { Loading @@ -63,7 +76,6 @@ class UdfpsAccessibilityOverlayViewModelTest(flags: FlagsParameterization) : Sys private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository private val fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository private val deviceEntryFingerprintAuthRepository = kosmos.deviceEntryFingerprintAuthRepository private val deviceEntryRepository = kosmos.fakeDeviceEntryRepository private val shadeTestUtil by lazy { kosmos.shadeTestUtil } Loading @@ -83,6 +95,22 @@ class UdfpsAccessibilityOverlayViewModelTest(flags: FlagsParameterization) : Sys @Before fun setup() { whenever(kosmos.udfpsUtils.isWithinSensorArea(any(), any(), any())).thenReturn(false) whenever( kosmos.udfpsUtils.getTouchInNativeCoordinates(anyInt(), any(), any(), anyBoolean()) ) .thenReturn(Point(0, 0)) whenever( kosmos.udfpsUtils.onTouchOutsideOfSensorArea( anyBoolean(), eq(null), anyInt(), anyInt(), any(), anyBoolean(), ) ) .thenReturn("Move left") underTest = kosmos.deviceEntryUdfpsAccessibilityOverlayViewModel overrideResource(R.integer.udfps_padding_debounce_duration, 0) } Loading @@ -100,6 +128,55 @@ class UdfpsAccessibilityOverlayViewModelTest(flags: FlagsParameterization) : Sys assertThat(visible).isTrue() } @Test fun contentDescription_setOnUdfpsTouchOutsideSensorArea() = testScope.runTest { val contentDescription by collectLastValue(underTest.contentDescription) setupVisibleStateOnLockscreen() underTest.onHoverEvent(mock<View>(), mock<MotionEvent>()) runCurrent() assertThat(contentDescription).isEqualTo("Move left") } @Test fun clearAccessibilityOverlayMessageReason_updatesWhenFocusChangesFromUdfpsOverlayToLockscreen() = testScope.runTest { val clearAccessibilityOverlayMessageReason by collectLastValue(underTest.clearAccessibilityOverlayMessageReason) val contentDescription by collectLastValue(underTest.contentDescription) setupVisibleStateOnLockscreen() kosmos.accessibilityActionsViewModelKosmos.clearUdfpsAccessibilityOverlayMessage("test") runCurrent() assertThat(clearAccessibilityOverlayMessageReason).isEqualTo("test") // UdfpsAccessibilityOverlayViewBinder collects clearAccessibilityOverlayMessageReason // and calls // viewModel.setContentDescription(null) - mock this here underTest.setContentDescription(null) runCurrent() assertThat(contentDescription).isNull() } @Test fun clearAccessibilityOverlayMessageReason_updatesAfterUdfpsOverlayFocusOnAlternateBouncer() = testScope.runTest { val clearAccessibilityOverlayMessageReason by collectLastValue(underTest.clearAccessibilityOverlayMessageReason) val contentDescription by collectLastValue(underTest.contentDescription) setupVisibleStateOnLockscreen() kosmos.alternateBouncerUdfpsAccessibilityOverlayViewModel .clearUdfpsAccessibilityOverlayMessage("test") runCurrent() assertThat(clearAccessibilityOverlayMessageReason).isEqualTo("test") // UdfpsAccessibilityOverlayViewBinder collects clearAccessibilityOverlayMessageReason // and calls // viewModel.setContentDescription(null) - mock this here underTest.setContentDescription(null) runCurrent() assertThat(contentDescription).isNull() } @Test fun touchExplorationNotEnabled_overlayNotVisible() = testScope.runTest { Loading packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt +24 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.biometrics.domain.interactor import android.annotation.SuppressLint import android.content.Context import android.hardware.fingerprint.FingerprintManager import android.util.Log Loading @@ -32,10 +33,14 @@ import javax.inject.Inject import kotlin.math.max import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map Loading Loading @@ -131,6 +136,25 @@ constructor( } .distinctUntilChanged() /** * Event flow that emits every time the user taps the screen and a UDFPS guidance message is * surfaced and then cleared. Modeled as a SharedFlow because a StateFlow fails to emit every * event to the subscriber, causing missed Talkback feedback and incorrect focusability state of * the UDFPS accessibility overlay. */ @SuppressLint("SharedFlowCreation") private val _clearAccessibilityOverlayMessageReason = MutableSharedFlow<String?>() /** Indicates the reason for clearing the UDFPS accessibility overlay content description */ val clearAccessibilityOverlayMessageReason: SharedFlow<String?> = _clearAccessibilityOverlayMessageReason.asSharedFlow() suspend fun clearUdfpsAccessibilityOverlayMessage(reason: String) { // Add delay to make sure we read the guidance message before clearing it delay(1000) _clearAccessibilityOverlayMessageReason.emit(reason) } companion object { private const val TAG = "UdfpsOverlayInteractor" } Loading packages/SystemUI/src/com/android/systemui/deviceentry/ui/binder/UdfpsAccessibilityOverlayBinder.kt +37 −6 Original line number Diff line number Diff line Loading @@ -18,27 +18,58 @@ package com.android.systemui.deviceentry.ui.binder import android.annotation.SuppressLint import android.util.Log import android.view.MotionEvent import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES import androidx.core.view.isInvisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay import com.android.systemui.deviceentry.ui.viewmodel.UdfpsAccessibilityOverlayViewModel import com.android.systemui.lifecycle.repeatWhenAttached object UdfpsAccessibilityOverlayBinder { private const val TAG = "UdfpsAccessibilityOverlayBinder" /** Forwards hover events to the view model to make guided announcements for accessibility. */ @SuppressLint("ClickableViewAccessibility") @JvmStatic fun bind( view: UdfpsAccessibilityOverlay, viewModel: UdfpsAccessibilityOverlayViewModel, ) { view.setOnHoverListener { v, event -> viewModel.onHoverEvent(v, event) } fun bind(view: UdfpsAccessibilityOverlay, viewModel: UdfpsAccessibilityOverlayViewModel) { view.repeatWhenAttached { // Repeat on CREATED because we update the visibility of the view repeatOnLifecycle(Lifecycle.State.CREATED) { viewModel.visible.collect { visible -> view.isInvisible = !visible } view.setOnHoverListener { v, event -> if (event.action == MotionEvent.ACTION_HOVER_ENTER) { launch { viewModel.onHoverEvent(v, event) } } false } launch { viewModel.visible.collect { visible -> view.isInvisible = !visible } } launch { viewModel.contentDescription.collect { contentDescription -> if (contentDescription != null) { view.importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_YES view.contentDescription = contentDescription } } } launch { viewModel.clearAccessibilityOverlayMessageReason.collect { reason -> Log.d( TAG, "clearing content description of UDFPS accessibility overlay " + "for reason: $reason", ) view.importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_NO view.contentDescription = null viewModel.setContentDescription(null) } } } } } Loading packages/SystemUI/src/com/android/systemui/deviceentry/ui/view/UdfpsAccessibilityOverlay.kt +2 −0 Original line number Diff line number Diff line Loading @@ -23,5 +23,7 @@ import android.view.View class UdfpsAccessibilityOverlay(context: Context?) : View(context) { init { accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_ASSERTIVE importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_AUTO isClickable = false } } packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/AlternateBouncerUdfpsAccessibilityOverlayViewModel.kt +12 −1 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.systemui.deviceentry.ui.viewmodel import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor import com.android.systemui.biometrics.UdfpsUtils import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor import javax.inject.Inject import kotlinx.coroutines.flow.Flow Loading @@ -26,13 +27,23 @@ import kotlinx.coroutines.flow.flowOf class AlternateBouncerUdfpsAccessibilityOverlayViewModel @Inject constructor( udfpsOverlayInteractor: UdfpsOverlayInteractor, private val udfpsOverlayInteractor: UdfpsOverlayInteractor, accessibilityInteractor: AccessibilityInteractor, udfpsUtils: UdfpsUtils, ) : UdfpsAccessibilityOverlayViewModel( udfpsOverlayInteractor, accessibilityInteractor, udfpsUtils, ) { /** Overlay is always visible if touch exploration is enabled on the alternate bouncer. */ override fun isVisibleWhenTouchExplorationEnabled(): Flow<Boolean> = flowOf(true) /** * Clears the content description to prevent the view from storing stale UDFPS directional * guidance messages for accessibility. */ suspend fun clearUdfpsAccessibilityOverlayMessage(reason: String) { udfpsOverlayInteractor.clearUdfpsAccessibilityOverlayMessage(reason) } } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt +79 −2 Original line number Diff line number Diff line Loading @@ -16,13 +16,17 @@ package com.android.systemui.deviceentry.domain.ui.viewmodel import android.graphics.Point import android.platform.test.flag.junit.FlagsParameterization import android.view.MotionEvent import android.view.View import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.accessibility.data.repository.fakeAccessibilityRepository import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository import com.android.systemui.biometrics.udfpsUtils import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository import com.android.systemui.deviceentry.data.ui.viewmodel.alternateBouncerUdfpsAccessibilityOverlayViewModel import com.android.systemui.deviceentry.data.ui.viewmodel.deviceEntryUdfpsAccessibilityOverlayViewModel import com.android.systemui.deviceentry.ui.viewmodel.DeviceEntryUdfpsAccessibilityOverlayViewModel import com.android.systemui.flags.Flags.FULL_SCREEN_USER_SWITCHER Loading @@ -34,6 +38,7 @@ import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepos import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.keyguard.ui.viewmodel.accessibilityActionsViewModelKosmos import com.android.systemui.keyguard.ui.viewmodel.fakeDeviceEntryIconViewModelTransition import com.android.systemui.kosmos.testScope import com.android.systemui.res.R Loading @@ -41,14 +46,22 @@ import com.android.systemui.shade.shadeTestUtil import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlin.test.Test import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Before import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyInt import org.mockito.kotlin.any import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.whenever import platform.test.runner.parameterized.ParameterizedAndroidJunit4 import platform.test.runner.parameterized.Parameters @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(ParameterizedAndroidJunit4::class) class UdfpsAccessibilityOverlayViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { Loading @@ -63,7 +76,6 @@ class UdfpsAccessibilityOverlayViewModelTest(flags: FlagsParameterization) : Sys private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository private val fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository private val deviceEntryFingerprintAuthRepository = kosmos.deviceEntryFingerprintAuthRepository private val deviceEntryRepository = kosmos.fakeDeviceEntryRepository private val shadeTestUtil by lazy { kosmos.shadeTestUtil } Loading @@ -83,6 +95,22 @@ class UdfpsAccessibilityOverlayViewModelTest(flags: FlagsParameterization) : Sys @Before fun setup() { whenever(kosmos.udfpsUtils.isWithinSensorArea(any(), any(), any())).thenReturn(false) whenever( kosmos.udfpsUtils.getTouchInNativeCoordinates(anyInt(), any(), any(), anyBoolean()) ) .thenReturn(Point(0, 0)) whenever( kosmos.udfpsUtils.onTouchOutsideOfSensorArea( anyBoolean(), eq(null), anyInt(), anyInt(), any(), anyBoolean(), ) ) .thenReturn("Move left") underTest = kosmos.deviceEntryUdfpsAccessibilityOverlayViewModel overrideResource(R.integer.udfps_padding_debounce_duration, 0) } Loading @@ -100,6 +128,55 @@ class UdfpsAccessibilityOverlayViewModelTest(flags: FlagsParameterization) : Sys assertThat(visible).isTrue() } @Test fun contentDescription_setOnUdfpsTouchOutsideSensorArea() = testScope.runTest { val contentDescription by collectLastValue(underTest.contentDescription) setupVisibleStateOnLockscreen() underTest.onHoverEvent(mock<View>(), mock<MotionEvent>()) runCurrent() assertThat(contentDescription).isEqualTo("Move left") } @Test fun clearAccessibilityOverlayMessageReason_updatesWhenFocusChangesFromUdfpsOverlayToLockscreen() = testScope.runTest { val clearAccessibilityOverlayMessageReason by collectLastValue(underTest.clearAccessibilityOverlayMessageReason) val contentDescription by collectLastValue(underTest.contentDescription) setupVisibleStateOnLockscreen() kosmos.accessibilityActionsViewModelKosmos.clearUdfpsAccessibilityOverlayMessage("test") runCurrent() assertThat(clearAccessibilityOverlayMessageReason).isEqualTo("test") // UdfpsAccessibilityOverlayViewBinder collects clearAccessibilityOverlayMessageReason // and calls // viewModel.setContentDescription(null) - mock this here underTest.setContentDescription(null) runCurrent() assertThat(contentDescription).isNull() } @Test fun clearAccessibilityOverlayMessageReason_updatesAfterUdfpsOverlayFocusOnAlternateBouncer() = testScope.runTest { val clearAccessibilityOverlayMessageReason by collectLastValue(underTest.clearAccessibilityOverlayMessageReason) val contentDescription by collectLastValue(underTest.contentDescription) setupVisibleStateOnLockscreen() kosmos.alternateBouncerUdfpsAccessibilityOverlayViewModel .clearUdfpsAccessibilityOverlayMessage("test") runCurrent() assertThat(clearAccessibilityOverlayMessageReason).isEqualTo("test") // UdfpsAccessibilityOverlayViewBinder collects clearAccessibilityOverlayMessageReason // and calls // viewModel.setContentDescription(null) - mock this here underTest.setContentDescription(null) runCurrent() assertThat(contentDescription).isNull() } @Test fun touchExplorationNotEnabled_overlayNotVisible() = testScope.runTest { Loading
packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt +24 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.biometrics.domain.interactor import android.annotation.SuppressLint import android.content.Context import android.hardware.fingerprint.FingerprintManager import android.util.Log Loading @@ -32,10 +33,14 @@ import javax.inject.Inject import kotlin.math.max import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map Loading Loading @@ -131,6 +136,25 @@ constructor( } .distinctUntilChanged() /** * Event flow that emits every time the user taps the screen and a UDFPS guidance message is * surfaced and then cleared. Modeled as a SharedFlow because a StateFlow fails to emit every * event to the subscriber, causing missed Talkback feedback and incorrect focusability state of * the UDFPS accessibility overlay. */ @SuppressLint("SharedFlowCreation") private val _clearAccessibilityOverlayMessageReason = MutableSharedFlow<String?>() /** Indicates the reason for clearing the UDFPS accessibility overlay content description */ val clearAccessibilityOverlayMessageReason: SharedFlow<String?> = _clearAccessibilityOverlayMessageReason.asSharedFlow() suspend fun clearUdfpsAccessibilityOverlayMessage(reason: String) { // Add delay to make sure we read the guidance message before clearing it delay(1000) _clearAccessibilityOverlayMessageReason.emit(reason) } companion object { private const val TAG = "UdfpsOverlayInteractor" } Loading
packages/SystemUI/src/com/android/systemui/deviceentry/ui/binder/UdfpsAccessibilityOverlayBinder.kt +37 −6 Original line number Diff line number Diff line Loading @@ -18,27 +18,58 @@ package com.android.systemui.deviceentry.ui.binder import android.annotation.SuppressLint import android.util.Log import android.view.MotionEvent import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES import androidx.core.view.isInvisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay import com.android.systemui.deviceentry.ui.viewmodel.UdfpsAccessibilityOverlayViewModel import com.android.systemui.lifecycle.repeatWhenAttached object UdfpsAccessibilityOverlayBinder { private const val TAG = "UdfpsAccessibilityOverlayBinder" /** Forwards hover events to the view model to make guided announcements for accessibility. */ @SuppressLint("ClickableViewAccessibility") @JvmStatic fun bind( view: UdfpsAccessibilityOverlay, viewModel: UdfpsAccessibilityOverlayViewModel, ) { view.setOnHoverListener { v, event -> viewModel.onHoverEvent(v, event) } fun bind(view: UdfpsAccessibilityOverlay, viewModel: UdfpsAccessibilityOverlayViewModel) { view.repeatWhenAttached { // Repeat on CREATED because we update the visibility of the view repeatOnLifecycle(Lifecycle.State.CREATED) { viewModel.visible.collect { visible -> view.isInvisible = !visible } view.setOnHoverListener { v, event -> if (event.action == MotionEvent.ACTION_HOVER_ENTER) { launch { viewModel.onHoverEvent(v, event) } } false } launch { viewModel.visible.collect { visible -> view.isInvisible = !visible } } launch { viewModel.contentDescription.collect { contentDescription -> if (contentDescription != null) { view.importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_YES view.contentDescription = contentDescription } } } launch { viewModel.clearAccessibilityOverlayMessageReason.collect { reason -> Log.d( TAG, "clearing content description of UDFPS accessibility overlay " + "for reason: $reason", ) view.importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_NO view.contentDescription = null viewModel.setContentDescription(null) } } } } } Loading
packages/SystemUI/src/com/android/systemui/deviceentry/ui/view/UdfpsAccessibilityOverlay.kt +2 −0 Original line number Diff line number Diff line Loading @@ -23,5 +23,7 @@ import android.view.View class UdfpsAccessibilityOverlay(context: Context?) : View(context) { init { accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_ASSERTIVE importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_AUTO isClickable = false } }
packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/AlternateBouncerUdfpsAccessibilityOverlayViewModel.kt +12 −1 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.systemui.deviceentry.ui.viewmodel import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor import com.android.systemui.biometrics.UdfpsUtils import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor import javax.inject.Inject import kotlinx.coroutines.flow.Flow Loading @@ -26,13 +27,23 @@ import kotlinx.coroutines.flow.flowOf class AlternateBouncerUdfpsAccessibilityOverlayViewModel @Inject constructor( udfpsOverlayInteractor: UdfpsOverlayInteractor, private val udfpsOverlayInteractor: UdfpsOverlayInteractor, accessibilityInteractor: AccessibilityInteractor, udfpsUtils: UdfpsUtils, ) : UdfpsAccessibilityOverlayViewModel( udfpsOverlayInteractor, accessibilityInteractor, udfpsUtils, ) { /** Overlay is always visible if touch exploration is enabled on the alternate bouncer. */ override fun isVisibleWhenTouchExplorationEnabled(): Flow<Boolean> = flowOf(true) /** * Clears the content description to prevent the view from storing stale UDFPS directional * guidance messages for accessibility. */ suspend fun clearUdfpsAccessibilityOverlayMessage(reason: String) { udfpsOverlayInteractor.clearUdfpsAccessibilityOverlayMessage(reason) } }