• 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_ZIGZAG_X_PRIMARY_DEVIANCE;
20 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_X_SECONDARY_DEVIANCE;
21 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_PRIMARY_DEVIANCE;
22 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_SECONDARY_DEVIANCE;
23 import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
24 import static com.android.systemui.classifier.Classifier.SHADE_DRAG;
25 
26 import android.graphics.Point;
27 import android.provider.DeviceConfig;
28 import android.view.MotionEvent;
29 
30 import com.android.systemui.util.DeviceConfigProxy;
31 
32 import java.util.ArrayList;
33 import java.util.List;
34 import java.util.Locale;
35 
36 import javax.inject.Inject;
37 
38 /**
39  * Penalizes gestures that change direction in either the x or y too much.
40  */
41 class ZigZagClassifier extends FalsingClassifier {
42 
43     // Define how far one can move back and forth over one inch of travel before being falsed.
44     // `PRIMARY` defines how far one can deviate in the primary direction of travel. I.e. if you're
45     // swiping vertically, you shouldn't have a lot of zig zag in the vertical direction. Since
46     // most swipes will follow somewhat of a 'C' or 'S' shape, we allow more deviance along the
47     // `SECONDARY` axis.
48     private static final float MAX_X_PRIMARY_DEVIANCE = .05f;
49     private static final float MAX_Y_PRIMARY_DEVIANCE = .15f;
50     private static final float MAX_X_SECONDARY_DEVIANCE = .4f;
51     private static final float MAX_Y_SECONDARY_DEVIANCE = .3f;
52 
53     private final float mMaxXPrimaryDeviance;
54     private final float mMaxYPrimaryDeviance;
55     private final float mMaxXSecondaryDeviance;
56     private final float mMaxYSecondaryDeviance;
57     private float mLastDevianceX;
58     private float mLastDevianceY;
59     private float mLastMaxXDeviance;
60     private float mLastMaxYDeviance;
61 
62     @Inject
ZigZagClassifier(FalsingDataProvider dataProvider, DeviceConfigProxy deviceConfigProxy)63     ZigZagClassifier(FalsingDataProvider dataProvider, DeviceConfigProxy deviceConfigProxy) {
64         super(dataProvider);
65 
66         mMaxXPrimaryDeviance = deviceConfigProxy.getFloat(
67                 DeviceConfig.NAMESPACE_SYSTEMUI,
68                 BRIGHTLINE_FALSING_ZIGZAG_X_PRIMARY_DEVIANCE,
69                 MAX_X_PRIMARY_DEVIANCE);
70 
71         mMaxYPrimaryDeviance = deviceConfigProxy.getFloat(
72                 DeviceConfig.NAMESPACE_SYSTEMUI,
73                 BRIGHTLINE_FALSING_ZIGZAG_Y_PRIMARY_DEVIANCE,
74                 MAX_Y_PRIMARY_DEVIANCE);
75 
76         mMaxXSecondaryDeviance = deviceConfigProxy.getFloat(
77                 DeviceConfig.NAMESPACE_SYSTEMUI,
78                 BRIGHTLINE_FALSING_ZIGZAG_X_SECONDARY_DEVIANCE,
79                 MAX_X_SECONDARY_DEVIANCE);
80 
81         mMaxYSecondaryDeviance = deviceConfigProxy.getFloat(
82                 DeviceConfig.NAMESPACE_SYSTEMUI,
83                 BRIGHTLINE_FALSING_ZIGZAG_Y_SECONDARY_DEVIANCE,
84                 MAX_Y_SECONDARY_DEVIANCE);
85 
86     }
87 
88     @Override
calculateFalsingResult( @lassifier.InteractionType int interactionType, double historyBelief, double historyConfidence)89     Result calculateFalsingResult(
90             @Classifier.InteractionType int interactionType,
91             double historyBelief, double historyConfidence) {
92         if (interactionType == BRIGHTNESS_SLIDER || interactionType == SHADE_DRAG) {
93             return Result.passed(0);
94         }
95 
96         List<MotionEvent> motionEvents = getRecentMotionEvents();
97         // Rotate horizontal gestures to be horizontal between their first and last point.
98         // Rotate vertical gestures to be vertical between their first and last point.
99         // Sum the absolute value of every dx and dy along the gesture. Compare this with the dx
100         // and dy
101         // between the first and last point.
102         // For horizontal lines, the difference in the x direction should be small.
103         // For vertical lines, the difference in the y direction should be small.
104 
105         if (motionEvents.size() < 3) {
106             return Result.passed(0);
107         }
108 
109         List<Point> rotatedPoints;
110         if (isHorizontal()) {
111             rotatedPoints = rotateHorizontal();
112         } else {
113             rotatedPoints = rotateVertical();
114         }
115 
116         float actualDx = Math
117                 .abs(rotatedPoints.get(0).x - rotatedPoints.get(rotatedPoints.size() - 1).x);
118         float actualDy = Math
119                 .abs(rotatedPoints.get(0).y - rotatedPoints.get(rotatedPoints.size() - 1).y);
120         logDebug("Actual: (" + actualDx + "," + actualDy + ")");
121         float runningAbsDx = 0;
122         float runningAbsDy = 0;
123         float pX = 0;
124         float pY = 0;
125         boolean firstLoop = true;
126         for (Point point : rotatedPoints) {
127             if (firstLoop) {
128                 pX = point.x;
129                 pY = point.y;
130                 firstLoop = false;
131                 continue;
132             }
133             runningAbsDx += Math.abs(point.x - pX);
134             runningAbsDy += Math.abs(point.y - pY);
135             pX = point.x;
136             pY = point.y;
137             logDebug("(x, y, runningAbsDx, runningAbsDy) - (" + pX + ", " + pY + ", " + runningAbsDx
138                     + ", " + runningAbsDy + ")");
139         }
140 
141         float devianceX = runningAbsDx - actualDx;
142         float devianceY = runningAbsDy - actualDy;
143         float distanceXIn = actualDx / getXdpi();
144         float distanceYIn = actualDy / getYdpi();
145         float totalDistanceIn = (float) Math
146                 .sqrt(distanceXIn * distanceXIn + distanceYIn * distanceYIn);
147 
148         float maxXDeviance;
149         float maxYDeviance;
150         if (actualDx > actualDy) {
151             maxXDeviance = mMaxXPrimaryDeviance * totalDistanceIn * getXdpi();
152             maxYDeviance = mMaxYSecondaryDeviance * totalDistanceIn * getYdpi();
153         } else {
154             maxXDeviance = mMaxXSecondaryDeviance * totalDistanceIn * getXdpi();
155             maxYDeviance = mMaxYPrimaryDeviance * totalDistanceIn * getYdpi();
156         }
157 
158         // These values are saved for logging reasons. {@see #getReason()}
159         mLastDevianceX = devianceX;
160         mLastDevianceY = devianceY;
161         mLastMaxXDeviance = maxXDeviance;
162         mLastMaxYDeviance = maxYDeviance;
163 
164         logDebug("Straightness Deviance: (" + devianceX + "," + devianceY + ") vs "
165                 + "(" + maxXDeviance + "," + maxYDeviance + ")");
166         return devianceX > maxXDeviance || devianceY > maxYDeviance
167             ? falsed(0.5, getReason()) : Result.passed(0.5);
168     }
169 
getReason()170     private String getReason() {
171         return String.format(
172                 (Locale) null,
173                 "{devianceX=%f, maxDevianceX=%s, devianceY=%s, maxDevianceY=%s}",
174                 mLastDevianceX, mLastMaxXDeviance, mLastDevianceY, mLastMaxYDeviance);
175     }
176 
getAtan2LastPoint()177     private float getAtan2LastPoint() {
178         MotionEvent firstEvent = getFirstMotionEvent();
179         MotionEvent lastEvent = getLastMotionEvent();
180         float offsetX = firstEvent.getX();
181         float offsetY = firstEvent.getY();
182         float lastX = lastEvent.getX() - offsetX;
183         float lastY = lastEvent.getY() - offsetY;
184 
185         return (float) Math.atan2(lastY, lastX);
186     }
187 
rotateVertical()188     private List<Point> rotateVertical() {
189         // Calculate the angle relative to the y axis.
190         double angle = Math.PI / 2 - getAtan2LastPoint();
191         logDebug("Rotating to vertical by: " + angle);
192         return rotateMotionEvents(getRecentMotionEvents(), -angle);
193     }
194 
rotateHorizontal()195     private List<Point> rotateHorizontal() {
196         // Calculate the angle relative to the x axis.
197         double angle = getAtan2LastPoint();
198         logDebug("Rotating to horizontal by: " + angle);
199         return rotateMotionEvents(getRecentMotionEvents(), angle);
200     }
201 
rotateMotionEvents(List<MotionEvent> motionEvents, double angle)202     private List<Point> rotateMotionEvents(List<MotionEvent> motionEvents, double angle) {
203         List<Point> points = new ArrayList<>();
204         double cosAngle = Math.cos(angle);
205         double sinAngle = Math.sin(angle);
206         MotionEvent firstEvent = motionEvents.get(0);
207         float offsetX = firstEvent.getX();
208         float offsetY = firstEvent.getY();
209         for (MotionEvent motionEvent : motionEvents) {
210             float x = motionEvent.getX() - offsetX;
211             float y = motionEvent.getY() - offsetY;
212             double rotatedX = cosAngle * x + sinAngle * y + offsetX;
213             double rotatedY = -sinAngle * x + cosAngle * y + offsetY;
214             points.add(new Point((int) rotatedX, (int) rotatedY));
215         }
216 
217         MotionEvent lastEvent = motionEvents.get(motionEvents.size() - 1);
218         Point firstPoint = points.get(0);
219         Point lastPoint = points.get(points.size() - 1);
220         logDebug(
221                 "Before: (" + firstEvent.getX() + "," + firstEvent.getY() + "), ("
222                         + lastEvent.getX() + ","
223                         + lastEvent.getY() + ")");
224         logDebug(
225                 "After: (" + firstPoint.x + "," + firstPoint.y + "), (" + lastPoint.x + ","
226                         + lastPoint.y
227                         + ")");
228 
229         return points;
230     }
231 
232 }
233