summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Kevin Chyn <kchyn@google.com> 2019-08-09 17:35:41 -0700
committer Kevin Chyn <kchyn@google.com> 2019-08-15 15:34:43 -0700
commit27fafb76551cfec876c01cfadfa7f9da6056312e (patch)
tree11f34ba66f6f8fd210e217de680e2c43e27aec1f
parent050315f61a27fc399e17a8396fa6b999ee98f211 (diff)
4/n: Make BiometricDialogView testable
Fixes: 138628043 Test: Manual BiometricPrompt test Test: atest BiometricDialogViewTest Change-Id: I105f44aad5ffe5f86a68ae688f7b56a5dd42c37d
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricDialogView.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/FaceDialogView.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/FingerprintDialogView.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/BiometricDialogViewTest.java204
4 files changed, 249 insertions, 22 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricDialogView.java
index 66718c157bed..2b4dde55ef72 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricDialogView.java
@@ -47,6 +47,7 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -82,7 +83,8 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet
protected static final int STATE_PENDING_CONFIRMATION = 3;
protected static final int STATE_AUTHENTICATED = 4;
- private final WakefulnessLifecycle mWakefulnessLifecycle;
+ @VisibleForTesting
+ final WakefulnessLifecycle mWakefulnessLifecycle;
private final AccessibilityManager mAccessibilityManager;
private final IBinder mWindowToken = new Binder();
private final Interpolator mLinearOutSlowIn;
@@ -96,14 +98,22 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet
protected final ViewGroup mLayout;
protected final LinearLayout mDialog;
- protected final TextView mTitleText;
- protected final TextView mSubtitleText;
- protected final TextView mDescriptionText;
- protected final ImageView mBiometricIcon;
- protected final TextView mErrorText;
- protected final Button mPositiveButton;
- protected final Button mNegativeButton;
- protected final Button mTryAgainButton;
+ @VisibleForTesting
+ final TextView mTitleText;
+ @VisibleForTesting
+ final TextView mSubtitleText;
+ @VisibleForTesting
+ final TextView mDescriptionText;
+ @VisibleForTesting
+ final ImageView mBiometricIcon;
+ @VisibleForTesting
+ final TextView mErrorText;
+ @VisibleForTesting
+ final Button mPositiveButton;
+ @VisibleForTesting
+ final Button mNegativeButton;
+ @VisibleForTesting
+ final Button mTryAgainButton;
protected final int mTextColor;
@@ -147,7 +157,8 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet
}
};
- private final WakefulnessLifecycle.Observer mWakefulnessObserver =
+ @VisibleForTesting
+ final WakefulnessLifecycle.Observer mWakefulnessObserver =
new WakefulnessLifecycle.Observer() {
@Override
public void onStartedGoingToSleep() {
@@ -213,11 +224,15 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet
}
public BiometricDialogView build(int type) {
+ return build(type, new Injector());
+ }
+
+ public BiometricDialogView build(int type, Injector injector) {
BiometricDialogView dialog;
if (type == TYPE_FINGERPRINT) {
- dialog = new FingerprintDialogView(mContext, mCallback);
+ dialog = new FingerprintDialogView(mContext, mCallback, injector);
} else if (type == TYPE_FACE) {
- dialog = new FaceDialogView(mContext, mCallback);
+ dialog = new FaceDialogView(mContext, mCallback, injector);
} else {
return null;
}
@@ -229,9 +244,15 @@ public abstract class BiometricDialogView extends LinearLayout implements Biomet
}
}
- protected BiometricDialogView(Context context, DialogViewCallback callback) {
+ public static class Injector {
+ public WakefulnessLifecycle getWakefulnessLifecycle() {
+ return Dependency.get(WakefulnessLifecycle.class);
+ }
+ }
+
+ protected BiometricDialogView(Context context, DialogViewCallback callback, Injector injector) {
super(context);
- mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
+ mWakefulnessLifecycle = injector.getWakefulnessLifecycle();
mCallback = callback;
mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/FaceDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/ui/FaceDialogView.java
index cfa17ee68922..bd8714812ab4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/FaceDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/FaceDialogView.java
@@ -33,6 +33,7 @@ import android.util.Log;
import android.view.View;
import android.view.ViewOutlineProvider;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
import com.android.systemui.biometrics.DialogViewCallback;
@@ -53,7 +54,8 @@ public class FaceDialogView extends BiometricDialogView {
private static final int TEXT_ANIMATE_DISTANCE = 32; // dp
private static final int SIZE_UNKNOWN = 0;
- private static final int SIZE_SMALL = 1;
+ @VisibleForTesting
+ static final int SIZE_SMALL = 1;
private static final int SIZE_GROWING = 2;
private static final int SIZE_BIG = 3;
@@ -153,13 +155,13 @@ public class FaceDialogView extends BiometricDialogView {
announceAccessibilityEvent();
};
- protected FaceDialogView(Context context,
- DialogViewCallback callback) {
- super(context, callback);
+ protected FaceDialogView(Context context, DialogViewCallback callback, Injector injector) {
+ super(context, callback, injector);
mIconController = new IconController();
}
- private void updateSize(int newSize) {
+ @VisibleForTesting
+ void updateSize(int newSize) {
final float padding = dpToPixels(IMPLICIT_Y_PADDING);
final float iconSmallPositionY = mDialog.getHeight() - mBiometricIcon.getHeight() - padding;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/FingerprintDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/ui/FingerprintDialogView.java
index 1c339ba6db54..e597080f89fd 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/FingerprintDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/FingerprintDialogView.java
@@ -33,9 +33,9 @@ public class FingerprintDialogView extends BiometricDialogView {
private static final String TAG = "FingerprintDialogView";
- protected FingerprintDialogView(Context context,
- DialogViewCallback callback) {
- super(context, callback);
+ protected FingerprintDialogView(Context context, DialogViewCallback callback,
+ Injector injector) {
+ super(context, callback, injector);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/BiometricDialogViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/BiometricDialogViewTest.java
new file mode 100644
index 000000000000..bbdd837bb446
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/BiometricDialogViewTest.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.ui;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotSame;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.spy;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.hardware.biometrics.BiometricPrompt;
+import android.os.Bundle;
+import android.os.UserManager;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.biometrics.DialogViewCallback;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+@SmallTest
+public class BiometricDialogViewTest extends SysuiTestCase {
+
+ FaceDialogView mFaceDialogView;
+
+ private static final String TITLE = "Title";
+ private static final String SUBTITLE = "Subtitle";
+ private static final String DESCRIPTION = "Description";
+ private static final String NEGATIVE_BUTTON = "Negative Button";
+
+ private static final String TEST_HELP = "Help";
+
+ TestableContext mTestableContext;
+ @Mock
+ private DialogViewCallback mCallback;
+ @Mock
+ private UserManager mUserManager;
+ @Mock
+ private DevicePolicyManager mDpm;
+
+ private static class Injector extends BiometricDialogView.Injector {
+ @Override
+ public WakefulnessLifecycle getWakefulnessLifecycle() {
+ final WakefulnessLifecycle lifecycle = new WakefulnessLifecycle();
+ lifecycle.dispatchFinishedWakingUp();
+ return lifecycle;
+ }
+ }
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mTestableContext = spy(mContext);
+ mTestableContext.addMockSystemService(UserManager.class, mUserManager);
+ mTestableContext.addMockSystemService(DevicePolicyManager.class, mDpm);
+ }
+
+ @Test
+ public void testContentStates_confirmationRequired_authenticated() {
+ mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback,
+ true /* requireConfirmation */);
+ mFaceDialogView.onAttachedToWindow();
+
+ // When starting authentication
+ assertEquals(View.VISIBLE, mFaceDialogView.mTitleText.getVisibility());
+ assertEquals(View.VISIBLE, mFaceDialogView.mSubtitleText.getVisibility());
+ assertEquals(View.VISIBLE, mFaceDialogView.mDescriptionText.getVisibility());
+ assertEquals(View.INVISIBLE, mFaceDialogView.mErrorText.getVisibility());
+ assertEquals(View.VISIBLE, mFaceDialogView.mPositiveButton.getVisibility());
+ assertEquals(View.VISIBLE, mFaceDialogView.mNegativeButton.getVisibility());
+ assertEquals(View.GONE, mFaceDialogView.mTryAgainButton.getVisibility());
+
+ // Contents are as expected
+ assertTrue(TITLE.contentEquals(mFaceDialogView.mTitleText.getText()));
+ assertTrue(SUBTITLE.contentEquals(mFaceDialogView.mSubtitleText.getText()));
+ assertTrue(DESCRIPTION.contentEquals(mFaceDialogView.mDescriptionText.getText()));
+ assertTrue(mFaceDialogView.mPositiveButton.getText().toString()
+ .contentEquals(mContext.getString(R.string.biometric_dialog_confirm)));
+ assertTrue(NEGATIVE_BUTTON.contentEquals(mFaceDialogView.mNegativeButton.getText()));
+ assertTrue(mFaceDialogView.mTryAgainButton.getText().toString()
+ .contentEquals(mContext.getString(R.string.biometric_dialog_try_again)));
+
+ // When help message is received
+ mFaceDialogView.onHelp(TEST_HELP);
+ assertEquals(mFaceDialogView.mErrorText.getVisibility(), View.VISIBLE);
+ assertTrue(TEST_HELP.contentEquals(mFaceDialogView.mErrorText.getText()));
+
+ // When authenticated, confirm button comes out
+ mFaceDialogView.onAuthenticationSucceeded();
+ assertEquals(View.VISIBLE, mFaceDialogView.mPositiveButton.getVisibility());
+ assertEquals(true, mFaceDialogView.mPositiveButton.isEnabled());
+ }
+
+ @Test
+ public void testContentStates_confirmationNotRequired_authenticated() {
+ mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback,
+ false /* requireConfirmation */);
+ mFaceDialogView.onAttachedToWindow();
+ mFaceDialogView.updateSize(FaceDialogView.SIZE_SMALL);
+
+ assertEquals(View.INVISIBLE, mFaceDialogView.mTitleText.getVisibility());
+ assertNotSame(View.VISIBLE, mFaceDialogView.mSubtitleText.getVisibility());
+ assertNotSame(View.VISIBLE, mFaceDialogView.mDescriptionText.getVisibility());
+ assertEquals(View.INVISIBLE, mFaceDialogView.mErrorText.getVisibility());
+ assertEquals(View.GONE, mFaceDialogView.mPositiveButton.getVisibility());
+ assertEquals(View.GONE, mFaceDialogView.mTryAgainButton.getVisibility());
+ assertEquals(View.GONE, mFaceDialogView.mTryAgainButton.getVisibility());
+ }
+
+ @Test
+ public void testContentStates_confirmationNotRequired_help() {
+ mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback,
+ false /* requireConfirmation */);
+ mFaceDialogView.onAttachedToWindow();
+
+ mFaceDialogView.onHelp(TEST_HELP);
+ assertEquals(mFaceDialogView.mErrorText.getVisibility(), View.VISIBLE);
+ assertTrue(TEST_HELP.contentEquals(mFaceDialogView.mErrorText.getText()));
+ }
+
+ @Test
+ public void testBack_sendsUserCanceled() {
+ // TODO: Need robolectric framework to wait for handler to complete
+ }
+
+ @Test
+ public void testScreenOff_sendsUserCanceled() {
+ // TODO: Need robolectric framework to wait for handler to complete
+ }
+
+ @Test
+ public void testRestoreState_contentStatesCorrect() {
+ mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback,
+ false /* requireConfirmation */);
+ mFaceDialogView.onAttachedToWindow();
+ mFaceDialogView.onAuthenticationFailed(TEST_HELP);
+
+ final Bundle bundle = new Bundle();
+ mFaceDialogView.onSaveState(bundle);
+
+ mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback,
+ false /* requireConfirmation */);
+ mFaceDialogView.restoreState(bundle);
+ mFaceDialogView.onAttachedToWindow();
+
+ assertEquals(View.VISIBLE, mFaceDialogView.mTryAgainButton.getVisibility());
+ }
+
+ private FaceDialogView buildFaceDialogView(Context context, DialogViewCallback callback,
+ boolean requireConfirmation) {
+ return (FaceDialogView) new BiometricDialogView.Builder(context)
+ .setCallback(callback)
+ .setBiometricPromptBundle(createTestDialogBundle())
+ .setRequireConfirmation(requireConfirmation)
+ .setUserId(0)
+ .setOpPackageName("test_package")
+ .build(BiometricDialogView.Builder.TYPE_FACE, new Injector());
+ }
+
+ private Bundle createTestDialogBundle() {
+ Bundle bundle = new Bundle();
+
+ bundle.putCharSequence(BiometricPrompt.KEY_TITLE, TITLE);
+ bundle.putCharSequence(BiometricPrompt.KEY_SUBTITLE, SUBTITLE);
+ bundle.putCharSequence(BiometricPrompt.KEY_DESCRIPTION, DESCRIPTION);
+ bundle.putCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT, NEGATIVE_BUTTON);
+
+ // RequireConfirmation is a hint to BiometricService. This can be forced to be required
+ // by user settings, and should be tested in BiometricService.
+ bundle.putBoolean(BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true);
+
+ return bundle;
+ }
+}