Merge "Use X location for side fingerprint sensor devices."
This commit is contained in:
@@ -26,6 +26,7 @@ import android.graphics.Rect
|
||||
import android.hardware.biometrics.BiometricOverlayConstants
|
||||
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
|
||||
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
|
||||
import android.hardware.biometrics.SensorLocationInternal
|
||||
import android.hardware.display.DisplayManager
|
||||
import android.hardware.fingerprint.FingerprintManager
|
||||
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
|
||||
@@ -113,6 +114,7 @@ class SidefpsController @Inject constructor(
|
||||
orientationListener.enable()
|
||||
}
|
||||
}
|
||||
private var overlayOffsets: SensorLocationInternal = SensorLocationInternal.DEFAULT
|
||||
|
||||
private val overlayViewParams = WindowManager.LayoutParams(
|
||||
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
@@ -158,11 +160,19 @@ class SidefpsController @Inject constructor(
|
||||
val view = layoutInflater.inflate(R.layout.sidefps_view, null, false)
|
||||
val display = context.display!!
|
||||
|
||||
val offsets = sensorProps.getLocation(display.uniqueId).let { location ->
|
||||
if (location == null) {
|
||||
Log.w(TAG, "No location specified for display: ${display.uniqueId}")
|
||||
}
|
||||
location ?: sensorProps.location
|
||||
}
|
||||
overlayOffsets = offsets
|
||||
|
||||
val lottie = view.findViewById(R.id.sidefps_animation) as LottieAnimationView
|
||||
lottie.setAnimation(display.asSideFpsAnimation())
|
||||
view.rotation = display.asSideFpsAnimationRotation()
|
||||
view.rotation = display.asSideFpsAnimationRotation(offsets.isYAligned())
|
||||
|
||||
updateOverlayParams(display, lottie.composition?.bounds ?: Rect())
|
||||
lottie.setAnimation(display.asSideFpsAnimation(offsets.isYAligned()))
|
||||
lottie.addLottieOnCompositionLoadedListener {
|
||||
if (overlayView == view) {
|
||||
updateOverlayParams(display, it.bounds)
|
||||
@@ -179,24 +189,37 @@ class SidefpsController @Inject constructor(
|
||||
val size = windowManager.maximumWindowMetrics.bounds
|
||||
val displayWidth = if (isPortrait) size.width() else size.height()
|
||||
val displayHeight = if (isPortrait) size.height() else size.width()
|
||||
val offsets = sensorProps.getLocation(display.uniqueId).let { location ->
|
||||
if (location == null) {
|
||||
Log.w(TAG, "No location specified for display: ${display.uniqueId}")
|
||||
}
|
||||
location ?: sensorProps.location
|
||||
}
|
||||
|
||||
// ignore sensorLocationX and sensorRadius since it's assumed to be on the side
|
||||
// of the device and centered at sensorLocationY
|
||||
val (x, y) = when (display.rotation) {
|
||||
Surface.ROTATION_90 ->
|
||||
Pair(offsets.sensorLocationY, 0)
|
||||
Surface.ROTATION_270 ->
|
||||
Pair(displayHeight - offsets.sensorLocationY - bounds.width(), displayWidth)
|
||||
Surface.ROTATION_180 ->
|
||||
Pair(0, displayHeight - offsets.sensorLocationY - bounds.height())
|
||||
else ->
|
||||
Pair(displayWidth, offsets.sensorLocationY)
|
||||
// ignore sensorRadius since it's assumed that the sensor is on the side and centered at
|
||||
// either sensorLocationX or sensorLocationY (both should not be set)
|
||||
val (x, y) = if (overlayOffsets.isYAligned()) {
|
||||
when (display.rotation) {
|
||||
Surface.ROTATION_90 ->
|
||||
Pair(overlayOffsets.sensorLocationY, 0)
|
||||
Surface.ROTATION_270 ->
|
||||
Pair(
|
||||
displayHeight - overlayOffsets.sensorLocationY - bounds.width(),
|
||||
displayWidth + bounds.height()
|
||||
)
|
||||
Surface.ROTATION_180 ->
|
||||
Pair(0, displayHeight - overlayOffsets.sensorLocationY - bounds.height())
|
||||
else ->
|
||||
Pair(displayWidth, overlayOffsets.sensorLocationY)
|
||||
}
|
||||
} else {
|
||||
when (display.rotation) {
|
||||
Surface.ROTATION_90 ->
|
||||
Pair(0, displayWidth - overlayOffsets.sensorLocationX - bounds.height())
|
||||
Surface.ROTATION_270 ->
|
||||
Pair(displayWidth, overlayOffsets.sensorLocationX - bounds.height())
|
||||
Surface.ROTATION_180 ->
|
||||
Pair(
|
||||
displayWidth - overlayOffsets.sensorLocationX - bounds.width(),
|
||||
displayHeight
|
||||
)
|
||||
else ->
|
||||
Pair(overlayOffsets.sensorLocationX, 0)
|
||||
}
|
||||
}
|
||||
overlayViewParams.x = x
|
||||
overlayViewParams.y = y
|
||||
@@ -209,8 +232,10 @@ class SidefpsController @Inject constructor(
|
||||
|
||||
// hide after a few seconds if the sensor is oriented down and there are
|
||||
// large overlapping system bars
|
||||
if ((context.display?.rotation == Surface.ROTATION_270) &&
|
||||
windowManager.currentWindowMetrics.windowInsets.hasBigNavigationBar()) {
|
||||
val rotation = context.display?.rotation
|
||||
if (windowManager.currentWindowMetrics.windowInsets.hasBigNavigationBar() &&
|
||||
((rotation == Surface.ROTATION_270 && overlayOffsets.isYAligned()) ||
|
||||
(rotation == Surface.ROTATION_180 && !overlayOffsets.isYAligned()))) {
|
||||
overlayHideAnimator = view.animate()
|
||||
.alpha(0f)
|
||||
.setStartDelay(3_000)
|
||||
@@ -245,18 +270,21 @@ private fun ActivityTaskManager.topClass(): String =
|
||||
getTasks(1).firstOrNull()?.topActivity?.className ?: ""
|
||||
|
||||
@RawRes
|
||||
private fun Display.asSideFpsAnimation(): Int = when (rotation) {
|
||||
Surface.ROTATION_0 -> R.raw.sfps_pulse
|
||||
Surface.ROTATION_180 -> R.raw.sfps_pulse
|
||||
else -> R.raw.sfps_pulse_landscape
|
||||
private fun Display.asSideFpsAnimation(yAligned: Boolean): Int = when (rotation) {
|
||||
Surface.ROTATION_0 -> if (yAligned) R.raw.sfps_pulse else R.raw.sfps_pulse_landscape
|
||||
Surface.ROTATION_180 -> if (yAligned) R.raw.sfps_pulse else R.raw.sfps_pulse_landscape
|
||||
else -> if (yAligned) R.raw.sfps_pulse_landscape else R.raw.sfps_pulse
|
||||
}
|
||||
|
||||
private fun Display.asSideFpsAnimationRotation(): Float = when (rotation) {
|
||||
private fun Display.asSideFpsAnimationRotation(yAligned: Boolean): Float = when (rotation) {
|
||||
Surface.ROTATION_90 -> if (yAligned) 0f else 180f
|
||||
Surface.ROTATION_180 -> 180f
|
||||
Surface.ROTATION_270 -> 180f
|
||||
Surface.ROTATION_270 -> if (yAligned) 180f else 0f
|
||||
else -> 0f
|
||||
}
|
||||
|
||||
private fun SensorLocationInternal.isYAligned(): Boolean = sensorLocationY != 0
|
||||
|
||||
private fun Display.isPortrait(): Boolean =
|
||||
rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.graphics.Rect
|
||||
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
|
||||
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
|
||||
import android.hardware.biometrics.BiometricOverlayConstants.REASON_UNKNOWN
|
||||
import android.hardware.biometrics.SensorLocationInternal
|
||||
import android.hardware.biometrics.SensorProperties
|
||||
import android.hardware.display.DisplayManager
|
||||
import android.hardware.display.DisplayManagerGlobal
|
||||
@@ -52,6 +53,7 @@ import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.recents.OverviewProxyService
|
||||
import com.android.systemui.util.concurrency.FakeExecutor
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
@@ -75,6 +77,12 @@ import org.mockito.junit.MockitoJUnit
|
||||
private const val DISPLAY_ID = 2
|
||||
private const val SENSOR_ID = 1
|
||||
|
||||
private const val DISPLAY_SIZE_X = 800
|
||||
private const val DISPLAY_SIZE_Y = 900
|
||||
|
||||
private val X_LOCATION = SensorLocationInternal("", 540, 0, 20)
|
||||
private val Y_LOCATION = SensorLocationInternal("", 0, 1500, 22)
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@TestableLooper.RunWithLooper
|
||||
@@ -101,6 +109,8 @@ class SidefpsControllerTest : SysuiTestCase() {
|
||||
lateinit var handler: Handler
|
||||
@Captor
|
||||
lateinit var overlayCaptor: ArgumentCaptor<View>
|
||||
@Captor
|
||||
lateinit var overlayViewParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>
|
||||
|
||||
private val executor = FakeExecutor(FakeSystemClock())
|
||||
private lateinit var overlayController: ISidefpsController
|
||||
@@ -125,6 +135,17 @@ class SidefpsControllerTest : SysuiTestCase() {
|
||||
this
|
||||
}
|
||||
}
|
||||
`when`(windowManager.maximumWindowMetrics).thenReturn(
|
||||
WindowMetrics(Rect(0, 0, DISPLAY_SIZE_X, DISPLAY_SIZE_Y), WindowInsets.CONSUMED)
|
||||
)
|
||||
}
|
||||
|
||||
private fun testWithDisplay(
|
||||
initInfo: DisplayInfo.() -> Unit = {},
|
||||
locations: List<SensorLocationInternal> = listOf(X_LOCATION),
|
||||
windowInsets: WindowInsets = insetsForSmallNavbar(),
|
||||
block: () -> Unit
|
||||
) {
|
||||
`when`(fingerprintManager.sensorPropertiesInternal).thenReturn(
|
||||
listOf(
|
||||
FingerprintSensorPropertiesInternal(
|
||||
@@ -133,22 +154,21 @@ class SidefpsControllerTest : SysuiTestCase() {
|
||||
5 /* maxEnrollmentsPerUser */,
|
||||
listOf() /* componentInfo */,
|
||||
FingerprintSensorProperties.TYPE_POWER_BUTTON,
|
||||
true /* resetLockoutRequiresHardwareAuthToken */
|
||||
true /* resetLockoutRequiresHardwareAuthToken */,
|
||||
locations
|
||||
)
|
||||
)
|
||||
)
|
||||
`when`(windowManager.maximumWindowMetrics).thenReturn(
|
||||
WindowMetrics(Rect(0, 0, 800, 800), WindowInsets.CONSUMED)
|
||||
)
|
||||
}
|
||||
|
||||
private fun testWithDisplay(initInfo: DisplayInfo.() -> Unit = {}, block: () -> Unit) {
|
||||
val displayInfo = DisplayInfo()
|
||||
displayInfo.initInfo()
|
||||
val dmGlobal = mock(DisplayManagerGlobal::class.java)
|
||||
val display = Display(dmGlobal, DISPLAY_ID, displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS)
|
||||
`when`(dmGlobal.getDisplayInfo(eq(DISPLAY_ID))).thenReturn(displayInfo)
|
||||
`when`(windowManager.defaultDisplay).thenReturn(display)
|
||||
`when`(windowManager.currentWindowMetrics).thenReturn(
|
||||
WindowMetrics(Rect(0, 0, DISPLAY_SIZE_X, DISPLAY_SIZE_Y), windowInsets)
|
||||
)
|
||||
|
||||
sideFpsController = SidefpsController(
|
||||
context.createDisplayContext(display), layoutInflater, fingerprintManager,
|
||||
@@ -244,29 +264,72 @@ class SidefpsControllerTest : SysuiTestCase() {
|
||||
hidesWithTaskbar(visible = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun showsWithTaskbarOnY() = testWithDisplay(
|
||||
{ rotation = Surface.ROTATION_0 },
|
||||
locations = listOf(Y_LOCATION)
|
||||
) {
|
||||
hidesWithTaskbar(visible = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun showsWithTaskbar90() = testWithDisplay({ rotation = Surface.ROTATION_90 }) {
|
||||
hidesWithTaskbar(visible = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun showsWithTaskbar90OnY() = testWithDisplay(
|
||||
{ rotation = Surface.ROTATION_90 },
|
||||
locations = listOf(Y_LOCATION)
|
||||
) {
|
||||
hidesWithTaskbar(visible = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun showsWithTaskbar180() = testWithDisplay({ rotation = Surface.ROTATION_180 }) {
|
||||
hidesWithTaskbar(visible = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun showsWithTaskbarCollapsedDown() = testWithDisplay({ rotation = Surface.ROTATION_270 }) {
|
||||
`when`(windowManager.currentWindowMetrics).thenReturn(
|
||||
WindowMetrics(Rect(0, 0, 800, 800), insetsForSmallNavbar())
|
||||
)
|
||||
fun showsWithTaskbar270OnY() = testWithDisplay(
|
||||
{ rotation = Surface.ROTATION_270 },
|
||||
locations = listOf(Y_LOCATION)
|
||||
) {
|
||||
hidesWithTaskbar(visible = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun hidesWithTaskbarDown() = testWithDisplay({ rotation = Surface.ROTATION_270 }) {
|
||||
`when`(windowManager.currentWindowMetrics).thenReturn(
|
||||
WindowMetrics(Rect(0, 0, 800, 800), insetsForLargeNavbar())
|
||||
)
|
||||
fun showsWithTaskbarCollapsedDown() = testWithDisplay(
|
||||
{ rotation = Surface.ROTATION_270 },
|
||||
windowInsets = insetsForSmallNavbar()
|
||||
) {
|
||||
hidesWithTaskbar(visible = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun showsWithTaskbarCollapsedDownOnY() = testWithDisplay(
|
||||
{ rotation = Surface.ROTATION_180 },
|
||||
locations = listOf(Y_LOCATION),
|
||||
windowInsets = insetsForSmallNavbar()
|
||||
) {
|
||||
hidesWithTaskbar(visible = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun hidesWithTaskbarDown() = testWithDisplay(
|
||||
{ rotation = Surface.ROTATION_180 },
|
||||
locations = listOf(X_LOCATION),
|
||||
windowInsets = insetsForLargeNavbar()
|
||||
) {
|
||||
hidesWithTaskbar(visible = false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun hidesWithTaskbarDownOnY() = testWithDisplay(
|
||||
{ rotation = Surface.ROTATION_270 },
|
||||
locations = listOf(Y_LOCATION),
|
||||
windowInsets = insetsForLargeNavbar()
|
||||
) {
|
||||
hidesWithTaskbar(visible = false)
|
||||
}
|
||||
|
||||
@@ -281,6 +344,28 @@ class SidefpsControllerTest : SysuiTestCase() {
|
||||
verify(windowManager, never()).removeView(any())
|
||||
verify(sidefpsView).visibility = if (visible) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setsXAlign() = testWithDisplay {
|
||||
overlayController.show(SENSOR_ID, REASON_UNKNOWN)
|
||||
executor.runAllReady()
|
||||
|
||||
verify(windowManager).addView(any(), overlayViewParamsCaptor.capture())
|
||||
|
||||
assertThat(overlayViewParamsCaptor.value.x).isEqualTo(X_LOCATION.sensorLocationX)
|
||||
assertThat(overlayViewParamsCaptor.value.y).isEqualTo(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setYAlign() = testWithDisplay(locations = listOf(Y_LOCATION)) {
|
||||
overlayController.show(SENSOR_ID, REASON_UNKNOWN)
|
||||
executor.runAllReady()
|
||||
|
||||
verify(windowManager).addView(any(), overlayViewParamsCaptor.capture())
|
||||
|
||||
assertThat(overlayViewParamsCaptor.value.x).isEqualTo(DISPLAY_SIZE_X)
|
||||
assertThat(overlayViewParamsCaptor.value.y).isEqualTo(Y_LOCATION.sensorLocationY)
|
||||
}
|
||||
}
|
||||
|
||||
private fun insetsForSmallNavbar() = insetsWithBottom(60)
|
||||
|
||||
Reference in New Issue
Block a user