diff options
10 files changed, 97 insertions, 47 deletions
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java index 1aaf19e8793f..c50340cfd247 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java @@ -147,10 +147,10 @@ public interface FalsingManager { } /** - * Listener that is alerted when a double tap is required to confirm a single tap. + * Listener that is alerted when an additional tap is required to confirm a single tap. **/ interface FalsingTapListener { - void onDoubleTapRequired(); + void onAdditionalTapRequired(); } /** Passed to {@link FalsingManager#onProximityEvent}. */ diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java index 31a2134851a2..bfbf37a5d3e9 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java @@ -291,7 +291,7 @@ public class BrightLineFalsingManager implements FalsingManager { FalsingClassifier.Result.falsed( 0, getClass().getSimpleName(), "bad history")); logDebug("False Single Tap: true (bad history)"); - mFalsingTapListeners.forEach(FalsingTapListener::onDoubleTapRequired); + mFalsingTapListeners.forEach(FalsingTapListener::onAdditionalTapRequired); return true; } else { mPriorResults = getPassedResult(0.1); @@ -321,7 +321,7 @@ public class BrightLineFalsingManager implements FalsingManager { mHistoryTracker.falseBelief(), mHistoryTracker.falseConfidence()); mPriorResults = Collections.singleton(result); - logDebug("False Double Tap: " + result.isFalse()); + logDebug("False Double Tap: " + result.isFalse() + " reason=" + result.getReason()); return result.isFalse(); } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java index 23d87ff980ca..f5f9655ef24b 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java @@ -303,9 +303,7 @@ class FalsingCollectorImpl implements FalsingCollector { @Override public void onTouchEvent(MotionEvent ev) { - if (!mKeyguardStateController.isShowing() - || (mStatusBarStateController.isDozing() - && !mStatusBarStateController.isPulsing())) { + if (!mKeyguardStateController.isShowing()) { avoidGesture(); return; } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 3429b9d0e503..1011a6d831e6 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -33,6 +33,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_Q import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import static com.android.systemui.statusbar.StatusBarState.SHADE; import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED; +import static com.android.systemui.statusbar.VibratorHelper.TOUCH_VIBRATION_ATTRIBUTES; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL; import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_FOLD_TO_AOD; import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_CLOSED; @@ -62,6 +63,7 @@ import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.os.Bundle; import android.os.Handler; import android.os.PowerManager; +import android.os.Process; import android.os.Trace; import android.os.UserManager; import android.os.VibrationEffect; @@ -236,6 +238,9 @@ public final class NotificationPanelViewController extends PanelViewController { private static final boolean SPEW_LOGCAT = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE); private static final boolean DEBUG_DRAWABLE = false; + private static final VibrationEffect ADDITIONAL_TAP_REQUIRED_VIBRATION_EFFECT = + VibrationEffect.get(VibrationEffect.EFFECT_STRENGTH_MEDIUM, false); + /** * The parallax amount of the quick settings translation when dragging down the panel */ @@ -682,14 +687,22 @@ public final class NotificationPanelViewController extends PanelViewController { private final FalsingTapListener mFalsingTapListener = new FalsingTapListener() { @Override - public void onDoubleTapRequired() { + public void onAdditionalTapRequired() { if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) { mTapAgainViewController.show(); } else { mKeyguardIndicationController.showTransientIndication( R.string.notification_tap_again); } - mVibratorHelper.vibrate(VibrationEffect.EFFECT_STRENGTH_MEDIUM); + + if (!mStatusBarStateController.isDozing()) { + mVibratorHelper.vibrate( + Process.myUid(), + mView.getContext().getPackageName(), + ADDITIONAL_TAP_REQUIRED_VIBRATION_EFFECT, + "falsing-additional-tap-required", + TOUCH_VIBRATION_ATTRIBUTES); + } } }; diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index abafecce2965..8d74a09ab8b1 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -73,7 +73,7 @@ public class NotificationShadeWindowViewController { private final AmbientState mAmbientState; private final PulsingGestureListener mPulsingGestureListener; - private GestureDetector mGestureDetector; + private GestureDetector mPulsingWakeupGestureHandler; private View mBrightnessMirror; private boolean mTouchActive; private boolean mTouchCancelled; @@ -149,7 +149,8 @@ public class NotificationShadeWindowViewController { /** Inflates the {@link R.layout#status_bar_expanded} layout and sets it up. */ public void setupExpandedStatusBar() { mStackScrollLayout = mView.findViewById(R.id.notification_stack_scroller); - mGestureDetector = new GestureDetector(mView.getContext(), mPulsingGestureListener); + mPulsingWakeupGestureHandler = new GestureDetector(mView.getContext(), + mPulsingGestureListener); mView.setInteractionEventHandler(new NotificationShadeWindowView.InteractionEventHandler() { @Override @@ -196,7 +197,7 @@ public class NotificationShadeWindowViewController { } mFalsingCollector.onTouchEvent(ev); - mGestureDetector.onTouchEvent(ev); + mPulsingWakeupGestureHandler.onTouchEvent(ev); mStatusBarKeyguardViewManager.onTouch(ev); if (mBrightnessMirror != null && mBrightnessMirror.getVisibility() == View.VISIBLE) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt index 9b3fe92f9225..084b7dc3a646 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt @@ -26,6 +26,7 @@ import com.android.systemui.Dumpable import com.android.systemui.dock.DockManager import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.FalsingManager +import com.android.systemui.plugins.FalsingManager.LOW_PENALTY import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent @@ -36,12 +37,12 @@ import javax.inject.Inject /** * If tap and/or double tap to wake is enabled, this gestureListener will wake the display on - * tap/double tap when the device is pulsing (AoD 2). Taps are gated by the proximity sensor and - * falsing manager. + * tap/double tap when the device is pulsing (AoD2) or transitioning to AoD. Taps are gated by the + * proximity sensor and falsing manager. * - * Touches go through the [NotificationShadeWindowViewController] when the device is pulsing. - * Otherwise, if the device is dozing and NOT pulsing, wake-ups are handled by - * [com.android.systemui.doze.DozeSensors]. + * Touches go through the [NotificationShadeWindowViewController] when the device is dozing but the + * screen is still ON and not in the true AoD display state. When the device is in the true AoD + * display state, wake-ups are handled by [com.android.systemui.doze.DozeSensors]. */ @CentralSurfacesComponent.CentralSurfacesScope class PulsingGestureListener @Inject constructor( @@ -75,12 +76,12 @@ class PulsingGestureListener @Inject constructor( dumpManager.registerDumpable(this) } - override fun onSingleTapConfirmed(e: MotionEvent): Boolean { - if (statusBarStateController.isPulsing && + override fun onSingleTapUp(e: MotionEvent): Boolean { + if (statusBarStateController.isDozing && singleTapEnabled && !dockManager.isDocked && !falsingManager.isProximityNear && - !falsingManager.isFalseTap(FalsingManager.MODERATE_PENALTY) + !falsingManager.isFalseTap(LOW_PENALTY) ) { centralSurfaces.wakeUpIfDozing( SystemClock.uptimeMillis(), @@ -91,8 +92,15 @@ class PulsingGestureListener @Inject constructor( return false } - override fun onDoubleTap(e: MotionEvent): Boolean { - if (statusBarStateController.isPulsing && + /** + * Receives [MotionEvent.ACTION_DOWN], [MotionEvent.ACTION_MOVE], and [MotionEvent.ACTION_UP] + * motion events for a double tap. + */ + override fun onDoubleTapEvent(e: MotionEvent): Boolean { + // React to the [MotionEvent.ACTION_UP] event after double tap is detected. Falsing + // checks MUST be on the ACTION_UP event. + if (e.actionMasked == MotionEvent.ACTION_UP && + statusBarStateController.isDozing && (doubleTapEnabled || singleTapEnabled) && !falsingManager.isProximityNear && !falsingManager.isFalseDoubleTap diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java index c74621df94c9..258f9fc5449f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java @@ -38,7 +38,7 @@ import javax.inject.Inject; public class VibratorHelper { private final Vibrator mVibrator; - private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES = + public static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES = VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH); private final Executor mExecutor; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java index acb5622c9790..3e9cf1e51b63 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java @@ -229,7 +229,10 @@ public class FalsingCollectorImplTest extends SysuiTestCase { } @Test - public void testAvoidDozingNotPulsing() { + public void testGestureWhenDozing() { + // We check the FalsingManager for taps during the transition to AoD (dozing=true, + // pulsing=false), so the FalsingCollector needs to continue to analyze events that occur + // while the device is dozing. MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0); MotionEvent up = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0); @@ -239,13 +242,13 @@ public class FalsingCollectorImplTest extends SysuiTestCase { mFalsingCollector.onTouchEvent(down); verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class)); - // Up event would normally flush the up event, but doesn't. + // Up event flushes mFalsingCollector.onTouchEvent(up); - verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class)); + verify(mFalsingDataProvider, times(2)).onMotionEvent(any(MotionEvent.class)); } @Test - public void testAvoidDozingButPulsing() { + public void testGestureWhenPulsing() { MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0); MotionEvent up = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java index e73071335c9d..b40d5ac69d7b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java @@ -1022,7 +1022,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { FalsingManager.FalsingTapListener listener = getFalsingTapListener(); mStatusBarStateController.setState(KEYGUARD); - listener.onDoubleTapRequired(); + listener.onAdditionalTapRequired(); verify(mKeyguardIndicationController).showTransientIndication(anyInt()); } @@ -1032,7 +1032,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { FalsingManager.FalsingTapListener listener = getFalsingTapListener(); mStatusBarStateController.setState(SHADE_LOCKED); - listener.onDoubleTapRequired(); + listener.onAdditionalTapRequired(); verify(mTapAgainViewController).show(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt index 97c0bb2c09ca..09add652483e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt @@ -89,7 +89,7 @@ class PulsingGestureListenerTest : SysuiTestCase() { @Test fun testGestureDetector_singleTapEnabled() { - whenever(statusBarStateController.isPulsing).thenReturn(true) + whenever(statusBarStateController.isDozing).thenReturn(true) // GIVEN tap is enabled, prox not covered whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true) @@ -100,7 +100,7 @@ class PulsingGestureListenerTest : SysuiTestCase() { whenever(falsingManager.isFalseTap(anyInt())).thenReturn(false) // WHEN there's a tap - underTest.onSingleTapConfirmed(downEv) + underTest.onSingleTapUp(upEv) // THEN wake up device if dozing verify(centralSurfaces).wakeUpIfDozing(anyLong(), anyObject(), anyString()) @@ -108,7 +108,7 @@ class PulsingGestureListenerTest : SysuiTestCase() { @Test fun testGestureDetector_doubleTapEnabled() { - whenever(statusBarStateController.isPulsing).thenReturn(true) + whenever(statusBarStateController.isDozing).thenReturn(true) // GIVEN double tap is enabled, prox not covered whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true) @@ -119,15 +119,27 @@ class PulsingGestureListenerTest : SysuiTestCase() { whenever(falsingManager.isFalseDoubleTap).thenReturn(false) // WHEN there's a double tap - underTest.onDoubleTap(downEv) + underTest.onDoubleTapEvent(upEv) // THEN wake up device if dozing verify(centralSurfaces).wakeUpIfDozing(anyLong(), anyObject(), anyString()) } @Test + fun testGestureDetector_doubleTapEnabled_onDownEvent_noFalsingCheck() { + // GIVEN tap is enabled + whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true) + + // WHEN there's a double tap on DOWN event + underTest.onDoubleTapEvent(downEv) + + // THEN don't check the falsing manager, should only be checked on the UP event + verify(falsingManager, never()).isFalseDoubleTap() + } + + @Test fun testGestureDetector_singleTapEnabled_falsing() { - whenever(statusBarStateController.isPulsing).thenReturn(true) + whenever(statusBarStateController.isDozing).thenReturn(true) // GIVEN tap is enabled, prox not covered whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true) @@ -138,29 +150,43 @@ class PulsingGestureListenerTest : SysuiTestCase() { whenever(falsingManager.isFalseTap(anyInt())).thenReturn(true) // WHEN there's a tap - underTest.onSingleTapConfirmed(downEv) + underTest.onSingleTapUp(upEv) // THEN the device doesn't wake up verify(centralSurfaces, never()).wakeUpIfDozing(anyLong(), anyObject(), anyString()) } @Test - fun testGestureDetector_notPulsing_noFalsingCheck() { - whenever(statusBarStateController.isPulsing).thenReturn(false) + fun testSingleTap_notDozing_noFalsingCheck() { + whenever(statusBarStateController.isDozing).thenReturn(false) - // GIVEN tap is enabled, prox not covered + // GIVEN tap is enabled whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true) // WHEN there's a tap - underTest.onSingleTapConfirmed(downEv) + underTest.onSingleTapUp(upEv) - // THEN the falsing manager never gets a call (because the device wasn't pulsing + // THEN the falsing manager never gets a call (because the device wasn't dozing + // during the tap) + verify(falsingManager, never()).isFalseTap(anyInt()) + } + + @Test + fun testDoubleTap_notDozing_noFalsingCheck() { + whenever(statusBarStateController.isDozing).thenReturn(false) + + // GIVEN tap is enabled + whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true) + // WHEN there's a tap + underTest.onDoubleTapEvent(upEv) + + // THEN the falsing manager never gets a call (because the device wasn't dozing // during the tap) verify(falsingManager, never()).isFalseTap(anyInt()) } @Test fun testGestureDetector_doubleTapEnabled_falsing() { - whenever(statusBarStateController.isPulsing).thenReturn(true) + whenever(statusBarStateController.isDozing).thenReturn(true) // GIVEN double tap is enabled, prox not covered whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true) @@ -170,8 +196,8 @@ class PulsingGestureListenerTest : SysuiTestCase() { // GIVEN the falsing manager thinks the tap is a false tap whenever(falsingManager.isFalseDoubleTap).thenReturn(true) - // WHEN there's a tap - underTest.onDoubleTap(downEv) + // WHEN there's a double tap ACTION_UP event + underTest.onDoubleTapEvent(upEv) // THEN the device doesn't wake up verify(centralSurfaces, never()).wakeUpIfDozing(anyLong(), anyObject(), anyString()) @@ -179,7 +205,7 @@ class PulsingGestureListenerTest : SysuiTestCase() { @Test fun testGestureDetector_singleTapEnabled_proxCovered() { - whenever(statusBarStateController.isPulsing).thenReturn(true) + whenever(statusBarStateController.isDozing).thenReturn(true) // GIVEN tap is enabled, not a false tap based on classifiers whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true) @@ -190,7 +216,7 @@ class PulsingGestureListenerTest : SysuiTestCase() { whenever(falsingManager.isProximityNear()).thenReturn(true) // WHEN there's a tap - underTest.onSingleTapConfirmed(downEv) + underTest.onSingleTapUp(upEv) // THEN the device doesn't wake up verify(centralSurfaces, never()).wakeUpIfDozing(anyLong(), anyObject(), anyString()) @@ -198,7 +224,7 @@ class PulsingGestureListenerTest : SysuiTestCase() { @Test fun testGestureDetector_doubleTapEnabled_proxCovered() { - whenever(statusBarStateController.isPulsing).thenReturn(true) + whenever(statusBarStateController.isDozing).thenReturn(true) // GIVEN double tap is enabled, not a false tap based on classifiers whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true) @@ -209,7 +235,7 @@ class PulsingGestureListenerTest : SysuiTestCase() { whenever(falsingManager.isProximityNear()).thenReturn(true) // WHEN there's a tap - underTest.onDoubleTap(downEv) + underTest.onDoubleTapEvent(upEv) // THEN the device doesn't wake up verify(centralSurfaces, never()).wakeUpIfDozing(anyLong(), anyObject(), anyString()) @@ -227,3 +253,4 @@ class PulsingGestureListenerTest : SysuiTestCase() { } private val downEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) +private val upEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0) |