blob: d0fe1c37d4fab1fc1a023120d279a3b6d9b5333f [file] [log] [blame]
/*
* Copyright (C) 2020 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.classifier;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_FLING_THRESHOLD_IN;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_SWIPE_THRESHOLD_IN;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_SCREEN_FRACTION_MAX_DISTANCE;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_VELOCITY_TO_DISTANCE;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_VERTICAL_FLING_THRESHOLD_IN;
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.SHADE_DRAG;
import android.provider.DeviceConfig;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import com.android.systemui.util.DeviceConfigProxy;
import java.util.List;
import java.util.Locale;
import javax.inject.Inject;
/**
* Ensure that the swipe + momentum covers a minimum distance.
*/
class DistanceClassifier extends FalsingClassifier {
private static final float HORIZONTAL_FLING_THRESHOLD_DISTANCE_IN = 1;
private static final float VERTICAL_FLING_THRESHOLD_DISTANCE_IN = 1.5f;
private static final float HORIZONTAL_SWIPE_THRESHOLD_DISTANCE_IN = 3;
private static final float VERTICAL_SWIPE_THRESHOLD_DISTANCE_IN = 3;
private static final float VELOCITY_TO_DISTANCE = 30f;
private static final float SCREEN_FRACTION_MAX_DISTANCE = 0.8f;
private final float mVerticalFlingThresholdPx;
private final float mHorizontalFlingThresholdPx;
private final float mVerticalSwipeThresholdPx;
private final float mHorizontalSwipeThresholdPx;
private final float mVelocityToDistanceMultiplier;
private boolean mDistanceDirty;
private DistanceVectors mCachedDistance;
@Inject
DistanceClassifier(FalsingDataProvider dataProvider, DeviceConfigProxy deviceConfigProxy) {
super(dataProvider);
mVelocityToDistanceMultiplier = deviceConfigProxy.getFloat(
DeviceConfig.NAMESPACE_SYSTEMUI,
BRIGHTLINE_FALSING_DISTANCE_VELOCITY_TO_DISTANCE,
VELOCITY_TO_DISTANCE);
float horizontalFlingThresholdIn = deviceConfigProxy.getFloat(
DeviceConfig.NAMESPACE_SYSTEMUI,
BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_FLING_THRESHOLD_IN,
HORIZONTAL_FLING_THRESHOLD_DISTANCE_IN);
float verticalFlingThresholdIn = deviceConfigProxy.getFloat(
DeviceConfig.NAMESPACE_SYSTEMUI,
BRIGHTLINE_FALSING_DISTANCE_VERTICAL_FLING_THRESHOLD_IN,
VERTICAL_FLING_THRESHOLD_DISTANCE_IN);
float horizontalSwipeThresholdIn = deviceConfigProxy.getFloat(
DeviceConfig.NAMESPACE_SYSTEMUI,
BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_SWIPE_THRESHOLD_IN,
HORIZONTAL_SWIPE_THRESHOLD_DISTANCE_IN);
float verticalSwipeThresholdIn = deviceConfigProxy.getFloat(
DeviceConfig.NAMESPACE_SYSTEMUI,
BRIGHTLINE_FALSING_DISTANCE_VERTICAL_SWIPE_THRESHOLD_IN,
VERTICAL_SWIPE_THRESHOLD_DISTANCE_IN);
float screenFractionMaxDistance = deviceConfigProxy.getFloat(
DeviceConfig.NAMESPACE_SYSTEMUI,
BRIGHTLINE_FALSING_DISTANCE_SCREEN_FRACTION_MAX_DISTANCE,
SCREEN_FRACTION_MAX_DISTANCE);
mHorizontalFlingThresholdPx = Math
.min(getWidthPixels() * screenFractionMaxDistance,
horizontalFlingThresholdIn * getXdpi());
mVerticalFlingThresholdPx = Math
.min(getHeightPixels() * screenFractionMaxDistance,
verticalFlingThresholdIn * getYdpi());
mHorizontalSwipeThresholdPx = Math
.min(getWidthPixels() * screenFractionMaxDistance,
horizontalSwipeThresholdIn * getXdpi());
mVerticalSwipeThresholdPx = Math
.min(getHeightPixels() * screenFractionMaxDistance,
verticalSwipeThresholdIn * getYdpi());
mDistanceDirty = true;
}
private DistanceVectors getDistances() {
if (mDistanceDirty) {
mCachedDistance = calculateDistances();
mDistanceDirty = false;
}
return mCachedDistance;
}
private DistanceVectors calculateDistances() {
// This code assumes that there will be no missed DOWN or UP events.
List<MotionEvent> motionEvents = getRecentMotionEvents();
if (motionEvents.size() < 3) {
logDebug("Only " + motionEvents.size() + " motion events recorded.");
return new DistanceVectors(0, 0, 0, 0);
}
VelocityTracker velocityTracker = VelocityTracker.obtain();
for (MotionEvent motionEvent : motionEvents) {
velocityTracker.addMovement(motionEvent);
}
velocityTracker.computeCurrentVelocity(1);
float vX = velocityTracker.getXVelocity();
float vY = velocityTracker.getYVelocity();
velocityTracker.recycle();
float dX = getLastMotionEvent().getX() - getFirstMotionEvent().getX();
float dY = getLastMotionEvent().getY() - getFirstMotionEvent().getY();
return new DistanceVectors(dX, dY, vX, vY);
}
@Override
public void onTouchEvent(MotionEvent motionEvent) {
mDistanceDirty = true;
}
@Override
Result calculateFalsingResult(
@Classifier.InteractionType int interactionType,
double historyBelief, double historyConfidence) {
if (interactionType == BRIGHTNESS_SLIDER
|| interactionType == SHADE_DRAG
|| interactionType == QS_COLLAPSE
|| interactionType == Classifier.UDFPS_AUTHENTICATION
|| interactionType == Classifier.LOCK_ICON
|| interactionType == Classifier.QS_SWIPE) {
return Result.passed(0);
}
return !getPassedFlingThreshold() ? falsed(0.5, getReason()) : Result.passed(0.5);
}
String getReason() {
DistanceVectors distanceVectors = getDistances();
return String.format(
(Locale) null,
"{distanceVectors=%s, isHorizontal=%s, velocityToDistanceMultiplier=%f, "
+ "horizontalFlingThreshold=%f, verticalFlingThreshold=%f, "
+ "horizontalSwipeThreshold=%f, verticalSwipeThreshold=%s}",
distanceVectors,
isHorizontal(),
mVelocityToDistanceMultiplier,
mHorizontalFlingThresholdPx,
mVerticalFlingThresholdPx,
mHorizontalSwipeThresholdPx,
mVerticalSwipeThresholdPx);
}
Result isLongSwipe() {
boolean longSwipe = getPassedDistanceThreshold();
logDebug("Is longSwipe? " + longSwipe);
return longSwipe ? Result.passed(0.5) : falsed(0.5, getReason());
}
private boolean getPassedDistanceThreshold() {
DistanceVectors distanceVectors = getDistances();
if (isHorizontal()) {
logDebug("Horizontal swipe distance: " + Math.abs(distanceVectors.mDx));
logDebug("Threshold: " + mHorizontalSwipeThresholdPx);
return Math.abs(distanceVectors.mDx) >= mHorizontalSwipeThresholdPx;
}
logDebug("Vertical swipe distance: " + Math.abs(distanceVectors.mDy));
logDebug("Threshold: " + mVerticalSwipeThresholdPx);
return Math.abs(distanceVectors.mDy) >= mVerticalSwipeThresholdPx;
}
private boolean getPassedFlingThreshold() {
DistanceVectors distanceVectors = getDistances();
float dX = distanceVectors.mDx + distanceVectors.mVx * mVelocityToDistanceMultiplier;
float dY = distanceVectors.mDy + distanceVectors.mVy * mVelocityToDistanceMultiplier;
if (isHorizontal()) {
logDebug("Horizontal swipe and fling distance: " + distanceVectors.mDx + ", "
+ distanceVectors.mVx * mVelocityToDistanceMultiplier);
logDebug("Threshold: " + mHorizontalFlingThresholdPx);
return Math.abs(dX) >= mHorizontalFlingThresholdPx;
}
logDebug("Vertical swipe and fling distance: " + distanceVectors.mDy + ", "
+ distanceVectors.mVy * mVelocityToDistanceMultiplier);
logDebug("Threshold: " + mVerticalFlingThresholdPx);
return Math.abs(dY) >= mVerticalFlingThresholdPx;
}
private class DistanceVectors {
final float mDx;
final float mDy;
private final float mVx;
private final float mVy;
DistanceVectors(float dX, float dY, float vX, float vY) {
this.mDx = dX;
this.mDy = dY;
this.mVx = vX;
this.mVy = vY;
}
@Override
public String toString() {
return String.format((Locale) null, "{dx=%f, vx=%f, dy=%f, vy=%f}", mDx, mVx, mDy, mVy);
}
}
}