Merge "Pattern/PIN view: only update layout if posture different" into udc-qpr-dev am: a9f420c212

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/24058364

Change-Id: I469b0eeb83612ed135766d2f2bd7a070dc9f9007
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Jonathon Axford
2023-07-18 08:58:40 +00:00
committed by Automerger Merge Worker
4 changed files with 102 additions and 45 deletions

View File

@@ -69,7 +69,7 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
(long) (125 * KeyguardPatternView.DISAPPEAR_MULTIPLIER_LOCKED), (long) (125 * KeyguardPatternView.DISAPPEAR_MULTIPLIER_LOCKED),
0.6f /* translationScale */, 0.6f /* translationScale */,
0.45f /* delayScale */, AnimationUtils.loadInterpolator( 0.45f /* delayScale */, AnimationUtils.loadInterpolator(
mContext, android.R.interpolator.fast_out_linear_in)); mContext, android.R.interpolator.fast_out_linear_in));
mDisappearYTranslation = getResources().getDimensionPixelSize( mDisappearYTranslation = getResources().getDimensionPixelSize(
R.dimen.disappear_y_translation); R.dimen.disappear_y_translation);
mYTrans = getResources().getDimensionPixelSize(R.dimen.pin_view_trans_y_entry); mYTrans = getResources().getDimensionPixelSize(R.dimen.pin_view_trans_y_entry);
@@ -82,8 +82,10 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
} }
void onDevicePostureChanged(@DevicePostureInt int posture) { void onDevicePostureChanged(@DevicePostureInt int posture) {
mLastDevicePosture = posture; if (mLastDevicePosture != posture) {
updateMargins(); mLastDevicePosture = posture;
updateMargins();
}
} }
@Override @Override

View File

@@ -104,8 +104,10 @@ public class KeyguardPatternView extends KeyguardInputView
} }
void onDevicePostureChanged(@DevicePostureInt int posture) { void onDevicePostureChanged(@DevicePostureInt int posture) {
mLastDevicePosture = posture; if (mLastDevicePosture != posture) {
updateMargins(); mLastDevicePosture = posture;
updateMargins();
}
} }
private void updateMargins() { private void updateMargins() {

View File

@@ -76,7 +76,7 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
private lateinit var mKeyguardMessageAreaController: private lateinit var mKeyguardMessageAreaController:
KeyguardMessageAreaController<BouncerKeyguardMessageArea> KeyguardMessageAreaController<BouncerKeyguardMessageArea>
@Mock private lateinit var mPostureController: DevicePostureController @Mock private lateinit var mPostureController: DevicePostureController
private lateinit var mKeyguardPatternViewController: KeyguardPatternViewController private lateinit var mKeyguardPatternViewController: KeyguardPatternViewController
private lateinit var fakeFeatureFlags: FakeFeatureFlags private lateinit var fakeFeatureFlags: FakeFeatureFlags
@@ -119,7 +119,7 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
mKeyguardPatternViewController.onViewAttached() mKeyguardPatternViewController.onViewAttached()
assertThat(getPatternTopGuideline()).isEqualTo(getExpectedTopGuideline()) assertThat(getPatternTopGuideline()).isEqualTo(getHalfOpenedBouncerHeightRatio())
} }
@Test @Test
@@ -131,15 +131,20 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
mKeyguardPatternViewController.onViewAttached() mKeyguardPatternViewController.onViewAttached()
// Verify view begins in posture state DEVICE_POSTURE_HALF_OPENED // 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 // Simulate posture change to state DEVICE_POSTURE_OPENED with callback
verify(mPostureController).addCallback(postureCallbackCaptor.capture()) verify(mPostureController).addCallback(postureCallbackCaptor.capture())
val postureCallback: DevicePostureController.Callback = postureCallbackCaptor.value val postureCallback: DevicePostureController.Callback = postureCallbackCaptor.value
postureCallback.onPostureChanged(DEVICE_POSTURE_OPENED) postureCallback.onPostureChanged(DEVICE_POSTURE_OPENED)
// Verify view is now in posture state DEVICE_POSTURE_OPENED // Simulate posture change to same state with callback
assertThat(getPatternTopGuideline()).isNotEqualTo(getExpectedTopGuideline()) 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 { private fun getPatternTopGuideline(): Float {
@@ -150,7 +155,7 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
return cs.getConstraint(R.id.pattern_top_guideline).layout.guidePercent 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) return mContext.resources.getFloat(R.dimen.half_opened_bouncer_height_ratio)
} }

