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

Commit e28c7676 authored by Hao Dong's avatar Hao Dong Committed by Android (Google) Code Review
Browse files

Merge "Improve bp landscape with shorter content." into main

parents d7d42ee6 b105573c
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -1122,6 +1122,8 @@
    <dimen name="biometric_prompt_panel_max_width">640dp</dimen>
    <dimen name="biometric_prompt_land_small_horizontal_guideline_padding">344dp</dimen>
    <dimen name="biometric_prompt_two_pane_udfps_horizontal_guideline_padding">114dp</dimen>
    <dimen name="biometric_prompt_two_pane_udfps_shorter_content_width">216dp</dimen>
    <dimen name="biometric_prompt_two_pane_udfps_shorter_horizontal_guideline_padding">661dp</dimen>
    <dimen name="biometric_prompt_two_pane_medium_horizontal_guideline_padding">640dp</dimen>
    <dimen name="biometric_prompt_one_pane_medium_top_guideline_padding">119dp</dimen>
    <dimen name="biometric_prompt_one_pane_medium_horizontal_guideline_padding">0dp</dimen>
+2 −2
Original line number Diff line number Diff line
@@ -364,7 +364,7 @@ object BiometricViewSizeBinder {
                            if (midGuideline != null) {
                                val left =
                                    if (bounds.left >= 0) {
                                        bounds.left
                                        abs(bounds.left)
                                    } else {
                                        view.width - abs(bounds.left)
                                    }
@@ -372,7 +372,7 @@ object BiometricViewSizeBinder {
                                    if (bounds.right >= 0) {
                                        view.width - abs(bounds.right)
                                    } else {
                                        bounds.right
                                        abs(bounds.right)
                                    }
                                val mid = (left + right) / 2
                                mediumConstraintSet.setGuidelineBegin(midGuideline.id, mid)
+84 −44
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.hardware.biometrics.BiometricPrompt
import android.hardware.biometrics.Flags.customBiometricPrompt
import android.hardware.biometrics.PromptContentView
import android.os.UserHandle
import android.text.TextPaint
import android.util.Log
import android.util.RotationUtils
import android.view.HapticFeedbackConstants
@@ -52,6 +53,7 @@ import com.android.systemui.biometrics.shared.model.PromptKind
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
import com.android.systemui.res.R
import com.android.systemui.util.kotlin.combine
import javax.inject.Inject
import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
@@ -260,15 +262,15 @@ constructor(
    val position: Flow<PromptPosition> =
        combine(
                _forceLargeSize,
                promptKind,
                displayStateInteractor.isLargeScreen,
                displayStateInteractor.currentRotation,
                modalities
            ) { forceLarge, isLargeScreen, rotation, modalities ->
            ) { forceLarge, promptKind, isLargeScreen, rotation, modalities ->
                when {
                    forceLarge ||
                        isLargeScreen ||
                        promptKind.value.isOnePaneNoSensorLandscapeBiometric() ->
                        PromptPosition.Bottom
                        promptKind.isOnePaneNoSensorLandscapeBiometric() -> PromptPosition.Bottom
                    rotation == DisplayRotation.ROTATION_90 -> PromptPosition.Right
                    rotation == DisplayRotation.ROTATION_270 -> PromptPosition.Left
                    rotation == DisplayRotation.ROTATION_180 && modalities.hasUdfps ->
@@ -308,6 +310,10 @@ constructor(
        context.resources.getDimensionPixelSize(
            R.dimen.biometric_prompt_two_pane_udfps_horizontal_guideline_padding
        )
    private val udfpsHorizontalShorterGuidelinePadding =
        context.resources.getDimensionPixelSize(
            R.dimen.biometric_prompt_two_pane_udfps_shorter_horizontal_guideline_padding
        )
    private val mediumTopGuidelinePadding =
        context.resources.getDimensionPixelSize(
            R.dimen.biometric_prompt_one_pane_medium_top_guideline_padding
@@ -449,47 +455,6 @@ constructor(
            }
        }

    /**
     * Rect for positioning prompt guidelines (left, top, right, unused)
     *
     * Negative values are used to signify that guideline measuring should be flipped, measuring
     * from opposite side of the screen
     */
    val guidelineBounds: Flow<Rect> =
        combine(iconPosition, promptKind, size, position, modalities) {
                _,
                promptKind,
                size,
                position,
                modalities ->
                when (position) {
                    PromptPosition.Bottom ->
                        if (promptKind.isOnePaneNoSensorLandscapeBiometric()) {
                            Rect(0, 0, 0, 0)
                        } else {
                            Rect(0, mediumTopGuidelinePadding, 0, 0)
                        }
                    PromptPosition.Right ->
                        if (size.isSmall) {
                            Rect(-smallHorizontalGuidelinePadding, 0, 0, 0)
                        } else if (modalities.hasUdfps) {
                            Rect(udfpsHorizontalGuidelinePadding, 0, 0, 0)
                        } else {
                            Rect(-mediumHorizontalGuidelinePadding, 0, 0, 0)
                        }
                    PromptPosition.Left ->
                        if (size.isSmall) {
                            Rect(0, 0, -smallHorizontalGuidelinePadding, 0)
                        } else if (modalities.hasUdfps) {
                            Rect(0, 0, udfpsHorizontalGuidelinePadding, 0)
                        } else {
                            Rect(0, 0, -mediumHorizontalGuidelinePadding, 0)
                        }
                    PromptPosition.Top -> Rect()
                }
            }
            .distinctUntilChanged()

    /** Padding for prompt UI elements */
    val promptPadding: Flow<Rect> =
        combine(size, displayStateInteractor.currentRotation) { size, rotation ->
@@ -556,6 +521,81 @@ constructor(
            if (contentView == null) description else ""
        }

    private val hasOnlyOneLineTitle: Flow<Boolean> =
        combine(title, subtitle, contentView, description) {
            title,
            subtitle,
            contentView,
            description ->
            if (subtitle.isNotEmpty() || contentView != null || description.isNotEmpty()) {
                false
            } else {
                val maxWidth =
                    context.resources.getDimensionPixelSize(
                        R.dimen.biometric_prompt_two_pane_udfps_shorter_content_width
                    )
                val attributes =
                    context.obtainStyledAttributes(
                        R.style.TextAppearance_AuthCredential_Title,
                        intArrayOf(android.R.attr.textSize)
                    )
                val paint = TextPaint()
                paint.textSize = attributes.getDimensionPixelSize(0, 0).toFloat()
                val textWidth = paint.measureText(title)
                attributes.recycle()
                textWidth / maxWidth <= 1
            }
        }

    /**
     * Rect for positioning prompt guidelines (left, top, right, unused)
     *
     * Negative values are used to signify that guideline measuring should be flipped, measuring
     * from opposite side of the screen
     */
    val guidelineBounds: Flow<Rect> =
        combine(iconPosition, promptKind, size, position, modalities, hasOnlyOneLineTitle) {
                _,
                promptKind,
                size,
                position,
                modalities,
                hasOnlyOneLineTitle ->
                var left = 0
                var top = 0
                var right = 0
                when (position) {
                    PromptPosition.Bottom -> {
                        val noSensorLandscape = promptKind.isOnePaneNoSensorLandscapeBiometric()
                        top = if (noSensorLandscape) 0 else mediumTopGuidelinePadding
                    }
                    PromptPosition.Right ->
                        left = getHorizontalPadding(size, modalities, hasOnlyOneLineTitle)
                    PromptPosition.Left ->
                        right = getHorizontalPadding(size, modalities, hasOnlyOneLineTitle)
                    PromptPosition.Top -> {}
                }
                Rect(left, top, right, 0)
            }
            .distinctUntilChanged()

    private fun getHorizontalPadding(
        size: PromptSize,
        modalities: BiometricModalities,
        hasOnlyOneLineTitle: Boolean
    ) =
        if (size.isSmall) {
            -smallHorizontalGuidelinePadding
        } else if (modalities.hasUdfps) {
            if (hasOnlyOneLineTitle) {
                -udfpsHorizontalShorterGuidelinePadding
            } else {
                udfpsHorizontalGuidelinePadding
            }
        } else {
            -mediumHorizontalGuidelinePadding
        }

    /** If the indicator (help, error) message should be shown. */
    val isIndicatorMessageVisible: Flow<Boolean> =
        combine(
+164 −4
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.content.pm.PackageManager.NameNotFoundException
import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.Point
import android.graphics.Rect
import android.graphics.drawable.BitmapDrawable
import android.hardware.biometrics.BiometricFingerprintConstants
import android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT
@@ -87,9 +88,6 @@ import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameter
import platform.test.runner.parameterized.Parameters
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -97,6 +95,8 @@ import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.junit.MockitoJUnit
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters

private const val USER_ID = 4
private const val REQUEST_ID = 4L
@@ -135,6 +135,27 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
    private val defaultLogoDescription = "Test Android App"
    private val logoDescriptionFromApp = "Test Cake App"
    private val packageNameForLogoWithOverrides = "should.use.overridden.logo"
    /** Prompt panel size padding */
    private val smallHorizontalGuidelinePadding =
        context.resources.getDimensionPixelSize(
            R.dimen.biometric_prompt_land_small_horizontal_guideline_padding
        )
    private val udfpsHorizontalGuidelinePadding =
        context.resources.getDimensionPixelSize(
            R.dimen.biometric_prompt_two_pane_udfps_horizontal_guideline_padding
        )
    private val udfpsHorizontalShorterGuidelinePadding =
        context.resources.getDimensionPixelSize(
            R.dimen.biometric_prompt_two_pane_udfps_shorter_horizontal_guideline_padding
        )
    private val mediumTopGuidelinePadding =
        context.resources.getDimensionPixelSize(
            R.dimen.biometric_prompt_one_pane_medium_top_guideline_padding
        )
    private val mediumHorizontalGuidelinePadding =
        context.resources.getDimensionPixelSize(
            R.dimen.biometric_prompt_two_pane_medium_horizontal_guideline_padding
        )

    private lateinit var fingerprintRepository: FakeFingerprintPropertyRepository
    private lateinit var promptRepository: FakePromptRepository
@@ -1369,6 +1390,142 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
            assertThat(logoDescription).isEqualTo(logoDescriptionFromApp)
        }

    @Test
    @EnableFlags(FLAG_CONSTRAINT_BP)
    fun position_bottom_rotation0() = runGenericTest {
        displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
        val position by collectLastValue(viewModel.position)
        assertThat(position).isEqualTo(PromptPosition.Bottom)
    } // TODO(b/335278136): Add test for no sensor landscape

    @Test
    @EnableFlags(FLAG_CONSTRAINT_BP)
    fun position_bottom_forceLarge() = runGenericTest {
        displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
        viewModel.onSwitchToCredential()
        val position by collectLastValue(viewModel.position)
        assertThat(position).isEqualTo(PromptPosition.Bottom)
    }

    @Test
    @EnableFlags(FLAG_CONSTRAINT_BP)
    fun position_bottom_largeScreen() = runGenericTest {
        displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
        displayStateRepository.setIsLargeScreen(true)
        val position by collectLastValue(viewModel.position)
        assertThat(position).isEqualTo(PromptPosition.Bottom)
    }

    @Test
    @EnableFlags(FLAG_CONSTRAINT_BP)
    fun position_right_rotation90() = runGenericTest {
        displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
        val position by collectLastValue(viewModel.position)
        assertThat(position).isEqualTo(PromptPosition.Right)
    }

    @Test
    @EnableFlags(FLAG_CONSTRAINT_BP)
    fun position_left_rotation270() = runGenericTest {
        displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
        val position by collectLastValue(viewModel.position)
        assertThat(position).isEqualTo(PromptPosition.Left)
    }

    @Test
    @EnableFlags(FLAG_CONSTRAINT_BP)
    fun position_top_rotation180() = runGenericTest {
        displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
        val position by collectLastValue(viewModel.position)
        if (testCase.modalities.hasUdfps) {
            assertThat(position).isEqualTo(PromptPosition.Top)
        } else {
            assertThat(position).isEqualTo(PromptPosition.Bottom)
        }
    }

    @Test
    @EnableFlags(FLAG_CONSTRAINT_BP)
    fun guideline_bottom() = runGenericTest {
        displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
        val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
        assertThat(guidelineBounds).isEqualTo(Rect(0, mediumTopGuidelinePadding, 0, 0))
    } // TODO(b/335278136): Add test for no sensor landscape

    @Test
    @EnableFlags(FLAG_CONSTRAINT_BP)
    fun guideline_right() = runGenericTest {
        displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)

        val isSmall = testCase.shouldStartAsImplicitFlow
        val guidelineBounds by collectLastValue(viewModel.guidelineBounds)

        if (isSmall) {
            assertThat(guidelineBounds).isEqualTo(Rect(-smallHorizontalGuidelinePadding, 0, 0, 0))
        } else if (testCase.modalities.hasUdfps) {
            assertThat(guidelineBounds).isEqualTo(Rect(udfpsHorizontalGuidelinePadding, 0, 0, 0))
        } else {
            assertThat(guidelineBounds).isEqualTo(Rect(-mediumHorizontalGuidelinePadding, 0, 0, 0))
        }
    }

    @Test
    @EnableFlags(FLAG_CONSTRAINT_BP)
    fun guideline_right_onlyShortTitle() =
        runGenericTest(subtitle = "") {
            displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)

            val isSmall = testCase.shouldStartAsImplicitFlow
            val guidelineBounds by collectLastValue(viewModel.guidelineBounds)

            if (!isSmall && testCase.modalities.hasUdfps) {
                assertThat(guidelineBounds)
                    .isEqualTo(Rect(-udfpsHorizontalShorterGuidelinePadding, 0, 0, 0))
            }
        }

    @Test
    @EnableFlags(FLAG_CONSTRAINT_BP)
    fun guideline_left() = runGenericTest {
        displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)

        val isSmall = testCase.shouldStartAsImplicitFlow
        val guidelineBounds by collectLastValue(viewModel.guidelineBounds)

        if (isSmall) {
            assertThat(guidelineBounds).isEqualTo(Rect(0, 0, -smallHorizontalGuidelinePadding, 0))
        } else if (testCase.modalities.hasUdfps) {
            assertThat(guidelineBounds).isEqualTo(Rect(0, 0, udfpsHorizontalGuidelinePadding, 0))
        } else {
            assertThat(guidelineBounds).isEqualTo(Rect(0, 0, -mediumHorizontalGuidelinePadding, 0))
        }
    }

    @Test
    @EnableFlags(FLAG_CONSTRAINT_BP)
    fun guideline_left_onlyShortTitle() =
        runGenericTest(subtitle = "") {
            displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)

            val isSmall = testCase.shouldStartAsImplicitFlow
            val guidelineBounds by collectLastValue(viewModel.guidelineBounds)

            if (!isSmall && testCase.modalities.hasUdfps) {
                assertThat(guidelineBounds)
                    .isEqualTo(Rect(0, 0, -udfpsHorizontalShorterGuidelinePadding, 0))
            }
        }

    @Test
    @EnableFlags(FLAG_CONSTRAINT_BP)
    fun guideline_top() = runGenericTest {
        displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
        val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
        if (testCase.modalities.hasUdfps) {
            assertThat(guidelineBounds).isEqualTo(Rect(0, 0, 0, 0))
        }
    }

    @Test
    fun iconViewLoaded() = runGenericTest {
        val isIconViewLoaded by collectLastValue(viewModel.isIconViewLoaded)
@@ -1399,6 +1556,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
    private fun runGenericTest(
        doNotStart: Boolean = false,
        allowCredentialFallback: Boolean = false,
        subtitle: String? = "s",
        description: String? = null,
        contentView: PromptContentView? = null,
        logoRes: Int = -1,
@@ -1437,6 +1595,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
            allowCredentialFallback = allowCredentialFallback,
            fingerprint = testCase.fingerprint,
            face = testCase.face,
            subtitleFromApp = subtitle,
            descriptionFromApp = description,
            contentViewFromApp = contentView,
            logoResFromApp = logoRes,
@@ -1625,6 +1784,7 @@ private fun PromptSelectorInteractor.initializePrompt(
    face: FaceSensorPropertiesInternal? = null,
    requireConfirmation: Boolean = false,
    allowCredentialFallback: Boolean = false,
    subtitleFromApp: String? = "s",
    descriptionFromApp: String? = null,
    contentViewFromApp: PromptContentView? = null,
    logoResFromApp: Int = -1,
@@ -1636,7 +1796,7 @@ private fun PromptSelectorInteractor.initializePrompt(
        PromptInfo().apply {
            logoDescription = logoDescriptionFromApp
            title = "t"
            subtitle = "s"
            subtitle = subtitleFromApp
            description = descriptionFromApp
            contentView = contentViewFromApp
            authenticators = listOf(face, fingerprint).extractAuthenticatorTypes()