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

Commit 0847f5a7 authored by Beverly Tai's avatar Beverly Tai Committed by Android (Google) Code Review
Browse files

Merge "Support touchExplorationEnabled on udfps" into sc-dev

parents d7d47480 f6346f45
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -20,7 +20,8 @@
    android:id="@+id/udfps_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    systemui:sensorTouchAreaCoefficient="0.5">
    systemui:sensorTouchAreaCoefficient="0.5"
    android:contentDescription="@string/accessibility_fingerprint_label">

    <ViewStub
        android:id="@+id/animation_view"
+13 −0
Original line number Diff line number Diff line
@@ -338,6 +338,8 @@
    <string name="accessibility_voice_assist_button">Voice Assist</string>
    <!-- Content description of the unlock button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
    <string name="accessibility_unlock_button">Unlock</string>
    <!-- Content description of the lock icon for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
    <string name="accessibility_lock_icon">Device locked</string>
    <!-- Content description hint of the unlock button when fingerprint is on (not shown on the screen). [CHAR LIMIT=NONE] -->
    <string name="accessibility_waiting_for_fingerprint">Waiting for fingerprint</string>
    <!-- Accessibility action of the unlock button when fingerpint is on (not shown on the screen). [CHAR LIMIT=NONE] -->
@@ -2958,4 +2960,15 @@

    <!-- Secondary label for alarm tile when there is no next alarm information [CHAR LIMIT=20] -->
    <string name="qs_alarm_tile_no_alarm">No alarm set</string>

    <!-- Accessibility label for fingerprint sensor [CHAR LIMIT=NONE] -->
    <string name="accessibility_fingerprint_label">Fingerprint sensor</string>
    <!-- Accessibility label for disabled udfps sensor [CHAR LIMIT=NONE] -->
    <string name="accessibility_udfps_disabled_button">Fingerprint sensor disabled</string>
    <!-- Accessibility action for tapping on an affordance that will bring up the user's
    pin/pattern/password bouncer (ie: "Double tap to authenticate") [CHAR LIMIT=NONE] -->
    <string name="accessibility_authenticate_hint">authenticate</string>
    <!-- Accessibility action for tapping on an affordance on an unlocked lock screen (ie: "Double
     tap to enter device") [CHAR LIMIT=NONE] -->
    <string name="accessibility_enter_hint">enter device</string>
</resources>
+66 −5
Original line number Diff line number Diff line
@@ -29,9 +29,12 @@ import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;

import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
@@ -63,6 +66,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
    @NonNull private final KeyguardStateController mKeyguardStateController;
    @NonNull private final FalsingManager mFalsingManager;
    @NonNull private final AuthController mAuthController;
    @NonNull private final AccessibilityManager mAccessibilityManager;

    private boolean mHasUdfpsOrFaceAuthFeatures;
    private boolean mUdfpsEnrolled;
@@ -93,19 +97,17 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
            @NonNull KeyguardStateController keyguardStateController,
            @NonNull FalsingManager falsingManager,
            @NonNull AuthController authController,
            @NonNull DumpManager dumpManager
            @NonNull DumpManager dumpManager,
            @NonNull AccessibilityManager accessibilityManager
    ) {
        super(view);
        if (mView != null) {
            mView.setOnClickListener(v -> onAffordanceClick());
            mView.setOnLongClickListener(v -> onAffordanceClick());
        }
        mStatusBarStateController = statusBarStateController;
        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
        mAuthController = authController;
        mKeyguardViewController = keyguardViewController;
        mKeyguardStateController = keyguardStateController;
        mFalsingManager = falsingManager;
        mAccessibilityManager = accessibilityManager;

        final Context context = view.getContext();
        mButton = context.getResources().getDrawable(
@@ -121,6 +123,11 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
        dumpManager.registerDumpable("LockIconViewController", this);
    }

    @Override
    protected void onInit() {
        mView.setAccessibilityDelegate(mAccessibilityDelegate);
    }

    @Override
    protected void onViewAttached() {
        // we check this here instead of onInit since the FingeprintManager + FaceManager may not
@@ -163,6 +170,8 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
        mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
        mStatusBarStateController.addCallback(mStatusBarStateListener);
        mKeyguardStateController.addCallback(mKeyguardStateCallback);
        mAccessibilityManager.addTouchExplorationStateChangeListener(
                mTouchExplorationStateChangeListener);

        updateVisibility();
    }
@@ -172,6 +181,8 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
        mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
        mStatusBarStateController.removeCallback(mStatusBarStateListener);
        mKeyguardStateController.removeCallback(mKeyguardStateCallback);
        mAccessibilityManager.removeTouchExplorationStateChangeListener(
                mTouchExplorationStateChangeListener);
    }

    public float getTop() {
@@ -210,20 +221,56 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
        mShowLockIcon = !mUdfpsEnrolled && !mCanDismissLockScreen && isLockScreen()
            && mFaceAuthEnrolled;

        updateClickListener();
        if (mShowButton) {
            mView.setImageDrawable(mButton);
            mView.setVisibility(View.VISIBLE);
            mView.setContentDescription(getResources().getString(
                    R.string.accessibility_udfps_disabled_button));
        } else if (mShowUnlockIcon) {
            mView.setImageDrawable(mUnlockIcon);
            mView.setVisibility(View.VISIBLE);
            mView.setContentDescription(getResources().getString(
                    R.string.accessibility_unlock_button));
        } else if (mShowLockIcon) {
            mView.setImageDrawable(mLockIcon);
            mView.setVisibility(View.VISIBLE);
            mView.setContentDescription(getResources().getString(
                    R.string.accessibility_lock_icon));
        } else {
            mView.setVisibility(View.INVISIBLE);
            mView.setContentDescription(null);
        }
    }

    private final View.AccessibilityDelegate mAccessibilityDelegate =
            new View.AccessibilityDelegate() {
        private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityAuthenticateHint =
                new AccessibilityNodeInfo.AccessibilityAction(
                        AccessibilityNodeInfoCompat.ACTION_CLICK,
                        getResources().getString(R.string.accessibility_authenticate_hint));
        private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityEnterHint =
                new AccessibilityNodeInfo.AccessibilityAction(
                        AccessibilityNodeInfoCompat.ACTION_CLICK,
                        getResources().getString(R.string.accessibility_enter_hint));
        public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfo info) {
            super.onInitializeAccessibilityNodeInfo(v, info);
            removeAllActions(info);
            if (mShowButton || mShowLockIcon) {
                info.addAction(mAccessibilityAuthenticateHint);
            } else if (mShowUnlockIcon) {
                info.addAction(mAccessibilityEnterHint);
            }
        }

        private void removeAllActions(AccessibilityNodeInfo info) {
            info.removeAction(mAccessibilityAuthenticateHint);
            info.removeAction(mAccessibilityEnterHint);
            info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
            mView.setLongClickable(false);
        }
    };

    private boolean isLockScreen() {
        return !mIsDozing
                && !mIsBouncerShowing
@@ -231,6 +278,17 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
                && mStatusBarState == StatusBarState.KEYGUARD;
    }

    private void updateClickListener() {
        if (mView != null) {
            mView.setOnClickListener(v -> onAffordanceClick());
            if (mAccessibilityManager.isTouchExplorationEnabled()) {
                mView.setOnLongClickListener(null);
            } else {
                mView.setOnLongClickListener(v -> onAffordanceClick());
            }
        }
    }

    @Override
    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
        pw.println("  mShowBouncerButton: " + mShowButton);
