Merge "Added success lotties to BP for fp" into tm-qpr-dev

This commit is contained in:
Joshua Mccloskey
2022-08-25 15:55:59 +00:00
committed by Android (Google) Code Review
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_ERROR -> true
STATE_AUTHENTICATING_ANIMATING_IN, STATE_AUTHENTICATING_ANIMATING_IN,
STATE_AUTHENTICATING -> oldState == STATE_ERROR || oldState == STATE_HELP STATE_AUTHENTICATING -> oldState == STATE_ERROR || oldState == STATE_HELP
STATE_AUTHENTICATED -> false STATE_AUTHENTICATED -> true
else -> false else -> false
} }
@@ -114,7 +114,13 @@ open class AuthBiometricFingerprintIconController(
R.raw.fingerprint_dialogue_fingerprint_to_error_lottie 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 else -> return null
} }
return if (id != null) return id else 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 override fun getStateForAfterError() = STATE_AUTHENTICATING

View File

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

View File

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

View File

@@ -42,20 +42,23 @@ import org.mockito.junit.MockitoJUnit
@SmallTest @SmallTest
class AuthBiometricFingerprintViewTest : SysuiTestCase() { class AuthBiometricFingerprintViewTest : SysuiTestCase() {
@JvmField @Rule @JvmField
@Rule
val mockitoRule = MockitoJUnit.rule() val mockitoRule = MockitoJUnit.rule()
@Mock @Mock
private lateinit var callback: AuthBiometricView.Callback private lateinit var callback: AuthBiometricView.Callback
@Mock @Mock
private lateinit var panelController: AuthPanelController private lateinit var panelController: AuthPanelController
private lateinit var biometricView: AuthBiometricView private lateinit var biometricView: AuthBiometricView
private fun createView(allowDeviceCredential: Boolean = false): AuthBiometricFingerprintView { 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 mContext, callback, panelController, allowDeviceCredential = allowDeviceCredential
) as AuthBiometricFingerprintView )
waitForIdleSync() waitForIdleSync()
return view return view
} }
@@ -73,6 +76,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
@Test @Test
fun testOnAuthenticationSucceeded_noConfirmationRequired_sendsActionAuthenticated() { fun testOnAuthenticationSucceeded_noConfirmationRequired_sendsActionAuthenticated() {
biometricView.onAuthenticationSucceeded(BiometricAuthenticator.TYPE_FINGERPRINT) biometricView.onAuthenticationSucceeded(BiometricAuthenticator.TYPE_FINGERPRINT)
TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync() waitForIdleSync()
assertThat(biometricView.isAuthenticated).isTrue() assertThat(biometricView.isAuthenticated).isTrue()
@@ -83,6 +87,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
fun testOnAuthenticationSucceeded_confirmationRequired_updatesDialogContents() { fun testOnAuthenticationSucceeded_confirmationRequired_updatesDialogContents() {
biometricView.setRequireConfirmation(true) biometricView.setRequireConfirmation(true)
biometricView.onAuthenticationSucceeded(BiometricAuthenticator.TYPE_FINGERPRINT) biometricView.onAuthenticationSucceeded(BiometricAuthenticator.TYPE_FINGERPRINT)
TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync() waitForIdleSync()
// TODO: this should be tested in the subclasses // TODO: this should be tested in the subclasses
@@ -104,6 +109,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
@Test @Test
fun testPositiveButton_sendsActionAuthenticated() { fun testPositiveButton_sendsActionAuthenticated() {
biometricView.mConfirmButton.performClick() biometricView.mConfirmButton.performClick()
TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync() waitForIdleSync()
verify(callback).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED) verify(callback).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED)
@@ -114,6 +120,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
fun testNegativeButton_beforeAuthentication_sendsActionButtonNegative() { fun testNegativeButton_beforeAuthentication_sendsActionButtonNegative() {
biometricView.onDialogAnimatedIn() biometricView.onDialogAnimatedIn()
biometricView.mNegativeButton.performClick() biometricView.mNegativeButton.performClick()
TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync() waitForIdleSync()
verify(callback).onAction(AuthBiometricView.Callback.ACTION_BUTTON_NEGATIVE) verify(callback).onAction(AuthBiometricView.Callback.ACTION_BUTTON_NEGATIVE)
@@ -126,6 +133,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
assertThat(biometricView.mNegativeButton.visibility).isEqualTo(View.GONE) assertThat(biometricView.mNegativeButton.visibility).isEqualTo(View.GONE)
biometricView.mCancelButton.performClick() biometricView.mCancelButton.performClick()
TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync() waitForIdleSync()
verify(callback).onAction(AuthBiometricView.Callback.ACTION_USER_CANCELED) verify(callback).onAction(AuthBiometricView.Callback.ACTION_USER_CANCELED)
@@ -134,6 +142,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
@Test @Test
fun testTryAgainButton_sendsActionTryAgain() { fun testTryAgainButton_sendsActionTryAgain() {
biometricView.mTryAgainButton.performClick() biometricView.mTryAgainButton.performClick()
TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync() waitForIdleSync()
verify(callback).onAction(AuthBiometricView.Callback.ACTION_BUTTON_TRY_AGAIN) verify(callback).onAction(AuthBiometricView.Callback.ACTION_BUTTON_TRY_AGAIN)
@@ -144,6 +153,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
@Test @Test
fun testOnErrorSendsActionError() { fun testOnErrorSendsActionError() {
biometricView.onError(BiometricAuthenticator.TYPE_FACE, "testError") biometricView.onError(BiometricAuthenticator.TYPE_FACE, "testError")
TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync() waitForIdleSync()
verify(callback).onAction(eq(AuthBiometricView.Callback.ACTION_ERROR)) verify(callback).onAction(eq(AuthBiometricView.Callback.ACTION_ERROR))
@@ -156,6 +166,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
val message = "another error" val message = "another error"
biometricView.onError(BiometricAuthenticator.TYPE_FACE, message) biometricView.onError(BiometricAuthenticator.TYPE_FACE, message)
TestableLooper.get(this).moveTimeForward(1000)
waitForIdleSync() waitForIdleSync()
assertThat(biometricView.isAuthenticating).isFalse() assertThat(biometricView.isAuthenticating).isFalse()
@@ -178,6 +189,7 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
val view = View(mContext) val view = View(mContext)
biometricView.setBackgroundView(view) biometricView.setBackgroundView(view)
biometricView.onAuthenticationSucceeded(BiometricAuthenticator.TYPE_FINGERPRINT) biometricView.onAuthenticationSucceeded(BiometricAuthenticator.TYPE_FINGERPRINT)
waitForIdleSync()
view.performClick() view.performClick()
verify(callback, never()) verify(callback, never())
@@ -225,14 +237,14 @@ class AuthBiometricFingerprintViewTest : SysuiTestCase() {
biometricView.onSaveState(state) biometricView.onSaveState(state)
assertThat(biometricView.mTryAgainButton.visibility).isEqualTo(View.GONE) assertThat(biometricView.mTryAgainButton.visibility).isEqualTo(View.GONE)
assertThat(state.getInt(AuthDialog.KEY_BIOMETRIC_TRY_AGAIN_VISIBILITY)) assertThat(state.getInt(AuthDialog.KEY_BIOMETRIC_TRY_AGAIN_VISIBILITY))
.isEqualTo(View.GONE) .isEqualTo(View.GONE)
assertThat(state.getInt(AuthDialog.KEY_BIOMETRIC_STATE)) assertThat(state.getInt(AuthDialog.KEY_BIOMETRIC_STATE))
.isEqualTo(AuthBiometricView.STATE_ERROR) .isEqualTo(AuthBiometricView.STATE_ERROR)
assertThat(biometricView.mIndicatorView.visibility).isEqualTo(View.VISIBLE) assertThat(biometricView.mIndicatorView.visibility).isEqualTo(View.VISIBLE)
assertThat(state.getBoolean(AuthDialog.KEY_BIOMETRIC_INDICATOR_ERROR_SHOWING)).isTrue() assertThat(state.getBoolean(AuthDialog.KEY_BIOMETRIC_INDICATOR_ERROR_SHOWING)).isTrue()
assertThat(biometricView.mIndicatorView.text).isEqualTo(failureMessage) assertThat(biometricView.mIndicatorView.text).isEqualTo(failureMessage)
assertThat(state.getString(AuthDialog.KEY_BIOMETRIC_INDICATOR_STRING)) assertThat(state.getString(AuthDialog.KEY_BIOMETRIC_INDICATOR_STRING))
.isEqualTo(failureMessage) .isEqualTo(failureMessage)
// TODO: Test dialog size. Should move requireConfirmation to buildBiometricPromptBundle // 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.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.util.Map; import java.util.Map;
/** /**
@@ -45,6 +46,9 @@ public class TestableLooper {
* catch crashes. * catch crashes.
*/ */
public static final boolean HOLD_MAIN_THREAD = false; 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 Looper mLooper;
private MessageQueue mQueue; private MessageQueue mQueue;
@@ -54,6 +58,19 @@ public class TestableLooper {
private Runnable mEmptyMessage; private Runnable mEmptyMessage;
private TestLooperManager mQueueWrapper; 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 { public TestableLooper(Looper l) throws Exception {
this(acquireLooperManager(l), l); this(acquireLooperManager(l), l);
} }
@@ -119,6 +136,33 @@ public class TestableLooper {
while (processQueuedMessages() != 0) ; 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() { private int processQueuedMessages() {
int count = 0; int count = 0;
mEmptyMessage = () -> { }; mEmptyMessage = () -> { };

View File

@@ -19,15 +19,19 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq; import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.InOrder;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
@@ -162,7 +166,7 @@ public class TestableLooperTest {
@Test @Test
public void testCorrectLooperExecution() throws Exception { public void testCorrectLooperExecution() throws Exception {
boolean[] hasRun = new boolean[] { false }; boolean[] hasRun = new boolean[]{false};
Runnable r = () -> { Runnable r = () -> {
assertEquals("Should run on main looper", Looper.getMainLooper(), Looper.myLooper()); assertEquals("Should run on main looper", Looper.getMainLooper(), Looper.myLooper());
hasRun[0] = true; hasRun[0] = true;
@@ -177,4 +181,63 @@ public class TestableLooperTest {
testableLooper.destroy(); 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);
}
} }