Add falsing protection to the pulsing state
Prox-gate the tap and double tap to wake gestures from pulsing/AoD2. Enable FalsingCollector logs: adb shell setprop log.tag.FalsingCollector DEBUG Test: manually trigger quickpickup (or receive a notification on AoD), cover the prox sensor, and then tap the screen - see device won't wake up Test: atest NotificationShadeWindowViewControllerTest BrightLineFalsingManagerTest PulsingGestureListenerTest Fixes: 242125976 Change-Id: I520df509b7804d765807477968257c94f55ef634
This commit is contained in:
@@ -102,6 +102,12 @@ public interface FalsingManager {
|
||||
*/
|
||||
boolean isFalseDoubleTap();
|
||||
|
||||
/**
|
||||
* Whether the last proximity event reported NEAR. May be used to short circuit motion events
|
||||
* that require the proximity sensor is not covered.
|
||||
*/
|
||||
boolean isProximityNear();
|
||||
|
||||
boolean isClassifierEnabled();
|
||||
|
||||
boolean shouldEnforceBouncer();
|
||||
|
||||
@@ -80,12 +80,14 @@ public class BrightLineFalsingManager implements FalsingManager {
|
||||
private final Collection<FalsingClassifier> mClassifiers;
|
||||
private final List<FalsingBeliefListener> mFalsingBeliefListeners = new ArrayList<>();
|
||||
private List<FalsingTapListener> mFalsingTapListeners = new ArrayList<>();
|
||||
private ProximityEvent mLastProximityEvent;
|
||||
|
||||
private boolean mDestroyed;
|
||||
|
||||
private final SessionListener mSessionListener = new SessionListener() {
|
||||
@Override
|
||||
public void onSessionEnded() {
|
||||
mLastProximityEvent = null;
|
||||
mClassifiers.forEach(FalsingClassifier::onSessionEnded);
|
||||
}
|
||||
|
||||
@@ -336,6 +338,7 @@ public class BrightLineFalsingManager implements FalsingManager {
|
||||
public void onProximityEvent(ProximityEvent proximityEvent) {
|
||||
// TODO: some of these classifiers might allow us to abort early, meaning we don't have to
|
||||
// make these calls.
|
||||
mLastProximityEvent = proximityEvent;
|
||||
mClassifiers.forEach((classifier) -> classifier.onProximityEvent(proximityEvent));
|
||||
}
|
||||
|
||||
@@ -347,6 +350,11 @@ public class BrightLineFalsingManager implements FalsingManager {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProximityNear() {
|
||||
return mLastProximityEvent != null && mLastProximityEvent.getCovered();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUnlockingDisabled() {
|
||||
return false;
|
||||
|
||||
@@ -47,9 +47,9 @@ import javax.inject.Inject;
|
||||
@SysUISingleton
|
||||
class FalsingCollectorImpl implements FalsingCollector {
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
private static final String TAG = "FalsingManager";
|
||||
private static final String PROXIMITY_SENSOR_TAG = "FalsingManager";
|
||||
private static final String TAG = "FalsingCollector";
|
||||
private static final String PROXIMITY_SENSOR_TAG = "FalsingCollector";
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
private static final long GESTURE_PROCESSING_DELAY_MS = 100;
|
||||
|
||||
private final FalsingDataProvider mFalsingDataProvider;
|
||||
|
||||
@@ -143,6 +143,11 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable {
|
||||
return mInternalFalsingManager.isFalseDoubleTap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProximityNear() {
|
||||
return mInternalFalsingManager.isProximityNear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClassifierEnabled() {
|
||||
return mInternalFalsingManager.isClassifierEnabled();
|
||||
|
||||
@@ -17,12 +17,9 @@
|
||||
package com.android.systemui.shade;
|
||||
|
||||
import android.app.StatusBarManager;
|
||||
import android.hardware.display.AmbientDisplayConfiguration;
|
||||
import android.media.AudioManager;
|
||||
import android.media.session.MediaSessionLegacyHelper;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.InputDevice;
|
||||
@@ -52,7 +49,6 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
|
||||
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
|
||||
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
|
||||
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
|
||||
import com.android.systemui.tuner.TunerService;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Optional;
|
||||
@@ -66,7 +62,6 @@ import javax.inject.Inject;
|
||||
public class NotificationShadeWindowViewController {
|
||||
private static final String TAG = "NotifShadeWindowVC";
|
||||
private final FalsingCollector mFalsingCollector;
|
||||
private final TunerService mTunerService;
|
||||
private final SysuiStatusBarStateController mStatusBarStateController;
|
||||
private final NotificationShadeWindowView mView;
|
||||
private final NotificationShadeDepthController mDepthController;
|
||||
@@ -77,6 +72,7 @@ public class NotificationShadeWindowViewController {
|
||||
private final StatusBarWindowStateController mStatusBarWindowStateController;
|
||||
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
|
||||
private final AmbientState mAmbientState;
|
||||
private final PulsingGestureListener mPulsingGestureListener;
|
||||
|
||||
private GestureDetector mGestureDetector;
|
||||
private View mBrightnessMirror;
|
||||
@@ -88,8 +84,6 @@ public class NotificationShadeWindowViewController {
|
||||
private final CentralSurfaces mService;
|
||||
private final NotificationShadeWindowController mNotificationShadeWindowController;
|
||||
private DragDownHelper mDragDownHelper;
|
||||
private boolean mDoubleTapEnabled;
|
||||
private boolean mSingleTapEnabled;
|
||||
private boolean mExpandingBelowNotch;
|
||||
private final DockManager mDockManager;
|
||||
private final NotificationPanelViewController mNotificationPanelViewController;
|
||||
@@ -102,7 +96,6 @@ public class NotificationShadeWindowViewController {
|
||||
public NotificationShadeWindowViewController(
|
||||
LockscreenShadeTransitionController transitionController,
|
||||
FalsingCollector falsingCollector,
|
||||
TunerService tunerService,
|
||||
SysuiStatusBarStateController statusBarStateController,
|
||||
DockManager dockManager,
|
||||
NotificationShadeDepthController depthController,
|
||||
@@ -117,10 +110,11 @@ public class NotificationShadeWindowViewController {
|
||||
CentralSurfaces centralSurfaces,
|
||||
NotificationShadeWindowController controller,
|
||||
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
|
||||
AmbientState ambientState) {
|
||||
AmbientState ambientState,
|
||||
PulsingGestureListener pulsingGestureListener
|
||||
) {
|
||||
mLockscreenShadeTransitionController = transitionController;
|
||||
mFalsingCollector = falsingCollector;
|
||||
mTunerService = tunerService;
|
||||
mStatusBarStateController = statusBarStateController;
|
||||
mView = notificationShadeWindowView;
|
||||
mDockManager = dockManager;
|
||||
@@ -136,6 +130,7 @@ public class NotificationShadeWindowViewController {
|
||||
mNotificationShadeWindowController = controller;
|
||||
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
|
||||
mAmbientState = ambientState;
|
||||
mPulsingGestureListener = pulsingGestureListener;
|
||||
|
||||
// This view is not part of the newly inflated expanded status bar.
|
||||
mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
|
||||
@@ -151,46 +146,7 @@ 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);
|
||||
|
||||
TunerService.Tunable tunable = (key, newValue) -> {
|
||||
AmbientDisplayConfiguration configuration =
|
||||
new AmbientDisplayConfiguration(mView.getContext());
|
||||
switch (key) {
|
||||
case Settings.Secure.DOZE_DOUBLE_TAP_GESTURE:
|
||||
mDoubleTapEnabled = configuration.doubleTapGestureEnabled(
|
||||
UserHandle.USER_CURRENT);
|
||||
break;
|
||||
case Settings.Secure.DOZE_TAP_SCREEN_GESTURE:
|
||||
mSingleTapEnabled = configuration.tapGestureEnabled(UserHandle.USER_CURRENT);
|
||||
}
|
||||
};
|
||||
mTunerService.addTunable(tunable,
|
||||
Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
|
||||
Settings.Secure.DOZE_TAP_SCREEN_GESTURE);
|
||||
|
||||
GestureDetector.SimpleOnGestureListener gestureListener =
|
||||
new GestureDetector.SimpleOnGestureListener() {
|
||||
@Override
|
||||
public boolean onSingleTapConfirmed(MotionEvent e) {
|
||||
if (mSingleTapEnabled && !mDockManager.isDocked()) {
|
||||
mService.wakeUpIfDozing(
|
||||
SystemClock.uptimeMillis(), mView, "SINGLE_TAP");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDoubleTap(MotionEvent e) {
|
||||
if (mDoubleTapEnabled || mSingleTapEnabled) {
|
||||
mService.wakeUpIfDozing(
|
||||
SystemClock.uptimeMillis(), mView, "DOUBLE_TAP");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
mGestureDetector = new GestureDetector(mView.getContext(), gestureListener);
|
||||
mGestureDetector = new GestureDetector(mView.getContext(), mPulsingGestureListener);
|
||||
|
||||
mLowLightClockController.ifPresent(controller -> controller.attachLowLightClockView(mView));
|
||||
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.shade
|
||||
|
||||
import android.hardware.display.AmbientDisplayConfiguration
|
||||
import android.os.SystemClock
|
||||
import android.os.UserHandle
|
||||
import android.provider.Settings
|
||||
import android.view.GestureDetector
|
||||
import android.view.MotionEvent
|
||||
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.statusbar.phone.CentralSurfaces
|
||||
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
|
||||
import com.android.systemui.tuner.TunerService
|
||||
import com.android.systemui.tuner.TunerService.Tunable
|
||||
import java.io.PrintWriter
|
||||
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.
|
||||
*
|
||||
* 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].
|
||||
*/
|
||||
@CentralSurfacesComponent.CentralSurfacesScope
|
||||
class PulsingGestureListener @Inject constructor(
|
||||
private val notificationShadeWindowView: NotificationShadeWindowView,
|
||||
private val falsingManager: FalsingManager,
|
||||
private val dockManager: DockManager,
|
||||
private val centralSurfaces: CentralSurfaces,
|
||||
private val ambientDisplayConfiguration: AmbientDisplayConfiguration,
|
||||
tunerService: TunerService,
|
||||
dumpManager: DumpManager
|
||||
) : GestureDetector.SimpleOnGestureListener(), Dumpable {
|
||||
private var doubleTapEnabled = false
|
||||
private var singleTapEnabled = false
|
||||
|
||||
init {
|
||||
val tunable = Tunable { key: String?, _: String? ->
|
||||
when (key) {
|
||||
Settings.Secure.DOZE_DOUBLE_TAP_GESTURE ->
|
||||
doubleTapEnabled = ambientDisplayConfiguration.doubleTapGestureEnabled(
|
||||
UserHandle.USER_CURRENT)
|
||||
Settings.Secure.DOZE_TAP_SCREEN_GESTURE ->
|
||||
singleTapEnabled = ambientDisplayConfiguration.tapGestureEnabled(
|
||||
UserHandle.USER_CURRENT)
|
||||
}
|
||||
}
|
||||
tunerService.addTunable(tunable,
|
||||
Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
|
||||
Settings.Secure.DOZE_TAP_SCREEN_GESTURE)
|
||||
|
||||
dumpManager.registerDumpable(this)
|
||||
}
|
||||
|
||||
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
|
||||
if (singleTapEnabled &&
|
||||
!dockManager.isDocked &&
|
||||
!falsingManager.isProximityNear &&
|
||||
!falsingManager.isFalseTap(FalsingManager.MODERATE_PENALTY)
|
||||
) {
|
||||
centralSurfaces.wakeUpIfDozing(
|
||||
SystemClock.uptimeMillis(),
|
||||
notificationShadeWindowView,
|
||||
"PULSING_SINGLE_TAP")
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onDoubleTap(e: MotionEvent): Boolean {
|
||||
if ((doubleTapEnabled || singleTapEnabled) &&
|
||||
!falsingManager.isProximityNear &&
|
||||
!falsingManager.isFalseDoubleTap
|
||||
) {
|
||||
centralSurfaces.wakeUpIfDozing(
|
||||
SystemClock.uptimeMillis(),
|
||||
notificationShadeWindowView,
|
||||
"PULSING_DOUBLE_TAP")
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun dump(pw: PrintWriter, args: Array<out String>) {
|
||||
pw.println("singleTapEnabled=$singleTapEnabled")
|
||||
pw.println("doubleTapEnabled=$doubleTapEnabled")
|
||||
pw.println("isDocked=${dockManager.isDocked}")
|
||||
pw.println("isProxCovered=${falsingManager.isProximityNear}")
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ import androidx.test.filters.SmallTest;
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.internal.logging.testing.FakeMetricsLogger;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.plugins.FalsingManager;
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -111,5 +112,51 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase {
|
||||
assertThat(mBrightLineFalsingManager.isFalseDoubleTap()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsProxNear_noProxEvents_defaultsToFalse() {
|
||||
assertThat(mBrightLineFalsingManager.isProximityNear()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsProxNear_receivesNearEvent() {
|
||||
mBrightLineFalsingManager.onProximityEvent(new FalsingManager.ProximityEvent() {
|
||||
@Override
|
||||
public boolean getCovered() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimestampNs() {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
assertThat(mBrightLineFalsingManager.isProximityNear()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsProxNear_receivesNearAndThenFarEvent() {
|
||||
mBrightLineFalsingManager.onProximityEvent(new FalsingManager.ProximityEvent() {
|
||||
@Override
|
||||
public boolean getCovered() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimestampNs() {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
mBrightLineFalsingManager.onProximityEvent(new FalsingManager.ProximityEvent() {
|
||||
@Override
|
||||
public boolean getCovered() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimestampNs() {
|
||||
return 5;
|
||||
}
|
||||
});
|
||||
assertThat(mBrightLineFalsingManager.isProximityNear()).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,19 +26,18 @@ import com.android.systemui.classifier.FalsingCollectorFake
|
||||
import com.android.systemui.dock.DockManager
|
||||
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
|
||||
import com.android.systemui.lowlightclock.LowLightClockController
|
||||
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
|
||||
import com.android.systemui.statusbar.LockscreenShadeTransitionController
|
||||
import com.android.systemui.statusbar.NotificationShadeDepthController
|
||||
import com.android.systemui.statusbar.NotificationShadeWindowController
|
||||
import com.android.systemui.statusbar.SysuiStatusBarStateController
|
||||
import com.android.systemui.statusbar.notification.stack.AmbientState
|
||||
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
|
||||
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
|
||||
import com.android.systemui.statusbar.phone.CentralSurfaces
|
||||
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
|
||||
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
|
||||
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager
|
||||
import com.android.systemui.statusbar.window.StatusBarWindowStateController
|
||||
import com.android.systemui.tuner.TunerService
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import java.util.Optional
|
||||
import org.junit.Before
|
||||
@@ -60,8 +59,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
|
||||
@Mock
|
||||
private lateinit var view: NotificationShadeWindowView
|
||||
@Mock
|
||||
private lateinit var tunserService: TunerService
|
||||
@Mock
|
||||
private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
|
||||
@Mock
|
||||
private lateinit var centralSurfaces: CentralSurfaces
|
||||
@@ -91,6 +88,8 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
|
||||
private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController
|
||||
@Mock
|
||||
private lateinit var lowLightClockController: LowLightClockController
|
||||
@Mock
|
||||
private lateinit var pulsingGestureListener: PulsingGestureListener
|
||||
|
||||
private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
|
||||
private lateinit var interactionEventHandler: InteractionEventHandler
|
||||
@@ -105,7 +104,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
|
||||
underTest = NotificationShadeWindowViewController(
|
||||
lockscreenShadeTransitionController,
|
||||
FalsingCollectorFake(),
|
||||
tunserService,
|
||||
sysuiStatusBarStateController,
|
||||
dockManager,
|
||||
notificationShadeDepthController,
|
||||
@@ -120,7 +118,8 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
|
||||
centralSurfaces,
|
||||
notificationShadeWindowController,
|
||||
keyguardUnlockAnimationController,
|
||||
ambientState
|
||||
ambientState,
|
||||
pulsingGestureListener
|
||||
)
|
||||
underTest.setupExpandedStatusBar()
|
||||
|
||||
|
||||
@@ -89,6 +89,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
|
||||
@Mock private LowLightClockController mLowLightClockController;
|
||||
@Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
|
||||
@Mock private AmbientState mAmbientState;
|
||||
@Mock private PulsingGestureListener mPulsingGestureListener;
|
||||
|
||||
@Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler>
|
||||
mInteractionEventHandlerCaptor;
|
||||
@@ -110,7 +111,6 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
|
||||
mController = new NotificationShadeWindowViewController(
|
||||
mLockscreenShadeTransitionController,
|
||||
new FalsingCollectorFake(),
|
||||
mTunerService,
|
||||
mStatusBarStateController,
|
||||
mDockManager,
|
||||
mNotificationShadeDepthController,
|
||||
@@ -125,7 +125,9 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
|
||||
mCentralSurfaces,
|
||||
mNotificationShadeWindowController,
|
||||
mKeyguardUnlockAnimationController,
|
||||
mAmbientState);
|
||||
mAmbientState,
|
||||
mPulsingGestureListener
|
||||
);
|
||||
mController.setupExpandedStatusBar();
|
||||
mController.setDragDownHelper(mDragDownHelper);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.shade
|
||||
|
||||
import android.hardware.display.AmbientDisplayConfiguration
|
||||
import android.provider.Settings.Secure.DOZE_DOUBLE_TAP_GESTURE
|
||||
import android.provider.Settings.Secure.DOZE_TAP_SCREEN_GESTURE
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper.RunWithLooper
|
||||
import android.view.MotionEvent
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.dock.DockManager
|
||||
import com.android.systemui.dump.DumpManager
|
||||
import com.android.systemui.plugins.FalsingManager
|
||||
import com.android.systemui.statusbar.phone.CentralSurfaces
|
||||
import com.android.systemui.tuner.TunerService
|
||||
import com.android.systemui.tuner.TunerService.Tunable
|
||||
import com.android.systemui.util.mockito.eq
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.ArgumentMatchers.anyInt
|
||||
import org.mockito.ArgumentMatchers.anyLong
|
||||
import org.mockito.ArgumentMatchers.anyObject
|
||||
import org.mockito.ArgumentMatchers.anyString
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@RunWithLooper(setAsMainLooper = true)
|
||||
@SmallTest
|
||||
class PulsingGestureListenerTest : SysuiTestCase() {
|
||||
@Mock
|
||||
private lateinit var view: NotificationShadeWindowView
|
||||
@Mock
|
||||
private lateinit var centralSurfaces: CentralSurfaces
|
||||
@Mock
|
||||
private lateinit var dockManager: DockManager
|
||||
@Mock
|
||||
private lateinit var falsingManager: FalsingManager
|
||||
@Mock
|
||||
private lateinit var ambientDisplayConfiguration: AmbientDisplayConfiguration
|
||||
@Mock
|
||||
private lateinit var tunerService: TunerService
|
||||
@Mock
|
||||
private lateinit var dumpManager: DumpManager
|
||||
|
||||
private lateinit var tunableCaptor: ArgumentCaptor<Tunable>
|
||||
private lateinit var underTest: PulsingGestureListener
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
underTest = PulsingGestureListener(
|
||||
view,
|
||||
falsingManager,
|
||||
dockManager,
|
||||
centralSurfaces,
|
||||
ambientDisplayConfiguration,
|
||||
tunerService,
|
||||
dumpManager
|
||||
)
|
||||
whenever(dockManager.isDocked).thenReturn(false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGestureDetector_singleTapEnabled() {
|
||||
// GIVEN tap is enabled, prox not covered
|
||||
whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
|
||||
updateSettings()
|
||||
whenever(falsingManager.isProximityNear).thenReturn(false)
|
||||
|
||||
// GIVEN the falsing manager does NOT think the tap is a false tap
|
||||
whenever(falsingManager.isFalseTap(anyInt())).thenReturn(false)
|
||||
|
||||
// WHEN there's a tap
|
||||
underTest.onSingleTapConfirmed(downEv)
|
||||
|
||||
// THEN wake up device if dozing
|
||||
verify(centralSurfaces).wakeUpIfDozing(anyLong(), anyObject(), anyString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGestureDetector_doubleTapEnabled() {
|
||||
// GIVEN double tap is enabled, prox not covered
|
||||
whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true)
|
||||
updateSettings()
|
||||
whenever(falsingManager.isProximityNear).thenReturn(false)
|
||||
|
||||
// GIVEN the falsing manager does NOT think the tap is a false tap
|
||||
whenever(falsingManager.isFalseDoubleTap).thenReturn(false)
|
||||
|
||||
// WHEN there's a double tap
|
||||
underTest.onDoubleTap(downEv)
|
||||
|
||||
// THEN wake up device if dozing
|
||||
verify(centralSurfaces).wakeUpIfDozing(anyLong(), anyObject(), anyString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGestureDetector_singleTapEnabled_falsing() {
|
||||
// GIVEN tap is enabled, prox not covered
|
||||
whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
|
||||
updateSettings()
|
||||
whenever(falsingManager.isProximityNear).thenReturn(false)
|
||||
|
||||
// GIVEN the falsing manager thinks the tap is a false tap
|
||||
whenever(falsingManager.isFalseTap(anyInt())).thenReturn(true)
|
||||
|
||||
// WHEN there's a tap
|
||||
underTest.onSingleTapConfirmed(downEv)
|
||||
|
||||
// THEN the device doesn't wake up
|
||||
verify(centralSurfaces, never()).wakeUpIfDozing(anyLong(), anyObject(), anyString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGestureDetector_doubleTapEnabled_falsing() {
|
||||
// GIVEN double tap is enabled, prox not covered
|
||||
whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true)
|
||||
updateSettings()
|
||||
whenever(falsingManager.isProximityNear).thenReturn(false)
|
||||
|
||||
// GIVEN the falsing manager thinks the tap is a false tap
|
||||
whenever(falsingManager.isFalseDoubleTap).thenReturn(true)
|
||||
|
||||
// WHEN there's a tap
|
||||
underTest.onDoubleTap(downEv)
|
||||
|
||||
// THEN the device doesn't wake up
|
||||
verify(centralSurfaces, never()).wakeUpIfDozing(anyLong(), anyObject(), anyString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGestureDetector_singleTapEnabled_proxCovered() {
|
||||
// GIVEN tap is enabled, not a false tap based on classifiers
|
||||
whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
|
||||
updateSettings()
|
||||
whenever(falsingManager.isFalseTap(anyInt())).thenReturn(false)
|
||||
|
||||
// GIVEN prox is covered
|
||||
whenever(falsingManager.isProximityNear()).thenReturn(true)
|
||||
|
||||
// WHEN there's a tap
|
||||
underTest.onSingleTapConfirmed(downEv)
|
||||
|
||||
// THEN the device doesn't wake up
|
||||
verify(centralSurfaces, never()).wakeUpIfDozing(anyLong(), anyObject(), anyString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGestureDetector_doubleTapEnabled_proxCovered() {
|
||||
// GIVEN double tap is enabled, not a false tap based on classifiers
|
||||
whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true)
|
||||
updateSettings()
|
||||
whenever(falsingManager.isFalseDoubleTap).thenReturn(false)
|
||||
|
||||
// GIVEN prox is covered
|
||||
whenever(falsingManager.isProximityNear()).thenReturn(true)
|
||||
|
||||
// WHEN there's a tap
|
||||
underTest.onDoubleTap(downEv)
|
||||
|
||||
// THEN the device doesn't wake up
|
||||
verify(centralSurfaces, never()).wakeUpIfDozing(anyLong(), anyObject(), anyString())
|
||||
}
|
||||
|
||||
fun updateSettings() {
|
||||
tunableCaptor = ArgumentCaptor.forClass(Tunable::class.java)
|
||||
verify(tunerService).addTunable(
|
||||
tunableCaptor.capture(),
|
||||
eq(DOZE_DOUBLE_TAP_GESTURE),
|
||||
eq(DOZE_TAP_SCREEN_GESTURE))
|
||||
tunableCaptor.value.onTuningChanged(DOZE_DOUBLE_TAP_GESTURE, "")
|
||||
tunableCaptor.value.onTuningChanged(DOZE_TAP_SCREEN_GESTURE, "")
|
||||
}
|
||||
}
|
||||
|
||||
private val downEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
|
||||
@@ -40,6 +40,7 @@ public class FalsingManagerFake implements FalsingManager {
|
||||
private boolean mIsReportingEnabled;
|
||||
private boolean mIsFalseRobustTap;
|
||||
private boolean mDestroyed;
|
||||
private boolean mIsProximityNear;
|
||||
|
||||
private final List<FalsingBeliefListener> mFalsingBeliefListeners = new ArrayList<>();
|
||||
private final List<FalsingTapListener> mTapListeners = new ArrayList<>();
|
||||
@@ -82,6 +83,10 @@ public class FalsingManagerFake implements FalsingManager {
|
||||
mIsFalseDoubleTap = falseDoubleTap;
|
||||
}
|
||||
|
||||
public void setIsProximityNear(boolean proxNear) {
|
||||
mIsProximityNear = proxNear;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSimpleTap() {
|
||||
checkDestroyed();
|
||||
@@ -100,6 +105,11 @@ public class FalsingManagerFake implements FalsingManager {
|
||||
return mIsFalseDoubleTap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProximityNear() {
|
||||
return mIsProximityNear;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setIsClassifierEnabled(boolean isClassifierEnabled) {
|
||||
mIsClassifierEnabled = isClassifierEnabled;
|
||||
|
||||
Reference in New Issue
Block a user