Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 001ec603 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Update lock icon location if auth setup is delayed" into sc-qpr1-dev

parents ba2546df 199da101
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -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;

+13 −5
Original line number Diff line number Diff line
@@ -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();
        }
    };
}
+5 −1
Original line number Diff line number Diff line
@@ -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.
+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();
    }
}