Added success lotties to BP for fp

Test: Manually verified animations worked as expected.
Fixes: 242783377
Change-Id: I95ea8c4d1ee1ecc1bd3c1ac3c0d33fe0e9803473
This commit is contained in:
Joshua McCloskey
2022-08-17 00:06:30 +00:00
committed by Joshua Mccloskey
parent bfe6b27698
commit 017715d094
9 changed files with 144 additions and 11 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -92,7 +92,7 @@ open class AuthBiometricFingerprintIconController(
STATE_ERROR -> true
STATE_AUTHENTICATING_ANIMATING_IN,
STATE_AUTHENTICATING -> oldState == STATE_ERROR || oldState == STATE_HELP
STATE_AUTHENTICATED -> false
STATE_AUTHENTICATED -> true
else -> false
}
@@ -114,7 +114,13 @@ open class AuthBiometricFingerprintIconController(
R.raw.fingerprint_dialogue_fingerprint_to_error_lottie
}
}
STATE_AUTHENTICATED -> R.raw.fingerprint_dialogue_fingerprint_to_error_lottie
STATE_AUTHENTICATED -> {
if (oldState == STATE_ERROR || oldState == STATE_HELP) {
R.raw.fingerprint_dialogue_error_to_success_lottie
} else {
R.raw.fingerprint_dialogue_fingerprint_to_success_lottie
}
}
else -> return null
}
return if (id != null) return id else null

View File

@@ -75,7 +75,7 @@ open class AuthBiometricFingerprintView(
}
}
override fun getDelayAfterAuthenticatedDurationMs() = 0
override fun getDelayAfterAuthenticatedDurationMs() = 500
override fun getStateForAfterError() = STATE_AUTHENTICATING

View File

