• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.classifier;
18 
19 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_FLING_THRESHOLD_IN;
20 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_SWIPE_THRESHOLD_IN;
21 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_SCREEN_FRACTION_MAX_DISTANCE;
22 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_VELOCITY_TO_DISTANCE;
23 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_VERTICAL_FLING_THRESHOLD_IN;
24 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_VERTICAL_SWIPE_THRESHOLD_IN;
25 import static com.android.systemui.classifier.Classifier.ALTERNATE_BOUNCER_SWIPE;
26 import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
27 import static com.android.systemui.classifier.Classifier.MEDIA_SEEKBAR;
28 import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
29 import static com.android.systemui.classifier.Classifier.QS_SWIPE_NESTED;
30 import static com.android.systemui.classifier.Classifier.SHADE_DRAG;
31 
32 import android.provider.DeviceConfig;
33 import android.view.MotionEvent;
34 import android.view.VelocityTracker;
35 
36 import com.android.systemui.util.DeviceConfigProxy;
37 
38 import java.util.List;
39 import java.util.Locale;
40 
41 import javax.inject.Inject;
42 
43 /**
44  * Ensure that the swipe + momentum covers a minimum distance.
45  */
46 class DistanceClassifier extends FalsingClassifier {
47 
48     private static final float HORIZONTAL_FLING_THRESHOLD_DISTANCE_IN = 1;
49     private static final float VERTICAL_FLING_THRESHOLD_DISTANCE_IN = 1.5f;
50     private static final float HORIZONTAL_SWIPE_THRESHOLD_DISTANCE_IN = 3;
51     private static final float VERTICAL_SWIPE_THRESHOLD_DISTANCE_IN = 3;
52     private static final float VELOCITY_TO_DISTANCE = 30f;
53     private static final float SCREEN_FRACTION_MAX_DISTANCE = 0.8f;
54 
55     private final float mVerticalFlingThresholdPx;
56     private final float mHorizontalFlingThresholdPx;
57     private final float mVerticalSwipeThresholdPx;
58     private final float mHorizontalSwipeThresholdPx;
59     private final float mVelocityToDistanceMultiplier;
60 
61     private boolean mDistanceDirty;
62     private DistanceVectors mCachedDistance;
63 
64     @Inject
DistanceClassifier(FalsingDataProvider dataProvider, DeviceConfigProxy deviceConfigProxy)65     DistanceClassifier(FalsingDataProvider dataProvider, DeviceConfigProxy deviceConfigProxy) {
66         super(dataProvider);
67 
68         mVelocityToDistanceMultiplier = deviceConfigProxy.getFloat(
69                 DeviceConfig.NAMESPACE_SYSTEMUI,
70                 BRIGHTLINE_FALSING_DISTANCE_VELOCITY_TO_DISTANCE,
71                 VELOCITY_TO_DISTANCE);
72 
73         float horizontalFlingThresholdIn = deviceConfigProxy.getFloat(
74                 DeviceConfig.NAMESPACE_SYSTEMUI,
75                 BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_FLING_THRESHOLD_IN,
76                 HORIZONTAL_FLING_THRESHOLD_DISTANCE_IN);
77 
78         float verticalFlingThresholdIn = deviceConfigProxy.getFloat(
79                 DeviceConfig.NAMESPACE_SYSTEMUI,
80                 BRIGHTLINE_FALSING_DISTANCE_VERTICAL_FLING_THRESHOLD_IN,
81                 VERTICAL_FLING_THRESHOLD_DISTANCE_IN);
82 
83         float horizontalSwipeThresholdIn = deviceConfigProxy.getFloat(
84                 DeviceConfig.NAMESPACE_SYSTEMUI,
85                 BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_SWIPE_THRESHOLD_IN,
86                 HORIZONTAL_SWIPE_THRESHOLD_DISTANCE_IN);
87 
88         float verticalSwipeThresholdIn = deviceConfigProxy.getFloat(
89                 DeviceConfig.NAMESPACE_SYSTEMUI,
90                 BRIGHTLINE_FALSING_DISTANCE_VERTICAL_SWIPE_THRESHOLD_IN,
91                 VERTICAL_SWIPE_THRESHOLD_DISTANCE_IN);
92 
93         float screenFractionMaxDistance = deviceConfigProxy.getFloat(
94                 DeviceConfig.NAMESPACE_SYSTEMUI,
95                 BRIGHTLINE_FALSING_DISTANCE_SCREEN_FRACTION_MAX_DISTANCE,
96                 SCREEN_FRACTION_MAX_DISTANCE);
97 
98         mHorizontalFlingThresholdPx = Math
99                 .min(getWidthPixels() * screenFractionMaxDistance,
100                         horizontalFlingThresholdIn * getXdpi());
101         mVerticalFlingThresholdPx = Math
102                 .min(getHeightPixels() * screenFractionMaxDistance,
103                         verticalFlingThresholdIn * getYdpi());
104         mHorizontalSwipeThresholdPx = Math
105                 .min(getWidthPixels() * screenFractionMaxDistance,
106                         horizontalSwipeThresholdIn * getXdpi());
107         mVerticalSwipeThresholdPx = Math
108                 .min(getHeightPixels() * screenFractionMaxDistance,
109                         verticalSwipeThresholdIn * getYdpi());
110         mDistanceDirty = true;
111     }
112 
getDistances()113     private DistanceVectors getDistances() {
114         if (mDistanceDirty) {
115             mCachedDistance = calculateDistances();
116             mDistanceDirty = false;
117         }
118 
119         return mCachedDistance;
120     }
121 
calculateDistances()122     private DistanceVectors calculateDistances() {
123         // This code assumes that there will be no missed DOWN or UP events.
124         List<MotionEvent> motionEvents = getRecentMotionEvents();
125 
126         if (motionEvents.size() < 3) {
127             logDebug("Only " + motionEvents.size() + " motion events recorded.");
128             return new DistanceVectors(0, 0, 0, 0);
129         }
130 
131         VelocityTracker velocityTracker = VelocityTracker.obtain();
132         for (MotionEvent motionEvent : motionEvents) {
133             velocityTracker.addMovement(motionEvent);
134         }
135         velocityTracker.computeCurrentVelocity(1);
136 
137         float vX = velocityTracker.getXVelocity();
138         float vY = velocityTracker.getYVelocity();
139 
140         velocityTracker.recycle();
141 
142         float dX = getLastMotionEvent().getX() - getFirstMotionEvent().getX();
143         float dY = getLastMotionEvent().getY() - getFirstMotionEvent().getY();
144 
145         return new DistanceVectors(dX, dY, vX, vY);
146     }
147 
148     @Override
onTouchEvent(MotionEvent motionEvent)149     public void onTouchEvent(MotionEvent motionEvent) {
150         mDistanceDirty = true;
151     }
152 
153     @Override
calculateFalsingResult( @lassifier.InteractionType int interactionType, double historyBelief, double historyConfidence)154     Result calculateFalsingResult(
155             @Classifier.InteractionType int interactionType,
156             double historyBelief, double historyConfidence) {
157         if (interactionType == BRIGHTNESS_SLIDER
158                 || interactionType == MEDIA_SEEKBAR
159                 || interactionType == SHADE_DRAG
160                 || interactionType == QS_COLLAPSE
161                 || interactionType == Classifier.UDFPS_AUTHENTICATION
162                 || interactionType == Classifier.QS_SWIPE_SIDE
163                 || interactionType == QS_SWIPE_NESTED
164                 || interactionType == ALTERNATE_BOUNCER_SWIPE) {
165             return Result.passed(0);
166         }
167 
168         return !getPassedFlingThreshold() ? falsed(0.5, getReason()) : Result.passed(0.5);
169     }
170 
getReason()171     String getReason() {
172         DistanceVectors distanceVectors = getDistances();
173 
174         return String.format(
175                 (Locale) null,
176                 "{distanceVectors=%s, isHorizontal=%s, velocityToDistanceMultiplier=%f, "
177                         + "horizontalFlingThreshold=%f, verticalFlingThreshold=%f, "
178                         + "horizontalSwipeThreshold=%f, verticalSwipeThreshold=%s}",
179                 distanceVectors,
180                 isHorizontal(),
181                 mVelocityToDistanceMultiplier,
182                 mHorizontalFlingThresholdPx,
183                 mVerticalFlingThresholdPx,
184                 mHorizontalSwipeThresholdPx,
185                 mVerticalSwipeThresholdPx);
186     }
187 
isLongSwipe()188     Result isLongSwipe() {
189         boolean longSwipe = getPassedDistanceThreshold();
190         logDebug("Is longSwipe? " + longSwipe);
191         return longSwipe ? Result.passed(0.5) : falsed(0.5, getReason());
192     }
193 
getPassedDistanceThreshold()194     private boolean getPassedDistanceThreshold() {
195         DistanceVectors distanceVectors = getDistances();
196         if (isHorizontal()) {
197             logDebug("Horizontal swipe distance: " + Math.abs(distanceVectors.mDx));
198             logDebug("Threshold: " + mHorizontalSwipeThresholdPx);
199 
200             return Math.abs(distanceVectors.mDx) >= mHorizontalSwipeThresholdPx;
201         }
202 
203         logDebug("Vertical swipe distance: " + Math.abs(distanceVectors.mDy));
204         logDebug("Threshold: " + mVerticalSwipeThresholdPx);
205         return Math.abs(distanceVectors.mDy) >= mVerticalSwipeThresholdPx;
206     }
207 
getPassedFlingThreshold()208     private boolean getPassedFlingThreshold() {
209         DistanceVectors distanceVectors = getDistances();
210 
211         float dX = distanceVectors.mDx + distanceVectors.mVx * mVelocityToDistanceMultiplier;
212         float dY = distanceVectors.mDy + distanceVectors.mVy * mVelocityToDistanceMultiplier;
213 
214         if (isHorizontal()) {
215             logDebug("Horizontal swipe and fling distance: " + distanceVectors.mDx + ", "
216                     + distanceVectors.mVx * mVelocityToDistanceMultiplier);
217             logDebug("Threshold: " + mHorizontalFlingThresholdPx);
218             return Math.abs(dX) >= mHorizontalFlingThresholdPx;
219         }
220 
221         logDebug("Vertical swipe and fling distance: " + distanceVectors.mDy + ", "
222                 + distanceVectors.mVy * mVelocityToDistanceMultiplier);
223         logDebug("Threshold: " + mVerticalFlingThresholdPx);
224         return Math.abs(dY) >= mVerticalFlingThresholdPx;
225     }
226 
227     private class DistanceVectors {
228         final float mDx;
229         final float mDy;
230         private final float mVx;
231         private final float mVy;
232 
DistanceVectors(float dX, float dY, float vX, float vY)233         DistanceVectors(float dX, float dY, float vX, float vY) {
234             this.mDx = dX;
235             this.mDy = dY;
236             this.mVx = vX;
237             this.mVy = vY;
238         }
239 
240         @Override
toString()241         public String toString() {
242             return String.format((Locale) null, "{dx=%f, vx=%f, dy=%f, vy=%f}", mDx, mVx, mDy, mVy);
243         }
244     }
245 }
246