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

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

Merge "Move the majority of lockIcon touch handling out of NSWVC" into main

parents 27d86955 7eca463b
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.keyguard;

import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
@@ -68,6 +69,7 @@ public class LockIconView extends FrameLayout implements Dumpable {
    private boolean mUseBackground = false;
    private float mDozeAmount = 0f;

    @SuppressLint("ClickableViewAccessibility")
    public LockIconView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mSensorRect = new RectF();
+23 −20
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import static com.android.systemui.flags.Flags.NEW_AOD_TRANSITION;
import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -57,7 +58,6 @@ import androidx.annotation.VisibleForTesting;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;

import com.android.systemui.Dumpable;
import com.android.systemui.res.R;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.AuthRippleController;
import com.android.systemui.biometrics.UdfpsController;
@@ -73,6 +73,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac
import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -256,6 +257,7 @@ public class LockIconViewController implements Dumpable {
    }

    /** Sets the LockIconView to the controller and rebinds any that depend on it. */
    @SuppressLint("ClickableViewAccessibility")
    public void setLockIconView(LockIconView lockIconView) {
        mView = lockIconView;
        mView.setImageDrawable(mIcon);
@@ -305,6 +307,8 @@ public class LockIconViewController implements Dumpable {
        if (lockIconView.isAttachedToWindow()) {
            registerCallbacks();
        }

        lockIconView.setOnTouchListener((view, motionEvent) -> onTouchEvent(motionEvent));
    }

    private void registerCallbacks() {
@@ -635,19 +639,18 @@ public class LockIconViewController implements Dumpable {
    };

    /**
     * Handles the touch if it is within the lock icon view and {@link #isActionable()} is true.
     * Handles the touch if {@link #isActionable()} is true.
     * Subsequently, will trigger {@link #onLongPress()} if a touch is continuously in the lock icon
     * area for {@link #mLongPressTimeout} ms.
     *
     * Touch speed debouncing mimics logic from the velocity tracker in {@link UdfpsController}.
     */
    public boolean onTouchEvent(MotionEvent event, Runnable onGestureDetectedRunnable) {
        if (!onInterceptTouchEvent(event)) {
    private boolean onTouchEvent(MotionEvent event) {
        if (!actionableDownEventStartedOnView(event)) {
            cancelTouches();
            return false;
        }

        mOnGestureDetectedRunnable = onGestureDetectedRunnable;
        switch(event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_HOVER_ENTER:
@@ -700,12 +703,8 @@ public class LockIconViewController implements Dumpable {
        return true;
    }

    /**
     * Intercepts the touch if the onDown event and current event are within this lock icon view's
     * bounds.
     */
    public boolean onInterceptTouchEvent(MotionEvent event) {
        if (!inLockIconArea(event) || !isActionable()) {
    private boolean actionableDownEventStartedOnView(MotionEvent event) {
        if (!isActionable()) {
            return false;
        }

@@ -729,9 +728,6 @@ public class LockIconViewController implements Dumpable {
            mAuthRippleController.showUnlockRipple(FINGERPRINT);
        }
        updateVisibility();
        if (mOnGestureDetectedRunnable != null) {
            mOnGestureDetectedRunnable.run();
        }

        // play device entry haptic (consistent with UDFPS controller longpress)
        vibrateOnLongPress();
@@ -751,12 +747,6 @@ public class LockIconViewController implements Dumpable {
        }
    }

    private boolean inLockIconArea(MotionEvent event) {
        mView.getHitRect(mSensorTouchLocation);
        return mSensorTouchLocation.contains((int) event.getX(), (int) event.getY())
                && mView.getVisibility() == View.VISIBLE;
    }

    private boolean isActionable() {
        if (mIsBouncerShowing) {
            Log.v(TAG, "lock icon long-press ignored, bouncer already showing.");
@@ -834,6 +824,19 @@ public class LockIconViewController implements Dumpable {
        }
    };

    /**
     * Whether the lock icon will handle a touch while dozing.
     */
    public boolean willHandleTouchWhileDozing(MotionEvent event) {
        // is in lock icon area
        mView.getHitRect(mSensorTouchLocation);
        final boolean inLockIconArea =
                mSensorTouchLocation.contains((int) event.getX(), (int) event.getY())
                        && mView.getVisibility() == View.VISIBLE;

        return inLockIconArea && actionableDownEventStartedOnView(event);
    }

    private final View.OnClickListener mA11yClickListener = v -> onLongPress();

    private final AccessibilityManager.AccessibilityStateChangeListener
+13 −22
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import static com.android.systemui.flags.Flags.TRACKPAD_GESTURE_COMMON;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;

import android.app.StatusBarManager;
import android.os.PowerManager;
import android.util.Log;
import android.view.GestureDetector;
import android.view.InputDevice;
@@ -36,7 +35,6 @@ import com.android.keyguard.KeyguardMessageAreaController;
import com.android.keyguard.LockIconViewController;
import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.systemui.Dumpable;
import com.android.systemui.res.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.back.domain.interactor.BackActionInteractor;
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor;
@@ -56,6 +54,7 @@ import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.log.BouncerLogger;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.res.R;
import com.android.systemui.shared.animation.DisableSubpixelTextTransitionListener;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -351,16 +350,6 @@ public class NotificationShadeWindowViewController implements Dumpable {
                if (mStatusBarStateController.isDozing()) {
                    mDozeScrimController.extendPulse();
                }
                mLockIconViewController.onTouchEvent(
                        ev,
                        /* onGestureDetectedRunnable */
                        () -> {
                            mService.userActivity();
                            mPowerInteractor.wakeUpIfDozing(
                                    "LOCK_ICON_TOUCH",
                                    PowerManager.WAKE_REASON_GESTURE);
                        }
                );

                // In case we start outside of the view bounds (below the status bar), we need to
                // dispatch the touch manually as the view system can't accommodate for touches
@@ -415,8 +404,18 @@ public class NotificationShadeWindowViewController implements Dumpable {

            private boolean shouldInterceptTouchEventInternal(MotionEvent ev) {
                mLastInterceptWasDragDownHelper = false;
                if (mStatusBarStateController.isDozing() && !mDozeServiceHost.isPulsing()
                        && !mDockManager.isDocked()) {
                // When the device starts dozing, there's a delay before the device's display state
                // changes from ON => DOZE to allow for the light reveal animation to run at
                // a higher refresh rate and to delay visual changes (ie: display blink) when
                // changing the display state. We'll call this specific state the
                // "aodDefermentState". In this state we:
                //     - don't want touches to get sent to underlying views, except the lock icon
                //     - handle the tap to wake gesture via the PulsingGestureListener
                if (mStatusBarStateController.isDozing()
                        && !mDozeServiceHost.isPulsing()
                        && !mDockManager.isDocked()
                        && !mLockIconViewController.willHandleTouchWhileDozing(ev)
                ) {
                    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
                        mShadeLogger.d("NSWVC: capture all touch events in always-on");
                    }
@@ -432,14 +431,6 @@ public class NotificationShadeWindowViewController implements Dumpable {
                    return true;
                }

                if (mLockIconViewController.onInterceptTouchEvent(ev)) {
                    // immediately return true; don't send the touch to the drag down helper
                    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
                        mShadeLogger.d("NSWVC: don't send touch to drag down helper");
                    }
                    return true;
                }

                if (mNotificationPanelViewController.isFullyExpanded()
                        && !mService.isBouncerShowing()
                        && !mStatusBarStateController.isDozing()) {
+1 −1
Original line number Diff line number Diff line
@@ -39,7 +39,6 @@ import android.view.View;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;

import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.AuthRippleController;
@@ -51,6 +50,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.ConfigurationController;
+26 −18
Original line number Diff line number Diff line
@@ -83,7 +83,6 @@ import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.util.mockito.any
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.util.Optional
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.test.TestScope
@@ -97,8 +96,9 @@ import org.mockito.Mockito.anyFloat
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
import java.util.Optional
import org.mockito.Mockito.`when` as whenever

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -425,29 +425,37 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
    }

    @Test
    fun shouldInterceptTouchEvent_notificationPanelViewControllerShouldIntercept() {
        // GIVEN not dozing
        whenever(sysuiStatusBarStateController.isDozing()).thenReturn(false)
    fun shouldInterceptTouchEvent_dozing_touchInLockIconArea_touchNotIntercepted() {
        // GIVEN dozing
        whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
        // AND alternate bouncer doesn't want the touch
        whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
            .thenReturn(false)
        // AND the lock icon doesn't want the touch
        whenever(lockIconViewController.onInterceptTouchEvent(DOWN_EVENT)).thenReturn(false)
        // AND the notification panel can accept touches
        whenever(notificationPanelViewController.isFullyExpanded()).thenReturn(true)
        whenever(dragDownHelper.isDragDownEnabled).thenReturn(true)
        whenever(centralSurfaces.isBouncerShowing()).thenReturn(false)

        // AND the drag down helper doesn't want the touch (to pull the shade down)
        whenever(dragDownHelper.onInterceptTouchEvent(DOWN_EVENT)).thenReturn(false)
        // AND the lock icon wants the touch
        whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT))
                .thenReturn(true)

        featureFlags.set(Flags.MIGRATE_NSSL, true)

        // WHEN asked if should intercept touch
        interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)
        // THEN touch should NOT be intercepted by NotificationShade
        assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isFalse()
    }

    @Test
    fun shouldInterceptTouchEvent_dozing_touchNotInLockIconArea_touchIntercepted() {
        // GIVEN dozing
        whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
        // AND alternate bouncer doesn't want the touch
        whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
                .thenReturn(false)
        // AND the lock icon does NOT want the touch
        whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT))
                .thenReturn(false)

        featureFlags.set(Flags.MIGRATE_NSSL, true)

        // Verify that NPVC gets a chance to use the touch
        verify(notificationPanelViewController).handleExternalInterceptTouch(DOWN_EVENT)
        // THEN touch should NOT be intercepted by NotificationShade
        assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue()
    }

    @Test