Loading packages/SystemUI/res/values/dimens.xml +2 −0 Original line number Diff line number Diff line Loading @@ -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> Loading packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt +2 −2 Original line number Diff line number Diff line Loading @@ -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) } Loading @@ -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) Loading packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt +84 −44 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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 -> Loading Loading @@ -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 Loading Loading @@ -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 -> Loading Loading @@ -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( Loading packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt +164 −4 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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) Loading Loading @@ -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, Loading Loading @@ -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, Loading Loading @@ -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, Loading @@ -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() Loading Loading
packages/SystemUI/res/values/dimens.xml +2 −0 Original line number Diff line number Diff line Loading @@ -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> Loading
packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt +2 −2 Original line number Diff line number Diff line Loading @@ -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) } Loading @@ -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) Loading
packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt +84 −44 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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 -> Loading Loading @@ -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 Loading Loading @@ -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 -> Loading Loading @@ -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( Loading
packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt +164 −4 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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) Loading Loading @@ -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, Loading Loading @@ -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, Loading Loading @@ -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, Loading @@ -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() Loading