Merge "Update lock icon location if auth setup is delayed" into sc-qpr1-dev am: 001ec6038a am: c2e1a34b88

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15638227

Change-Id: Ie1260a2ede9785584d497e9aa182e3e58c52af03
This commit is contained in:
TreeHugger Robot
2021-08-23 20:19:17 +00:00
committed by Automerger Merge Worker
4 changed files with 213 additions and 7 deletions

View File

@@ -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;
@@ -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;

View File

@@ -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 {
@@ -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;
@@ -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();
}
};
}

View File

@@ -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.

View File

@@ -0,0 +1,189 @@
/*
* 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();
}
}