Adds assets, removes SFPS indicator, updates BP controller for rear display/tent mode

Adds new assets for BiometricPrompt in rear display/tent mode, and
updates AuthBiometricFingerprintIconController accordingly to use new
assets and hide the SFPS indicator. Also ensures the icon is updated on
all configuration changes.

Test: atest SideFpsControllerTest
Test: atest AuthBiometricFingerprintViewTest
Test: (manual) adb shell cmd device_state state 3 to turn on rear display mode, then verify correct asset across all configuration changes
Fixes: 265198124
Change-Id: Ib0fad9aeddb484df7c62f7a78f477a541dedb872
This commit is contained in:
Grace Cheng
2023-02-14 19:51:53 +00:00
parent fe18a7bb78
commit 566ca7306a
9 changed files with 315 additions and 22 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -231,21 +231,29 @@ open class AuthBiometricFingerprintIconController(
if (isReverseDefaultRotation) (rotation + 1) % 4 else rotation
@RawRes
private fun getSideFpsAnimationForTransition(rotation: Int): Int = when (rotation) {
Surface.ROTATION_90 -> if (isDeviceFolded) {
R.raw.biometricprompt_folded_base_topleft
} else {
R.raw.biometricprompt_portrait_base_topleft
}
Surface.ROTATION_270 -> if (isDeviceFolded) {
R.raw.biometricprompt_folded_base_bottomright
} else {
R.raw.biometricprompt_portrait_base_bottomright
}
else -> if (isDeviceFolded) {
R.raw.biometricprompt_folded_base_default
} else {
R.raw.biometricprompt_landscape_base
private fun getSideFpsAnimationForTransition(rotation: Int): Int {
when (rotation) {
Surface.ROTATION_90 -> if (context.isInRearDisplayMode()) {
return R.raw.biometricprompt_rear_portrait_reverse_base
} else if (isDeviceFolded) {
return R.raw.biometricprompt_folded_base_topleft
} else {
return R.raw.biometricprompt_portrait_base_topleft
}
Surface.ROTATION_270 -> if (context.isInRearDisplayMode()) {
return R.raw.biometricprompt_rear_portrait_base
} else if (isDeviceFolded) {
return R.raw.biometricprompt_folded_base_bottomright
} else {
return R.raw.biometricprompt_portrait_base_bottomright
}
else -> if (context.isInRearDisplayMode()) {
return R.raw.biometricprompt_rear_landscape_base
} else if (isDeviceFolded) {
return R.raw.biometricprompt_folded_base_default
} else {
return R.raw.biometricprompt_landscape_base
}
}
}

View File

@@ -658,6 +658,7 @@ public abstract class AuthBiometricView extends LinearLayout {
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mIconController.onConfigurationChanged(newConfig);
updateState(mSavedState.getInt(AuthDialog.KEY_BIOMETRIC_STATE));
}
@Override

View File

@@ -172,12 +172,16 @@ constructor(
override fun show(
sensorId: Int,
@BiometricOverlayConstants.ShowReason reason: Int
) =
if (reason.isReasonToAutoShow(activityTaskManager)) {
) {
if (
reason.isReasonToAutoShow(activityTaskManager) &&
!context.isInRearDisplayMode()
) {
show(SideFpsUiRequestSource.AUTO_SHOW, reason)
} else {
hide(SideFpsUiRequestSource.AUTO_SHOW)
}
}
override fun hide(sensorId: Int) = hide(SideFpsUiRequestSource.AUTO_SHOW)
}

View File

@@ -36,6 +36,7 @@ import android.view.WindowManager
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityManager
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.R
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
@@ -117,4 +118,7 @@ object Utils {
@Retention(RetentionPolicy.SOURCE)
@IntDef(CREDENTIAL_PIN, CREDENTIAL_PATTERN, CREDENTIAL_PASSWORD)
internal annotation class CredentialType
}
}
fun Context.isInRearDisplayMode(): Boolean = resources.getIntArray(
com.android.internal.R.array.config_rearDisplayDeviceStates).isNotEmpty()

View File

@@ -15,27 +15,49 @@
*/
package com.android.systemui.biometrics
import android.content.Context
import android.hardware.biometrics.BiometricAuthenticator
import android.hardware.biometrics.SensorLocationInternal
import android.hardware.biometrics.SensorProperties
import android.hardware.display.DisplayManagerGlobal
import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.FingerprintSensorProperties
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.os.Bundle
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import android.view.Display
import android.view.DisplayAdjustments
import android.view.DisplayInfo
import android.view.Surface
import android.view.View
import android.view.ViewGroup
import androidx.test.filters.SmallTest
import com.airbnb.lottie.LottieAnimationView
import com.android.systemui.R
import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATING_ANIMATING_IN
import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestableContext
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenEver
private const val DISPLAY_ID = 2
private const val SENSOR_ID = 1
@RunWith(AndroidTestingRunner::class)
@RunWithLooper(setAsMainLooper = true)
@@ -49,10 +71,23 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
@Mock
private lateinit var callback: AuthBiometricView.Callback
@Mock
private lateinit var fingerprintManager: FingerprintManager
@Mock
private lateinit var iconView: LottieAnimationView
@Mock
private lateinit var iconViewOverlay: LottieAnimationView
@Mock
private lateinit var iconLayoutParamSize: Pair<Int, Int>
@Mock
private lateinit var panelController: AuthPanelController
private lateinit var biometricView: AuthBiometricView
private lateinit var iconController: AuthBiometricFingerprintIconController
private fun createView(allowDeviceCredential: Boolean = false): AuthBiometricFingerprintView {
val view: AuthBiometricFingerprintView =
@@ -277,5 +312,186 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
verify(callback).onAction(AuthBiometricView.Callback.ACTION_USE_DEVICE_CREDENTIAL)
}
private fun testWithSfpsDisplay(
isReverseDefaultRotation: Boolean = false,
inRearDisplayMode: Boolean = false,
isFolded: Boolean = false,
initInfo: DisplayInfo.() -> Unit = {},
block: () -> Unit
) {
val displayInfo = DisplayInfo()
displayInfo.initInfo()
val dmGlobal = mock(DisplayManagerGlobal::class.java)
val display = Display(dmGlobal, DISPLAY_ID, displayInfo,
DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS)
whenEver(dmGlobal.getDisplayInfo(eq(DISPLAY_ID))).thenReturn(displayInfo)
val iconControllerContext = context.createDisplayContext(display) as SysuiTestableContext
iconControllerContext.orCreateTestableResources.addOverride(
com.android.internal.R.bool.config_reverseDefaultRotation,
isReverseDefaultRotation
)
val rearDisplayDeviceStates = if (inRearDisplayMode) intArrayOf(3) else intArrayOf()
iconControllerContext.orCreateTestableResources.addOverride(
com.android.internal.R.array.config_rearDisplayDeviceStates,
rearDisplayDeviceStates
)
val layoutParams = mock(ViewGroup.LayoutParams::class.java)
whenEver(iconView.layoutParams).thenReturn(layoutParams)
whenEver(iconViewOverlay.layoutParams).thenReturn(layoutParams)
var locations = listOf(SensorLocationInternal("", 2500, 0, 0))
whenEver(fingerprintManager.sensorPropertiesInternal)
.thenReturn(
listOf(
FingerprintSensorPropertiesInternal(
SENSOR_ID,
SensorProperties.STRENGTH_STRONG,
5 /* maxEnrollmentsPerUser */,
listOf() /* componentInfo */,
FingerprintSensorProperties.TYPE_POWER_BUTTON,
true /* halControlsIllumination */,
true /* resetLockoutRequiresHardwareAuthToken */,
locations
)
)
)
iconControllerContext.addMockSystemService(Context.FINGERPRINT_SERVICE, fingerprintManager)
iconController = AuthBiometricFingerprintIconController(
iconControllerContext,
iconView,
iconViewOverlay
)
iconController.onFoldUpdated(isFolded)
biometricView.mIconController = iconController
block()
}
@Test
fun sfpsRearDisplay_showsCorrectAnimationAssetsAcrossRotations() {
testWithSfpsDisplay(
isReverseDefaultRotation = false,
inRearDisplayMode = true,
isFolded = false,
{ rotation = Surface.ROTATION_0 }
) { biometricView.updateState(STATE_AUTHENTICATING_ANIMATING_IN) }
testWithSfpsDisplay(
isReverseDefaultRotation = false,
inRearDisplayMode = true,
isFolded = false,
{ rotation = Surface.ROTATION_90 }
) { biometricView.updateState(STATE_AUTHENTICATING_ANIMATING_IN) }
testWithSfpsDisplay(
isReverseDefaultRotation = false,
inRearDisplayMode = true,
isFolded = false,
{ rotation = Surface.ROTATION_180 }
) { biometricView.updateState(STATE_AUTHENTICATING_ANIMATING_IN) }
testWithSfpsDisplay(
isReverseDefaultRotation = false,
inRearDisplayMode = true,
isFolded = false,
{ rotation = Surface.ROTATION_270 }
) { biometricView.updateState(STATE_AUTHENTICATING_ANIMATING_IN) }
val expectedLottieAssetOrder: List<Int> = listOf(
R.raw.biometricprompt_rear_landscape_base,
R.raw.biometricprompt_rear_portrait_reverse_base,
R.raw.biometricprompt_rear_landscape_base,
R.raw.biometricprompt_rear_portrait_base,
)
val lottieAssetCaptor: ArgumentCaptor<Int> = ArgumentCaptor.forClass(Int::class.java)
verify(iconView, times(4)).setAnimation(lottieAssetCaptor.capture())
val observedLottieAssetOrder: List<Int> = lottieAssetCaptor.getAllValues()
assertThat(observedLottieAssetOrder).containsExactlyElementsIn(expectedLottieAssetOrder)
.inOrder()
}
@Test
fun sfpsDefaultDisplayFolded_showsAnimationsAssetsCorrectlyAcrossRotations() {
testWithSfpsDisplay(
isReverseDefaultRotation = false,
inRearDisplayMode = false,
isFolded = true,
{ rotation = Surface.ROTATION_0 }
) { biometricView.updateState(STATE_AUTHENTICATING_ANIMATING_IN) }
testWithSfpsDisplay(
isReverseDefaultRotation = false,
inRearDisplayMode = false,
isFolded = true,
{ rotation = Surface.ROTATION_90 }
) { biometricView.updateState(STATE_AUTHENTICATING_ANIMATING_IN); }
testWithSfpsDisplay(
isReverseDefaultRotation = false,
inRearDisplayMode = false,
isFolded = true,
{ rotation = Surface.ROTATION_180 }
) { biometricView.updateState(STATE_AUTHENTICATING_ANIMATING_IN); }
testWithSfpsDisplay(
isReverseDefaultRotation = false,
inRearDisplayMode = false,
isFolded = true,
{ rotation = Surface.ROTATION_270 }
) { biometricView.updateState(STATE_AUTHENTICATING_ANIMATING_IN); }
val expectedLottieAssetOrder: List<Int> = listOf(
R.raw.biometricprompt_folded_base_default,
R.raw.biometricprompt_folded_base_topleft,
R.raw.biometricprompt_folded_base_default,
R.raw.biometricprompt_folded_base_bottomright,
)
val lottieAssetCaptor: ArgumentCaptor<Int> = ArgumentCaptor.forClass(Int::class.java)
verify(iconView, times(4)).setAnimation(lottieAssetCaptor.capture())
val observedLottieAssetOrder: List<Int> = lottieAssetCaptor.getAllValues()
assertThat(observedLottieAssetOrder).containsExactlyElementsIn(expectedLottieAssetOrder)
.inOrder()
}
@Test
fun sfpsDefaultDisplayUnfolded_showsAnimationsAssetsCorrectlyAcrossRotations() {
testWithSfpsDisplay(
isReverseDefaultRotation = false,
inRearDisplayMode = false,
isFolded = false,
{ rotation = Surface.ROTATION_0 }
) { biometricView.updateState(STATE_AUTHENTICATING_ANIMATING_IN) }
testWithSfpsDisplay(
isReverseDefaultRotation = false,
inRearDisplayMode = false,
isFolded = false,
{ rotation = Surface.ROTATION_90 }
) { biometricView.updateState(STATE_AUTHENTICATING_ANIMATING_IN) }
testWithSfpsDisplay(
isReverseDefaultRotation = false,
inRearDisplayMode = false,
isFolded = false,
{ rotation = Surface.ROTATION_180 }
) { biometricView.updateState(STATE_AUTHENTICATING_ANIMATING_IN) }
testWithSfpsDisplay(
isReverseDefaultRotation = false,
inRearDisplayMode = false,
isFolded = false,
{ rotation = Surface.ROTATION_270 }
) { biometricView.updateState(STATE_AUTHENTICATING_ANIMATING_IN) }
val expectedLottieAssetOrder: List<Int> = listOf(
R.raw.biometricprompt_landscape_base,
R.raw.biometricprompt_portrait_base_topleft,
R.raw.biometricprompt_landscape_base,
R.raw.biometricprompt_portrait_base_bottomright,
)
val lottieAssetCaptor: ArgumentCaptor<Int> = ArgumentCaptor.forClass(Int::class.java)
verify(iconView, times(4)).setAnimation(lottieAssetCaptor.capture())
val observedLottieAssetOrder: List<Int> = lottieAssetCaptor.getAllValues()
assertThat(observedLottieAssetOrder).containsExactlyElementsIn(expectedLottieAssetOrder)
.inOrder()
}
override fun waitForIdleSync() = TestableLooper.get(this).processAllMessages()
}

View File

@@ -169,6 +169,7 @@ class SideFpsControllerTest : SysuiTestCase() {
isReverseDefaultRotation: Boolean = false,
initInfo: DisplayInfo.() -> Unit = {},
windowInsets: WindowInsets = insetsForSmallNavbar(),
inRearDisplayMode: Boolean = false,
block: () -> Unit
) {
this.deviceConfig = deviceConfig
@@ -229,6 +230,12 @@ class SideFpsControllerTest : SysuiTestCase() {
isReverseDefaultRotation
)
val rearDisplayDeviceStates = if (inRearDisplayMode) intArrayOf(3) else intArrayOf()
sideFpsControllerContext.orCreateTestableResources.addOverride(
com.android.internal.R.array.config_rearDisplayDeviceStates,
rearDisplayDeviceStates
)
sideFpsController =
SideFpsController(
sideFpsControllerContext,
@@ -542,10 +549,62 @@ class SideFpsControllerTest : SysuiTestCase() {
{ rotation = Surface.ROTATION_270 }
) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
@Test
fun verifiesSfpsIndicatorNotAddedInRearDisplayMode_0() =
testWithDisplay(
deviceConfig = DeviceConfig.Y_ALIGNED,
isReverseDefaultRotation = false,
{ rotation = Surface.ROTATION_0 },
inRearDisplayMode = true,
) {
verifySfpsIndicator_notAdded_InRearDisplayMode()
}
@Test
fun verifiesSfpsIndicatorNotAddedInRearDisplayMode_90() =
testWithDisplay(
deviceConfig = DeviceConfig.Y_ALIGNED,
isReverseDefaultRotation = false,
{ rotation = Surface.ROTATION_90 },
inRearDisplayMode = true,
) {
verifySfpsIndicator_notAdded_InRearDisplayMode()
}
@Test
fun verifiesSfpsIndicatorNotAddedInRearDisplayMode_180() =
testWithDisplay(
deviceConfig = DeviceConfig.Y_ALIGNED,
isReverseDefaultRotation = false,
{ rotation = Surface.ROTATION_180 },
inRearDisplayMode = true,
) {
verifySfpsIndicator_notAdded_InRearDisplayMode()
}
@Test
fun verifiesSfpsIndicatorNotAddedInRearDisplayMode_270() =
testWithDisplay(
deviceConfig = DeviceConfig.Y_ALIGNED,
isReverseDefaultRotation = false,
{ rotation = Surface.ROTATION_270 },
inRearDisplayMode = true,
) {
verifySfpsIndicator_notAdded_InRearDisplayMode()
}
private fun verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible: Boolean) {
sideFpsController.overlayOffsets = sensorLocation
}
private fun verifySfpsIndicator_notAdded_InRearDisplayMode() {
sideFpsController.overlayOffsets = sensorLocation
overlayController.show(SENSOR_ID, REASON_UNKNOWN)
executor.runAllReady()
verify(windowManager, never()).addView(any(), any())
}
fun alternateBouncerVisibility_showAndHideSideFpsUI() = testWithDisplay {
// WHEN alternate bouncer is visible
keyguardBouncerRepository.setAlternateVisible(true)
@@ -582,7 +641,7 @@ class SideFpsControllerTest : SysuiTestCase() {
* in other rotations have been omitted.
*/
@Test
fun verifiesIndicatorPlacementForXAlignedSensor_0() {
fun verifiesIndicatorPlacementForXAlignedSensor_0() =
testWithDisplay(
deviceConfig = DeviceConfig.X_ALIGNED,
isReverseDefaultRotation = false,
@@ -599,7 +658,6 @@ class SideFpsControllerTest : SysuiTestCase() {
assertThat(overlayViewParamsCaptor.value.x).isEqualTo(sensorLocation.sensorLocationX)
assertThat(overlayViewParamsCaptor.value.y).isEqualTo(0)
}
}
/**
* {@link SideFpsController#updateOverlayParams} calculates indicator placement for ROTATION_270
@@ -608,7 +666,7 @@ class SideFpsControllerTest : SysuiTestCase() {
* correctly, tests for indicator placement in other rotations have been omitted.
*/
@Test
fun verifiesIndicatorPlacementForXAlignedSensor_InReverseDefaultRotation_270() {
fun verifiesIndicatorPlacementForXAlignedSensor_InReverseDefaultRotation_270() =
testWithDisplay(
deviceConfig = DeviceConfig.X_ALIGNED,
isReverseDefaultRotation = true,
@@ -625,7 +683,6 @@ class SideFpsControllerTest : SysuiTestCase() {
assertThat(overlayViewParamsCaptor.value.x).isEqualTo(sensorLocation.sensorLocationX)
assertThat(overlayViewParamsCaptor.value.y).isEqualTo(0)
}
}
/**
* {@link SideFpsController#updateOverlayParams} calculates indicator placement for ROTATION_0,