From 8bc983efc439c321c8f548d384d807c31daf9180 Mon Sep 17 00:00:00 2001 From: Jorim Jaggi Date: Wed, 10 Dec 2014 17:45:50 +0100 Subject: [PATCH] Improve transition when going to Wifi/BT detail panel Animate the height change of the lower QS panel when going into Wifi/BT or any other panel which is higher than the default size. Bug: 18665311 Change-Id: Ic1d1f19183cf6b502d6b623388b315ed6e0a3c9f --- packages/SystemUI/res/layout/qs_panel.xml | 4 +- .../com/android/systemui/qs/QSContainer.java | 76 +++++++++++++++++++ .../src/com/android/systemui/qs/QSPanel.java | 13 ++++ .../phone/NotificationPanelView.java | 74 +++++++++++++++--- 4 files changed, 153 insertions(+), 14 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/qs/QSContainer.java diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml index c76d4425efa95..187316885f9f8 100644 --- a/packages/SystemUI/res/layout/qs_panel.xml +++ b/packages/SystemUI/res/layout/qs_panel.xml @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> - - + diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java new file mode 100644 index 0000000000000..cfe8d07dd14b5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.qs; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.FrameLayout; + +import com.android.systemui.R; + +/** + * Wrapper view with background which contains {@link QSPanel} + */ +public class QSContainer extends FrameLayout { + + private int mHeightOverride = -1; + private QSPanel mQSPanel; + + public QSContainer(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mQSPanel = (QSPanel) findViewById(R.id.quick_settings_panel); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + updateBottom(); + } + + /** + * Overrides the height of this view (post-layout), so that the content is clipped to that + * height and the background is set to that height. + * + * @param heightOverride the overridden height + */ + public void setHeightOverride(int heightOverride) { + mHeightOverride = heightOverride; + updateBottom(); + } + + /** + * The height this view wants to be. This is different from {@link #getMeasuredHeight} such that + * during closing the detail panel, this already returns the smaller height. + */ + public int getDesiredHeight() { + if (mQSPanel.isClosingDetail()) { + return mQSPanel.getGridHeight() + getPaddingTop() + getPaddingBottom(); + } else { + return getMeasuredHeight(); + } + } + + private void updateBottom() { + int height = mHeightOverride != -1 ? mHeightOverride : getMeasuredHeight(); + setBottom(getTop() + height); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 91b15697f94dc..974235e71dbcd 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -67,8 +67,10 @@ public class QSPanel extends ViewGroup { private int mPanelPaddingBottom; private int mDualTileUnderlap; private int mBrightnessPaddingTop; + private int mGridHeight; private boolean mExpanded; private boolean mListening; + private boolean mClosingDetail; private Record mDetailRecord; private Callback mCallback; @@ -320,6 +322,14 @@ public class QSPanel extends ViewGroup { showDetail(false, mDetailRecord); } + public boolean isClosingDetail() { + return mClosingDetail; + } + + public int getGridHeight() { + return mGridHeight; + } + private void handleShowDetail(Record r, boolean show) { if (r instanceof TileRecord) { handleShowDetailTile((TileRecord) r, show); @@ -364,6 +374,7 @@ public class QSPanel extends ViewGroup { setDetailRecord(r); listener = mHideGridContentWhenDone; } else { + mClosingDetail = true; setGridContentVisibility(true); listener = mTeardownDetailWhenDone; fireScanStateChanged(false); @@ -426,6 +437,7 @@ public class QSPanel extends ViewGroup { if (mDetail.getMeasuredHeight() < h) { mDetail.measure(exactly(width), exactly(h)); } + mGridHeight = h; setMeasuredDimension(width, Math.max(h, mDetail.getMeasuredHeight())); } @@ -537,6 +549,7 @@ public class QSPanel extends ViewGroup { public void onAnimationEnd(Animator animation) { mDetailContent.removeAllViews(); setDetailRecord(null); + mClosingDetail = false; }; }; 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 d2dc42512efcc..1b00e5993e96b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -40,6 +40,7 @@ import android.widget.TextView; import com.android.keyguard.KeyguardStatusView; import com.android.systemui.R; +import com.android.systemui.qs.QSContainer; import com.android.systemui.qs.QSPanel; import com.android.systemui.statusbar.ExpandableView; import com.android.systemui.statusbar.FlingAnimationUtils; @@ -71,7 +72,7 @@ public class NotificationPanelView extends PanelView implements private StatusBarHeaderView mHeader; private KeyguardUserSwitcher mKeyguardUserSwitcher; private KeyguardStatusBarView mKeyguardStatusBar; - private View mQsContainer; + private QSContainer mQsContainer; private QSPanel mQsPanel; private KeyguardStatusView mKeyguardStatusView; private ObservableScrollView mScrollView; @@ -161,6 +162,7 @@ public class NotificationPanelView extends PanelView implements private boolean mKeyguardStatusViewAnimating; private boolean mHeaderAnimatingIn; private ObjectAnimator mQsContainerAnimator; + private ValueAnimator mQsSizeChangeAnimator; private boolean mShadeEmpty; @@ -188,7 +190,7 @@ public class NotificationPanelView extends PanelView implements mHeader.setOnClickListener(this); mKeyguardStatusBar = (KeyguardStatusBarView) findViewById(R.id.keyguard_header); mKeyguardStatusView = (KeyguardStatusView) findViewById(R.id.keyguard_status_view); - mQsContainer = findViewById(R.id.quick_settings_container); + mQsContainer = (QSContainer) findViewById(R.id.quick_settings_container); mQsPanel = (QSPanel) findViewById(R.id.quick_settings_panel); mClockView = (TextView) findViewById(R.id.clock_view); mScrollView = (ObservableScrollView) findViewById(R.id.scroll_view); @@ -283,21 +285,35 @@ public class NotificationPanelView extends PanelView implements mKeyguardStatusView.setPivotY((FONT_HEIGHT - CAP_HEIGHT) / 2048f * mClockView.getTextSize()); // Calculate quick setting heights. + int oldMaxHeight = mQsMaxExpansionHeight; mQsMinExpansionHeight = mKeyguardShowing ? 0 : mHeader.getCollapsedHeight() + mQsPeekHeight; - mQsMaxExpansionHeight = mHeader.getExpandedHeight() + mQsContainer.getHeight(); + mQsMaxExpansionHeight = mHeader.getExpandedHeight() + mQsContainer.getDesiredHeight(); positionClockAndNotifications(); - if (mQsExpanded) { - if (mQsFullyExpanded) { - mQsExpansionHeight = mQsMaxExpansionHeight; - requestScrollerTopPaddingUpdate(false /* animate */); + if (mQsExpanded && mQsFullyExpanded) { + mQsExpansionHeight = mQsMaxExpansionHeight; + requestScrollerTopPaddingUpdate(false /* animate */); + requestPanelHeightUpdate(); + + // Size has changed, start an animation. + if (mQsMaxExpansionHeight != oldMaxHeight) { + startQsSizeChangeAnimation(oldMaxHeight, mQsMaxExpansionHeight); } - } else { + } else if (!mQsExpanded) { setQsExpansion(mQsMinExpansionHeight + mLastOverscroll); - mNotificationStackScroller.setStackHeight(getExpandedHeight()); } + mNotificationStackScroller.setStackHeight(getExpandedHeight()); updateHeader(); mNotificationStackScroller.updateIsSmallScreen( mHeader.getCollapsedHeight() + mQsPeekHeight); + + // If we are running a size change animation, the animation takes care of the height of + // the container. However, if we are not animating, we always need to make the QS container + // the desired height so when closing the QS detail, it stays smaller after the size change + // animation is finished but the detail view is still being animated away (this animation + // takes longer than the size change animation). + if (mQsSizeChangeAnimator == null) { + mQsContainer.setHeightOverride(mQsContainer.getDesiredHeight()); + } } @Override @@ -310,6 +326,32 @@ public class NotificationPanelView extends PanelView implements mSecureCameraLaunchManager.destroy(); } + private void startQsSizeChangeAnimation(int oldHeight, final int newHeight) { + if (mQsSizeChangeAnimator != null) { + oldHeight = (int) mQsSizeChangeAnimator.getAnimatedValue(); + mQsSizeChangeAnimator.cancel(); + } + mQsSizeChangeAnimator = ValueAnimator.ofInt(oldHeight, newHeight); + mQsSizeChangeAnimator.setDuration(300); + mQsSizeChangeAnimator.setInterpolator(mFastOutSlowInInterpolator); + mQsSizeChangeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + requestScrollerTopPaddingUpdate(false /* animate */); + requestPanelHeightUpdate(); + int height = (int) mQsSizeChangeAnimator.getAnimatedValue(); + mQsContainer.setHeightOverride(height - mHeader.getExpandedHeight()); + } + }); + mQsSizeChangeAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mQsSizeChangeAnimator = null; + } + }); + mQsSizeChangeAnimator.start(); + } + /** * Positions the clock and notifications dynamically depending on how many notifications are * showing. @@ -1113,7 +1155,7 @@ public class NotificationPanelView extends PanelView implements private void setQsTranslation(float height) { if (!mHeaderAnimatingIn) { - mQsContainer.setY(height - mQsContainer.getHeight() + getHeaderTranslation()); + mQsContainer.setY(height - mQsContainer.getDesiredHeight() + getHeaderTranslation()); } if (mKeyguardShowing) { mHeader.setY(interpolate(getQsExpansionFraction(), -mHeader.getHeight(), 0)); @@ -1137,6 +1179,8 @@ public class NotificationPanelView extends PanelView implements : maxQs; return (int) interpolate(getExpandedFraction(), mQsMinExpansionHeight, max); + } else if (mQsSizeChangeAnimator != null) { + return (int) mQsSizeChangeAnimator.getAnimatedValue(); } else if (mKeyguardShowing && mScrollYOverride == -1) { // We can only do the smoother transition on Keyguard when we also are not collapsing @@ -1348,14 +1392,20 @@ public class NotificationPanelView extends PanelView implements + mNotificationStackScroller.getBottomStackPeekSize() + mNotificationStackScroller.getCollapseSecondCardPadding(); } + int maxQsHeight = mQsMaxExpansionHeight; + + // If an animation is changing the size of the QS panel, take the animated value. + if (mQsSizeChangeAnimator != null) { + maxQsHeight = (int) mQsSizeChangeAnimator.getAnimatedValue(); + } float totalHeight = Math.max( - mQsMaxExpansionHeight + mNotificationStackScroller.getNotificationTopPadding(), + maxQsHeight + mNotificationStackScroller.getNotificationTopPadding(), mStatusBarState == StatusBarState.KEYGUARD ? mClockPositionResult.stackScrollerPadding - mTopPaddingAdjustment : 0) + notificationHeight; if (totalHeight > mNotificationStackScroller.getHeight()) { - float fullyCollapsedHeight = mQsMaxExpansionHeight + float fullyCollapsedHeight = maxQsHeight + mNotificationStackScroller.getMinStackHeight() + mNotificationStackScroller.getNotificationTopPadding() - getScrollViewScrollY();