summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/SwipeHelper.java75
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java84
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java191
3 files changed, 258 insertions, 92 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 1fe218a885fb..81ba23f27f23 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -517,35 +517,16 @@ public class SwipeHelper implements Gefingerpoken {
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- if (mCurrView != null) {
- float maxVelocity = MAX_DISMISS_VELOCITY * mDensityScale;
- mVelocityTracker.computeCurrentVelocity(1000 /* px/sec */, maxVelocity);
- float escapeVelocity = SWIPE_ESCAPE_VELOCITY * mDensityScale;
- float velocity = getVelocity(mVelocityTracker);
- float perpendicularVelocity = getPerpendicularVelocity(mVelocityTracker);
-
- float translation = getTranslation(mCurrView);
- // Decide whether to dismiss the current view
- boolean childSwipedFarEnough = DISMISS_IF_SWIPED_FAR_ENOUGH &&
- Math.abs(translation) > 0.4 * getSize(mCurrView);
- boolean childSwipedFastEnough = (Math.abs(velocity) > escapeVelocity) &&
- (Math.abs(velocity) > Math.abs(perpendicularVelocity)) &&
- (velocity > 0) == (translation > 0);
- boolean falsingDetected = mCallback.isAntiFalsingNeeded();
-
- if (mFalsingManager.isClassiferEnabled()) {
- falsingDetected = falsingDetected && mFalsingManager.isFalseTouch();
- } else {
- falsingDetected = falsingDetected && !mTouchAboveFalsingThreshold;
- }
-
- boolean dismissChild = mCallback.canChildBeDismissed(mCurrView)
- && !falsingDetected && (childSwipedFastEnough || childSwipedFarEnough)
- && ev.getActionMasked() == MotionEvent.ACTION_UP;
+ if (mCurrView == null) {
+ break;
+ }
+ mVelocityTracker.computeCurrentVelocity(1000 /* px/sec */, getMaxVelocity());
+ float velocity = getVelocity(mVelocityTracker);
- if (dismissChild) {
+ if (!handleUpEvent(ev, mCurrView, velocity, getTranslation(mCurrView))) {
+ if (isDismissGesture(ev)) {
// flingadingy
- dismissChild(mCurrView, childSwipedFastEnough ? velocity : 0f);
+ dismissChild(mCurrView, swipedFastEnough() ? velocity : 0f);
} else {
// snappity
mCallback.onDragCancelled(mCurrView);
@@ -562,6 +543,46 @@ public class SwipeHelper implements Gefingerpoken {
return (int) (mFalsingThreshold * factor);
}
+ private float getMaxVelocity() {
+ return MAX_DISMISS_VELOCITY * mDensityScale;
+ }
+
+ protected float getEscapeVelocity() {
+ return SWIPE_ESCAPE_VELOCITY * mDensityScale;
+ }
+
+ protected boolean swipedFarEnough() {
+ float translation = getTranslation(mCurrView);
+ return DISMISS_IF_SWIPED_FAR_ENOUGH && Math.abs(translation) > 0.4 * getSize(mCurrView);
+ }
+
+ protected boolean isDismissGesture(MotionEvent ev) {
+ boolean falsingDetected = mCallback.isAntiFalsingNeeded();
+ if (mFalsingManager.isClassiferEnabled()) {
+ falsingDetected = falsingDetected && mFalsingManager.isFalseTouch();
+ } else {
+ falsingDetected = falsingDetected && !mTouchAboveFalsingThreshold;
+ }
+ return !falsingDetected && (swipedFastEnough() || swipedFarEnough())
+ && ev.getActionMasked() == MotionEvent.ACTION_UP
+ && mCallback.canChildBeDismissed(mCurrView);
+ }
+
+ protected boolean swipedFastEnough() {
+ float velocity = getVelocity(mVelocityTracker);
+ float perpendicularVelocity = getPerpendicularVelocity(mVelocityTracker);
+ float translation = getTranslation(mCurrView);
+ boolean ret = (Math.abs(velocity) > getEscapeVelocity()) &&
+ (Math.abs(velocity) > Math.abs(perpendicularVelocity)) &&
+ (velocity > 0) == (translation > 0);
+ return ret;
+ }
+
+ protected boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
+ float translation) {
+ return false;
+ }
+
public interface Callback {
View getChildAtPosition(MotionEvent ev);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
index 375459f0f30c..fcc48bf8d510 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
@@ -29,11 +29,18 @@ import com.android.systemui.R;
public class NotificationSettingsIconRow extends FrameLayout implements View.OnClickListener {
+ private static final int GEAR_ALPHA_ANIM_DURATION = 200;
+
public interface SettingsIconRowListener {
/**
* Called when the gear behind a notification is touched.
*/
public void onGearTouched(ExpandableNotificationRow row, int x, int y);
+
+ /**
+ * Called when a notification is slid back over the gear.
+ */
+ public void onSettingsIconRowReset(NotificationSettingsIconRow row);
}
private ExpandableNotificationRow mParent;
@@ -45,6 +52,8 @@ public class NotificationSettingsIconRow extends FrameLayout implements View.OnC
private boolean mSettingsFadedIn = false;
private boolean mAnimating = false;
private boolean mOnLeft = true;
+ private boolean mDismissing = false;
+ private boolean mSnapping = false;
private int[] mGearLocation = new int[2];
private int[] mParentLocation = new int[2];
@@ -78,8 +87,14 @@ public class NotificationSettingsIconRow extends FrameLayout implements View.OnC
public void resetState() {
setGearAlpha(0f);
+ mSettingsFadedIn = false;
mAnimating = false;
+ mSnapping = false;
+ mDismissing = false;
setIconLocation(true /* on left */);
+ if (mListener != null) {
+ mListener.onSettingsIconRowReset(this);
+ }
}
public void setGearListener(SettingsIconRowListener listener) {
@@ -94,20 +109,24 @@ public class NotificationSettingsIconRow extends FrameLayout implements View.OnC
return mParent;
}
- private void setGearAlpha(float alpha) {
+ public void setGearAlpha(float alpha) {
if (alpha == 0) {
mSettingsFadedIn = false; // Can fade in again once it's gone.
setVisibility(View.INVISIBLE);
} else {
- if (alpha == 1) {
- mSettingsFadedIn = true;
- }
setVisibility(View.VISIBLE);
}
mGearIcon.setAlpha(alpha);
}
/**
+ * Returns whether the icon is on the left side of the view or not.
+ */
+ public boolean isIconOnLeft() {
+ return mOnLeft;
+ }
+
+ /**
* Returns the horizontal space in pixels required to display the gear behind a notification.
*/
public float getSpaceForGear() {
@@ -119,7 +138,7 @@ public class NotificationSettingsIconRow extends FrameLayout implements View.OnC
* if entire view is visible.
*/
public boolean isVisible() {
- return mSettingsFadedIn;
+ return mGearIcon.getAlpha() > 0;
}
public void cancelFadeAnimator() {
@@ -129,16 +148,18 @@ public class NotificationSettingsIconRow extends FrameLayout implements View.OnC
}
public void updateSettingsIcons(final float transX, final float size) {
- if (mAnimating || (mGearIcon.getAlpha() == 0)) {
- // Don't adjust when animating or settings aren't visible
+ if (mAnimating || !mSettingsFadedIn) {
+ // Don't adjust when animating, or if the gear hasn't been shown yet.
return;
}
- setIconLocation(transX > 0 /* fromLeft */);
+
final float fadeThreshold = size * 0.3f;
final float absTrans = Math.abs(transX);
float desiredAlpha = 0;
- if (absTrans <= fadeThreshold) {
+ if (absTrans == 0) {
+ desiredAlpha = 0;
+ } else if (absTrans <= fadeThreshold) {
desiredAlpha = 1;
} else {
desiredAlpha = 1 - ((absTrans - fadeThreshold) / (size - fadeThreshold));
@@ -148,6 +169,12 @@ public class NotificationSettingsIconRow extends FrameLayout implements View.OnC
public void fadeInSettings(final boolean fromLeft, final float transX,
final float notiThreshold) {
+ if (mDismissing || mAnimating) {
+ return;
+ }
+ if (isIconLocationChange(transX)) {
+ setGearAlpha(0f);
+ }
setIconLocation(transX > 0 /* fromLeft */);
mFadeAnimator = ValueAnimator.ofFloat(mGearIcon.getAlpha(), 1);
mFadeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@@ -164,40 +191,53 @@ public class NotificationSettingsIconRow extends FrameLayout implements View.OnC
});
mFadeAnimator.addListener(new AnimatorListenerAdapter() {
@Override
- public void onAnimationCancel(Animator animation) {
- super.onAnimationCancel(animation);
- mAnimating = false;
- mSettingsFadedIn = false;
+ public void onAnimationStart(Animator animation) {
+ mAnimating = true;
}
@Override
- public void onAnimationStart(Animator animation) {
- super.onAnimationStart(animation);
- mAnimating = true;
+ public void onAnimationCancel(Animator animation) {
+ // TODO should animate back to 0f from current alpha
+ mGearIcon.setAlpha(0f);
}
@Override
public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
mAnimating = false;
- mSettingsFadedIn = true;
+ mSettingsFadedIn = mGearIcon.getAlpha() == 1;
}
});
mFadeAnimator.setInterpolator(Interpolators.ALPHA_IN);
- mFadeAnimator.setDuration(200);
+ mFadeAnimator.setDuration(GEAR_ALPHA_ANIM_DURATION);
mFadeAnimator.start();
}
- private void setIconLocation(boolean onLeft) {
- if (onLeft == mOnLeft) {
+ public void setIconLocation(boolean onLeft) {
+ if (onLeft == mOnLeft || mSnapping) {
// Same side? Do nothing.
return;
}
-
setTranslationX(onLeft ? 0 : (mParent.getWidth() - mHorizSpaceForGear));
mOnLeft = onLeft;
}
+ public boolean isIconLocationChange(float translation) {
+ boolean onLeft = translation > mGearIcon.getPaddingStart();
+ boolean onRight = translation < -mGearIcon.getPaddingStart();
+ if ((mOnLeft && onRight) || (!mOnLeft && onLeft)) {
+ return true;
+ }
+ return false;
+ }
+
+ public void setDismissing() {
+ mDismissing = true;
+ }
+
+ public void setSnapping(boolean snapping) {
+ mSnapping = snapping;
+ }
+
@Override
public void onClick(View v) {
if (v.getId() == R.id.gear_icon) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 2da47874b49b..4cb0dea118ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -372,6 +372,11 @@ public class NotificationStackScrollLayout extends ViewGroup
}
@Override
+ public void onSettingsIconRowReset(NotificationSettingsIconRow row) {
+ mSwipeHelper.setSnappedToGear(false);
+ }
+
+ @Override
protected void onDraw(Canvas canvas) {
canvas.drawRect(0, mCurrentBounds.top, getWidth(), mCurrentBounds.bottom, mBackgroundPaint);
if (DEBUG) {
@@ -717,11 +722,15 @@ public class NotificationStackScrollLayout extends ViewGroup
mDragAnimPendingChildren.remove(animView);
}
- if (targetLeft == 0 && mCurrIconRow != null) {
- mCurrIconRow.resetState();
- mCurrIconRow = null;
- if (mGearExposedView != null && mGearExposedView == mTranslatingParentView) {
- mGearExposedView = null;
+ if (mCurrIconRow != null) {
+ if (targetLeft == 0) {
+ mCurrIconRow.resetState();
+ mCurrIconRow = null;
+ if (mGearExposedView != null && mGearExposedView == mTranslatingParentView) {
+ mGearExposedView = null;
+ }
+ } else {
+ mSwipeHelper.setSnappedToGear(true);
}
}
}
@@ -3379,15 +3388,11 @@ public class NotificationStackScrollLayout extends ViewGroup
}
private class NotificationSwipeHelper extends SwipeHelper {
- private static final int MOVE_STATE_LEFT = -1;
- private static final int MOVE_STATE_UNDEFINED = 0;
- private static final int MOVE_STATE_RIGHT = 1;
-
private static final long GEAR_SHOW_DELAY = 60;
-
private CheckForDrag mCheckForDrag;
private Handler mHandler;
- private int mMoveState = MOVE_STATE_UNDEFINED;
+ private boolean mGearSnappedTo;
+ private boolean mGearSnappedOnLeft;
public NotificationSwipeHelper(int swipeDirection, Callback callback, Context context) {
super(swipeDirection, callback, context);
@@ -3400,6 +3405,10 @@ public class NotificationStackScrollLayout extends ViewGroup
mTranslatingParentView = currView;
// Reset check for drag gesture
+ cancelCheckForDrag();
+ if (mCurrIconRow != null) {
+ mCurrIconRow.setSnapping(false);
+ }
mCheckForDrag = null;
mCurrIconRow = null;
@@ -3411,17 +3420,32 @@ public class NotificationStackScrollLayout extends ViewGroup
mCurrIconRow = ((ExpandableNotificationRow) currView).getSettingsRow();
mCurrIconRow.setGearListener(NotificationStackScrollLayout.this);
}
- mMoveState = MOVE_STATE_UNDEFINED;
}
@Override
public void onMoveUpdate(View view, float translation, float delta) {
- final int newMoveState = (delta < 0) ? MOVE_STATE_RIGHT : MOVE_STATE_LEFT;
- if (mMoveState != MOVE_STATE_UNDEFINED && mMoveState != newMoveState) {
- // Changed directions, make sure we check for drag again.
- mCheckForDrag = null;
+ if (mCurrIconRow != null) {
+ mCurrIconRow.setSnapping(false); // If we're moving, we're not snapping.
+
+ // If the gear is visible and the movement is towards it it's not a location change.
+ boolean onLeft = mGearSnappedTo ? mGearSnappedOnLeft : mCurrIconRow.isIconOnLeft();
+ boolean locationChange = isTowardsGear(translation, onLeft)
+ ? false : mCurrIconRow.isIconLocationChange(translation);
+ if (locationChange) {
+ // Don't consider it "snapped" if location has changed.
+ setSnappedToGear(false);
+
+ // Changed directions, make sure we check to fade in icon again.
+ if (!mHandler.hasCallbacks(mCheckForDrag)) {
+ // No check scheduled, set null to schedule a new one.
+ mCheckForDrag = null;
+ } else {
+ // Check scheduled, reset alpha and update location; check will fade it in
+ mCurrIconRow.setGearAlpha(0f);
+ mCurrIconRow.setIconLocation(translation > 0 /* onLeft */);
+ }
+ }
}
- mMoveState = newMoveState;
final boolean gutsExposed = (view instanceof ExpandableNotificationRow)
&& ((ExpandableNotificationRow) view).areGutsExposed();
@@ -3434,35 +3458,99 @@ public class NotificationStackScrollLayout extends ViewGroup
@Override
public void dismissChild(final View view, float velocity) {
- cancelCheckForDrag();
super.dismissChild(view, velocity);
+ cancelCheckForDrag();
+ setSnappedToGear(false);
}
@Override
public void snapChild(final View animView, final float targetLeft, float velocity) {
+ super.snapChild(animView, targetLeft, velocity);
+ if (targetLeft == 0) {
+ cancelCheckForDrag();
+ setSnappedToGear(false);
+ }
+ }
+
+
+ @Override
+ public boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
+ float translation) {
+ if (mCurrIconRow == null) {
+ cancelCheckForDrag();
+ return false; // Let SwipeHelper handle it.
+ }
+
+ boolean gestureTowardsGear = isTowardsGear(velocity, mCurrIconRow.isIconOnLeft());
+ boolean gestureFastEnough = Math.abs(velocity) > getEscapeVelocity();
+
+ if (mGearSnappedTo && mCurrIconRow.isVisible()) {
+ if (mGearSnappedOnLeft == mCurrIconRow.isIconOnLeft()) {
+ boolean coveringGear =
+ Math.abs(getTranslation(animView)) <= getSpaceForGear(animView) * 0.6f;
+ if (gestureTowardsGear || coveringGear) {
+ // Gesture is towards or covering the gear
+ snapChild(animView, 0 /* leftTarget */, velocity);
+ } else if (isDismissGesture(ev)) {
+ // Gesture is a dismiss that's not towards the gear
+ dismissChild(animView, swipedFastEnough() ? velocity : 0f);
+ } else {
+ // Didn't move enough to dismiss or cover, snap to the gear
+ snapToGear(animView, velocity);
+ }
+ } else if ((!gestureFastEnough && swipedEnoughToShowGear(animView))
+ || (gestureTowardsGear && !swipedFarEnough())) {
+ // The gear has been snapped to previously, however, the gear is now on the
+ // other side. If gesture is towards gear and not too far snap to the gear.
+ snapToGear(animView, velocity);
+ } else {
+ dismissOrSnapBack(animView, velocity, ev);
+ }
+ } else if ((!gestureFastEnough && swipedEnoughToShowGear(animView))
+ || gestureTowardsGear) {
+ // Gear has not been snapped to previously and this is gear revealing gesture
+ snapToGear(animView, velocity);
+ } else {
+ dismissOrSnapBack(animView, velocity, ev);
+ }
+ return true;
+ }
+
+ private void dismissOrSnapBack(View animView, float velocity, MotionEvent ev) {
+ if (isDismissGesture(ev)) {
+ dismissChild(animView, swipedFastEnough() ? velocity : 0f);
+ } else {
+ snapChild(animView, 0 /* leftTarget */, velocity);
+ }
+ }
+
+ private void snapToGear(View animView, float velocity) {
+ final float snapBackThreshold = getSpaceForGear(animView);
+ final float target = mCurrIconRow.isIconOnLeft() ? snapBackThreshold
+ : -snapBackThreshold;
+ mGearExposedView = mTranslatingParentView;
+ if (mGearDisplayedListener != null
+ && (animView instanceof ExpandableNotificationRow)) {
+ mGearDisplayedListener.onGearDisplayed((ExpandableNotificationRow) animView);
+ }
+ if (mCurrIconRow != null) {
+ mCurrIconRow.setSnapping(true);
+ setSnappedToGear(true);
+ }
+ super.snapChild(animView, target, velocity);
+ }
+
+ private boolean swipedEnoughToShowGear(View animView) {
final float snapBackThreshold = getSpaceForGear(animView);
final float translation = getTranslation(animView);
final boolean fromLeft = translation > 0;
final float absTrans = Math.abs(translation);
final float notiThreshold = getSize(mTranslatingParentView) * 0.4f;
- boolean pastGear = (fromLeft && translation >= snapBackThreshold * 0.4f
- && translation <= notiThreshold) ||
- (!fromLeft && absTrans >= snapBackThreshold * 0.4f
- && absTrans <= notiThreshold);
-
- if (pastGear && !isPinnedHeadsUp(animView)
- && (animView instanceof ExpandableNotificationRow)) {
- // bouncity
- final float target = fromLeft ? snapBackThreshold : -snapBackThreshold;
- mGearExposedView = mTranslatingParentView;
- if (mGearDisplayedListener != null) {
- mGearDisplayedListener.onGearDisplayed((ExpandableNotificationRow) animView);
- }
- super.snapChild(animView, target, velocity);
- } else {
- super.snapChild(animView, 0, velocity);
- }
+ // If the notification can't be dismissed then how far it can move is
+ // restricted -- reduce the distance it needs to move in this case.
+ final float multiplier = canChildBeDismissed(animView) ? 0.4f : 0.2f;
+ return absTrans >= snapBackThreshold * 0.4f && absTrans <= notiThreshold;
}
@Override
@@ -3499,6 +3587,25 @@ public class NotificationStackScrollLayout extends ViewGroup
}
/**
+ * Returns whether the gesture is towards the gear location or not.
+ */
+ private boolean isTowardsGear(float velocity, boolean onLeft) {
+ if (mCurrIconRow == null) {
+ return false;
+ }
+ return mCurrIconRow.isVisible()
+ && ((onLeft && velocity <= 0) || (!onLeft && velocity >= 0));
+ }
+
+ /**
+ * Indicates the the gear has been snapped to.
+ */
+ private void setSnappedToGear(boolean snapped) {
+ mGearSnappedOnLeft = (mCurrIconRow != null) ? mCurrIconRow.isIconOnLeft() : false;
+ mGearSnappedTo = snapped && mCurrIconRow != null;
+ }
+
+ /**
* Returns the horizontal space in pixels required to display the gear behind a
* notification.
*/
@@ -3510,7 +3617,7 @@ public class NotificationStackScrollLayout extends ViewGroup
}
private void checkForDrag() {
- if (mCheckForDrag == null) {
+ if (mCheckForDrag == null || !mHandler.hasCallbacks(mCheckForDrag)) {
mCheckForDrag = new CheckForDrag();
mHandler.postDelayed(mCheckForDrag, GEAR_SHOW_DELAY);
}
@@ -3521,7 +3628,6 @@ public class NotificationStackScrollLayout extends ViewGroup
mCurrIconRow.cancelFadeAnimator();
}
mHandler.removeCallbacks(mCheckForDrag);
- mCheckForDrag = null;
}
private final class CheckForDrag implements Runnable {
@@ -3531,14 +3637,13 @@ public class NotificationStackScrollLayout extends ViewGroup
final float absTransX = Math.abs(translation);
final float bounceBackToGearWidth = getSpaceForGear(mTranslatingParentView);
final float notiThreshold = getSize(mTranslatingParentView) * 0.4f;
- if (mCurrIconRow != null && absTransX >= bounceBackToGearWidth * 0.4
+ if ((mCurrIconRow != null && (!mCurrIconRow.isVisible()
+ || mCurrIconRow.isIconLocationChange(translation)))
+ && absTransX >= bounceBackToGearWidth * 0.4
&& absTransX < notiThreshold) {
- // Show icon
+ // Fade in the gear
mCurrIconRow.fadeInSettings(translation > 0 /* fromLeft */, translation,
notiThreshold);
- } else {
- // Allow more to be posted if this wasn't a drag.
- mCheckForDrag = null;
}
}
}
@@ -3551,7 +3656,7 @@ public class NotificationStackScrollLayout extends ViewGroup
final View prevGearExposedView = mGearExposedView;
mGearExposedView = null;
-
+ mGearSnappedTo = false;
Animator anim = getViewTranslationAnimator(prevGearExposedView,
0 /* leftTarget */, null /* updateListener */);
if (anim != null) {