Loading packages/SystemUI/src/com/android/keyguard/LockIconView.java +6 −1 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.widget.FrameLayout; import android.widget.ImageView; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import com.android.settingslib.Utils; import com.android.systemui.Dumpable; Loading Loading @@ -79,7 +80,11 @@ public class LockIconView extends FrameLayout implements Dumpable { mLockIcon.setImageDrawable(drawable); } void setCenterLocation(@NonNull PointF center, int radius) { /** * Set the location of the lock icon. */ @VisibleForTesting public void setCenterLocation(@NonNull PointF center, int radius) { mLockIconCenter = center; mRadius = radius; Loading packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +13 −5 Original line number Diff line number Diff line Loading @@ -68,7 +68,8 @@ import javax.inject.Inject; /** * Controls when to show the LockIcon affordance (lock/unlocked icon or circle) on lock screen. * * This view will only be shown if the user has UDFPS or FaceAuth enrolled * For devices with UDFPS, the lock icon will show at the sensor location. Else, the lock * icon will show a set distance from the bottom of the device. */ @StatusBarComponent.StatusBarScope public class LockIconViewController extends ViewController<LockIconView> implements Dumpable { Loading Loading @@ -172,15 +173,14 @@ public class LockIconViewController extends ViewController<LockIconView> impleme @Override protected void onInit() { mAuthController.addCallback(mAuthControllerCallback); mUdfpsSupported = mAuthController.getUdfpsSensorLocation() != null; mView.setAccessibilityDelegate(mAccessibilityDelegate); } @Override protected void onViewAttached() { // we check this here instead of onInit since the FingerprintManager + FaceManager may not // have started up yet onInit mUdfpsSupported = mAuthController.getUdfpsSensorLocation() != null; updateConfiguration(); updateKeyguardShowing(); mUserUnlockedWithBiometric = false; Loading Loading @@ -584,4 +584,12 @@ public class LockIconViewController extends ViewController<LockIconView> impleme public void setAlpha(float alpha) { mView.setAlpha(alpha); } private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() { @Override public void onAllAuthenticatorsRegistered() { mUdfpsSupported = mAuthController.getUdfpsSensorLocation() != null; updateConfiguration(); } }; } packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +5 −1 Original line number Diff line number Diff line Loading @@ -786,7 +786,11 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, .build(sensorIds, credentialAllowed, mFpProps, mFaceProps); } interface Callback { /** * AuthController callback used to receive signal for when biometric authenticators are * registered. */ public interface Callback { /** * Called when authenticators are registered. If authenticators are already * registered before this call, this callback will never be triggered. Loading packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java 0 → 100644 +189 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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.keyguard; import static junit.framework.Assert.assertEquals; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; import android.graphics.PointF; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.os.Vibrator; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.DisplayMetrics; import android.view.View; import android.view.accessibility.AccessibilityManager; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardViewController; import com.android.keyguard.LockIconView; import com.android.keyguard.LockIconViewController; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.AuthRippleController; import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.DelayableExecutor; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.List; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper public class LockIconViewControllerTest extends SysuiTestCase { private @Mock LockIconView mLockIconView; private @Mock Context mContext; private @Mock Resources mResources; private @Mock DisplayMetrics mDisplayMetrics; private @Mock StatusBarStateController mStatusBarStateController; private @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor; private @Mock KeyguardViewController mKeyguardViewController; private @Mock KeyguardStateController mKeyguardStateController; private @Mock FalsingManager mFalsingManager; private @Mock AuthController mAuthController; private @Mock DumpManager mDumpManager; private @Mock AccessibilityManager mAccessibilityManager; private @Mock ConfigurationController mConfigurationController; private @Mock DelayableExecutor mDelayableExecutor; private @Mock Vibrator mVibrator; private @Mock AuthRippleController mAuthRippleController; private LockIconViewController mLockIconViewController; // Capture listeners so that they can be used to send events @Captor private ArgumentCaptor<View.OnAttachStateChangeListener> mAttachCaptor = ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); private View.OnAttachStateChangeListener mAttachListener; @Captor private ArgumentCaptor<AuthController.Callback> mAuthControllerCallbackCaptor; private AuthController.Callback mAuthControllerCallback; @Captor private ArgumentCaptor<PointF> mPointCaptor; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); when(mLockIconView.getResources()).thenReturn(mResources); when(mLockIconView.getContext()).thenReturn(mContext); when(mContext.getResources()).thenReturn(mResources); when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics); mLockIconViewController = new LockIconViewController( mLockIconView, mStatusBarStateController, mKeyguardUpdateMonitor, mKeyguardViewController, mKeyguardStateController, mFalsingManager, mAuthController, mDumpManager, mAccessibilityManager, mConfigurationController, mDelayableExecutor, mVibrator, mAuthRippleController ); } @Test public void testUpdateFingerprintLocationOnInit() { // GIVEN fp sensor location is available pre-init final PointF udfpsLocation = new PointF(50, 75); final int radius = 33; final FingerprintSensorPropertiesInternal fpProps = new FingerprintSensorPropertiesInternal( /* sensorId */ 0, /* strength */ 0, /* max enrollments per user */ 5, /* component info */ new ArrayList<>(), /* sensorType */ 3, /* resetLockoutRequiresHwToken */ false, (int) udfpsLocation.x, (int) udfpsLocation.y, radius); when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation); when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps)); // WHEN lock icon view controller is initialized and attached mLockIconViewController.init(); captureAttachListener(); mAttachListener.onViewAttachedToWindow(null); // THEN lock icon view location is updated with the same coordinates as fpProps verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(radius)); assertEquals(udfpsLocation, mPointCaptor.getValue()); } @Test public void testUpdateFingerprintLocationOnAuthenticatorsRegistered() { // GIVEN fp sensor location is not available pre-init when(mAuthController.getFingerprintSensorLocation()).thenReturn(null); when(mAuthController.getUdfpsProps()).thenReturn(null); mLockIconViewController.init(); // GIVEN fp sensor location is available post-init captureAuthControllerCallback(); final PointF udfpsLocation = new PointF(50, 75); final int radius = 33; final FingerprintSensorPropertiesInternal fpProps = new FingerprintSensorPropertiesInternal( /* sensorId */ 0, /* strength */ 0, /* max enrollments per user */ 5, /* component info */ new ArrayList<>(), /* sensorType */ 3, /* resetLockoutRequiresHwToken */ false, (int) udfpsLocation.x, (int) udfpsLocation.y, radius); when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation); when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps)); // WHEN all authenticators are registered mAuthControllerCallback.onAllAuthenticatorsRegistered(); // THEN lock icon view location is updated with the same coordinates as fpProps verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(radius)); assertEquals(udfpsLocation, mPointCaptor.getValue()); } private void captureAuthControllerCallback() { verify(mAuthController).addCallback(mAuthControllerCallbackCaptor.capture()); mAuthControllerCallback = mAuthControllerCallbackCaptor.getValue(); } private void captureAttachListener() { verify(mLockIconView).addOnAttachStateChangeListener(mAttachCaptor.capture()); mAttachListener = mAttachCaptor.getValue(); } } Loading
packages/SystemUI/src/com/android/keyguard/LockIconView.java +6 −1 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.widget.FrameLayout; import android.widget.ImageView; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import com.android.settingslib.Utils; import com.android.systemui.Dumpable; Loading Loading @@ -79,7 +80,11 @@ public class LockIconView extends FrameLayout implements Dumpable { mLockIcon.setImageDrawable(drawable); } void setCenterLocation(@NonNull PointF center, int radius) { /** * Set the location of the lock icon. */ @VisibleForTesting public void setCenterLocation(@NonNull PointF center, int radius) { mLockIconCenter = center; mRadius = radius; Loading
packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +13 −5 Original line number Diff line number Diff line Loading @@ -68,7 +68,8 @@ import javax.inject.Inject; /** * Controls when to show the LockIcon affordance (lock/unlocked icon or circle) on lock screen. * * This view will only be shown if the user has UDFPS or FaceAuth enrolled * For devices with UDFPS, the lock icon will show at the sensor location. Else, the lock * icon will show a set distance from the bottom of the device. */ @StatusBarComponent.StatusBarScope public class LockIconViewController extends ViewController<LockIconView> implements Dumpable { Loading Loading @@ -172,15 +173,14 @@ public class LockIconViewController extends ViewController<LockIconView> impleme @Override protected void onInit() { mAuthController.addCallback(mAuthControllerCallback); mUdfpsSupported = mAuthController.getUdfpsSensorLocation() != null; mView.setAccessibilityDelegate(mAccessibilityDelegate); } @Override protected void onViewAttached() { // we check this here instead of onInit since the FingerprintManager + FaceManager may not // have started up yet onInit mUdfpsSupported = mAuthController.getUdfpsSensorLocation() != null; updateConfiguration(); updateKeyguardShowing(); mUserUnlockedWithBiometric = false; Loading Loading @@ -584,4 +584,12 @@ public class LockIconViewController extends ViewController<LockIconView> impleme public void setAlpha(float alpha) { mView.setAlpha(alpha); } private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() { @Override public void onAllAuthenticatorsRegistered() { mUdfpsSupported = mAuthController.getUdfpsSensorLocation() != null; updateConfiguration(); } }; }
packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +5 −1 Original line number Diff line number Diff line Loading @@ -786,7 +786,11 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, .build(sensorIds, credentialAllowed, mFpProps, mFaceProps); } interface Callback { /** * AuthController callback used to receive signal for when biometric authenticators are * registered. */ public interface Callback { /** * Called when authenticators are registered. If authenticators are already * registered before this call, this callback will never be triggered. Loading
packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java 0 → 100644 +189 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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.keyguard; import static junit.framework.Assert.assertEquals; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; import android.graphics.PointF; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.os.Vibrator; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.DisplayMetrics; import android.view.View; import android.view.accessibility.AccessibilityManager; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardViewController; import com.android.keyguard.LockIconView; import com.android.keyguard.LockIconViewController; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.AuthRippleController; import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.DelayableExecutor; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.List; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper public class LockIconViewControllerTest extends SysuiTestCase { private @Mock LockIconView mLockIconView; private @Mock Context mContext; private @Mock Resources mResources; private @Mock DisplayMetrics mDisplayMetrics; private @Mock StatusBarStateController mStatusBarStateController; private @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor; private @Mock KeyguardViewController mKeyguardViewController; private @Mock KeyguardStateController mKeyguardStateController; private @Mock FalsingManager mFalsingManager; private @Mock AuthController mAuthController; private @Mock DumpManager mDumpManager; private @Mock AccessibilityManager mAccessibilityManager; private @Mock ConfigurationController mConfigurationController; private @Mock DelayableExecutor mDelayableExecutor; private @Mock Vibrator mVibrator; private @Mock AuthRippleController mAuthRippleController; private LockIconViewController mLockIconViewController; // Capture listeners so that they can be used to send events @Captor private ArgumentCaptor<View.OnAttachStateChangeListener> mAttachCaptor = ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); private View.OnAttachStateChangeListener mAttachListener; @Captor private ArgumentCaptor<AuthController.Callback> mAuthControllerCallbackCaptor; private AuthController.Callback mAuthControllerCallback; @Captor private ArgumentCaptor<PointF> mPointCaptor; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); when(mLockIconView.getResources()).thenReturn(mResources); when(mLockIconView.getContext()).thenReturn(mContext); when(mContext.getResources()).thenReturn(mResources); when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics); mLockIconViewController = new LockIconViewController( mLockIconView, mStatusBarStateController, mKeyguardUpdateMonitor, mKeyguardViewController, mKeyguardStateController, mFalsingManager, mAuthController, mDumpManager, mAccessibilityManager, mConfigurationController, mDelayableExecutor, mVibrator, mAuthRippleController ); } @Test public void testUpdateFingerprintLocationOnInit() { // GIVEN fp sensor location is available pre-init final PointF udfpsLocation = new PointF(50, 75); final int radius = 33; final FingerprintSensorPropertiesInternal fpProps = new FingerprintSensorPropertiesInternal( /* sensorId */ 0, /* strength */ 0, /* max enrollments per user */ 5, /* component info */ new ArrayList<>(), /* sensorType */ 3, /* resetLockoutRequiresHwToken */ false, (int) udfpsLocation.x, (int) udfpsLocation.y, radius); when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation); when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps)); // WHEN lock icon view controller is initialized and attached mLockIconViewController.init(); captureAttachListener(); mAttachListener.onViewAttachedToWindow(null); // THEN lock icon view location is updated with the same coordinates as fpProps verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(radius)); assertEquals(udfpsLocation, mPointCaptor.getValue()); } @Test public void testUpdateFingerprintLocationOnAuthenticatorsRegistered() { // GIVEN fp sensor location is not available pre-init when(mAuthController.getFingerprintSensorLocation()).thenReturn(null); when(mAuthController.getUdfpsProps()).thenReturn(null); mLockIconViewController.init(); // GIVEN fp sensor location is available post-init captureAuthControllerCallback(); final PointF udfpsLocation = new PointF(50, 75); final int radius = 33; final FingerprintSensorPropertiesInternal fpProps = new FingerprintSensorPropertiesInternal( /* sensorId */ 0, /* strength */ 0, /* max enrollments per user */ 5, /* component info */ new ArrayList<>(), /* sensorType */ 3, /* resetLockoutRequiresHwToken */ false, (int) udfpsLocation.x, (int) udfpsLocation.y, radius); when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation); when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps)); // WHEN all authenticators are registered mAuthControllerCallback.onAllAuthenticatorsRegistered(); // THEN lock icon view location is updated with the same coordinates as fpProps verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(radius)); assertEquals(udfpsLocation, mPointCaptor.getValue()); } private void captureAuthControllerCallback() { verify(mAuthController).addCallback(mAuthControllerCallbackCaptor.capture()); mAuthControllerCallback = mAuthControllerCallbackCaptor.getValue(); } private void captureAttachListener() { verify(mLockIconView).addOnAttachStateChangeListener(mAttachCaptor.capture()); mAttachListener = mAttachCaptor.getValue(); } }