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

Commit 9f2f76aa authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Add orientation listener to all biometric overlays." into sc-dev am: b1720128

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

Change-Id: Icf58d1d773f1296c3b0e61a0c83df45c4bc3e205
parents 01f6847c b1720128
Loading
Loading
Loading
Loading
+14 −41
Original line number Diff line number Diff line
@@ -48,10 +48,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.OrientationEventListener;
import android.view.Surface;
import android.view.WindowManager;

import com.android.internal.R;
@@ -72,6 +69,8 @@ import java.util.Set;
import javax.inject.Inject;
import javax.inject.Provider;

import kotlin.Unit;

/**
 * Receives messages sent from {@link com.android.server.biometrics.BiometricService} and shows the
 * appropriate biometric UI (e.g. BiometricDialogView).
@@ -107,7 +106,8 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
    TaskStackListener mTaskStackListener;
    @VisibleForTesting
    IBiometricSysuiReceiver mReceiver;
    @NonNull private final BiometricOrientationEventListener mOrientationListener;
    @VisibleForTesting
    @NonNull final BiometricOrientationEventListener mOrientationListener;
    @Nullable private final List<FaceSensorPropertiesInternal> mFaceProps;
    @Nullable private List<FingerprintSensorPropertiesInternal> mFpProps;
    @Nullable private List<FingerprintSensorPropertiesInternal> mUdfpsProps;
@@ -120,42 +120,6 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
        }
    }

    private class BiometricOrientationEventListener extends OrientationEventListener {
        @Surface.Rotation private int mLastRotation;

        BiometricOrientationEventListener(Context context) {
            super(context);
            mLastRotation = context.getDisplay().getRotation();
        }

        @Override
        public void onOrientationChanged(int orientation) {
            if (orientation == ORIENTATION_UNKNOWN) {
                return;
            }

            final Display display = mContext.getDisplay();
            if (display == null) {
                return;
            }

            final int rotation = display.getRotation();
            if (mLastRotation != rotation) {
                mLastRotation = rotation;

                if (mCurrentDialog != null) {
                    mCurrentDialog.onOrientationChanged();
                }
                if (mUdfpsController != null) {
                    mUdfpsController.onOrientationChanged();
                }
                if (mSidefpsController != null) {
                    mSidefpsController.onOrientationChanged();
                }
            }
        }
    }

    @NonNull
    private final IFingerprintAuthenticatorsRegisteredCallback
            mFingerprintAuthenticatorsRegisteredCallback =
@@ -468,7 +432,10 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
        mUdfpsControllerFactory = udfpsControllerFactory;
        mSidefpsControllerFactory = sidefpsControllerFactory;
        mWindowManager = windowManager;
        mOrientationListener = new BiometricOrientationEventListener(context);
        mOrientationListener = new BiometricOrientationEventListener(context, () -> {
            onOrientationChanged();
            return Unit.INSTANCE;
        });

        mFaceProps = mFaceManager != null ? mFaceManager.getSensorPropertiesInternal() : null;

@@ -790,6 +757,12 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
        }
    }

    private void onOrientationChanged() {
        if (mCurrentDialog != null) {
            mCurrentDialog.onOrientationChanged();
        }
    }

    protected AuthDialog buildDialog(PromptInfo promptInfo, boolean requireConfirmation,
            int userId, int[] sensorIds, boolean credentialAllowed, String opPackageName,
            boolean skipIntro, long operationId,
+60 −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.biometrics

import android.content.Context
import android.view.OrientationEventListener

/**
 * An [OrientationEventListener] that invokes the [onOrientationChanged] callback whenever
 * the orientation of the device has changed in order to keep overlays for biometric sensors
 * aligned with the device's screen.
 */
class BiometricOrientationEventListener(
    private val context: Context,
    private val onOrientationChanged: () -> Unit
) : OrientationEventListener(context) {

    /** If actively listening (not available in base class). */
    var enabled: Boolean = false
        private set

    private var lastRotation = context.display?.rotation ?: ORIENTATION_UNKNOWN

    override fun onOrientationChanged(orientation: Int) {
        if (orientation == ORIENTATION_UNKNOWN) {
            return
        }

        val rotation = context.display?.rotation ?: return
        if (lastRotation != rotation) {
            lastRotation = rotation

            onOrientationChanged()
        }
    }

    override fun enable() {
        enabled = true
        super.enable()
    }

    override fun disable() {
        enabled = false
        super.disable()
    }
}
+15 −3
Original line number Diff line number Diff line
@@ -41,6 +41,8 @@ import com.android.systemui.util.concurrency.DelayableExecutor;

import javax.inject.Inject;

import kotlin.Unit;

/**
 * Shows and hides the side fingerprint sensor (side-fps) overlay and handles side fps touch events.
 */
