Modify SwipeDismissLayout to perform its own exit animation
Instead of relying on the window animation system, in the special case of a swipe-dismiss, disable any default window exit animation and perform a custom animation. This bypasses some bugs in the window animator codebase and allows us to have a nice "rebound" animation if the user doesn't swipe far/fast enough to trigger a dismiss. Bug: 33041168 Change-Id: Ied45700d35a59950bacef1ba0650eaa5bc60fadb
This commit is contained in:
@@ -2946,8 +2946,11 @@ public class Activity extends ContextThemeWrapper
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public void onWindowDismissed(boolean finishTask) {
|
||||
public void onWindowDismissed(boolean finishTask, boolean suppressWindowTransition) {
|
||||
finish(finishTask ? FINISH_TASK_WITH_ACTIVITY : DONT_FINISH_TASK_WITH_ACTIVITY);
|
||||
if (suppressWindowTransition) {
|
||||
overridePendingTransition(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -744,7 +744,7 @@ public class Dialog implements DialogInterface, Window.Callback,
|
||||
|
||||
/** @hide */
|
||||
@Override
|
||||
public void onWindowDismissed(boolean finishTask) {
|
||||
public void onWindowDismissed(boolean finishTask, boolean suppressWindowTransition) {
|
||||
dismiss();
|
||||
}
|
||||
|
||||
|
||||
@@ -581,8 +581,10 @@ public abstract class Window {
|
||||
* Called when a window is dismissed. This informs the callback that the
|
||||
* window is gone, and it should finish itself.
|
||||
* @param finishTask True if the task should also be finished.
|
||||
* @param suppressWindowTransition True if the resulting exit and enter window transition
|
||||
* animations should be suppressed.
|
||||
*/
|
||||
void onWindowDismissed(boolean finishTask);
|
||||
void onWindowDismissed(boolean finishTask, boolean suppressWindowTransition);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@@ -871,9 +873,10 @@ public abstract class Window {
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public final void dispatchOnWindowDismissed(boolean finishTask) {
|
||||
public final void dispatchOnWindowDismissed(
|
||||
boolean finishTask, boolean suppressWindowTransition) {
|
||||
if (mOnWindowDismissedCallback != null) {
|
||||
mOnWindowDismissedCallback.onWindowDismissed(finishTask);
|
||||
mOnWindowDismissedCallback.onWindowDismissed(finishTask, suppressWindowTransition);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2990,19 +2990,17 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
|
||||
swipeDismiss.setOnDismissedListener(new SwipeDismissLayout.OnDismissedListener() {
|
||||
@Override
|
||||
public void onDismissed(SwipeDismissLayout layout) {
|
||||
dispatchOnWindowDismissed(false /*finishTask*/);
|
||||
dispatchOnWindowDismissed(false /*finishTask*/, true /*suppressWindowTransition*/);
|
||||
}
|
||||
});
|
||||
swipeDismiss.setOnSwipeProgressChangedListener(
|
||||
new SwipeDismissLayout.OnSwipeProgressChangedListener() {
|
||||
private static final float ALPHA_DECREASE = 0.5f;
|
||||
private boolean mIsTranslucent = false;
|
||||
@Override
|
||||
public void onSwipeProgressChanged(
|
||||
SwipeDismissLayout layout, float progress, float translate) {
|
||||
SwipeDismissLayout layout, float alpha, float translate) {
|
||||
WindowManager.LayoutParams newParams = getAttributes();
|
||||
newParams.x = (int) translate;
|
||||
newParams.alpha = 1 - (progress * ALPHA_DECREASE);
|
||||
newParams.alpha = alpha;
|
||||
setAttributes(newParams);
|
||||
|
||||
int flags = 0;
|
||||
|
||||
@@ -416,7 +416,8 @@ public class DecorCaptionView extends ViewGroup implements View.OnTouchListener,
|
||||
if (mClickTarget == mMaximize) {
|
||||
maximizeWindow();
|
||||
} else if (mClickTarget == mClose) {
|
||||
mOwner.dispatchOnWindowDismissed(true /*finishTask*/);
|
||||
mOwner.dispatchOnWindowDismissed(
|
||||
true /*finishTask*/, false /*suppressWindowTransition*/);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
|
||||
package com.android.internal.widget;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.TimeInterpolator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.animation.ValueAnimator.AnimatorUpdateListener;
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
@@ -30,6 +34,7 @@ import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
/**
|
||||
@@ -49,12 +54,12 @@ public class SwipeDismissLayout extends FrameLayout {
|
||||
/**
|
||||
* Called when the layout has been swiped and the position of the window should change.
|
||||
*
|
||||
* @param progress A number in [0, 1] representing how far to the
|
||||
* right the window has been swiped
|
||||
* @param alpha A number in [0, 1] representing what the alpha transparency of the window
|
||||
* should be.
|
||||
* @param translate A number in [0, w], where w is the width of the
|
||||
* layout. This is equivalent to progress * layout.getWidth().
|
||||
*/
|
||||
void onSwipeProgressChanged(SwipeDismissLayout layout, float progress, float translate);
|
||||
void onSwipeProgressChanged(SwipeDismissLayout layout, float alpha, float translate);
|
||||
|
||||
void onSwipeCancelled(SwipeDismissLayout layout);
|
||||
}
|
||||
@@ -72,6 +77,9 @@ public class SwipeDismissLayout extends FrameLayout {
|
||||
private boolean mDiscardIntercept;
|
||||
private VelocityTracker mVelocityTracker;
|
||||
private float mTranslationX;
|
||||
private boolean mBlockGesture = false;
|
||||
|
||||
private final DismissAnimator mDismissAnimator = new DismissAnimator();
|
||||
|
||||
private OnDismissedListener mDismissedListener;
|
||||
private OnSwipeProgressChangedListener mProgressListener;
|
||||
@@ -168,6 +176,10 @@ public class SwipeDismissLayout extends FrameLayout {
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
checkGesture((ev));
|
||||
if (mBlockGesture) {
|
||||
return true;
|
||||
}
|
||||
if (!mDismissable) {
|
||||
return super.onInterceptTouchEvent(ev);
|
||||
}
|
||||
@@ -231,6 +243,10 @@ public class SwipeDismissLayout extends FrameLayout {
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
checkGesture((ev));
|
||||
if (mBlockGesture) {
|
||||
return true;
|
||||
}
|
||||
if (mVelocityTracker == null || !mDismissable) {
|
||||
return super.onTouchEvent(ev);
|
||||
}
|
||||
@@ -240,9 +256,9 @@ public class SwipeDismissLayout extends FrameLayout {
|
||||
case MotionEvent.ACTION_UP:
|
||||
updateDismiss(ev);
|
||||
if (mDismissed) {
|
||||
dismiss();
|
||||
mDismissAnimator.animateDismissal(ev.getRawX() - mDownX);
|
||||
} else if (mSwiping) {
|
||||
cancel();
|
||||
mDismissAnimator.animateRecovery(ev.getRawX() - mDownX);
|
||||
}
|
||||
resetMembers();
|
||||
break;
|
||||
@@ -270,7 +286,8 @@ public class SwipeDismissLayout extends FrameLayout {
|
||||
private void setProgress(float deltaX) {
|
||||
mTranslationX = deltaX;
|
||||
if (mProgressListener != null && deltaX >= 0) {
|
||||
mProgressListener.onSwipeProgressChanged(this, deltaX / getWidth(), deltaX);
|
||||
mProgressListener.onSwipeProgressChanged(
|
||||
this, progressToAlpha(deltaX / getWidth()), deltaX);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,4 +395,91 @@ public class SwipeDismissLayout extends FrameLayout {
|
||||
|
||||
mDismissable = dismissable;
|
||||
}
|
||||
|
||||
private void checkGesture(MotionEvent ev) {
|
||||
if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
|
||||
mBlockGesture = mDismissAnimator.isAnimating();
|
||||
}
|
||||
}
|
||||
|
||||
private float progressToAlpha(float progress) {
|
||||
return 1 - progress * progress * progress;
|
||||
}
|
||||
|
||||
private class DismissAnimator implements AnimatorUpdateListener, Animator.AnimatorListener {
|
||||
private final TimeInterpolator DISMISS_INTERPOLATOR = new DecelerateInterpolator(1.5f);
|
||||
private final long DISMISS_DURATION = 250;
|
||||
|
||||
private final ValueAnimator mDismissAnimator = new ValueAnimator();
|
||||
private boolean mWasCanceled = false;
|
||||
private boolean mDismissOnComplete = false;
|
||||
|
||||
/* package */ DismissAnimator() {
|
||||
mDismissAnimator.addUpdateListener(this);
|
||||
mDismissAnimator.addListener(this);
|
||||
}
|
||||
|
||||
/* package */ void animateDismissal(float currentTranslation) {
|
||||
animate(
|
||||
currentTranslation / getWidth(),
|
||||
1,
|
||||
DISMISS_DURATION,
|
||||
DISMISS_INTERPOLATOR,
|
||||
true /* dismiss */);
|
||||
}
|
||||
|
||||
/* package */ void animateRecovery(float currentTranslation) {
|
||||
animate(
|
||||
currentTranslation / getWidth(),
|
||||
0,
|
||||
DISMISS_DURATION,
|
||||
DISMISS_INTERPOLATOR,
|
||||
false /* don't dismiss */);
|
||||
}
|
||||
|
||||
/* package */ boolean isAnimating() {
|
||||
return mDismissAnimator.isStarted();
|
||||
}
|
||||
|
||||
private void animate(float from, float to, long duration, TimeInterpolator interpolator,
|
||||
boolean dismissOnComplete) {
|
||||
mDismissAnimator.cancel();
|
||||
mDismissOnComplete = dismissOnComplete;
|
||||
mDismissAnimator.setFloatValues(from, to);
|
||||
mDismissAnimator.setDuration(duration);
|
||||
mDismissAnimator.setInterpolator(interpolator);
|
||||
mDismissAnimator.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
float value = (Float) animation.getAnimatedValue();
|
||||
setProgress(value * getWidth());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
mWasCanceled = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
mWasCanceled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (!mWasCanceled) {
|
||||
if (mDismissOnComplete) {
|
||||
dismiss();
|
||||
} else {
|
||||
cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user