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
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index c76d442..1873168 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.
-->
-<FrameLayout
+<com.android.systemui.qs.QSContainer
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/quick_settings_container"
android:layout_width="match_parent"
@@ -28,4 +28,4 @@
android:background="#0000"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
-</FrameLayout>
+</com.android.systemui.qs.QSContainer>
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 0000000..cfe8d07
--- /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 91b1569..974235e 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 @@
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 @@
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 @@
setDetailRecord(r);
listener = mHideGridContentWhenDone;
} else {
+ mClosingDetail = true;
setGridContentVisibility(true);
listener = mTeardownDetailWhenDone;
fireScanStateChanged(false);
@@ -426,6 +437,7 @@
if (mDetail.getMeasuredHeight() < h) {
mDetail.measure(exactly(width), exactly(h));
}
+ mGridHeight = h;
setMeasuredDimension(width, Math.max(h, mDetail.getMeasuredHeight()));
}
@@ -537,6 +549,7 @@
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 d2dc425..1b00e59 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 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 @@
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 @@
private boolean mKeyguardStatusViewAnimating;
private boolean mHeaderAnimatingIn;
private ObjectAnimator mQsContainerAnimator;
+ private ValueAnimator mQsSizeChangeAnimator;
private boolean mShadeEmpty;
@@ -188,7 +190,7 @@
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 @@
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 @@
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 @@
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 @@
: 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 @@
+ 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();