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