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_DIAGONAL_HORIZONTAL_ANGLE_RANGE;
20 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DIAGONAL_VERTICAL_ANGLE_RANGE;
21 import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE;
22 import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE;
23 
24 import android.provider.DeviceConfig;
25 
26 import com.android.systemui.util.DeviceConfigProxy;
27 
28 import java.util.Locale;
29 
30 import javax.inject.Inject;
31 
32 /**
33  * False on swipes that are too close to 45 degrees.
34  *
35  * Horizontal swipes may have a different threshold than vertical.
36  *
37  * This falser should not run on "affordance" swipes, as they will always be close to 45.
38  */
39 class DiagonalClassifier extends FalsingClassifier {
40 
41     private static final float HORIZONTAL_ANGLE_RANGE = (float) (5f / 360f * Math.PI * 2f);
42     private static final float VERTICAL_ANGLE_RANGE = (float) (5f / 360f * Math.PI * 2f);
43     private static final float DIAGONAL = (float) (Math.PI / 4); // 45 deg
44     private static final float NINETY_DEG = (float) (Math.PI / 2);
45     private static final float ONE_HUNDRED_EIGHTY_DEG = (float) (Math.PI);
46     private static final float THREE_HUNDRED_SIXTY_DEG = (float) (2 * Math.PI);
47 
48     private final float mHorizontalAngleRange;
49     private final float mVerticalAngleRange;
50 
51     @Inject
DiagonalClassifier(FalsingDataProvider dataProvider, DeviceConfigProxy deviceConfigProxy)52     DiagonalClassifier(FalsingDataProvider dataProvider, DeviceConfigProxy deviceConfigProxy) {
53         super(dataProvider);
54 
55         mHorizontalAngleRange = deviceConfigProxy.getFloat(
56                 DeviceConfig.NAMESPACE_SYSTEMUI,
57                 BRIGHTLINE_FALSING_DIAGONAL_HORIZONTAL_ANGLE_RANGE,
58                 HORIZONTAL_ANGLE_RANGE);
59         mVerticalAngleRange = deviceConfigProxy.getFloat(
60                 DeviceConfig.NAMESPACE_SYSTEMUI,
61                 BRIGHTLINE_FALSING_DIAGONAL_VERTICAL_ANGLE_RANGE,
62                 VERTICAL_ANGLE_RANGE);
63     }
64 
calculateFalsingResult( @lassifier.InteractionType int interactionType, double historyBelief, double historyConfidence)65     Result calculateFalsingResult(
66             @Classifier.InteractionType int interactionType,
67             double historyBelief, double historyConfidence) {
68         float angle = getAngle();
69 
70         if (angle == Float.MAX_VALUE) {  // Unknown angle
71             return Result.passed(0);
72         }
73 
74         if (interactionType == LEFT_AFFORDANCE
75                 || interactionType == RIGHT_AFFORDANCE) {
76             return Result.passed(0);
77         }
78 
79         float minAngle = DIAGONAL - mHorizontalAngleRange;
80         float maxAngle = DIAGONAL + mHorizontalAngleRange;
81         if (isVertical()) {
82             minAngle = DIAGONAL - mVerticalAngleRange;
83             maxAngle = DIAGONAL + mVerticalAngleRange;
84         }
85 
86         boolean falsed = angleBetween(angle, minAngle, maxAngle)
87                 || angleBetween(angle, minAngle + NINETY_DEG, maxAngle + NINETY_DEG)
88                 || angleBetween(angle, minAngle - NINETY_DEG, maxAngle - NINETY_DEG)
89                 || angleBetween(angle, minAngle + ONE_HUNDRED_EIGHTY_DEG,
90                 maxAngle + ONE_HUNDRED_EIGHTY_DEG);
91         return falsed ? falsed(0.5f, getReason()) : Result.passed(0.5);
92     }
93 
getReason()94     private String getReason() {
95         return String.format(
96                 (Locale) null,
97                 "{angle=%f, vertical=%s}",
98                 getAngle(),
99                 isVertical());
100     }
101 
angleBetween(float angle, float min, float max)102     private boolean angleBetween(float angle, float min, float max) {
103         // No need to normalize angle as it is guaranteed to be between 0 and 2*PI.
104         min = normalizeAngle(min);
105         max = normalizeAngle(max);
106 
107         if (min > max) {  // Can happen when angle is close to 0.
108             return angle >= min || angle <= max;
109         }
110 
111         return angle >= min && angle <= max;
112     }
113 
normalizeAngle(float angle)114     private float normalizeAngle(float angle) {
115         if (angle < 0) {
116             return THREE_HUNDRED_SIXTY_DEG + (angle % THREE_HUNDRED_SIXTY_DEG);
117         } else if (angle > THREE_HUNDRED_SIXTY_DEG) {
118             return angle % THREE_HUNDRED_SIXTY_DEG;
119         }
120         return angle;
121     }
122 }
123