View File

@@ -19,6 +19,8 @@ package com.android.keyguard
import android.testing.AndroidTestingRunner import android.testing.AndroidTestingRunner
import android.testing.TestableLooper import android.testing.TestableLooper
import android.view.View import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils 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
import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED 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.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.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@@ -51,7 +55,10 @@ import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class) @RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper @TestableLooper.RunWithLooper
class KeyguardPinViewControllerTest : SysuiTestCase() { 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 @Mock private lateinit var keyguardMessageArea: BouncerKeyguardMessageArea
@@ -83,64 +90,73 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
@Mock lateinit var deleteButton: NumPadButton @Mock lateinit var deleteButton: NumPadButton
@Mock lateinit var enterButton: View @Mock lateinit var enterButton: View
private lateinit var pinViewController: KeyguardPinViewController
@Captor lateinit var postureCallbackCaptor: ArgumentCaptor<DevicePostureController.Callback> @Captor lateinit var postureCallbackCaptor: ArgumentCaptor<DevicePostureController.Callback>
@Before @Before
fun setup() { fun setup() {
MockitoAnnotations.initMocks(this) 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) .thenReturn(keyguardMessageArea)
Mockito.`when`( Mockito.`when`(
keyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea::class.java)) keyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea::class.java))
) )
.thenReturn(keyguardMessageAreaController) .thenReturn(keyguardMessageAreaController)
`when`(keyguardPinView.passwordTextViewId).thenReturn(R.id.pinEntry) `when`(mockKeyguardPinView.passwordTextViewId).thenReturn(R.id.pinEntry)
`when`(keyguardPinView.findViewById<PasswordTextView>(R.id.pinEntry)) `when`(mockKeyguardPinView.findViewById<PasswordTextView>(R.id.pinEntry))
.thenReturn(passwordTextView) .thenReturn(passwordTextView)
`when`(keyguardPinView.resources).thenReturn(context.resources) `when`(mockKeyguardPinView.resources).thenReturn(context.resources)
`when`(keyguardPinView.findViewById<NumPadButton>(R.id.delete_button)) `when`(mockKeyguardPinView.findViewById<NumPadButton>(R.id.delete_button))
.thenReturn(deleteButton) .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: // For posture tests:
`when`(keyguardPinView.buttons).thenReturn(arrayOf()) `when`(mockKeyguardPinView.buttons).thenReturn(arrayOf())
`when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6) `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)
pinViewController = objectKeyguardPINView =
KeyguardPinViewController( View.inflate(mContext, R.layout.keyguard_pin_view, null)
keyguardPinView, .findViewById(R.id.keyguard_pin_view) as KeyguardPINView
keyguardUpdateMonitor, }
securityMode,
lockPatternUtils, private fun constructPinViewController(
mKeyguardSecurityCallback, mKeyguardPinView: KeyguardPINView
keyguardMessageAreaControllerFactory, ): KeyguardPinViewController {
mLatencyTracker, return KeyguardPinViewController(
liftToActivateListener, mKeyguardPinView,
mEmergencyButtonController, keyguardUpdateMonitor,
falsingCollector, securityMode,
postureController, lockPatternUtils,
featureFlags mKeyguardSecurityCallback,
) keyguardMessageAreaControllerFactory,
mLatencyTracker,
liftToActivateListener,
mEmergencyButtonController,
falsingCollector,
postureController,
featureFlags
)
} }
@Test @Test
fun onViewAttached_deviceHalfFolded_propagatedToPinView() { fun onViewAttached_deviceHalfFolded_propagatedToPatternView() {
`when`(postureController.devicePosture).thenReturn(DEVICE_POSTURE_HALF_OPENED) val pinViewController = constructPinViewController(objectKeyguardPINView)
overrideResource(R.dimen.half_opened_bouncer_height_ratio, 0.5f)
whenever(postureController.devicePosture).thenReturn(DEVICE_POSTURE_HALF_OPENED)
pinViewController.onViewAttached() pinViewController.onViewAttached()
verify(keyguardPinView).onDevicePostureChanged(DEVICE_POSTURE_HALF_OPENED) assertThat(getPinTopGuideline()).isEqualTo(getHalfOpenedBouncerHeightRatio())
} }
@Test @Test
fun onDevicePostureChanged_deviceHalfFolded_propagatedToPinView() { fun onDevicePostureChanged_deviceOpened_propagatedToPatternView() {
`when`(postureController.devicePosture).thenReturn(DEVICE_POSTURE_HALF_OPENED) 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 view begins in posture state DEVICE_POSTURE_HALF_OPENED // Verify view begins in posture state DEVICE_POSTURE_HALF_OPENED
pinViewController.onViewAttached() assertThat(getPinTopGuideline()).isEqualTo(getHalfOpenedBouncerHeightRatio())
verify(keyguardPinView).onDevicePostureChanged(DEVICE_POSTURE_HALF_OPENED)
// Simulate posture change to state DEVICE_POSTURE_OPENED with callback // Simulate posture change to state DEVICE_POSTURE_OPENED with callback
verify(postureController).addCallback(postureCallbackCaptor.capture()) verify(postureController).addCallback(postureCallbackCaptor.capture())
@@ -148,31 +164,57 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
postureCallback.onPostureChanged(DEVICE_POSTURE_OPENED) postureCallback.onPostureChanged(DEVICE_POSTURE_OPENED)
// Verify view is now in posture state 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 @Test
fun startAppearAnimation() { fun startAppearAnimation() {
val pinViewController = constructPinViewController(mockKeyguardPinView)
pinViewController.startAppearAnimation() pinViewController.startAppearAnimation()
verify(keyguardMessageAreaController) verify(keyguardMessageAreaController)
.setMessage(context.resources.getString(R.string.keyguard_enter_your_pin), false) .setMessage(context.resources.getString(R.string.keyguard_enter_your_pin), false)
} }
@Test @Test
fun startAppearAnimation_withExistingMessage() { fun startAppearAnimation_withExistingMessage() {
val pinViewController = constructPinViewController(mockKeyguardPinView)
Mockito.`when`(keyguardMessageAreaController.message).thenReturn("Unlock to continue.") Mockito.`when`(keyguardMessageAreaController.message).thenReturn("Unlock to continue.")
pinViewController.startAppearAnimation() pinViewController.startAppearAnimation()
verify(keyguardMessageAreaController, Mockito.never()).setMessage(anyString(), anyBoolean()) verify(keyguardMessageAreaController, Mockito.never()).setMessage(anyString(), anyBoolean())
} }
@Test @Test
fun startAppearAnimation_withAutoPinConfirmationFailedPasswordAttemptsLessThan5() { fun startAppearAnimation_withAutoPinConfirmationFailedPasswordAttemptsLessThan5() {
val pinViewController = constructPinViewController(mockKeyguardPinView)
`when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true) `when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true)
`when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)
`when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true) `when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true)
`when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(3) `when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(3)
`when`(passwordTextView.text).thenReturn("") `when`(passwordTextView.text).thenReturn("")
pinViewController.startAppearAnimation() pinViewController.startAppearAnimation()
verify(deleteButton).visibility = View.INVISIBLE verify(deleteButton).visibility = View.INVISIBLE
verify(enterButton).visibility = View.INVISIBLE verify(enterButton).visibility = View.INVISIBLE
verify(passwordTextView).setUsePinShapes(true) verify(passwordTextView).setUsePinShapes(true)
@@ -181,12 +223,15 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
@Test @Test
fun startAppearAnimation_withAutoPinConfirmationFailedPasswordAttemptsMoreThan5() { fun startAppearAnimation_withAutoPinConfirmationFailedPasswordAttemptsMoreThan5() {
val pinViewController = constructPinViewController(mockKeyguardPinView)
`when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true) `when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true)
`when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)
`when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true) `when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true)
`when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(6) `when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(6)
`when`(passwordTextView.text).thenReturn("") `when`(passwordTextView.text).thenReturn("")
pinViewController.startAppearAnimation() pinViewController.startAppearAnimation()
verify(deleteButton).visibility = View.VISIBLE verify(deleteButton).visibility = View.VISIBLE
verify(enterButton).visibility = View.VISIBLE verify(enterButton).visibility = View.VISIBLE
verify(passwordTextView).setUsePinShapes(true) verify(passwordTextView).setUsePinShapes(true)
@@ -195,7 +240,10 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
@Test @Test
fun handleLockout_readsNumberOfErrorAttempts() { fun handleLockout_readsNumberOfErrorAttempts() {
val pinViewController = constructPinViewController(mockKeyguardPinView)
pinViewController.handleAttemptLockout(0) pinViewController.handleAttemptLockout(0)
verify(lockPatternUtils).getCurrentFailedPasswordAttempts(anyInt()) verify(lockPatternUtils).getCurrentFailedPasswordAttempts(anyInt())
} }
} }