Merge "Adds assets, removes SFPS indicator, updates BP controller for rear display/tent mode" into tm-qpr-dev

This commit is contained in:
Grace Cheng
2023-02-22 23:30:25 +00:00
committed by Android (Google) Code Review
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 if (isReverseDefaultRotation) (rotation + 1) % 4 else rotation
@RawRes @RawRes
private fun getSideFpsAnimationForTransition(rotation: Int): Int = when (rotation) { private fun getSideFpsAnimationForTransition(rotation: Int): Int {
Surface.ROTATION_90 -> if (isDeviceFolded) { when (rotation) {
R.raw.biometricprompt_folded_base_topleft Surface.ROTATION_90 -> if (context.isInRearDisplayMode()) {
} else { return R.raw.biometricprompt_rear_portrait_reverse_base
R.raw.biometricprompt_portrait_base_topleft } else if (isDeviceFolded) {
} return R.raw.biometricprompt_folded_base_topleft
Surface.ROTATION_270 -> if (isDeviceFolded) { } else {
R.raw.biometricprompt_folded_base_bottomright return R.raw.biometricprompt_portrait_base_topleft
} else { }
R.raw.biometricprompt_portrait_base_bottomright Surface.ROTATION_270 -> if (context.isInRearDisplayMode()) {
} return R.raw.biometricprompt_rear_portrait_base
else -> if (isDeviceFolded) { } else if (isDeviceFolded) {
R.raw.biometricprompt_folded_base_default return R.raw.biometricprompt_folded_base_bottomright
} else { } else {
R.raw.biometricprompt_landscape_base 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) { protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig); super.onConfigurationChanged(newConfig);
mIconController.onConfigurationChanged(newConfig); mIconController.onConfigurationChanged(newConfig);
updateState(mSavedState.getInt(AuthDialog.KEY_BIOMETRIC_STATE));
} }
@Override @Override

View File

@@ -172,12 +172,16 @@ constructor(
override fun show( override fun show(
sensorId: Int, sensorId: Int,
@BiometricOverlayConstants.ShowReason reason: Int @BiometricOverlayConstants.ShowReason reason: Int
) = ) {
if (reason.isReasonToAutoShow(activityTaskManager)) { if (
reason.isReasonToAutoShow(activityTaskManager) &&
!context.isInRearDisplayMode()
) {
show(SideFpsUiRequestSource.AUTO_SHOW, reason) show(SideFpsUiRequestSource.AUTO_SHOW, reason)
} else { } else {
hide(SideFpsUiRequestSource.AUTO_SHOW) hide(SideFpsUiRequestSource.AUTO_SHOW)
} }
}
override fun hide(sensorId: Int) = 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.AccessibilityEvent
import android.view.accessibility.AccessibilityManager import android.view.accessibility.AccessibilityManager
import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternUtils
import com.android.systemui.R
import java.lang.annotation.Retention import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy import java.lang.annotation.RetentionPolicy
@@ -118,3 +119,6 @@ object Utils {
@IntDef(CREDENTIAL_PIN, CREDENTIAL_PATTERN, CREDENTIAL_PASSWORD) @IntDef(CREDENTIAL_PIN, CREDENTIAL_PATTERN, CREDENTIAL_PASSWORD)
internal annotation class CredentialType 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 package com.android.systemui.biometrics
import android.content.Context
import android.hardware.biometrics.BiometricAuthenticator 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.os.Bundle
import android.testing.AndroidTestingRunner import android.testing.AndroidTestingRunner
import android.testing.TestableLooper import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper 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.View
import android.view.ViewGroup
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.airbnb.lottie.LottieAnimationView
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATING_ANIMATING_IN
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestableContext
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers import org.mockito.ArgumentMatchers
import org.mockito.ArgumentMatchers.eq import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.never import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit 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) @RunWith(AndroidTestingRunner::class)
@RunWithLooper(setAsMainLooper = true) @RunWithLooper(setAsMainLooper = true)
@@ -49,10 +71,23 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
@Mock @Mock
private lateinit var callback: AuthBiometricView.Callback 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 @Mock
private lateinit var panelController: AuthPanelController private lateinit var panelController: AuthPanelController
private lateinit var biometricView: AuthBiometricView private lateinit var biometricView: AuthBiometricView
private lateinit var iconController: AuthBiometricFingerprintIconController
private fun createView(allowDeviceCredential: Boolean = false): AuthBiometricFingerprintView { private fun createView(allowDeviceCredential: Boolean = false): AuthBiometricFingerprintView {
val view: AuthBiometricFingerprintView = val view: AuthBiometricFingerprintView =
@@ -277,5 +312,186 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
verify(callback).onAction(AuthBiometricView.Callback.ACTION_USE_DEVICE_CREDENTIAL) 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() override fun waitForIdleSync() = TestableLooper.get(this).processAllMessages()
} }

View File

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