Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b405bdfa authored by Grace Cheng's avatar Grace Cheng Committed by Android (Google) Code Review
Browse files

Merge "Replaces lockscreen UDFPS guidance disruptive accessibility announcements" into main

parents eb5bb034 ac1e85b8
Loading
Loading
Loading
Loading
+120 −4
Original line number Diff line number Diff line
@@ -16,14 +16,19 @@

package com.android.systemui.deviceentry.domain.ui.viewmodel

import android.graphics.Point
import android.graphics.Rect
import android.platform.test.flag.junit.FlagsParameterization
import android.view.MotionEvent
import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES
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.deviceEntryUdfpsAccessibilityOverlayViewModel
import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay
import com.android.systemui.deviceentry.ui.viewmodel.DeviceEntryUdfpsAccessibilityOverlayViewModel
import com.android.systemui.flags.Flags.FULL_SCREEN_USER_SWITCHER
import com.android.systemui.flags.andSceneContainer
@@ -31,6 +36,7 @@ import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.keyguardTouchHandlingInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -40,22 +46,37 @@ import com.android.systemui.res.R
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.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.Mock
import org.mockito.junit.MockitoJUnit
import org.mockito.kotlin.any
import org.mockito.kotlin.whenever
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters

@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
class UdfpsAccessibilityOverlayViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
class DeviceEntryUdfpsAccessibilityOverlayViewModelTest(flags: FlagsParameterization) :
    SysuiTestCase() {
    @JvmField @Rule val mockito = MockitoJUnit.rule()

    private val kosmos =
        testKosmos().apply {
            fakeFeatureFlagsClassic.apply { set(FULL_SCREEN_USER_SWITCHER, false) }
        }

    @Mock private lateinit var motionEvent: MotionEvent

    private val customizeLockscreenString =
        context.resources.getString(R.string.accessibility_desc_customize_lock_screen)
    private val deviceEntryIconTransition = kosmos.fakeDeviceEntryIconViewModelTransition
    private val testScope = kosmos.testScope
    private val biometricSettingsRepository = kosmos.fakeBiometricSettingsRepository
@@ -63,10 +84,10 @@ 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 }

    private lateinit var view: UdfpsAccessibilityOverlay
    private lateinit var underTest: DeviceEntryUdfpsAccessibilityOverlayViewModel

    companion object {
@@ -84,6 +105,8 @@ class UdfpsAccessibilityOverlayViewModelTest(flags: FlagsParameterization) : Sys
    @Before
    fun setup() {
        underTest = kosmos.deviceEntryUdfpsAccessibilityOverlayViewModel
        view = UdfpsAccessibilityOverlay(mContext)
        view.udfpsAccessibilityOverlayViewModel = underTest
        overrideResource(R.integer.udfps_padding_debounce_duration, 0)
    }

@@ -142,6 +165,81 @@ class UdfpsAccessibilityOverlayViewModelTest(flags: FlagsParameterization) : Sys
            assertThat(visible).isFalse()
        }

    @Test
    fun udfpsDirectionalFeedbackReturnsNull_ifNotListeningForUdfps() =
        testScope.runTest {
            setupVisibleStateOnLockscreen()
            setUdfpsListeningState(false)
            setupMotionEvent(MotionEvent.ACTION_HOVER_ENTER)
            runCurrent()
            assertThat(underTest.getUdfpsDirectionalFeedbackOnHoverEnterOrMove(motionEvent))
                .isEqualTo(null)
        }

    @Test
    fun udfpsDirectionalFeedback_onTouchOutsideSensorArea() =
        testScope.runTest {
            setupVisibleStateOnLockscreen()
            setUdfpsListeningState(true)
            setupMotionEvent(MotionEvent.ACTION_HOVER_ENTER)
            runCurrent()
            assertThat(underTest.getUdfpsDirectionalFeedbackOnHoverEnterOrMove(motionEvent))
                .isEqualTo("Move left")

            setupMotionEvent(MotionEvent.ACTION_HOVER_MOVE)
            runCurrent()
            assertThat(underTest.getUdfpsDirectionalFeedbackOnHoverEnterOrMove(motionEvent))
                .isEqualTo("Move left")
        }

    @Test
    fun udfpsDirectionalFeedback_updatesOnUdfpsTouchOutsideSensorArea() =
        testScope.runTest {
            var guidanceMessage = "Move left"
            setupVisibleStateOnLockscreen()
            setUdfpsListeningState(true)
            setupMotionEvent(MotionEvent.ACTION_HOVER_ENTER)
            view.dispatchGenericMotionEvent(motionEvent)
            runCurrent()
            assertThat(view.importantForAccessibility).isEqualTo(IMPORTANT_FOR_ACCESSIBILITY_YES)
            assertThat(view.contentDescription).isEqualTo(getContentDescription(guidanceMessage))

            guidanceMessage = "Move right"
            setupMotionEvent(MotionEvent.ACTION_HOVER_MOVE)
            mockTouchOutsideSensorArea(guidanceMessage)
            view.dispatchGenericMotionEvent(motionEvent)
            runCurrent()
            assertThat(view.importantForAccessibility).isEqualTo(IMPORTANT_FOR_ACCESSIBILITY_YES)
            assertThat(view.contentDescription).isEqualTo(getContentDescription(guidanceMessage))
        }

    private fun TestScope.setUdfpsListeningState(isListening: Boolean) {
        setupUdfpsUtils()
        val isListeningForUdfps by collectLastValue(underTest.isListeningForUdfps)
        kosmos.deviceEntryFingerprintAuthRepository.setIsRunning(isListening)
        if (isListening) {
            kosmos.keyguardTouchHandlingInteractor.setUdfpsAccessibilityOverlayBounds(
                Rect(0, 1000, 1000, 2000)
            )
        } else {
            kosmos.keyguardTouchHandlingInteractor.setUdfpsAccessibilityOverlayBounds(null)
        }
        runCurrent()
        assertThat(isListeningForUdfps).isEqualTo(isListening)
    }

    private fun setupUdfpsUtils() {
        whenever(kosmos.udfpsUtils.getTouchInNativeCoordinates(any(), any(), any(), anyBoolean()))
            .thenReturn(Point(0, 0))
        whenever(kosmos.udfpsUtils.isWithinSensorArea(any(), any(), any(), anyBoolean()))
            .thenReturn(false)
        mockTouchOutsideSensorArea("Move left")
    }

    private fun setupMotionEvent(eventType: Int) {
        whenever(motionEvent.action).thenReturn(eventType)
    }

    private suspend fun setupVisibleStateOnLockscreen() {
        // A11y enabled
        accessibilityRepository.isTouchExplorationEnabled.value = true
@@ -176,4 +274,22 @@ class UdfpsAccessibilityOverlayViewModelTest(flags: FlagsParameterization) : Sys
        shadeTestUtil.setQsExpansion(0f)
        shadeTestUtil.setLockscreenShadeExpansion(0f)
    }

    private fun mockTouchOutsideSensorArea(guidanceMessage: String) {
        whenever(
                kosmos.udfpsUtils.onTouchOutsideOfSensorArea(
                    any(),
                    any(),
                    any(),
                    any(),
                    any(),
                    anyBoolean(),
                )
            )
            .thenReturn(guidanceMessage)
    }

    private fun getContentDescription(guidanceMessage: String): CharSequence {
        return customizeLockscreenString + "\n" + guidanceMessage
    }
}
+1 −5
Original line number Diff line number Diff line
@@ -33,7 +33,6 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -57,9 +56,9 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -362,8 +361,6 @@ class KeyguardTouchHandlingInteractorTest : SysuiTestCase() {
        }
    }



    @Test
    @EnableFlags(FLAG_DOUBLE_TAP_TO_SLEEP)
    fun isDoubleTapEnabled_flagEnabledAndConfigDisabled_alwaysFalse() {
@@ -436,7 +433,6 @@ class KeyguardTouchHandlingInteractorTest : SysuiTestCase() {
                transitionInteractor = kosmos.keyguardTransitionInteractor,
                repository = keyguardRepository,
                logger = logger,
                featureFlags = kosmos.fakeFeatureFlagsClassic,
                broadcastDispatcher = fakeBroadcastDispatcher,
                accessibilityManager = kosmos.accessibilityManagerWrapper,
                pulsingGestureListener = kosmos.pulsingGestureListener,
+35 −0
Original line number Diff line number Diff line
@@ -16,12 +16,16 @@

package com.android.systemui.keyguard.ui.viewmodel

import android.graphics.Rect
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.keyguardTouchHandlingInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -148,4 +152,35 @@ class AccessibilityActionsViewModelTest : SysuiTestCase() {

            assertThat(isOnKeyguard).isEqualTo(false)
        }

    @Test
    fun udfpsAccessibilityOverlayBounds_isNull_whenNotListeningForUdfps() =
        testScope.runTest {
            val accessibilityOverlayBoundsWhenListeningForUdfps by
                collectLastValue(underTest.accessibilityOverlayBoundsWhenListeningForUdfps)
            setUdfpsListeningState(false)
            assertThat(accessibilityOverlayBoundsWhenListeningForUdfps).isNull()
        }

    @Test
    fun updatesUdfpsAccessibilityOverlayBoundsWhenListeningForUdfps() =
        testScope.runTest {
            val accessibilityOverlayBoundsWhenListeningForUdfps by
                collectLastValue(underTest.accessibilityOverlayBoundsWhenListeningForUdfps)
            setUdfpsListeningState(true)
            assertThat(accessibilityOverlayBoundsWhenListeningForUdfps)
                .isEqualTo(Rect(0, 1000, 1000, 2000))
        }

    private fun setUdfpsListeningState(isListening: Boolean) {
        kosmos.fingerprintPropertyRepository.supportsUdfps()
        kosmos.deviceEntryFingerprintAuthRepository.setIsRunning(isListening)
        if (isListening) {
            kosmos.keyguardTouchHandlingInteractor.setUdfpsAccessibilityOverlayBounds(
                Rect(0, 1000, 1000, 2000)
            )
        } else {
            kosmos.keyguardTouchHandlingInteractor.setUdfpsAccessibilityOverlayBounds(null)
        }
    }
}
+78 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.keyguard.ui.viewmodel

import android.graphics.Rect
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.domain.interactor.keyguardTouchHandlingInteractor
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class KeyguardTouchHandlingViewModelTest : SysuiTestCase() {

    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private lateinit var underTest: KeyguardTouchHandlingViewModel

    @Before
    fun setUp() {
        underTest = kosmos.keyguardTouchHandlingViewModel
    }

    @Test
    fun udfpsAccessibilityOverlayBounds_isNull_whenNotListeningForUdfps() =
        testScope.runTest {
            val accessibilityOverlayBoundsWhenListeningForUdfps by
                collectLastValue(underTest.accessibilityOverlayBoundsWhenListeningForUdfps)
            setUdfpsListeningState(false)
            assertThat(accessibilityOverlayBoundsWhenListeningForUdfps).isNull()
        }

    @Test
    fun updatesUdfpsAccessibilityOverlayBoundsWhenListeningForUdfps() =
        testScope.runTest {
            val accessibilityOverlayBoundsWhenListeningForUdfps by
                collectLastValue(underTest.accessibilityOverlayBoundsWhenListeningForUdfps)
            setUdfpsListeningState(true)
            assertThat(accessibilityOverlayBoundsWhenListeningForUdfps)
                .isEqualTo(Rect(0, 1000, 1000, 2000))
        }

    private fun setUdfpsListeningState(isListening: Boolean) {
        if (isListening) {
            kosmos.fingerprintPropertyRepository.supportsUdfps()
            kosmos.keyguardTouchHandlingInteractor.setUdfpsAccessibilityOverlayBounds(
                Rect(0, 1000, 1000, 2000)
            )
        } else {
            kosmos.keyguardTouchHandlingInteractor.setUdfpsAccessibilityOverlayBounds(null)
        }
        kosmos.deviceEntryFingerprintAuthRepository.setIsRunning(isListening)
    }
}
+3 −2
Original line number Diff line number Diff line
@@ -67,9 +67,9 @@ import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.common.domain.interactor.SysUIStateDisplaysInteractor;
import com.android.systemui.common.ui.view.TouchHandlingView;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor;
import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlayOverlappingTouchHandlingView;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlagsClassic;
@@ -507,7 +507,8 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {

        mMainHandler = new Handler(Looper.getMainLooper());

        TouchHandlingView touchHandlingView = mock(TouchHandlingView.class);
        UdfpsAccessibilityOverlayOverlappingTouchHandlingView touchHandlingView =
                mock(UdfpsAccessibilityOverlayOverlappingTouchHandlingView.class);
        when(mView.requireViewById(R.id.keyguard_long_press))
                .thenReturn(touchHandlingView);

Loading