Fix BaseSwipeDetector#setState() called inside another setState()

Clients of BaseSwipeDetector are required to call finishedScrolling(),
which calls setState(IDLE). An obvious place to call this is in
onDragEnd(), which itself is called from a setState(SETTLING). If the
client does this, then the SETTLING state actually clobbers the IDLE
state, leading to undefined behavior. The reason we don't see this in
practice is because we usually call finishedScrolling() after an
animation from onDragEnd() instead of calling it immediately.

To fix this, we add a simple queue such that any calls to setState()
while one is in progress have to wait and are executed in turn. This
ensures we get all the proper state callbacks and end in the correct
one.

Also fix an incorrect call in AbstractStateChangeTouchController which
was masked by this bug. We were calling setState(IDLE) in onDragStart(),
which only worked because the original setState(DRAGGING) incorrectly
clobbered this. Now we only setState(IDLE) (via finishedScrolling())
when we fully clear the state, i.e. when the interaction is finished.

Test: added testInterleavedSetState

Bug: 141939911
Change-Id: Iae630ee7101921b57a85d40646468cf19f59b674
This commit is contained in:
Tony Wickham
2020-01-09 15:19:02 -08:00
parent 2e0b8e39c7
commit 3f330429a3
3 changed files with 39 additions and 4 deletions

View File

@@ -508,7 +508,7 @@ public abstract class AbstractStateChangeTouchController
mAtomicComponentsController.getAnimationPlayer().end();
mAtomicComponentsController = null;
}
cancelAnimationControllers();
clearState();
boolean shouldGoToTargetState = true;
if (mPendingAnimation != null) {
boolean reachedTarget = mToState == targetState;
@@ -546,13 +546,13 @@ public abstract class AbstractStateChangeTouchController
mAtomicAnim = null;
}
mScheduleResumeAtomicComponent = false;
mDetector.finishedScrolling();
mDetector.setDetectableScrollConditions(0, false);
}
private void cancelAnimationControllers() {
mCurrentAnimation = null;
cancelAtomicComponentsController();
mDetector.finishedScrolling();
mDetector.setDetectableScrollConditions(0, false);
}
private void cancelAtomicComponentsController() {