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

Commit 51056127 authored by axfordjc's avatar axfordjc
Browse files

Pattern/PIN view: only update layout if posture different

Layout of Pattern or Pin view was updated whenever posture changed, now it only updates if the change leads to a different state. Primarily done to reduce jank.

Bug: 290413053
Test: KeyguardPatternViewControllerTest, KeyguardPINViewControllerTest
Change-Id: Icb972debff74d3a8afd0944ee74a520ee4b951fc
parent 267f49b1
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -82,9 +82,11 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
    }

    void onDevicePostureChanged(@DevicePostureInt int posture) {
        if (mLastDevicePosture != posture) {
            mLastDevicePosture = posture;
            updateMargins();
        }
    }

    @Override
    protected void resetState() {
+4 −2
Original line number Diff line number Diff line
@@ -104,9 +104,11 @@ public class KeyguardPatternView extends KeyguardInputView
    }

    void onDevicePostureChanged(@DevicePostureInt int posture) {
        if (mLastDevicePosture != posture) {
            mLastDevicePosture = posture;
            updateMargins();
        }
    }

    private void updateMargins() {
        // Update the guideline based on the device posture...
+11 −6
Original line number Diff line number Diff line
@@ -119,7 +119,7 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {

        mKeyguardPatternViewController.onViewAttached()

        assertThat(getPatternTopGuideline()).isEqualTo(getExpectedTopGuideline())
        assertThat(getPatternTopGuideline()).isEqualTo(getHalfOpenedBouncerHeightRatio())
    }

    @Test
@@ -131,15 +131,20 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
        mKeyguardPatternViewController.onViewAttached()

        // Verify view begins in posture state DEVICE_POSTURE_HALF_OPENED
        assertThat(getPatternTopGuideline()).isEqualTo(getExpectedTopGuideline())
        assertThat(getPatternTopGuideline()).isEqualTo(getHalfOpenedBouncerHeightRatio())

        // Simulate posture change to state DEVICE_POSTURE_OPENED with callback
        verify(mPostureController).addCallback(postureCallbackCaptor.capture())
        val postureCallback: DevicePostureController.Callback = postureCallbackCaptor.value
        postureCallback.onPostureChanged(DEVICE_POSTURE_OPENED)

        // Verify view is now in posture state DEVICE_POSTURE_OPENED
        assertThat(getPatternTopGuideline()).isNotEqualTo(getExpectedTopGuideline())
        // Simulate posture change to same state with callback
        assertThat(getPatternTopGuideline()).isNotEqualTo(getHalfOpenedBouncerHeightRatio())

        postureCallback.onPostureChanged(DEVICE_POSTURE_OPENED)

        // Verify view is still in posture state DEVICE_POSTURE_OPENED
        assertThat(getPatternTopGuideline()).isNotEqualTo(getHalfOpenedBouncerHeightRatio())
    }

    private fun getPatternTopGuideline(): Float {
@@ -150,7 +155,7 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
        return cs.getConstraint(R.id.pattern_top_guideline).layout.guidePercent
    }

    private fun getExpectedTopGuideline(): Float {
    private fun getHalfOpenedBouncerHeightRatio(): Float {
        return mContext.resources.getFloat(R.dimen.half_opened_bouncer_height_ratio)
    }

+81 −33
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ package com.android.keyguard
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
@@ -32,6 +34,8 @@ import com.android.systemui.flags.Flags
import com.android.systemui.statusbar.policy.DevicePostureController
import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED
import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -51,7 +55,10 @@ import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class KeyguardPinViewControllerTest : SysuiTestCase() {
    @Mock private lateinit var keyguardPinView: KeyguardPINView

    private lateinit var objectKeyguardPINView: KeyguardPINView

    @Mock private lateinit var mockKeyguardPinView: KeyguardPINView

    @Mock private lateinit var keyguardMessageArea: BouncerKeyguardMessageArea

@@ -83,33 +90,38 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
    @Mock lateinit var deleteButton: NumPadButton
    @Mock lateinit var enterButton: View

    private lateinit var pinViewController: KeyguardPinViewController

    @Captor lateinit var postureCallbackCaptor: ArgumentCaptor<DevicePostureController.Callback>

    @Before
    fun setup() {
        MockitoAnnotations.initMocks(this)
        Mockito.`when`(keyguardPinView.requireViewById<View>(R.id.bouncer_message_area))
        Mockito.`when`(mockKeyguardPinView.requireViewById<View>(R.id.bouncer_message_area))
            .thenReturn(keyguardMessageArea)
        Mockito.`when`(
                keyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea::class.java))
            )
            .thenReturn(keyguardMessageAreaController)
        `when`(keyguardPinView.passwordTextViewId).thenReturn(R.id.pinEntry)
        `when`(keyguardPinView.findViewById<PasswordTextView>(R.id.pinEntry))
        `when`(mockKeyguardPinView.passwordTextViewId).thenReturn(R.id.pinEntry)
        `when`(mockKeyguardPinView.findViewById<PasswordTextView>(R.id.pinEntry))
            .thenReturn(passwordTextView)
        `when`(keyguardPinView.resources).thenReturn(context.resources)
        `when`(keyguardPinView.findViewById<NumPadButton>(R.id.delete_button))
        `when`(mockKeyguardPinView.resources).thenReturn(context.resources)
        `when`(mockKeyguardPinView.findViewById<NumPadButton>(R.id.delete_button))
            .thenReturn(deleteButton)
        `when`(keyguardPinView.findViewById<View>(R.id.key_enter)).thenReturn(enterButton)
        `when`(mockKeyguardPinView.findViewById<View>(R.id.key_enter)).thenReturn(enterButton)
        // For posture tests:
        `when`(keyguardPinView.buttons).thenReturn(arrayOf())
        `when`(mockKeyguardPinView.buttons).thenReturn(arrayOf())
        `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)

        pinViewController =
            KeyguardPinViewController(
                keyguardPinView,
        objectKeyguardPINView =
            View.inflate(mContext, R.layout.keyguard_pin_view, null)
                .findViewById(R.id.keyguard_pin_view) as KeyguardPINView
    }

    private fun constructPinViewController(
        mKeyguardPinView: KeyguardPINView
    ): KeyguardPinViewController {
        return KeyguardPinViewController(
            mKeyguardPinView,
            keyguardUpdateMonitor,
            securityMode,
            lockPatternUtils,
@@ -125,22 +137,26 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
    }

    @Test
    fun onViewAttached_deviceHalfFolded_propagatedToPinView() {
        `when`(postureController.devicePosture).thenReturn(DEVICE_POSTURE_HALF_OPENED)
    fun onViewAttached_deviceHalfFolded_propagatedToPatternView() {
        val pinViewController = constructPinViewController(objectKeyguardPINView)
        overrideResource(R.dimen.half_opened_bouncer_height_ratio, 0.5f)
        whenever(postureController.devicePosture).thenReturn(DEVICE_POSTURE_HALF_OPENED)

        pinViewController.onViewAttached()

        verify(keyguardPinView).onDevicePostureChanged(DEVICE_POSTURE_HALF_OPENED)
        assertThat(getPinTopGuideline()).isEqualTo(getHalfOpenedBouncerHeightRatio())
    }

    @Test
    fun onDevicePostureChanged_deviceHalfFolded_propagatedToPinView() {
        `when`(postureController.devicePosture).thenReturn(DEVICE_POSTURE_HALF_OPENED)
    fun onDevicePostureChanged_deviceOpened_propagatedToPatternView() {
        val pinViewController = constructPinViewController(objectKeyguardPINView)
        overrideResource(R.dimen.half_opened_bouncer_height_ratio, 0.5f)

        // Verify view begins in posture state DEVICE_POSTURE_HALF_OPENED
        whenever(postureController.devicePosture).thenReturn(DEVICE_POSTURE_HALF_OPENED)
        pinViewController.onViewAttached()

        verify(keyguardPinView).onDevicePostureChanged(DEVICE_POSTURE_HALF_OPENED)
        // Verify view begins in posture state DEVICE_POSTURE_HALF_OPENED
        assertThat(getPinTopGuideline()).isEqualTo(getHalfOpenedBouncerHeightRatio())

        // Simulate posture change to state DEVICE_POSTURE_OPENED with callback
        verify(postureController).addCallback(postureCallbackCaptor.capture())
@@ -148,31 +164,57 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
        postureCallback.onPostureChanged(DEVICE_POSTURE_OPENED)

        // Verify view is now in posture state DEVICE_POSTURE_OPENED
        verify(keyguardPinView).onDevicePostureChanged(DEVICE_POSTURE_OPENED)
        assertThat(getPinTopGuideline()).isNotEqualTo(getHalfOpenedBouncerHeightRatio())

        // Simulate posture change to same state with callback
        postureCallback.onPostureChanged(DEVICE_POSTURE_OPENED)

        // Verify view is still in posture state DEVICE_POSTURE_OPENED
        assertThat(getPinTopGuideline()).isNotEqualTo(getHalfOpenedBouncerHeightRatio())
    }

    private fun getPinTopGuideline(): Float {
        val cs = ConstraintSet()
        val container = objectKeyguardPINView.findViewById(R.id.pin_container) as ConstraintLayout
        cs.clone(container)
        return cs.getConstraint(R.id.pin_pad_top_guideline).layout.guidePercent
    }

    private fun getHalfOpenedBouncerHeightRatio(): Float {
        return mContext.resources.getFloat(R.dimen.half_opened_bouncer_height_ratio)
    }

    @Test
    fun startAppearAnimation() {
        val pinViewController = constructPinViewController(mockKeyguardPinView)

        pinViewController.startAppearAnimation()

        verify(keyguardMessageAreaController)
            .setMessage(context.resources.getString(R.string.keyguard_enter_your_pin), false)
    }

    @Test
    fun startAppearAnimation_withExistingMessage() {
        val pinViewController = constructPinViewController(mockKeyguardPinView)
        Mockito.`when`(keyguardMessageAreaController.message).thenReturn("Unlock to continue.")

        pinViewController.startAppearAnimation()

        verify(keyguardMessageAreaController, Mockito.never()).setMessage(anyString(), anyBoolean())
    }

    @Test
    fun startAppearAnimation_withAutoPinConfirmationFailedPasswordAttemptsLessThan5() {
        val pinViewController = constructPinViewController(mockKeyguardPinView)
        `when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true)
        `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)
        `when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true)
        `when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(3)
        `when`(passwordTextView.text).thenReturn("")

        pinViewController.startAppearAnimation()

        verify(deleteButton).visibility = View.INVISIBLE
        verify(enterButton).visibility = View.INVISIBLE
        verify(passwordTextView).setUsePinShapes(true)
@@ -181,12 +223,15 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {

    @Test
    fun startAppearAnimation_withAutoPinConfirmationFailedPasswordAttemptsMoreThan5() {
        val pinViewController = constructPinViewController(mockKeyguardPinView)
        `when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true)
        `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)
        `when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true)
        `when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(6)
        `when`(passwordTextView.text).thenReturn("")

        pinViewController.startAppearAnimation()

        verify(deleteButton).visibility = View.VISIBLE
        verify(enterButton).visibility = View.VISIBLE
        verify(passwordTextView).setUsePinShapes(true)
@@ -195,7 +240,10 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {

    @Test
    fun handleLockout_readsNumberOfErrorAttempts() {
        val pinViewController = constructPinViewController(mockKeyguardPinView)

        pinViewController.handleAttemptLockout(0)

        verify(lockPatternUtils).getCurrentFailedPasswordAttempts(anyInt())
    }
}