Merge changes from topic "b172655679-bayes-falsing" into sc-dev
* changes: Add Falsing to the QS BrightnessSlider Add documentation for Falsing.
This commit is contained in:
240
packages/SystemUI/docs/falsing.md
Normal file
240
packages/SystemUI/docs/falsing.md
Normal file
@@ -0,0 +1,240 @@
|
||||
# Falsing in SystemUI
|
||||
|
||||
Phones are easily and often accidentally-activated in owners' pockets ("falsing" or "pocket
|
||||
dialing"). Because a phone's screen can be turned on with a single tap, and because we have further
|
||||
actions that be activated with basic tapping and swiping, it is critical that we
|
||||
analyze touch events on the screen for intentional vs accidental behavior. With analysis,
|
||||
features within SystemUI have an opportunity to ignore or even undo accidental interactions as they
|
||||
are occurring.
|
||||
|
||||
## Technical Details
|
||||
|
||||
The `FalsingManager` tracks all touch interactions happening on a phone's lock screen.
|
||||
|
||||
If you support any sort of touch gestures on the lock screen, you **must**, at a
|
||||
minimum, inform the `FalsingManager` of what touches are on touch targets vs not (things that may be
|
||||
intentional). If you do not tell the `FalsingManager`, it will assume touches on your feature are
|
||||
always accidental and penalize the session accordingly.
|
||||
|
||||
Individual touch targets do not _have_ to be separated out; it's acceptable to
|
||||
wrap your whole feature in one virtual block that reports touches to the
|
||||
`FalsingManager`, however more granular tracking will result in better results
|
||||
across the whole lock screen.
|
||||
|
||||
You can _act_ on the results of the `FalsingManager`. Instead of only telling
|
||||
the `FalsingManager` that touch events were on touch targets, you can further use the
|
||||
returned results to decide if you want to respond to an owner's touch, if you
|
||||
want to prompt them to confirm their action, or if you simply want to ignore the
|
||||
touch.
|
||||
|
||||
The flow through the system looks like such:
|
||||
|
||||
1. Gesture on the screen.
|
||||
2. The `FalsingManager` makes a note of all of the `MotionEvents`.
|
||||
* If no feature/touch target receives the `MotionEvents`, skip to 4.
|
||||
3. Your touch target receives the `MotionEvents`.
|
||||
* Once your feature is ready to respond to the gesture in a substantive manner, it queries
|
||||
the `FalsingManager`.
|
||||
- Dragging animations, touch ripples, and other purely visual effects should not query.
|
||||
- Query once you are ready to launch a new feature or dialogue, or are otherwise going to
|
||||
change the state of the UI.
|
||||
- Generally, wait until `MotionEvent.ACTION_UP` to query or `View.OnClickListener#onClick`.
|
||||
- Only query once per gesture, at the end.
|
||||
* If the `FalsingManager` says it looks good, respond to the touch.
|
||||
4. The `FalsingManager` checks to see if anyone queried about the gesture. If not, mark it as
|
||||
accidental.
|
||||
|
||||
There is also an event fired by the `FalsingManager` that can be listened to by anyone, that
|
||||
indicates that the the `FalsingManager` believes the phone is actively being pocket-dialed. When
|
||||
fired, modal features, such as quick settings, keyguard bouncer, and others should retract
|
||||
themselves to prevent further pocket-dialing.
|
||||
|
||||
## Falsing "Belief" and History
|
||||
|
||||
The `FalsingManager` maintains a recent history of false analyses. Using
|
||||
Bayesian statistics, it updates a "belief" in whether recent
|
||||
gestures are intentional or not. Any gesture that it is not explicitly queried about is treated as
|
||||
accidental, increasing the overall belief in
|
||||
false-iness. Gestures that are explicitly queried and that pass the relevant heuristics
|
||||
reduce belief that falsing is occurring. This information is tracked within the `HistoryTracker`.
|
||||
|
||||
Changes in belief may influence internal heurstics within the `FalsingManager`,
|
||||
making it easier or harder for an owner to interact with their device. (An owner
|
||||
will always be able to interact with their device, but we may require double
|
||||
taps, or more deliberate swipes.)
|
||||
|
||||
## Responding to Touch Events
|
||||
|
||||
The methods below inform the `FalsingManager` that a tap is occurring within an expected touch
|
||||
target. Match the methods with the gesture you expect the device owner to use.
|
||||
|
||||
### Single Tap
|
||||
|
||||
`FalsingManager#isFalseTap(boolean robustCheck, double falsePenalty)`. This
|
||||
method tells the `FalsingManager` that you want to validate a single tap. It
|
||||
returns true if it thinks the tap should be rejected (i.e. the tap looks more
|
||||
like a swipe) and false otherwise.
|
||||
|
||||
`robustCheck` determines what heuristics are used. If set to false, the method
|
||||
performs a only very basic checking, checking that observed `MotionEvent`s are
|
||||
all within some small x & y region ("touch slop").
|
||||
|
||||
When `robustCheck` is set to true, several more advanced rules are additionally
|
||||
applied:
|
||||
|
||||
1. If the device recognizes a face (i.e. face-auth) the tap is **accepted**.
|
||||
2. If the tap is the _second_ tap in recent history and looks like a valid Double Tap
|
||||
the tap is **accepted**. This works exactly like `FalsingManager#isFalseDoubleTap`.
|
||||
3. If the `HistoryTracker` reports strong belief in recent falsing, the tap is
|
||||
**rejected**.
|
||||
4. Otherwise the tap is **accepted**.
|
||||
|
||||
All the above rules are applied only after first confirming the gesture does
|
||||
in fact look like a basic tap.
|
||||
|
||||
`falsePenalty` is a measure of how much the `HistoryTracker`'s belief should be
|
||||
penalized in the event that the tap is rejected. This value is only used if
|
||||
`robustCheck` is set to true.
|
||||
|
||||
A value of `0` means no change in belief. A value of `1` means a _very_ strong
|
||||
confidence in a false tap. In general, as a single tap on the screen is not
|
||||
verifiable, a small value should be supplied - on the order of `0.1`. Pass `0`
|
||||
if you don't want to penalize belief at all. Pass a higher value
|
||||
the earlier in the UX flow your interaction occurs. Once an owner is farther
|
||||
along in a UX flow (multiple taps or swipes), its safer to assume that a single
|
||||
accidental tap should cause less of a penalty.
|
||||
|
||||
### Double Tap
|
||||
|
||||
`FalsingManager#isFalseDoubleTap()`. This method tells the `FalsingManager` that
|
||||
your UI wants to validate a double tap. There are no parameters to pass to this method.
|
||||
Call this when you explicitly receive and want to verify a double tap, _not_ a single tap.
|
||||
|
||||
Note that `FalsingManager#isFalseTap(boolean robustCheck, double falsePenalty)`
|
||||
will also check for double taps when `robustCheck` is set to true. If you are
|
||||
willing to use single taps, use that instead.
|
||||
|
||||
### Swipes and Other Gestures
|
||||
|
||||
`FalsingManager#isFalseTouch(@Classifier.InteractionType int interactionType)`.
|
||||
Use this for any non-tap interactions. This includes expanding notifications,
|
||||
expanding quick settings, pulling up the bouncer, and more. You must pass
|
||||
the type of interaction you are evaluating when calling it. A large set of
|
||||
heuristics will be applied to analyze the gesture, and the exact rules vary depending upon
|
||||
the `InteractionType`.
|
||||
|
||||
### Ignoring A Gesture
|
||||
|
||||
`FalsingCollector#avoidGesture()`. Tell the `FalsingManager` to pretend like the
|
||||
observed gesture never happened. **This method must be called when the observed
|
||||
`MotionEvent` is `MotionEvent.ACTION_DOWN`.** Attempting to call this method
|
||||
later in a gesture will not work.
|
||||
|
||||
Notice that this method is actually a method on `FalsingCollector`. It is
|
||||
forcefully telling the `FalsingManager` to wholly pretend the gesture never
|
||||
happened. This is intended for security and PII sensitive gestures, such as
|
||||
password inputs. Please don't use this as a shortcut for avoiding the
|
||||
FalsingManager. Falsing works better the more behavior it is told about.
|
||||
|
||||
### Other Considerations
|
||||
|
||||
Please try to call the `FalsingManager` only once per gesture. Wait until you
|
||||
are ready to act on the owner's action, and then query the `FalsingManager`. The `FalsingManager`
|
||||
will update its belief in pocket dialing based only on the last call made, so multiple calls per
|
||||
gesture are not well defined.
|
||||
|
||||
The `FalsingManager` does not update its belief in pocket-dialing until a new
|
||||
gesture starts. That is to say, if the owner makes a bad tap on your feature,
|
||||
the belief in pocket dialing will not incorporate this new data until the
|
||||
following gesture begins.
|
||||
|
||||
If you expect a mix of taps, double taps, and swipes on your feature, segment them
|
||||
accordingly. Figure out which `FalsingManager` method you need to call first, rather than relying
|
||||
on multiple calls to the `FalsingManager` to act as a sieve.
|
||||
|
||||
Don't:
|
||||
```
|
||||
if (!mFalsingManager.isFalseTap(false, 0)) {
|
||||
// its a tap
|
||||
} else if (!mFalsingManager.isFalseTouch(GESTURE_A) {
|
||||
// do thing a
|
||||
} else if (!mFalsingManager.isFalseTouch(GESTURE_B) {
|
||||
// do thing b
|
||||
} else {
|
||||
// must be a false.
|
||||
}
|
||||
```
|
||||
|
||||
Do:
|
||||
```
|
||||
void onTap() {
|
||||
if (!mFalsingManager.isFalseTap(false, 0)) {
|
||||
// its a tap
|
||||
}
|
||||
|
||||
void onGestureA() {
|
||||
if (!mFalsingManager.isFalseTouch(GESTURE_A) {
|
||||
// do thing a
|
||||
}
|
||||
}
|
||||
|
||||
void onGestureB() {
|
||||
if (!mFalsingManager.isFalseTouch(GESTURE_B) {
|
||||
// do thing b
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Influencing Belief
|
||||
|
||||
`FalsingCollector#updateFalseConfidence(FalsingClassifier.Result result)`. This
|
||||
method allows you to directly change the `FalsingManager`'s belief in the state
|
||||
of pocket dialing. If the owner does something unusual with their phone that you
|
||||
think indicates pocket dialing, you can call:
|
||||
|
||||
```
|
||||
mFalsingCollector.updateFalseConfidence(
|
||||
FalsingClassifier.Result.falsed(0.6, "Owner is doing something fishy"));
|
||||
```
|
||||
|
||||
A belief value of `1` indicates a 100% confidence of false behavior. A belief
|
||||
value of `0` would make no change in the `FalsingManager` and should be avoided
|
||||
as it simply creates noise in the logs. Generally, a middle value between the
|
||||
two extremes makes sense.
|
||||
|
||||
A good example of where this is used is in the "Pattern" password input. We
|
||||
avoid recording those gestures in the `FalsingManager`, but we have the pattern input update
|
||||
the `FalsingManager` directly in some cases. If the owner simply taps on the pattern input, we
|
||||
record it as a false, (patterns are always 4 "cells" long, so single "cell" inputs are penalized).
|
||||
|
||||
Conversely, if you think the owner does something that deserves a nice reward:
|
||||
|
||||
```
|
||||
mFalsingCollector.updateFalseConfidence(
|
||||
FalsingClassifier.Result.passed(0.6));
|
||||
```
|
||||
|
||||
Again, useful on password inputs where the FalsingManager is avoiding recording
|
||||
the gesture. This is used on the "pin" password input, to recognize successful
|
||||
taps on the input buttons.
|
||||
|
||||
## Global Falsing Event
|
||||
|
||||
If the `FalsingManager`'s belief in falsing crosses some internally defined
|
||||
threshold, it will fire an event that other parts of the system can listen for.
|
||||
This even indicates that the owner is likely actively pocket-dialing, and any
|
||||
currently open activities on the phone should retract themselves.
|
||||
|
||||
To subscribe to this event, call
|
||||
`FalsingManager#addFalsingBeliefListener(FalsingBeliefListener listener)`.
|
||||
`FalsingBeliefListener` is a simple one method interface that will be called
|
||||
after when activities should retract themselves.
|
||||
|
||||
**Do Listen For This**. Your code will work without it, but it is a handy,
|
||||
universal signal that will save the phone owner a lot of accidents. A simple
|
||||
implementation looks like:
|
||||
|
||||
```
|
||||
mFalsingManager.addFalsingBeliefListener(MyFeatureClass::hide);
|
||||
```
|
||||
@@ -37,6 +37,7 @@ public abstract class Classifier {
|
||||
public static final int GENERIC = 7;
|
||||
public static final int BOUNCER_UNLOCK = 8;
|
||||
public static final int PULSE_EXPAND = 9;
|
||||
public static final int BRIGHTNESS_SLIDER = 10;
|
||||
|
||||
@IntDef({
|
||||
QUICK_SETTINGS,
|
||||
@@ -48,7 +49,8 @@ public abstract class Classifier {
|
||||
RIGHT_AFFORDANCE,
|
||||
GENERIC,
|
||||
BOUNCER_UNLOCK,
|
||||
PULSE_EXPAND
|
||||
PULSE_EXPAND,
|
||||
BRIGHTNESS_SLIDER
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface InteractionType {}
|
||||
|
||||
@@ -148,6 +148,10 @@ class DistanceClassifier extends FalsingClassifier {
|
||||
Result calculateFalsingResult(
|
||||
@Classifier.InteractionType int interactionType,
|
||||
double historyBelief, double historyConfidence) {
|
||||
if (interactionType == Classifier.BRIGHTNESS_SLIDER) {
|
||||
return Result.passed(0);
|
||||
}
|
||||
|
||||
return !getPassedFlingThreshold() ? falsed(0.5, getReason()) : Result.passed(0.5);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.systemui.classifier;
|
||||
|
||||
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD;
|
||||
import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
|
||||
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
|
||||
|
||||
import android.provider.DeviceConfig;
|
||||
@@ -115,7 +116,7 @@ class ProximityClassifier extends FalsingClassifier {
|
||||
Result calculateFalsingResult(
|
||||
@Classifier.InteractionType int interactionType,
|
||||
double historyBelief, double historyConfidence) {
|
||||
if (interactionType == QUICK_SETTINGS) {
|
||||
if (interactionType == QUICK_SETTINGS || interactionType == BRIGHTNESS_SLIDER) {
|
||||
return Result.passed(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.systemui.classifier;
|
||||
|
||||
|
||||
import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
|
||||
import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
|
||||
import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE;
|
||||
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
|
||||
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
|
||||
@@ -45,6 +46,7 @@ public class TypeClassifier extends FalsingClassifier {
|
||||
boolean up = isUp();
|
||||
boolean right = isRight();
|
||||
|
||||
double confidence = 1;
|
||||
boolean wrongDirection = true;
|
||||
switch (interactionType) {
|
||||
case QUICK_SETTINGS:
|
||||
@@ -52,6 +54,11 @@ public class TypeClassifier extends FalsingClassifier {
|
||||
case NOTIFICATION_DRAG_DOWN:
|
||||
wrongDirection = !vertical || up;
|
||||
break;
|
||||
case BRIGHTNESS_SLIDER:
|
||||
confidence = 0; // Owners may return to original brightness.
|
||||
// A more sophisticated thing to do here would be to look at the size of the
|
||||
// vertical change relative to the screen size. _Some_ amount of vertical
|
||||
// change should be expected.
|
||||
case NOTIFICATION_DISMISS:
|
||||
wrongDirection = vertical;
|
||||
break;
|
||||
@@ -70,7 +77,7 @@ public class TypeClassifier extends FalsingClassifier {
|
||||
break;
|
||||
}
|
||||
|
||||
return wrongDirection ? falsed(1, getReason(interactionType)) : Result.passed(0.5);
|
||||
return wrongDirection ? falsed(confidence, getReason(interactionType)) : Result.passed(0.5);
|
||||
}
|
||||
|
||||
private String getReason(int interactionType) {
|
||||
|
||||
@@ -20,6 +20,7 @@ import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHT
|
||||
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_X_SECONDARY_DEVIANCE;
|
||||
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_PRIMARY_DEVIANCE;
|
||||
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_SECONDARY_DEVIANCE;
|
||||
import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.provider.DeviceConfig;
|
||||
@@ -87,6 +88,10 @@ class ZigZagClassifier extends FalsingClassifier {
|
||||
Result calculateFalsingResult(
|
||||
@Classifier.InteractionType int interactionType,
|
||||
double historyBelief, double historyConfidence) {
|
||||
if (interactionType == BRIGHTNESS_SLIDER) {
|
||||
return Result.passed(0);
|
||||
}
|
||||
|
||||
List<MotionEvent> motionEvents = getRecentMotionEvents();
|
||||
// Rotate horizontal gestures to be horizontal between their first and last point.
|
||||
// Rotate vertical gestures to be vertical between their first and last point.
|
||||
|
||||
@@ -27,7 +27,10 @@ import android.widget.SeekBar;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.settingslib.RestrictedLockUtils;
|
||||
import com.android.systemui.Gefingerpoken;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.classifier.Classifier;
|
||||
import com.android.systemui.plugins.FalsingManager;
|
||||
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
|
||||
import com.android.systemui.util.ViewController;
|
||||
|
||||
@@ -42,9 +45,7 @@ import javax.inject.Inject;
|
||||
*
|
||||
* @see BrightnessMirrorController
|
||||
*/
|
||||
public class BrightnessSlider
|
||||
extends ViewController<View>
|
||||
implements ToggleSlider {
|
||||
public class BrightnessSlider extends ViewController<View> implements ToggleSlider {
|
||||
|
||||
private Listener mListener;
|
||||
private ToggleSlider mMirror;
|
||||
@@ -52,15 +53,34 @@ public class BrightnessSlider
|
||||
private BrightnessMirrorController mMirrorController;
|
||||
private boolean mTracking;
|
||||
private final boolean mUseMirror;
|
||||
private final FalsingManager mFalsingManager;
|
||||
|
||||
private final Gefingerpoken mOnInterceptListener = new Gefingerpoken() {
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
int action = ev.getActionMasked();
|
||||
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
|
||||
mFalsingManager.isFalseTouch(Classifier.BRIGHTNESS_SLIDER);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
BrightnessSlider(
|
||||
View rootView,
|
||||
BrightnessSliderView brightnessSliderView,
|
||||
boolean useMirror
|
||||
) {
|
||||
boolean useMirror,
|
||||
FalsingManager falsingManager) {
|
||||
super(rootView);
|
||||
mBrightnessSliderView = brightnessSliderView;
|
||||
mUseMirror = useMirror;
|
||||
mFalsingManager = falsingManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,6 +98,7 @@ public class BrightnessSlider
|
||||
protected void onViewAttached() {
|
||||
mBrightnessSliderView.setOnSeekBarChangeListener(mSeekListener);
|
||||
mBrightnessSliderView.setOnCheckedChangeListener(mCheckListener);
|
||||
mBrightnessSliderView.setOnInterceptListener(mOnInterceptListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -85,6 +106,7 @@ public class BrightnessSlider
|
||||
mBrightnessSliderView.setOnSeekBarChangeListener(null);
|
||||
mBrightnessSliderView.setOnCheckedChangeListener(null);
|
||||
mBrightnessSliderView.setOnDispatchTouchEventListener(null);
|
||||
mBrightnessSliderView.setOnInterceptListener(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -247,10 +269,12 @@ public class BrightnessSlider
|
||||
public static class Factory {
|
||||
|
||||
BrightnessControllerSettings mSettings;
|
||||
private final FalsingManager mFalsingManager;
|
||||
|
||||
@Inject
|
||||
public Factory(BrightnessControllerSettings settings) {
|
||||
public Factory(BrightnessControllerSettings settings, FalsingManager falsingManager) {
|
||||
mSettings = settings;
|
||||
mFalsingManager = falsingManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -270,7 +294,7 @@ public class BrightnessSlider
|
||||
private BrightnessSlider fromTree(ViewGroup root, boolean useMirror) {
|
||||
BrightnessSliderView v = root.requireViewById(R.id.brightness_slider);
|
||||
|
||||
return new BrightnessSlider(root, v, useMirror);
|
||||
return new BrightnessSlider(root, v, useMirror, mFalsingManager);
|
||||
}
|
||||
|
||||
/** Get the layout to inflate based on what slider to use */
|
||||
|
||||
@@ -31,6 +31,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.settingslib.RestrictedLockUtils;
|
||||
import com.android.systemui.Gefingerpoken;
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
@@ -54,6 +55,7 @@ public class BrightnessSliderView extends FrameLayout {
|
||||
private TextView mLabel;
|
||||
private final CharSequence mText;
|
||||
private DispatchTouchEventListener mListener;
|
||||
private Gefingerpoken mOnInterceptListener;
|
||||
|
||||
public BrightnessSliderView(Context context) {
|
||||
this(context, null);
|
||||
@@ -105,6 +107,15 @@ public class BrightnessSliderView extends FrameLayout {
|
||||
return super.dispatchTouchEvent(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
|
||||
// We prevent disallowing on this view, but bubble it up to our parents.
|
||||
// We need interception to handle falsing.
|
||||
if (mParent != null) {
|
||||
mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches a listener to the {@link ToggleSeekBar} in the view so changes can be observed
|
||||
* @param seekListener use {@code null} to remove listener
|
||||
@@ -195,6 +206,18 @@ public class BrightnessSliderView extends FrameLayout {
|
||||
return mSlider.getProgress();
|
||||
}
|
||||
|
||||
public void setOnInterceptListener(Gefingerpoken onInterceptListener) {
|
||||
mOnInterceptListener = onInterceptListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
if (mOnInterceptListener != null) {
|
||||
return mOnInterceptListener.onInterceptTouchEvent(ev);
|
||||
}
|
||||
return super.onInterceptTouchEvent(ev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface to attach a listener for {@link View#dispatchTouchEvent}.
|
||||
*/
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.systemui.classifier;
|
||||
|
||||
import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.testing.AndroidTestingRunner;
|
||||
@@ -96,13 +98,9 @@ public class DistanceClassifierTest extends ClassifierTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPass_swipe() {
|
||||
|
||||
public void testPass_BrightnessSliderAlwaysPasses() {
|
||||
mClassifier.onTouchEvent(appendDownEvent(1, 1));
|
||||
assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue();
|
||||
|
||||
mClassifier.onTouchEvent(appendMoveEvent(1, mDataProvider.getYdpi() * 3, 3));
|
||||
mClassifier.onTouchEvent(appendUpEvent(1, mDataProvider.getYdpi() * 3, 300));
|
||||
assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse();
|
||||
assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 1).isFalse())
|
||||
.isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,12 @@
|
||||
|
||||
package com.android.systemui.classifier;
|
||||
|
||||
import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
|
||||
import static com.android.systemui.classifier.Classifier.GENERIC;
|
||||
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.testing.AndroidTestingRunner;
|
||||
@@ -72,7 +73,7 @@ public class ProximityClassifierTest extends ClassifierTest {
|
||||
public void testPass_uncovered() {
|
||||
touchDown();
|
||||
touchUp(10);
|
||||
assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse(), is(false));
|
||||
assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -81,7 +82,7 @@ public class ProximityClassifierTest extends ClassifierTest {
|
||||
mClassifier.onProximityEvent(createSensorEvent(true, 1));
|
||||
mClassifier.onProximityEvent(createSensorEvent(false, 2));
|
||||
touchUp(20);
|
||||
assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse(), is(false));
|
||||
assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -90,7 +91,17 @@ public class ProximityClassifierTest extends ClassifierTest {
|
||||
mClassifier.onProximityEvent(createSensorEvent(true, 1));
|
||||
mClassifier.onProximityEvent(createSensorEvent(false, 11));
|
||||
touchUp(10);
|
||||
assertThat(mClassifier.classifyGesture(QUICK_SETTINGS, 0.5, 0).isFalse(), is(false));
|
||||
assertThat(mClassifier.classifyGesture(QUICK_SETTINGS, 0.5, 0).isFalse()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPass_brightnessSlider() {
|
||||
touchDown();
|
||||
mClassifier.onProximityEvent(createSensorEvent(true, 1));
|
||||
mClassifier.onProximityEvent(createSensorEvent(false, 11));
|
||||
touchUp(10);
|
||||
assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse())
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -99,7 +110,7 @@ public class ProximityClassifierTest extends ClassifierTest {
|
||||
mClassifier.onProximityEvent(createSensorEvent(true, 1));
|
||||
mClassifier.onProximityEvent(createSensorEvent(false, 11));
|
||||
touchUp(10);
|
||||
assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse(), is(true));
|
||||
assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -110,7 +121,7 @@ public class ProximityClassifierTest extends ClassifierTest {
|
||||
mClassifier.onProximityEvent(createSensorEvent(true, 96));
|
||||
mClassifier.onProximityEvent(createSensorEvent(false, 100));
|
||||
touchUp(100);
|
||||
assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse(), is(true));
|
||||
assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -120,7 +131,7 @@ public class ProximityClassifierTest extends ClassifierTest {
|
||||
mClassifier.onProximityEvent(createSensorEvent(false, 11));
|
||||
touchUp(10);
|
||||
when(mDistanceClassifier.isLongSwipe()).thenReturn(mPassedResult);
|
||||
assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse(), is(false));
|
||||
assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse();
|
||||
}
|
||||
|
||||
private void touchDown() {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.systemui.classifier;
|
||||
|
||||
import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
|
||||
import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
|
||||
import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE;
|
||||
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
|
||||
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
|
||||
@@ -277,4 +278,46 @@ public class TypeClassifierTest extends ClassifierTest {
|
||||
when(mDataProvider.isRight()).thenReturn(true);
|
||||
assertThat(mClassifier.classifyGesture(RIGHT_AFFORDANCE, 0.5, 0).isFalse()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPass_BrightnessSlider() {
|
||||
when(mDataProvider.isVertical()).thenReturn(false);
|
||||
|
||||
when(mDataProvider.isUp()).thenReturn(false); // up and right should cause no effect.
|
||||
when(mDataProvider.isRight()).thenReturn(false);
|
||||
assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isFalse();
|
||||
|
||||
when(mDataProvider.isUp()).thenReturn(true);
|
||||
when(mDataProvider.isRight()).thenReturn(false);
|
||||
assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isFalse();
|
||||
|
||||
when(mDataProvider.isUp()).thenReturn(false);
|
||||
when(mDataProvider.isRight()).thenReturn(true);
|
||||
assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isFalse();
|
||||
|
||||
when(mDataProvider.isUp()).thenReturn(true);
|
||||
when(mDataProvider.isRight()).thenReturn(true);
|
||||
assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFalse_BrightnessSlider() {
|
||||
when(mDataProvider.isVertical()).thenReturn(true);
|
||||
|
||||
when(mDataProvider.isUp()).thenReturn(false); // up and right should cause no effect.
|
||||
when(mDataProvider.isRight()).thenReturn(false);
|
||||
assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isTrue();
|
||||
|
||||
when(mDataProvider.isUp()).thenReturn(true);
|
||||
when(mDataProvider.isRight()).thenReturn(false);
|
||||
assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isTrue();
|
||||
|
||||
when(mDataProvider.isUp()).thenReturn(false);
|
||||
when(mDataProvider.isRight()).thenReturn(true);
|
||||
assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isTrue();
|
||||
|
||||
when(mDataProvider.isUp()).thenReturn(true);
|
||||
when(mDataProvider.isRight()).thenReturn(true);
|
||||
assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.systemui.classifier;
|
||||
|
||||
import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.testing.AndroidTestingRunner;
|
||||
@@ -82,6 +84,13 @@ public class ZigZagClassifierTest extends ClassifierTest {
|
||||
assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPass_brightnessSliderAlwaysPasses() {
|
||||
appendMoveEvent(0, 0);
|
||||
appendMoveEvent(0, 100);
|
||||
appendMoveEvent(0, 1);
|
||||
assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 1).isFalse()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFail_minimumTouchesVertical() {
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.widget.SeekBar
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.settingslib.RestrictedLockUtils
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.classifier.FalsingManagerFake
|
||||
import com.android.systemui.statusbar.policy.BrightnessMirrorController
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.capture
|
||||
@@ -75,6 +76,7 @@ class BrightnessSliderTest : SysuiTestCase() {
|
||||
private lateinit var checkedChangeCaptor: ArgumentCaptor<CompoundButton.OnCheckedChangeListener>
|
||||
@Mock
|
||||
private lateinit var compoundButton: CompoundButton
|
||||
private var mFalsingManager: FalsingManagerFake = FalsingManagerFake()
|
||||
|
||||
private lateinit var mController: BrightnessSlider
|
||||
|
||||
@@ -85,7 +87,7 @@ class BrightnessSliderTest : SysuiTestCase() {
|
||||
whenever(mirrorController.toggleSlider).thenReturn(mirror)
|
||||
whenever(motionEvent.copy()).thenReturn(motionEvent)
|
||||
|
||||
mController = BrightnessSlider(rootView, brightnessSliderView, true)
|
||||
mController = BrightnessSlider(rootView, brightnessSliderView, true, mFalsingManager)
|
||||
mController.init()
|
||||
mController.setOnChangedListener(listener)
|
||||
}
|
||||
@@ -160,7 +162,8 @@ class BrightnessSliderTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun testSettingMirrorWhenNotUseMirrorIsNoOp() {
|
||||
val otherController = BrightnessSlider(rootView, brightnessSliderView, false)
|
||||
val otherController = BrightnessSlider(
|
||||
rootView, brightnessSliderView, false, mFalsingManager)
|
||||
otherController.init()
|
||||
|
||||
otherController.setMirrorControllerAndMirror(mirrorController)
|
||||
|
||||
Reference in New Issue
Block a user