Loading packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt +18 −5 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.systemui.bouncer.ui.composable import android.view.HapticFeedbackConstants import android.view.MotionEvent import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.AnimationSpec Loading Loading @@ -49,6 +50,7 @@ import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInteropFilter import androidx.compose.ui.platform.LocalView import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp Loading Loading @@ -110,6 +112,7 @@ fun PinPad( onClicked = viewModel::onPinButtonClicked, scaling = buttonScaleAnimatables[index]::value, isAnimationEnabled = isDigitButtonAnimationEnabled, onPointerDown = viewModel::onDigitButtonDown, ) } Loading @@ -133,6 +136,7 @@ fun PinPad( onClicked = viewModel::onPinButtonClicked, scaling = buttonScaleAnimatables[10]::value, isAnimationEnabled = isDigitButtonAnimationEnabled, onPointerDown = viewModel::onDigitButtonDown ) ActionButton( Loading @@ -155,6 +159,7 @@ private fun DigitButton( digit: Int, isInputEnabled: Boolean, onClicked: (Int) -> Unit, onPointerDown: () -> Unit, scaling: () -> Float, isAnimationEnabled: Boolean, ) { Loading @@ -164,6 +169,7 @@ private fun DigitButton( backgroundColor = MaterialTheme.colorScheme.surfaceVariant, foregroundColor = MaterialTheme.colorScheme.onSurfaceVariant, isAnimationEnabled = isAnimationEnabled, onPointerDown = onPointerDown, modifier = Modifier.graphicsLayer { val scale = if (isAnimationEnabled) scaling() else 1f Loading Loading @@ -235,6 +241,7 @@ private fun PinPadButton( isAnimationEnabled: Boolean, modifier: Modifier = Modifier, onLongPressed: (() -> Unit)? = null, onPointerDown: (() -> Unit)? = null, content: @Composable (contentColor: () -> Color) -> Unit, ) { val interactionSource = remember { MutableInteractionSource() } Loading Loading @@ -308,6 +315,12 @@ private fun PinPadButton( onClick = onClicked, onLongClick = onLongPressed ) .pointerInteropFilter { motionEvent -> if (motionEvent.action == MotionEvent.ACTION_DOWN) { onPointerDown?.let { it() } } false } }, ) { content(contentColor::value) Loading packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt +18 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.systemui.bouncer.ui.viewmodel import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.view.KeyEvent.KEYCODE_0 import android.view.KeyEvent.KEYCODE_4 import android.view.KeyEvent.KEYCODE_A Loading @@ -31,6 +33,7 @@ import com.android.systemui.authentication.data.repository.fakeAuthenticationRep import com.android.systemui.authentication.domain.interactor.authenticationInteractor import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.data.repository.fakeSimBouncerRepository import com.android.systemui.classifier.fakeFalsingCollector import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope import com.android.systemui.lifecycle.activateIn Loading @@ -41,6 +44,7 @@ import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlin.random.Random import kotlin.random.nextInt import kotlin.test.assertTrue import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.map Loading @@ -60,12 +64,13 @@ class PinBouncerViewModelTest : SysuiTestCase() { private val testScope = kosmos.testScope private val sceneInteractor by lazy { kosmos.sceneInteractor } private val authenticationInteractor by lazy { kosmos.authenticationInteractor } private val underTest = private val underTest by lazy { kosmos.pinBouncerViewModelFactory.create( isInputEnabled = MutableStateFlow(true), onIntentionalUserInput = {}, authenticationMethod = AuthenticationMethodModel.Pin, ) } @Before fun setUp() { Loading Loading @@ -475,6 +480,18 @@ class PinBouncerViewModelTest : SysuiTestCase() { assertThat(pin).containsExactly(*expectedPin) } @Test @EnableFlags(com.android.systemui.Flags.FLAG_COMPOSE_BOUNCER) @DisableFlags(com.android.systemui.Flags.FLAG_SCENE_CONTAINER) fun onDigitButtonDown_avoidGesture_invoked() = testScope.runTest { lockDeviceAndOpenPinBouncer() underTest.onDigitButtonDown() assertTrue(kosmos.fakeFalsingCollector.wasLastGestureAvoided()) } private fun TestScope.switchToScene(toScene: SceneKey) { val currentScene by collectLastValue(sceneInteractor.currentScene) val bouncerHidden = currentScene == Scenes.Bouncer && toScene != Scenes.Bouncer Loading packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt +10 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import com.android.keyguard.PinShapeAdapter import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags import com.android.systemui.res.R import dagger.assisted.Assisted import dagger.assisted.AssistedFactory Loading Loading @@ -265,6 +266,15 @@ constructor( } } /** Notifies that the user has pressed down on a digit button. */ fun onDigitButtonDown() { if (ComposeBouncerFlags.isOnlyComposeBouncerEnabled()) { // Current PIN bouncer informs FalsingInteractor#avoidGesture() upon every Pin button // touch. super.onDown() } } @AssistedFactory interface Factory { fun create( Loading packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java +11 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import javax.inject.Inject; public class FalsingCollectorFake implements FalsingCollector { public KeyEvent lastKeyEvent = null; public boolean avoidGestureInvoked = false; @Override public void init() { Loading Loading @@ -87,6 +88,16 @@ public class FalsingCollectorFake implements FalsingCollector { @Override public void avoidGesture() { avoidGestureInvoked = true; } /** * @return whether {@link #avoidGesture()} was invoked. */ public boolean wasLastGestureAvoided() { boolean wasLastGestureAvoided = avoidGestureInvoked; avoidGestureInvoked = false; return wasLastGestureAvoided; } @Override Loading packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingCollectorKosmos.kt +3 −1 Original line number Diff line number Diff line Loading @@ -18,4 +18,6 @@ package com.android.systemui.classifier import com.android.systemui.kosmos.Kosmos var Kosmos.falsingCollector by Kosmos.Fixture<FalsingCollector> { FalsingCollectorFake() } var Kosmos.fakeFalsingCollector by Kosmos.Fixture { FalsingCollectorFake() } var Kosmos.falsingCollector by Kosmos.Fixture<FalsingCollector> { fakeFalsingCollector } Loading
packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt +18 −5 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.systemui.bouncer.ui.composable import android.view.HapticFeedbackConstants import android.view.MotionEvent import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.AnimationSpec Loading Loading @@ -49,6 +50,7 @@ import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInteropFilter import androidx.compose.ui.platform.LocalView import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp Loading Loading @@ -110,6 +112,7 @@ fun PinPad( onClicked = viewModel::onPinButtonClicked, scaling = buttonScaleAnimatables[index]::value, isAnimationEnabled = isDigitButtonAnimationEnabled, onPointerDown = viewModel::onDigitButtonDown, ) } Loading @@ -133,6 +136,7 @@ fun PinPad( onClicked = viewModel::onPinButtonClicked, scaling = buttonScaleAnimatables[10]::value, isAnimationEnabled = isDigitButtonAnimationEnabled, onPointerDown = viewModel::onDigitButtonDown ) ActionButton( Loading @@ -155,6 +159,7 @@ private fun DigitButton( digit: Int, isInputEnabled: Boolean, onClicked: (Int) -> Unit, onPointerDown: () -> Unit, scaling: () -> Float, isAnimationEnabled: Boolean, ) { Loading @@ -164,6 +169,7 @@ private fun DigitButton( backgroundColor = MaterialTheme.colorScheme.surfaceVariant, foregroundColor = MaterialTheme.colorScheme.onSurfaceVariant, isAnimationEnabled = isAnimationEnabled, onPointerDown = onPointerDown, modifier = Modifier.graphicsLayer { val scale = if (isAnimationEnabled) scaling() else 1f Loading Loading @@ -235,6 +241,7 @@ private fun PinPadButton( isAnimationEnabled: Boolean, modifier: Modifier = Modifier, onLongPressed: (() -> Unit)? = null, onPointerDown: (() -> Unit)? = null, content: @Composable (contentColor: () -> Color) -> Unit, ) { val interactionSource = remember { MutableInteractionSource() } Loading Loading @@ -308,6 +315,12 @@ private fun PinPadButton( onClick = onClicked, onLongClick = onLongPressed ) .pointerInteropFilter { motionEvent -> if (motionEvent.action == MotionEvent.ACTION_DOWN) { onPointerDown?.let { it() } } false } }, ) { content(contentColor::value) Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt +18 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.systemui.bouncer.ui.viewmodel import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.view.KeyEvent.KEYCODE_0 import android.view.KeyEvent.KEYCODE_4 import android.view.KeyEvent.KEYCODE_A Loading @@ -31,6 +33,7 @@ import com.android.systemui.authentication.data.repository.fakeAuthenticationRep import com.android.systemui.authentication.domain.interactor.authenticationInteractor import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.data.repository.fakeSimBouncerRepository import com.android.systemui.classifier.fakeFalsingCollector import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope import com.android.systemui.lifecycle.activateIn Loading @@ -41,6 +44,7 @@ import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlin.random.Random import kotlin.random.nextInt import kotlin.test.assertTrue import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.map Loading @@ -60,12 +64,13 @@ class PinBouncerViewModelTest : SysuiTestCase() { private val testScope = kosmos.testScope private val sceneInteractor by lazy { kosmos.sceneInteractor } private val authenticationInteractor by lazy { kosmos.authenticationInteractor } private val underTest = private val underTest by lazy { kosmos.pinBouncerViewModelFactory.create( isInputEnabled = MutableStateFlow(true), onIntentionalUserInput = {}, authenticationMethod = AuthenticationMethodModel.Pin, ) } @Before fun setUp() { Loading Loading @@ -475,6 +480,18 @@ class PinBouncerViewModelTest : SysuiTestCase() { assertThat(pin).containsExactly(*expectedPin) } @Test @EnableFlags(com.android.systemui.Flags.FLAG_COMPOSE_BOUNCER) @DisableFlags(com.android.systemui.Flags.FLAG_SCENE_CONTAINER) fun onDigitButtonDown_avoidGesture_invoked() = testScope.runTest { lockDeviceAndOpenPinBouncer() underTest.onDigitButtonDown() assertTrue(kosmos.fakeFalsingCollector.wasLastGestureAvoided()) } private fun TestScope.switchToScene(toScene: SceneKey) { val currentScene by collectLastValue(sceneInteractor.currentScene) val bouncerHidden = currentScene == Scenes.Bouncer && toScene != Scenes.Bouncer Loading
packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt +10 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import com.android.keyguard.PinShapeAdapter import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags import com.android.systemui.res.R import dagger.assisted.Assisted import dagger.assisted.AssistedFactory Loading Loading @@ -265,6 +266,15 @@ constructor( } } /** Notifies that the user has pressed down on a digit button. */ fun onDigitButtonDown() { if (ComposeBouncerFlags.isOnlyComposeBouncerEnabled()) { // Current PIN bouncer informs FalsingInteractor#avoidGesture() upon every Pin button // touch. super.onDown() } } @AssistedFactory interface Factory { fun create( Loading
packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java +11 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import javax.inject.Inject; public class FalsingCollectorFake implements FalsingCollector { public KeyEvent lastKeyEvent = null; public boolean avoidGestureInvoked = false; @Override public void init() { Loading Loading @@ -87,6 +88,16 @@ public class FalsingCollectorFake implements FalsingCollector { @Override public void avoidGesture() { avoidGestureInvoked = true; } /** * @return whether {@link #avoidGesture()} was invoked. */ public boolean wasLastGestureAvoided() { boolean wasLastGestureAvoided = avoidGestureInvoked; avoidGestureInvoked = false; return wasLastGestureAvoided; } @Override Loading
packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingCollectorKosmos.kt +3 −1 Original line number Diff line number Diff line Loading @@ -18,4 +18,6 @@ package com.android.systemui.classifier import com.android.systemui.kosmos.Kosmos var Kosmos.falsingCollector by Kosmos.Fixture<FalsingCollector> { FalsingCollectorFake() } var Kosmos.fakeFalsingCollector by Kosmos.Fixture { FalsingCollectorFake() } var Kosmos.falsingCollector by Kosmos.Fixture<FalsingCollector> { fakeFalsingCollector }