Add fling animation when snapping split divider bar

Bug: 172704238
Test: atest WMShellUnitTests
Test: manul check the fling behavior
Change-Id: Iead37be86d55de2ce357165a2f6ae910f36969cb
This commit is contained in:
Jerry Chang
2020-12-10 13:03:53 +08:00
parent 9547fb5fd5
commit 5935f559b6
3 changed files with 50 additions and 18 deletions

View File

@@ -112,7 +112,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
}
if (mMoving) {
final int position = mSplitLayout.getDividePosition() + touchPos - mStartPos;
mSplitLayout.updateDividePosition(position);
mSplitLayout.updateDivideBounds(position);
}
break;
case MotionEvent.ACTION_UP:
@@ -131,7 +131,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
final int position = mSplitLayout.getDividePosition() + touchPos - mStartPos;
final DividerSnapAlgorithm.SnapTarget snapTarget =
mSplitLayout.findSnapTarget(position, velocity);
mSplitLayout.setSnapTarget(snapTarget);
mSplitLayout.snapToTarget(position, snapTarget);
break;
}
return true;

View File

@@ -22,6 +22,9 @@ import static android.view.WindowManager.DOCKED_TOP;
import static com.android.internal.policy.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END;
import static com.android.internal.policy.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -31,6 +34,7 @@ import android.view.SurfaceControl;
import androidx.annotation.Nullable;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.wm.shell.animation.Interpolators;
/**
* Records and handles layout of splits. Helps to calculate proper bounds when configuration or
@@ -145,17 +149,23 @@ public class SplitLayout {
* Updates bounds with the passing position. Usually used to update recording bounds while
* performing animation or dragging divider bar to resize the splits.
*/
public void updateDividePosition(int position) {
void updateDivideBounds(int position) {
updateBounds(position);
mLayoutChangeListener.onBoundsChanging(this);
}
void setDividePosition(int position) {
mDividePosition = position;
updateBounds(mDividePosition);
mLayoutChangeListener.onBoundsChanged(this);
}
/**
* Sets new divide position and updates bounds correspondingly. Notifies listener if the new
* target indicates dismissing split.
*/
public void setSnapTarget(DividerSnapAlgorithm.SnapTarget snapTarget) {
switch(snapTarget.flag) {
public void snapToTarget(int currentPosition, DividerSnapAlgorithm.SnapTarget snapTarget) {
switch (snapTarget.flag) {
case FLAG_DISMISS_START:
mLayoutChangeListener.onSnappedToDismiss(false /* snappedToEnd */);
break;
@@ -163,9 +173,7 @@ public class SplitLayout {
mLayoutChangeListener.onSnappedToDismiss(true /* snappedToEnd */);
break;
default:
mDividePosition = snapTarget.position;
updateBounds(mDividePosition);
mLayoutChangeListener.onBoundsChanged(this);
flingDividePosition(currentPosition, snapTarget.position);
break;
}
}
@@ -189,6 +197,27 @@ public class SplitLayout {
isLandscape ? DOCKED_LEFT : DOCKED_TOP /* dockSide */);
}
private void flingDividePosition(int from, int to) {
ValueAnimator animator = ValueAnimator
.ofInt(from, to)
.setDuration(250);
animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
animator.addUpdateListener(
animation -> updateDivideBounds((int) animation.getAnimatedValue()));
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
setDividePosition(to);
}
@Override
public void onAnimationCancel(Animator animation) {
setDividePosition(to);
}
});
animator.start();
}
private static boolean isLandscape(Rect bounds) {
return bounds.width() > bounds.height();
}

View File

@@ -69,24 +69,27 @@ public class SplitLayoutTests extends ShellTestCase {
}
@Test
public void testUpdateDividePosition() {
mSplitLayout.updateDividePosition(anyInt());
public void testUpdateDivideBounds() {
mSplitLayout.updateDivideBounds(anyInt());
verify(mLayoutChangeListener).onBoundsChanging(any(SplitLayout.class));
}
@Test
public void testSetSnapTarget() {
DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0,
@UiThreadTest
public void testSnapToTarget() {
DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */,
DividerSnapAlgorithm.SnapTarget.FLAG_NONE);
mSplitLayout.setSnapTarget(snapTarget);
verify(mLayoutChangeListener).onBoundsChanged(any(SplitLayout.class));
mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
verify(mLayoutChangeListener).onBoundsChanging(any(SplitLayout.class));
// verify it callbacks properly when the snap target indicates dismissing split.
snapTarget = getSnapTarget(0, DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START);
mSplitLayout.setSnapTarget(snapTarget);
snapTarget = getSnapTarget(0 /* position */,
DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START);
mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
verify(mLayoutChangeListener).onSnappedToDismiss(eq(false));
snapTarget = getSnapTarget(0, DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END);
mSplitLayout.setSnapTarget(snapTarget);
snapTarget = getSnapTarget(0 /* position */,
DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END);
mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
verify(mLayoutChangeListener).onSnappedToDismiss(eq(true));
}