@@ -52,6 +54,8 @@ public class SidefpsController {
    private final FingerprintManager mFingerprintManager;
    private final WindowManager mWindowManager;
    private final DelayableExecutor mFgExecutor;
    @VisibleForTesting @NonNull final BiometricOrientationEventListener mOrientationListener;

    // TODO: update mDisplayHeight and mDisplayWidth for multi-display devices
    private final int mDisplayHeight;
    private final int mDisplayWidth;
@@ -95,6 +99,10 @@ public class SidefpsController {
        mFingerprintManager = checkNotNull(fingerprintManager);
        mWindowManager = windowManager;
        mFgExecutor = fgExecutor;
        mOrientationListener = new BiometricOrientationEventListener(context, () -> {
            onOrientationChanged();
            return Unit.INSTANCE;
        });

        mSensorProps = findFirstSidefps();
        checkArgument(mSensorProps != null);
@@ -119,14 +127,15 @@ public class SidefpsController {
        mFingerprintManager.setSidefpsController(mSidefpsControllerImpl);
    }

    void show() {
    private void show() {
        mView = (SidefpsView) mInflater.inflate(R.layout.sidefps_view, null, false);
        mView.setSensorProperties(mSensorProps);
        mWindowManager.addView(mView, computeLayoutParams());

        mOrientationListener.enable();
    }

    void hide() {
    private void hide() {
        if (mView != null) {
            mWindowManager.removeView(mView);
            mView.setOnTouchListener(null);
@@ -135,13 +144,16 @@ public class SidefpsController {
        } else {
            Log.v(TAG, "hideUdfpsOverlay | the overlay is already hidden");
        }

        mOrientationListener.disable();
    }

    void onOrientationChanged() {
    private void onOrientationChanged() {
        // If mView is null or if view is hidden, then return.
        if (mView == null || !mIsVisible) {
            return;
        }

        // If the overlay needs to be displayed with a new configuration, destroy the current
        // overlay, and re-create and show the overlay with the updated LayoutParams.
        hide();
+12 −1
Original line number Diff line number Diff line
@@ -79,6 +79,8 @@ import java.util.Optional;

import javax.inject.Inject;

import kotlin.Unit;

/**
 * Shows and hides the under-display fingerprint sensor (UDFPS) overlay, handles UDFPS touch events,
 * and coordinates triggering of the high-brightness mode (HBM).
@@ -118,6 +120,7 @@ public class UdfpsController implements DozeReceiver {
    @NonNull private final AccessibilityManager mAccessibilityManager;
    @NonNull private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
    @Nullable private final UdfpsHbmProvider mHbmProvider;
    @VisibleForTesting @NonNull final BiometricOrientationEventListener mOrientationListener;
    // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
    // sensors, this, in addition to a lot of the code here, will be updated.
    @VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps;
@@ -511,6 +514,10 @@ public class UdfpsController implements DozeReceiver {
        mHbmProvider = hbmProvider.orElse(null);
        screenLifecycle.addObserver(mScreenObserver);
        mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
        mOrientationListener = new BiometricOrientationEventListener(context, () -> {
            onOrientationChanged();
            return Unit.INSTANCE;
        });

        mSensorProps = findFirstUdfps();
        // At least one UDFPS sensor exists
@@ -650,7 +657,7 @@ public class UdfpsController implements DozeReceiver {
        return mCoreLayoutParams;
    }

    void onOrientationChanged() {
    private void onOrientationChanged() {
        // When the configuration changes it's almost always necessary to destroy and re-create
        // the overlay's window to pass it the new LayoutParams.
        // Hiding the overlay will destroy its window. It's safe to hide the overlay regardless
@@ -668,6 +675,7 @@ public class UdfpsController implements DozeReceiver {
        if (mView == null) {
            try {
                Log.v(TAG, "showUdfpsOverlay | adding window reason=" + reason);

                mView = (UdfpsView) mInflater.inflate(R.layout.udfps_view, null, false);
                mOnFingerDown = false;
                mView.setSensorProperties(mSensorProps);
@@ -675,6 +683,7 @@ public class UdfpsController implements DozeReceiver {
                UdfpsAnimationViewController animation = inflateUdfpsAnimation(reason);
                animation.init();
                mView.setAnimationViewController(animation);
                mOrientationListener.enable();

                // This view overlaps the sensor area, so prevent it from being selectable
                // during a11y.
@@ -768,6 +777,8 @@ public class UdfpsController implements DozeReceiver {
        } else {
            Log.v(TAG, "hideUdfpsOverlay | the overlay is already hidden");
        }

        mOrientationListener.disable();
    }

    /**
+15 −1
Original line number Diff line number Diff line
@@ -20,7 +20,9 @@ import static android.hardware.biometrics.BiometricManager.Authenticators;
import static android.hardware.biometrics.BiometricManager.BIOMETRIC_MULTI_SENSOR_FACE_THEN_FINGERPRINT;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -55,12 +57,13 @@ import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
import android.os.Bundle;
import android.os.RemoteException;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.testing.TestableLooper.RunWithLooper;
import android.view.WindowManager;

import androidx.test.filters.SmallTest;

import com.android.internal.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.CommandQueue;
@@ -539,6 +542,17 @@ public class AuthControllerTest extends SysuiTestCase {
        verify(mUdfpsController).onAodInterrupt(eq(pos), eq(pos), eq(majorMinor), eq(majorMinor));
    }

    @Test
    public void testSubscribesToOrientationChangesWhenShowingDialog() {
        assertFalse(mAuthController.mOrientationListener.getEnabled());

        showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */);
        assertTrue(mAuthController.mOrientationListener.getEnabled());

        mAuthController.hideAuthenticationDialog();
        assertFalse(mAuthController.mOrientationListener.getEnabled());
    }

    // Helpers

    private void showDialog(int[] sensorIds, boolean credentialAllowed) {
Loading