@@ -298,4 +356,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
            updateVisibility();
        }
    };

    private final AccessibilityManager.TouchExplorationStateChangeListener
            mTouchExplorationStateChangeListener = enabled -> updateClickListener();
}
+36 −5
Original line number Diff line number Diff line
@@ -28,7 +28,6 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.RectF;
@@ -55,6 +54,7 @@ import android.view.Surface;
import android.view.VelocityTracker;
import android.view.View;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;

import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -108,6 +108,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
    @NonNull private final Handler mMainHandler;
    @NonNull private final FalsingManager mFalsingManager;
    @NonNull private final PowerManager mPowerManager;
    @NonNull private final AccessibilityManager mAccessibilityManager;
    // 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;
@@ -276,6 +277,13 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
    private final UdfpsView.OnTouchListener mOnTouchListener = (view, event) ->
            onTouch(view, event, true);

    @SuppressLint("ClickableViewAccessibility")
    private final UdfpsView.OnHoverListener mOnHoverListener = (view, event) ->
            onTouch(view, event, true);

    private final AccessibilityManager.TouchExplorationStateChangeListener
            mTouchExplorationStateChangeListener = enabled -> updateTouchListener();

    /**
     * @param x coordinate
     * @param y coordinate
@@ -300,6 +308,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
                udfpsView.onTouchOutsideView();
                break;
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_HOVER_ENTER:
                // To simplify the lifecycle of the velocity tracker, make sure it's never null
                // after ACTION_DOWN, and always null after ACTION_CANCEL or ACTION_UP.
                if (mVelocityTracker == null) {
@@ -322,6 +331,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
                break;

            case MotionEvent.ACTION_MOVE:
            case MotionEvent.ACTION_HOVER_MOVE:
                final int idx = mActivePointerId == -1
                        ? event.getPointerId(0)
                        : event.findPointerIndex(mActivePointerId);
@@ -388,6 +398,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback {

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_HOVER_EXIT:
                mActivePointerId = -1;
                if (mVelocityTracker != null) {
                    mVelocityTracker.recycle();
@@ -409,10 +420,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback {

    @Inject
    public UdfpsController(@NonNull Context context,
            @Main Resources resources,
            @NonNull LayoutInflater inflater,
            @Nullable FingerprintManager fingerprintManager,
            WindowManager windowManager,
            @NonNull WindowManager windowManager,
            @NonNull StatusBarStateController statusBarStateController,
            @Main DelayableExecutor fgExecutor,
            @NonNull StatusBar statusBar,
@@ -421,7 +431,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
            @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
            @NonNull KeyguardViewMediator keyguardViewMediator,
            @NonNull FalsingManager falsingManager,
            @NonNull PowerManager powerManager) {
            @NonNull PowerManager powerManager,
            @NonNull AccessibilityManager accessibilityManager) {
        mContext = context;
        // TODO (b/185124905): inject main handler and vibrator once done prototyping
        mMainHandler = new Handler(Looper.getMainLooper());
@@ -440,6 +451,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
        mKeyguardViewMediator = keyguardViewMediator;
        mFalsingManager = falsingManager;
        mPowerManager = powerManager;
        mAccessibilityManager = accessibilityManager;

        mSensorProps = findFirstUdfps();
        // At least one UDFPS sensor exists
@@ -577,7 +589,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
                    mView.setAnimationViewController(animation);

                    mWindowManager.addView(mView, computeLayoutParams(animation));
                    mView.setOnTouchListener(mOnTouchListener);
                    mAccessibilityManager.addTouchExplorationStateChangeListener(
                            mTouchExplorationStateChangeListener);
                    updateTouchListener();
                } catch (RuntimeException e) {
                    Log.e(TAG, "showUdfpsOverlay | failed to add window", e);
                }
@@ -650,7 +664,10 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
                onFingerUp();
                mWindowManager.removeView(mView);
                mView.setOnTouchListener(null);
                mView.setOnHoverListener(null);
                mView.setAnimationViewController(null);
                mAccessibilityManager.removeTouchExplorationStateChangeListener(
                        mTouchExplorationStateChangeListener);
                mView = null;
            } else {
                Log.v(TAG, "hideUdfpsOverlay | the overlay is already hidden");
@@ -758,4 +775,18 @@ public class UdfpsController implements DozeReceiver, HbmCallback {
                return defaultEffect;
        }
    }

    private void updateTouchListener() {
        if (mView == null) {
            return;
        }

        if (mAccessibilityManager.isTouchExplorationEnabled()) {
            mView.setOnHoverListener(mOnHoverListener);
            mView.setOnTouchListener(null);
        } else {
            mView.setOnHoverListener(null);
            mView.setOnTouchListener(mOnTouchListener);
        }
    }
}
+5 −12
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.content.res.Resources;
import android.content.res.TypedArray;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.SensorProperties;
@@ -40,6 +39,7 @@ import android.testing.TestableLooper.RunWithLooper;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;

import androidx.test.filters.SmallTest;

@@ -85,8 +85,6 @@ public class UdfpsControllerTest extends SysuiTestCase {

    // Dependencies
    @Mock
    private Resources mResources;
    @Mock
    private LayoutInflater mLayoutInflater;
    @Mock
    private FingerprintManager mFingerprintManager;
@@ -110,6 +108,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
    private FalsingManager mFalsingManager;
    @Mock
    private PowerManager mPowerManager;
    @Mock
    private AccessibilityManager mAccessibilityManager;

    private FakeExecutor mFgExecutor;

@@ -151,7 +151,6 @@ public class UdfpsControllerTest extends SysuiTestCase {
        mFgExecutor = new FakeExecutor(new FakeSystemClock());
        mUdfpsController = new UdfpsController(
                mContext,
                mResources,
                mLayoutInflater,
                mFingerprintManager,
                mWindowManager,
@@ -163,7 +162,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
                mKeyguardUpdateMonitor,
                mKeyguardViewMediator,
                mFalsingManager,
                mPowerManager);
                mPowerManager,
                mAccessibilityManager);
        verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
        mOverlayController = mOverlayCaptor.getValue();

@@ -174,16 +174,9 @@ public class UdfpsControllerTest extends SysuiTestCase {
        when(mBrightnessValues.length()).thenReturn(2);
        when(mBrightnessValues.getFloat(0, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(1f);
        when(mBrightnessValues.getFloat(1, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(2f);
        when(mResources.obtainTypedArray(com.android.internal.R.array.config_screenBrightnessNits))
                .thenReturn(mBrightnessValues);
        when(mBrightnessBacklight.length()).thenReturn(2);
        when(mBrightnessBacklight.getFloat(0, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(1f);
        when(mBrightnessBacklight.getFloat(1, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(2f);
        when(mResources.obtainTypedArray(
                com.android.internal.R.array.config_autoBrightnessDisplayValuesNits))
                .thenReturn(mBrightnessBacklight);
        when(mResources.getIntArray(com.android.internal.R.array.config_screenBrightnessBacklight))
                .thenReturn(new int[]{1, 2});
    }

    @Test