@@ -468,6 +468,7 @@ public abstract class AuthBiometricView extends LinearLayout {
break;
case STATE_AUTHENTICATED:
removePendingAnimations();
if (mSize != AuthDialog.SIZE_SMALL) {
mConfirmButton.setVisibility(View.GONE);
mNegativeButton.setVisibility(View.GONE);

View File

@@ -41,11 +41,13 @@ import org.mockito.junit.MockitoJUnit
@SmallTest
class AuthBiometricFingerprintAndFaceViewTest : SysuiTestCase() {
@JvmField @Rule
@JvmField
@Rule
var mockitoRule = MockitoJUnit.rule()
@Mock
private lateinit var callback: AuthBiometricView.Callback
@Mock
private lateinit var panelController: AuthPanelController
@@ -67,6 +69,7 @@ class AuthBiometricFingerprintAndFaceViewTest : SysuiTestCase() {
fun fingerprintSuccessDoesNotRequireExplicitConfirmation() {
biometricView.onDialogAnimatedIn()
biometricView.onAuthenticationSucceeded(TYPE_FINGERPRINT)
TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync()
assertThat(biometricView.isAuthenticated).isTrue()
@@ -86,6 +89,7 @@ class AuthBiometricFingerprintAndFaceViewTest : SysuiTestCase() {
// icon acts as confirm button
biometricView.mIconView.performClick()
TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync()
assertThat(biometricView.isAuthenticated).isTrue()
@@ -102,6 +106,7 @@ class AuthBiometricFingerprintAndFaceViewTest : SysuiTestCase() {
verify(callback, never()).onAction(AuthBiometricView.Callback.ACTION_ERROR)
biometricView.onError(TYPE_FINGERPRINT, "that's a nope")
TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync()
verify(callback).onAction(AuthBiometricView.Callback.ACTION_ERROR)

View File

@@ -42,20 +42,23 @@ import org.mockito.junit.MockitoJUnit
@SmallTest
class AuthBiometricFingerprintViewTest : SysuiTestCase() {
@JvmField @Rule
@JvmField
@Rule
val mockitoRule = MockitoJUnit.rule()
@Mock
private lateinit var callback: AuthBiometricView.Callback
@Mock
private lateinit var panelController: AuthPanelController
private lateinit var biometricView: AuthBiometricView
private fun createView(allowDeviceCredential: Boolean = false): AuthBiometricFingerprintView {
val view = R.layout.auth_biometric_fingerprint_view.asTestAuthBiometricView(
val view: AuthBiometricFingerprintView =
R.layout.auth_biometric_fingerprint_view.asTestAuthBiometricView(
mContext, callback, panelController, allowDeviceCredential = allowDeviceCredential
) as AuthBiometricFingerprintView
)
waitForIdleSync()
return view
}
@@ -73,6 +76,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
@Test
fun testOnAuthenticationSucceeded_noConfirmationRequired_sendsActionAuthenticated() {
biometricView.onAuthenticationSucceeded(BiometricAuthenticator.TYPE_FINGERPRINT)
TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync()
assertThat(biometricView.isAuthenticated).isTrue()
@@ -83,6 +87,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
fun testOnAuthenticationSucceeded_confirmationRequired_updatesDialogContents() {
biometricView.setRequireConfirmation(true)
biometricView.onAuthenticationSucceeded(BiometricAuthenticator.TYPE_FINGERPRINT)
TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync()
// TODO: this should be tested in the subclasses
@@ -104,6 +109,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
@Test
fun testPositiveButton_sendsActionAuthenticated() {
biometricView.mConfirmButton.performClick()
TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync()
verify(callback).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED)
@@ -114,6 +120,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
fun testNegativeButton_beforeAuthentication_sendsActionButtonNegative() {
biometricView.onDialogAnimatedIn()
biometricView.mNegativeButton.performClick()
TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync()
verify(callback).onAction(AuthBiometricView.Callback.ACTION_BUTTON_NEGATIVE)
@@ -126,6 +133,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
assertThat(biometricView.mNegativeButton.visibility).isEqualTo(View.GONE)
biometricView.mCancelButton.performClick()
TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync()
verify(callback).onAction(AuthBiometricView.Callback.ACTION_USER_CANCELED)
@@ -134,6 +142,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
@Test
fun testTryAgainButton_sendsActionTryAgain() {
biometricView.mTryAgainButton.performClick()
TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync()
verify(callback).onAction(AuthBiometricView.Callback.ACTION_BUTTON_TRY_AGAIN)
@@ -144,6 +153,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
@Test
fun testOnErrorSendsActionError() {
biometricView.onError(BiometricAuthenticator.TYPE_FACE, "testError")
TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync()
verify(callback).onAction(eq(AuthBiometricView.Callback.ACTION_ERROR))
@@ -156,6 +166,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
val message = "another error"
biometricView.onError(BiometricAuthenticator.TYPE_FACE, message)
TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync()
assertThat(biometricView.isAuthenticating).isFalse()
@@ -178,6 +189,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
val view = View(mContext)
biometricView.setBackgroundView(view)
biometricView.onAuthenticationSucceeded(BiometricAuthenticator.TYPE_FINGERPRINT)
waitForIdleSync()
view.performClick()
verify(callback, never())
@@ -225,14 +237,14 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
biometricView.onSaveState(state)
assertThat(biometricView.mTryAgainButton.visibility).isEqualTo(View.GONE)
assertThat(state.getInt(AuthDialog.KEY_BIOMETRIC_TRY_AGAIN_VISIBILITY))
.isEqualTo(View.GONE)
.isEqualTo(View.GONE)
assertThat(state.getInt(AuthDialog.KEY_BIOMETRIC_STATE))
.isEqualTo(AuthBiometricView.STATE_ERROR)
.isEqualTo(AuthBiometricView.STATE_ERROR)
assertThat(biometricView.mIndicatorView.visibility).isEqualTo(View.VISIBLE)
assertThat(state.getBoolean(AuthDialog.KEY_BIOMETRIC_INDICATOR_ERROR_SHOWING)).isTrue()
assertThat(biometricView.mIndicatorView.text).isEqualTo(failureMessage)
assertThat(state.getString(AuthDialog.KEY_BIOMETRIC_INDICATOR_STRING))
.isEqualTo(failureMessage)
.isEqualTo(failureMessage)
// TODO: Test dialog size. Should move requireConfirmation to buildBiometricPromptBundle

View File

@@ -30,6 +30,7 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.util.Map;
/**
@@ -45,6 +46,9 @@ public class TestableLooper {
* catch crashes.
*/
public static final boolean HOLD_MAIN_THREAD = false;
private static final Field MESSAGE_QUEUE_MESSAGES_FIELD;
private static final Field MESSAGE_NEXT_FIELD;
private static final Field MESSAGE_WHEN_FIELD;
private Looper mLooper;
private MessageQueue mQueue;
@@ -54,6 +58,19 @@ public class TestableLooper {
private Runnable mEmptyMessage;
private TestLooperManager mQueueWrapper;
static {
try {
MESSAGE_QUEUE_MESSAGES_FIELD = MessageQueue.class.getDeclaredField("mMessages");
MESSAGE_QUEUE_MESSAGES_FIELD.setAccessible(true);
MESSAGE_NEXT_FIELD = Message.class.getDeclaredField("next");
MESSAGE_NEXT_FIELD.setAccessible(true);
MESSAGE_WHEN_FIELD = Message.class.getDeclaredField("when");
MESSAGE_WHEN_FIELD.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new RuntimeException("Failed to initialize TestableLooper", e);
}
}
public TestableLooper(Looper l) throws Exception {
this(acquireLooperManager(l), l);
}
@@ -119,6 +136,33 @@ public class TestableLooper {
while (processQueuedMessages() != 0) ;
}
public void moveTimeForward(long milliSeconds) {
try {
Message msg = getMessageLinkedList();
while (msg != null) {
long updatedWhen = msg.getWhen() - milliSeconds;
if (updatedWhen < 0) {
updatedWhen = 0;
}
MESSAGE_WHEN_FIELD.set(msg, updatedWhen);
msg = (Message) MESSAGE_NEXT_FIELD.get(msg);
}
} catch (IllegalAccessException e) {
throw new RuntimeException("Access failed in TestableLooper: set - Message.when", e);
}
}
private Message getMessageLinkedList() {
try {
MessageQueue queue = mLooper.getQueue();
return (Message) MESSAGE_QUEUE_MESSAGES_FIELD.get(queue);
} catch (IllegalAccessException e) {
throw new RuntimeException(
"Access failed in TestableLooper: get - MessageQueue.mMessages",
e);
}
}
private int processQueuedMessages() {
int count = 0;
mEmptyMessage = () -> { };

View File

@@ -19,15 +19,19 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import android.os.Handler;
import android.os.Looper;
@@ -162,7 +166,7 @@ public class TestableLooperTest {
@Test
public void testCorrectLooperExecution() throws Exception {
boolean[] hasRun = new boolean[] { false };
boolean[] hasRun = new boolean[]{false};
Runnable r = () -> {
assertEquals("Should run on main looper", Looper.getMainLooper(), Looper.myLooper());
hasRun[0] = true;
@@ -177,4 +181,63 @@ public class TestableLooperTest {
testableLooper.destroy();
}
}
@Test
public void testDelayedDispatchNoTimeMove() {
Handler handler = spy(new Handler(mTestableLooper.getLooper()));
InOrder inOrder = inOrder(handler);
final Message messageA = handler.obtainMessage(1);
final Message messageB = handler.obtainMessage(2);
handler.sendMessageDelayed(messageA, 0);
handler.sendMessageDelayed(messageB, 0);
mTestableLooper.processAllMessages();
inOrder.verify(handler).dispatchMessage(messageA);
inOrder.verify(handler).dispatchMessage(messageB);
}
@Test
public void testDelayedMessageDoesntSend() {
Handler handler = spy(new Handler(mTestableLooper.getLooper()));
InOrder inOrder = inOrder(handler);
final Message messageA = handler.obtainMessage(1);
final Message messageB = handler.obtainMessage(2);
final Message messageC = handler.obtainMessage(3);
handler.sendMessageDelayed(messageA, 0);
handler.sendMessageDelayed(messageB, 0);
handler.sendMessageDelayed(messageC, 500);
mTestableLooper.processAllMessages();
inOrder.verify(handler).dispatchMessage(messageA);
inOrder.verify(handler).dispatchMessage(messageB);
verify(handler, never()).dispatchMessage(messageC);
}
@Test
public void testMessageSendsAfterDelay() {
Handler handler = spy(new Handler(mTestableLooper.getLooper()));
InOrder inOrder = inOrder(handler);
final Message messageA = handler.obtainMessage(1);
final Message messageB = handler.obtainMessage(2);
final Message messageC = handler.obtainMessage(3);
handler.sendMessageDelayed(messageA, 0);
handler.sendMessageDelayed(messageB, 0);
handler.sendMessageDelayed(messageC, 500);
mTestableLooper.moveTimeForward(500);
mTestableLooper.processAllMessages();
inOrder.verify(handler).dispatchMessage(messageA);
inOrder.verify(handler).dispatchMessage(messageB);
inOrder.verify(handler).dispatchMessage(messageC);
}
}