Improve motion when expanding/collapsing status bar.
- Don't fade the whole panel anymore.
- Parallax effect for QS header translation, fade on keyguard.
- Improve fling curve for dismissing the panel.
- Improve peeking behavior.
Bug: 14804452
Bug: 15407838
Change-Id: I34b7bcd457cb8a037e0bb06e9802ec66d2b39b73
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 63698e4..b5f38ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -238,6 +238,11 @@
}
@Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+
+ @Override
public void onMethodSecureChanged(boolean methodSecure) {
updateTrust();
updateCameraVisibility();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 30d6e9f..19cfd2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -55,6 +55,7 @@
private static final int CAP_HEIGHT = 1456;
private static final int FONT_HEIGHT = 2163;
+ private static final float HEADER_RUBBERBAND_FACTOR = 2.15f;
private static final float LOCK_ICON_ACTIVE_SCALE = 1.2f;
private KeyguardPageSwipeHelper mPageSwiper;
@@ -97,7 +98,6 @@
private ValueAnimator mQsExpansionAnimator;
private FlingAnimationUtils mFlingAnimationUtils;
private int mStatusBarMinHeight;
- private boolean mHeaderHidden;
private boolean mUnlockIconActive;
private int mNotificationsHeaderCollideDistance;
private int mUnlockMoveDistance;
@@ -207,9 +207,7 @@
boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending();
int stackScrollerPadding;
if (mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
- int bottom = mStackScrollerOverscrolling
- ? mHeader.getCollapsedHeight()
- : mHeader.getBottom();
+ int bottom = mHeader.getCollapsedHeight();
stackScrollerPadding = bottom + mQsPeekHeight
+ mNotificationTopPadding;
mTopPaddingAdjustment = 0;
@@ -562,6 +560,10 @@
}
public void setKeyguardShowing(boolean keyguardShowing) {
+ if (!mKeyguardShowing && keyguardShowing) {
+ setQsTranslation(mQsExpansionHeight);
+ mHeader.setTranslationY(0f);
+ }
mKeyguardShowing = keyguardShowing;
updateQsState();
}
@@ -602,10 +604,9 @@
}
private void setQsTranslation(float height) {
- mQsContainer.setY(height - mQsContainer.getHeight());
+ mQsContainer.setY(height - mQsContainer.getHeight() + getHeaderTranslation());
}
-
private void requestScrollerTopPaddingUpdate(boolean animate) {
mNotificationStackScroller.updateTopPadding(mQsExpansionHeight,
mScrollView.getScrollY(),
@@ -728,6 +729,11 @@
@Override
protected int getMaxPanelHeight() {
+ if (mStatusBar.getBarState() != StatusBarState.KEYGUARD
+ && mNotificationStackScroller.getNotGoneChildCount() == 0) {
+ return (int) ((mQsMinExpansionHeight + getOverExpansionAmount())
+ * HEADER_RUBBERBAND_FACTOR);
+ }
// TODO: Figure out transition for collapsing when QS is open, adjust height here.
int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin();
int maxHeight = mNotificationStackScroller.getHeight() - emptyBottomMargin
@@ -746,8 +752,22 @@
positionClockAndNotifications();
}
mNotificationStackScroller.setStackHeight(expandedHeight);
- updateKeyguardHeaderVisibility();
+ updateHeader();
updateUnlockIcon();
+ updateNotificationTranslucency();
+ }
+
+ private void updateNotificationTranslucency() {
+ float alpha = (mNotificationStackScroller.getNotificationsTopY()
+ + mNotificationStackScroller.getItemHeight())
+ / (mQsMinExpansionHeight
+ + mNotificationStackScroller.getItemHeight() / 2);
+ alpha = Math.max(0, Math.min(alpha, 1));
+ alpha = (float) Math.pow(alpha, 0.75);
+
+ // TODO: Draw a rect with DST_OUT over the notifications to achieve the same effect -
+ // this would be much more efficient.
+ mNotificationStackScroller.setAlpha(alpha);
}
@Override
@@ -786,51 +806,61 @@
/**
* Hides the header when notifications are colliding with it.
*/
- private void updateKeyguardHeaderVisibility() {
+ private void updateHeader() {
if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
|| mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
- boolean hidden;
- if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
-
- // When on Keyguard, we hide the header as soon as the top card of the notification
- // stack scroller is close enough (collision distance) to the bottom of the header.
- hidden = mNotificationStackScroller.getNotificationsTopY()
- <= mHeader.getBottom() + mNotificationsHeaderCollideDistance;
- } else {
-
- // In SHADE_LOCKED, the top card is already really close to the header. Hide it as
- // soon as we start translating the stack.
- hidden = mNotificationStackScroller.getTranslationY() < 0;
- }
-
- if (hidden && !mHeaderHidden) {
- mHeader.animate()
- .alpha(0f)
- .withLayer()
- .translationY(-mHeader.getHeight()/2)
- .setInterpolator(mFastOutLinearInterpolator)
- .setDuration(200);
- } else if (!hidden && mHeaderHidden) {
- mHeader.animate()
- .alpha(1f)
- .withLayer()
- .translationY(0)
- .setInterpolator(mLinearOutSlowInInterpolator)
- .setDuration(200);
- }
- mHeaderHidden = hidden;
+ updateHeaderKeyguard();
} else {
- mHeader.animate().cancel();
- mHeader.setAlpha(1f);
- mHeader.setTranslationY(0f);
- if (mHeader.getLayerType() != LAYER_TYPE_NONE) {
- mHeader.setLayerType(LAYER_TYPE_NONE, null);
- }
- mHeaderHidden = false;
+ updateHeaderShade();
}
}
+ private void updateHeaderShade() {
+ mHeader.setAlpha(1f);
+ mHeader.setTranslationY(getHeaderTranslation());
+ setQsTranslation(mQsExpansionHeight);
+ }
+
+ private float getHeaderTranslation() {
+ if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
+ || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
+ return 0;
+ }
+ if (mNotificationStackScroller.getNotGoneChildCount() == 0) {
+ if (mExpandedHeight / HEADER_RUBBERBAND_FACTOR >= mQsMinExpansionHeight) {
+ return 0;
+ } else {
+ return mExpandedHeight / HEADER_RUBBERBAND_FACTOR - mQsMinExpansionHeight;
+ }
+ }
+ return Math.min(0, mNotificationStackScroller.getTranslationY()) / HEADER_RUBBERBAND_FACTOR;
+ }
+
+ private void updateHeaderKeyguard() {
+ mHeader.setTranslationY(0f);
+ float alpha;
+ if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
+
+ // When on Keyguard, we hide the header as soon as the top card of the notification
+ // stack scroller is close enough (collision distance) to the bottom of the header.
+ alpha = mNotificationStackScroller.getNotificationsTopY()
+ /
+ (mQsMinExpansionHeight + mNotificationsHeaderCollideDistance);
+
+ } else {
+
+ // In SHADE_LOCKED, the top card is already really close to the header. Hide it as
+ // soon as we start translating the stack.
+ alpha = mNotificationStackScroller.getNotificationsTopY() / mQsMinExpansionHeight;
+ }
+ alpha = Math.max(0, Math.min(alpha, 1));
+ alpha = (float) Math.pow(alpha, 0.75);
+ mHeader.setAlpha(alpha);
+ mKeyguardBottomArea.setAlpha(alpha);
+ setQsTranslation(mQsExpansionHeight);
+ }
+
@Override
protected void onExpandingStarted() {
super.onExpandingStarted();
@@ -1007,4 +1037,13 @@
? mKeyguardBottomArea.getPhoneImageView()
: mKeyguardBottomArea.getCameraImageView();
}
+
+ @Override
+ protected float getPeekHeight() {
+ if (mNotificationStackScroller.getNotGoneChildCount() > 0) {
+ return mNotificationStackScroller.getPeekHeight();
+ } else {
+ return mQsMinExpansionHeight;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 12aa004..1e45ce3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -93,15 +93,14 @@
}
private void runPeekAnimation() {
+ mPeekHeight = getPeekHeight();
if (DEBUG) logf("peek to height=%.1f", mPeekHeight);
if (mHeightAnimator != null) {
return;
}
- if (mPeekAnimator == null) {
- mPeekAnimator = ObjectAnimator.ofFloat(this,
- "expandedHeight", mPeekHeight)
- .setDuration(250);
- }
+ mPeekAnimator = ObjectAnimator.ofFloat(this,
+ "expandedHeight", mPeekHeight)
+ .setDuration(250);
mPeekAnimator.start();
}
@@ -115,9 +114,6 @@
protected void loadDimens() {
final Resources res = getContext().getResources();
- mPeekHeight = res.getDimension(R.dimen.peek_height)
- + getPaddingBottom(); // our window might have a dropshadow
-
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();
mHintDistance = res.getDimension(R.dimen.hint_move_distance);
@@ -758,4 +754,6 @@
}
public abstract void resetViews();
+
+ protected abstract float getPeekHeight();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 19c090c..f70010e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -31,6 +31,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.app.ActivityManager;
@@ -221,6 +222,8 @@
LinearLayout mStatusIcons;
// the icons themselves
IconMerger mNotificationIcons;
+ View mNotificationIconArea;
+
// [+>
View mMoreIcon;
@@ -300,16 +303,6 @@
: null;
private int mNavigationIconHints = 0;
- private final Animator.AnimatorListener mMakeIconsInvisible = new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- // double-check to avoid races
- if (mStatusBarContents.getAlpha() == 0) {
- if (DEBUG) Log.d(TAG, "makeIconsInvisible");
- mStatusBarContents.setVisibility(View.INVISIBLE);
- }
- }
- };
// ensure quick settings is disabled until the current user makes it through the setup wizard
private boolean mUserSetup = false;
@@ -383,8 +376,8 @@
private boolean mDozing;
private Interpolator mLinearOutSlowIn;
- private Interpolator mAlphaOut = new PathInterpolator(0f, 0.4f, 1f, 1f);
- private Interpolator mAlphaIn = new PathInterpolator(0f, 0f, 0.8f, 1f);
+ private Interpolator mAlphaIn = new PathInterpolator(0f, 0.2f, 1f, 1f);
+ private Interpolator mAlphaOut = new PathInterpolator(0f, 0f, 0.8f, 1f);
private final OnChildLocationsChangedListener mOnChildLocationsChangedListener =
new OnChildLocationsChangedListener() {
@@ -608,6 +601,7 @@
mSystemIconArea = (LinearLayout) mStatusBarView.findViewById(R.id.system_icon_area);
mSystemIcons = (LinearLayout) mStatusBarView.findViewById(R.id.system_icons);
mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons);
+ mNotificationIconArea = mStatusBarView.findViewById(R.id.notification_icon_area_inner);
mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons);
mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon);
mNotificationIcons.setOverflowIndicator(mMoreIcon);
@@ -1423,7 +1417,7 @@
/**
* State is one or more of the DISABLE constants from StatusBarManager.
*/
- public void disable(int state) {
+ public void disable(int state, boolean animate) {
mDisabledUnmodified = state;
state = adjustDisableFlags(state);
final int old = mDisabled;
@@ -1461,9 +1455,9 @@
if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
mSystemIconArea.animate().cancel();
if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
- animateStatusBarHide(mSystemIconArea);
+ animateStatusBarHide(mSystemIconArea, animate);
} else {
- animateStatusBarShow(mSystemIconArea);
+ animateStatusBarShow(mSystemIconArea, animate);
}
}
@@ -1496,9 +1490,9 @@
if (mTicking) {
haltTicker();
}
- animateStatusBarHide(mNotificationIcons);
+ animateStatusBarHide(mNotificationIconArea, animate);
} else {
- animateStatusBarShow(mNotificationIcons);
+ animateStatusBarShow(mNotificationIconArea, animate);
}
}
}
@@ -1506,35 +1500,49 @@
/**
* Animates {@code v}, a view that is part of the status bar, out.
*/
- private void animateStatusBarHide(View v) {
+ private void animateStatusBarHide(final View v, boolean animate) {
+ v.animate().cancel();
+ if (!animate) {
+ v.setAlpha(0f);
+ v.setVisibility(View.INVISIBLE);
+ return;
+ }
v.animate()
.alpha(0f)
- .withLayer()
.setDuration(160)
- .setInterpolator(mAlphaIn)
.setStartDelay(0)
- .setListener(mMakeIconsInvisible)
- .start();
+ .setInterpolator(mAlphaOut)
+ .withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ v.setVisibility(View.INVISIBLE);
+ }
+ });
}
/**
* Animates {@code v}, a view that is part of the status bar, in.
*/
- private void animateStatusBarShow(View v) {
+ private void animateStatusBarShow(View v, boolean animate) {
+ v.animate().cancel();
v.setVisibility(View.VISIBLE);
+ if (!animate) {
+ v.setAlpha(1f);
+ return;
+ }
v.animate()
.alpha(1f)
- .withLayer()
- .setInterpolator(mAlphaOut)
.setDuration(320)
- .setStartDelay(0);
+ .setInterpolator(mAlphaIn)
+ .setStartDelay(50);
// Synchronize the motion with the Keyguard fading if necessary.
if (mKeyguardFadingAway) {
v.animate()
.setDuration(mKeyguardFadingAwayDuration)
.setInterpolator(mLinearOutSlowIn)
- .setStartDelay(mKeyguardFadingAwayDelay);
+ .setStartDelay(mKeyguardFadingAwayDelay)
+ .start();
}
}
@@ -1636,7 +1644,7 @@
visibilityChanged(true);
mWaitingForKeyguardExit = false;
- disable(mDisabledUnmodified);
+ disable(mDisabledUnmodified, !force /* animate */);
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
}
@@ -1810,10 +1818,6 @@
// Shrink the window to the size of the status bar only
mStatusBarWindowManager.setStatusBarExpanded(false);
- if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
- setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
- }
-
// Close any "App info" popups that might have snuck on-screen
dismissPopups();
@@ -1824,7 +1828,7 @@
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
showBouncer();
- disable(mDisabledUnmodified);
+ disable(mDisabledUnmodified, true /* animate */);
}
public boolean interceptTouchEvent(MotionEvent event) {
@@ -2337,15 +2341,6 @@
mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
}
- void setNotificationIconVisibility(boolean visible, int anim) {
- int old = mNotificationIcons.getVisibility();
- int v = visible ? View.VISIBLE : View.INVISIBLE;
- if (old != v) {
- mNotificationIcons.setVisibility(v);
- mNotificationIcons.startAnimation(loadAnim(anim, null));
- }
- }
-
static final float saturate(float a) {
return a < 0f ? 0f : (a > 1f ? 1f : a);
}
@@ -2835,7 +2830,7 @@
mKeyguardFadingAwayDelay = delay;
mKeyguardFadingAwayDuration = fadeoutDuration;
mWaitingForKeyguardExit = false;
- disable(mDisabledUnmodified);
+ disable(mDisabledUnmodified, true /* animate */);
}
/**
@@ -3093,7 +3088,7 @@
@Override
public void setBouncerShowing(boolean bouncerShowing) {
super.setBouncerShowing(bouncerShowing);
- disable(mDisabledUnmodified);
+ disable(mDisabledUnmodified, true /* animate */);
}
public void onScreenTurnedOff() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 3a17177..3cddcfb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -111,7 +111,14 @@
@Override
public void onAllPanelsCollapsed() {
super.onAllPanelsCollapsed();
- mBar.makeExpandedInvisible();
+
+ // Close the status bar in the next frame so we can show the end of the animation.
+ postOnAnimation(new Runnable() {
+ @Override
+ public void run() {
+ mBar.makeExpandedInvisible();
+ }
+ });
mLastFullyOpenedPanel = null;
}
@@ -166,27 +173,7 @@
@Override
public void panelExpansionChanged(PanelView panel, float frac) {
super.panelExpansionChanged(panel, frac);
-
- if (DEBUG) {
- Log.v(TAG, "panelExpansionChanged: f=" + frac);
- }
-
mScrimController.setPanelExpansion(frac);
-
- // fade out the panel as it gets buried into the status bar to avoid overdrawing the
- // status bar on the last frame of a close animation
- final int H = mBar.getStatusBarHeight();
- final float ph = panel.getExpandedHeight() + panel.getPaddingBottom();
- float alpha = 1f;
- if (ph < 2*H) {
- if (ph < H) alpha = 0f;
- else alpha = (ph - H) / H;
- alpha = alpha * alpha; // get there faster
- }
- if (panel.getAlpha() != alpha) {
- panel.setAlpha(alpha);
- }
-
mBar.updateCarrierLabelVisibility(false);
mBar.userActivity();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index cd304d0..9e9de5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -405,4 +405,9 @@
updateVisibilities();
updateSystemIconsLayoutParams();
}
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return !mKeyguardShowing || mExpanded;
+ }
}