Analyze vertical swipes on QS's Scroll View.

When media controls are shown, the full qs+media controls often takes
up more than one full screen. Swiping up on this causes the qs to
vertically scroll instead of collapse.

Prior to this cl, we weren't analyzing such swipes in the
FalsingManager. Now we do.

No explicit action is taken in the event of a false swipe. It simply
gives us more signal. Enough bad swipes may implicitly cause the
lock screen to reset itself.

Fixes: 241708312
Test: manual
Change-Id: I5adfb3a22b2f0f51efb87d56a6cfc5459cd67006
This commit is contained in:
Dave Mankoff
2022-09-15 22:05:08 +00:00
parent a33f6c1c05
commit d33284a22d
14 changed files with 137 additions and 33 deletions

View File

@@ -220,7 +220,7 @@ public class BrightLineFalsingManager implements FalsingManager {
return r;
}).collect(Collectors.toList());
logDebug("False Gesture: " + localResult[0]);
logDebug("False Gesture (type: " + interactionType + "): " + localResult[0]);
return localResult[0];
}
@@ -454,6 +454,12 @@ public class BrightLineFalsingManager implements FalsingManager {
}
}
static void logVerbose(String msg) {
if (DEBUG) {
Log.v(TAG, msg);
}
}
static void logInfo(String msg) {
Log.i(TAG, msg);
RECENT_INFO_LOG.add(msg);

View File

@@ -42,8 +42,9 @@ public abstract class Classifier {
public static final int QS_COLLAPSE = 12;
public static final int UDFPS_AUTHENTICATION = 13;
public static final int LOCK_ICON = 14;
public static final int QS_SWIPE = 15;
public static final int QS_SWIPE_SIDE = 15;
public static final int BACK_GESTURE = 16;
public static final int QS_SWIPE_NESTED = 17;
@IntDef({
QUICK_SETTINGS,
@@ -62,7 +63,8 @@ public abstract class Classifier {
BRIGHTNESS_SLIDER,
UDFPS_AUTHENTICATION,
LOCK_ICON,
QS_SWIPE,
QS_SWIPE_SIDE,
QS_SWIPE_NESTED,
BACK_GESTURE
})
@Retention(RetentionPolicy.SOURCE)

View File

@@ -24,6 +24,7 @@ import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHT
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_VERTICAL_SWIPE_THRESHOLD_IN;
import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
import static com.android.systemui.classifier.Classifier.QS_SWIPE_NESTED;
import static com.android.systemui.classifier.Classifier.SHADE_DRAG;
import android.provider.DeviceConfig;
@@ -156,7 +157,8 @@ class DistanceClassifier extends FalsingClassifier {
|| interactionType == QS_COLLAPSE
|| interactionType == Classifier.UDFPS_AUTHENTICATION
|| interactionType == Classifier.LOCK_ICON
|| interactionType == Classifier.QS_SWIPE) {
|| interactionType == Classifier.QS_SWIPE_SIDE
|| interactionType == QS_SWIPE_NESTED) {
return Result.passed(0);
}

View File

@@ -147,6 +147,11 @@ public abstract class FalsingClassifier {
BrightLineFalsingManager.logDebug(msg);
}
/** */
public static void logVerbose(String msg) {
BrightLineFalsingManager.logVerbose(msg);
}
/** */
public static void logInfo(String msg) {
BrightLineFalsingManager.logInfo(msg);

View File

@@ -78,10 +78,10 @@ public class FalsingDataProvider {
void onMotionEvent(MotionEvent motionEvent) {
List<MotionEvent> motionEvents = unpackMotionEvent(motionEvent);
FalsingClassifier.logDebug("Unpacked into: " + motionEvents.size());
FalsingClassifier.logVerbose("Unpacked into: " + motionEvents.size());
if (BrightLineFalsingManager.DEBUG) {
for (MotionEvent m : motionEvents) {
FalsingClassifier.logDebug(
FalsingClassifier.logVerbose(
"x,y,t: " + m.getX() + "," + m.getY() + "," + m.getEventTime());
}
}
@@ -92,7 +92,7 @@ public class FalsingDataProvider {
}
mRecentMotionEvents.addAll(motionEvents);
FalsingClassifier.logDebug("Size: " + mRecentMotionEvents.size());
FalsingClassifier.logVerbose("Size: " + mRecentMotionEvents.size());
mMotionEventListeners.forEach(listener -> listener.onMotionEvent(motionEvent));

View File

@@ -19,7 +19,7 @@ package com.android.systemui.classifier;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD;
import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
import static com.android.systemui.classifier.Classifier.QS_SWIPE;
import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import android.provider.DeviceConfig;
@@ -119,7 +119,7 @@ class ProximityClassifier extends FalsingClassifier {
@Classifier.InteractionType int interactionType,
double historyBelief, double historyConfidence) {
if (interactionType == QUICK_SETTINGS || interactionType == BRIGHTNESS_SLIDER
|| interactionType == QS_COLLAPSE || interactionType == QS_SWIPE) {
|| interactionType == QS_COLLAPSE || interactionType == QS_SWIPE_SIDE) {
return Result.passed(0);
}

View File

@@ -24,7 +24,8 @@ import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
import static com.android.systemui.classifier.Classifier.PULSE_EXPAND;
import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
import static com.android.systemui.classifier.Classifier.QS_SWIPE;
import static com.android.systemui.classifier.Classifier.QS_SWIPE_NESTED;
import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE;
import static com.android.systemui.classifier.Classifier.SHADE_DRAG;
@@ -86,9 +87,12 @@ public class TypeClassifier extends FalsingClassifier {
case QS_COLLAPSE:
wrongDirection = !vertical || !up;
break;
case QS_SWIPE:
case QS_SWIPE_SIDE:
wrongDirection = vertical;
break;
case QS_SWIPE_NESTED:
wrongDirection = !vertical;
break;
default:
wrongDirection = true;
break;

View File

@@ -137,8 +137,8 @@ class ZigZagClassifier extends FalsingClassifier {
runningAbsDy += Math.abs(point.y - pY);
pX = point.x;
pY = point.y;
logDebug("(x, y, runningAbsDx, runningAbsDy) - (" + pX + ", " + pY + ", " + runningAbsDx
+ ", " + runningAbsDy + ")");
logVerbose("(x, y, runningAbsDx, runningAbsDy) - ("
+ pX + ", " + pY + ", " + runningAbsDx + ", " + runningAbsDy + ")");
}
float devianceX = runningAbsDx - actualDx;

View File

@@ -28,21 +28,29 @@ import android.widget.ScrollView;
public class NonInterceptingScrollView extends ScrollView {
private final int mTouchSlop;
private float mDownY;
private boolean mScrollEnabled = true;
private boolean mPreventingIntercept;
public NonInterceptingScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
public boolean isPreventingIntercept() {
return mPreventingIntercept;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN:
mPreventingIntercept = false;
if (canScrollVertically(1)) {
// If we can scroll down, make sure we're not intercepted by the parent
mPreventingIntercept = true;
final ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
@@ -62,10 +70,13 @@ public class NonInterceptingScrollView extends ScrollView {
public boolean onInterceptTouchEvent(MotionEvent ev) {
// If there's a touch on this view and we can scroll down, we don't want to be intercepted
int action = ev.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN:
// If we can scroll down, make sure non of our parents intercepts us.
mPreventingIntercept = false;
// If we can scroll down, make sure none of our parents intercepts us.
if (canScrollVertically(1)) {
mPreventingIntercept = true;
final ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);

View File

@@ -131,6 +131,10 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
updateClippingPath();
}
public NonInterceptingScrollView getQSPanelContainer() {
return mQSPanelContainer;
}
public void disable(int state1, int state2, boolean animate) {
final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
if (disabled == mQsDisabled) return;

View File

@@ -16,8 +16,13 @@
package com.android.systemui.qs;
import android.content.res.Configuration;
import static com.android.systemui.classifier.Classifier.QS_SWIPE_NESTED;
import android.content.res.Configuration;
import android.view.MotionEvent;
import android.view.View;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.ViewController;
@@ -30,6 +35,8 @@ public class QSContainerImplController extends ViewController<QSContainerImpl> {
private final QSPanelController mQsPanelController;
private final QuickStatusBarHeaderController mQuickStatusBarHeaderController;
private final ConfigurationController mConfigurationController;
private final FalsingManager mFalsingManager;
private final NonInterceptingScrollView mQSPanelContainer;
private final ConfigurationController.ConfigurationListener mConfigurationListener =
new ConfigurationController.ConfigurationListener() {
@@ -39,14 +46,32 @@ public class QSContainerImplController extends ViewController<QSContainerImpl> {
}
};
private final View.OnTouchListener mContainerTouchHandler = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
if (mQSPanelContainer.isPreventingIntercept()) {
// There's really no action here to take, but we need to tell the FalsingManager
mFalsingManager.isFalseTouch(QS_SWIPE_NESTED);
}
}
return false;
}
};
@Inject
QSContainerImplController(QSContainerImpl view, QSPanelController qsPanelController,
QSContainerImplController(
QSContainerImpl view,
QSPanelController qsPanelController,
QuickStatusBarHeaderController quickStatusBarHeaderController,
ConfigurationController configurationController) {
ConfigurationController configurationController,
FalsingManager falsingManager) {
super(view);
mQsPanelController = qsPanelController;
mQuickStatusBarHeaderController = quickStatusBarHeaderController;
mConfigurationController = configurationController;
mFalsingManager = falsingManager;
mQSPanelContainer = mView.getQSPanelContainer();
}
@Override
@@ -62,11 +87,13 @@ public class QSContainerImplController extends ViewController<QSContainerImpl> {
protected void onViewAttached() {
mView.updateResources(mQsPanelController, mQuickStatusBarHeaderController);
mConfigurationController.addCallback(mConfigurationListener);
mQSPanelContainer.setOnTouchListener(mContainerTouchHandler);
}
@Override
protected void onViewDetached() {
mConfigurationController.removeCallback(mConfigurationListener);
mQSPanelContainer.setOnTouchListener(null);
}
public QSContainerImpl getView() {

View File

@@ -16,7 +16,7 @@
package com.android.systemui.qs;
import static com.android.systemui.classifier.Classifier.QS_SWIPE;
import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE;
import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
import static com.android.systemui.qs.QSPanel.QS_SHOW_BRIGHTNESS;
import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
@@ -76,7 +76,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
mFalsingManager.isFalseTouch(QS_SWIPE);
mFalsingManager.isFalseTouch(QS_SWIPE_SIDE);
}
return false;
}

View File

@@ -17,7 +17,7 @@
package com.android.systemui.classifier;
import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
import static com.android.systemui.classifier.Classifier.QS_SWIPE;
import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE;
import static com.google.common.truth.Truth.assertThat;
@@ -106,9 +106,9 @@ public class DistanceClassifierTest extends ClassifierTest {
}
@Test
public void testPass_QsSwipeAlwaysPasses() {
public void testPass_QsSwipeSideAlwaysPasses() {
mClassifier.onTouchEvent(appendDownEvent(1, 1));
assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 1).isFalse())
assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 1).isFalse())
.isFalse();
}
}

View File

@@ -22,7 +22,8 @@ import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE;
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
import static com.android.systemui.classifier.Classifier.PULSE_EXPAND;
import static com.android.systemui.classifier.Classifier.QS_SWIPE;
import static com.android.systemui.classifier.Classifier.QS_SWIPE_NESTED;
import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE;
import static com.android.systemui.classifier.Classifier.UNLOCK;
@@ -323,44 +324,86 @@ public class TypeClassifierTest extends ClassifierTest {
}
@Test
public void testPass_QsSwipe() {
public void testPass_QsSwipeSide() {
when(mDataProvider.isVertical()).thenReturn(false);
when(mDataProvider.isUp()).thenReturn(false); // up and right should cause no effect.
when(mDataProvider.isRight()).thenReturn(false);
assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isFalse();
assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 0).isFalse()).isFalse();
when(mDataProvider.isUp()).thenReturn(true);
when(mDataProvider.isRight()).thenReturn(false);
assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isFalse();
assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 0).isFalse()).isFalse();
when(mDataProvider.isUp()).thenReturn(false);
when(mDataProvider.isRight()).thenReturn(true);
assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isFalse();
assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 0).isFalse()).isFalse();
when(mDataProvider.isUp()).thenReturn(true);
when(mDataProvider.isRight()).thenReturn(true);
assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isFalse();
assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 0).isFalse()).isFalse();
}
@Test
public void testFalse_QsSwipe() {
public void testFalse_QsSwipeSide() {
when(mDataProvider.isVertical()).thenReturn(true);
when(mDataProvider.isUp()).thenReturn(false); // up and right should cause no effect.
when(mDataProvider.isRight()).thenReturn(false);
assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isTrue();
assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 0).isFalse()).isTrue();
when(mDataProvider.isUp()).thenReturn(true);
when(mDataProvider.isRight()).thenReturn(false);
assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isTrue();
assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 0).isFalse()).isTrue();
when(mDataProvider.isUp()).thenReturn(false);
when(mDataProvider.isRight()).thenReturn(true);
assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isTrue();
assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 0).isFalse()).isTrue();
when(mDataProvider.isUp()).thenReturn(true);
when(mDataProvider.isRight()).thenReturn(true);
assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isTrue();
assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 0).isFalse()).isTrue();
}
@Test
public void testPass_QsNestedSwipe() {
when(mDataProvider.isVertical()).thenReturn(true);
when(mDataProvider.isUp()).thenReturn(false); // up and right should cause no effect.
when(mDataProvider.isRight()).thenReturn(false);
assertThat(mClassifier.classifyGesture(QS_SWIPE_NESTED, 0.5, 0).isFalse()).isFalse();
when(mDataProvider.isUp()).thenReturn(true);
when(mDataProvider.isRight()).thenReturn(false);
assertThat(mClassifier.classifyGesture(QS_SWIPE_NESTED, 0.5, 0).isFalse()).isFalse();
when(mDataProvider.isUp()).thenReturn(false);
when(mDataProvider.isRight()).thenReturn(true);
assertThat(mClassifier.classifyGesture(QS_SWIPE_NESTED, 0.5, 0).isFalse()).isFalse();
when(mDataProvider.isUp()).thenReturn(true);
when(mDataProvider.isRight()).thenReturn(true);
assertThat(mClassifier.classifyGesture(QS_SWIPE_NESTED, 0.5, 0).isFalse()).isFalse();
}
@Test
public void testFalse_QsNestedSwipe() {
when(mDataProvider.isVertical()).thenReturn(false);
when(mDataProvider.isUp()).thenReturn(false); // up and right should cause no effect.
when(mDataProvider.isRight()).thenReturn(false);
assertThat(mClassifier.classifyGesture(QS_SWIPE_NESTED, 0.5, 0).isFalse()).isTrue();
when(mDataProvider.isUp()).thenReturn(true);
when(mDataProvider.isRight()).thenReturn(false);
assertThat(mClassifier.classifyGesture(QS_SWIPE_NESTED, 0.5, 0).isFalse()).isTrue();
when(mDataProvider.isUp()).thenReturn(false);
when(mDataProvider.isRight()).thenReturn(true);
assertThat(mClassifier.classifyGesture(QS_SWIPE_NESTED, 0.5, 0).isFalse()).isTrue();
when(mDataProvider.isUp()).thenReturn(true);
when(mDataProvider.isRight()).thenReturn(true);
assertThat(mClassifier.classifyGesture(QS_SWIPE_NESTED, 0.5, 0).isFalse()).isTrue();
}
}