diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java index d5f858c4df941..8ad200951eb47 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java @@ -34,7 +34,7 @@ public interface QS extends FragmentBase { String ACTION = "com.android.systemui.action.PLUGIN_QS"; - int VERSION = 12; + int VERSION = 13; String TAG = "QS"; @@ -70,6 +70,11 @@ public interface QS extends FragmentBase { void setContainerController(QSContainerController controller); void setExpandClickListener(OnClickListener onClickListener); + /** + * Returns the height difference between the QSPanel container and the QuickQSPanel container + */ + int getHeightDiff(); + View getHeader(); default void setHasNotifications(boolean hasNotifications) { diff --git a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml index 6b054a91d46b9..619591d4f7eca 100644 --- a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml +++ b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml @@ -20,4 +20,6 @@ android:id="@+id/qs_pager" android:layout_width="match_parent" android:layout_height="0dp" - android:layout_weight="1"/> + android:layout_weight="1" + android:clipChildren="false" + android:clipToPadding="false" /> diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index 3e2da7204c057..6d1f8f7d01b1c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java @@ -30,6 +30,7 @@ import com.android.systemui.qs.QSPanel.QSTileLayout; import com.android.systemui.qs.QSPanelControllerBase.TileRecord; import java.util.ArrayList; +import java.util.List; import java.util.Set; public class PagedTileLayout extends ViewPager implements QSTileLayout { @@ -332,6 +333,18 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { mPageListener = listener; } + public List getSpecsForPage(int page) { + ArrayList out = new ArrayList<>(); + if (page < 0) return out; + int perPage = mPages.get(0).maxTiles(); + int startOfPage = page * perPage; + int endOfPage = (page + 1) * perPage; + for (int i = startOfPage; i < endOfPage && i < mTiles.size(); i++) { + out.add(mTiles.get(i).tile.getTileSpec()); + } + return out; + } + private void distributeTiles() { emptyAndInflateOrRemovePages(); @@ -491,6 +504,11 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { return currentPage.mRecords.size(); } + public int getNumTilesFirstPage() { + if (mPages.size() == 0) return 0; + return mPages.get(0).mRecords.size(); + } + public void startTileReveal(Set tileSpecs, final Runnable postAnimation) { if (tileSpecs.isEmpty() || mPages.size() < 2 || getScrollX() != 0 || !beginFakeDrag()) { // Do not start the reveal animation unless there are tiles to animate, multiple @@ -556,8 +574,9 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { updateSelected(); if (mPageIndicator == null) return; if (mPageListener != null) { + int pageNumber = isLayoutRtl() ? mPages.size() - 1 - position : position; mPageListener.onPageChanged(isLayoutRtl() ? position == mPages.size() - 1 - : position == 0); + : position == 0, pageNumber); } } @@ -575,8 +594,12 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { mPageIndicatorPosition = position + positionOffset; mPageIndicator.setLocation(mPageIndicatorPosition); if (mPageListener != null) { + int pageNumber = isLayoutRtl() ? mPages.size() - 1 - position : position; mPageListener.onPageChanged(positionOffsetPixels == 0 && - (isLayoutRtl() ? position == mPages.size() - 1 : position == 0)); + (isLayoutRtl() ? position == mPages.size() - 1 : position == 0), + // Send only valid page number on integer pages + positionOffsetPixels == 0 ? pageNumber : PageListener.INVALID_PAGE + ); } } @@ -626,6 +649,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { }; public interface PageListener { - void onPageChanged(boolean isFirst); + int INVALID_PAGE = -1; + void onPageChanged(boolean isFirst, int pageNumber); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index 44d5e21e6354b..6c7d6e01e9ec7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -20,6 +20,8 @@ import static com.android.systemui.qs.dagger.QSFragmentModule.QS_FOOTER; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.util.Log; +import android.util.Pair; +import android.util.SparseArray; import android.view.View; import android.view.View.OnAttachStateChangeListener; import android.view.View.OnLayoutChangeListener; @@ -57,11 +59,14 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha private static final String ALLOW_FANCY_ANIMATION = "sysui_qs_fancy_anim"; private static final String MOVE_FULL_ROWS = "sysui_qs_move_whole_rows"; - public static final float EXPANDED_TILE_DELAY = .86f; + private static final float EXPANDED_TILE_DELAY = .86f; + //Non first page delays + private static final float QS_TILE_LABEL_FADE_OUT_START = 0.15f; + private static final float QS_TILE_LABEL_FADE_OUT_END = 0.7f; + private static final float QQS_FADE_IN_INTERVAL = 0.1f; + public static final float SHORT_PARALLAX_AMOUNT = 0.1f; - private static final long QQS_FADE_IN_DURATION = 200L; - // Fade out faster than fade in to finish before QQS hides. - private static final long QQS_FADE_OUT_DURATION = 50L; + /** * List of all views that will be reset when clearing animation state @@ -86,33 +91,54 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha private PagedTileLayout mPagedLayout; private boolean mOnFirstPage = true; - private QSExpansionPathInterpolator mQSExpansionPathInterpolator; + private int mCurrentPage = 0; + private final QSExpansionPathInterpolator mQSExpansionPathInterpolator; + // Animator for elements in the first page, including secondary labels and qqs brightness + // slider, as well as animating the alpha of the QS tile layout (as we are tracking QQS tiles) private TouchAnimator mFirstPageAnimator; - private TouchAnimator mFirstPageDelayedAnimator; + // TranslationX animator for QQS/QS tiles private TouchAnimator mTranslationXAnimator; + // TranslationY animator for QS tiles (and their components) in the first page private TouchAnimator mTranslationYAnimator; - private TouchAnimator mNonfirstPageAnimator; - private TouchAnimator mNonfirstPageDelayedAnimator; + // TranslationY animator for QQS tiles (and their components) + private TouchAnimator mQQSTranslationYAnimator; + // Animates alpha of permanent views (QS tile layout, QQS tiles) when not in first page + private TouchAnimator mNonfirstPageAlphaAnimator; + // TranslatesY the QS Tile layout using QS.getHeightDiff() + private TouchAnimator mQSTileLayoutTranslatorAnimator; // This animates fading of SecurityFooter and media divider private TouchAnimator mAllPagesDelayedAnimator; + // Animator for brightness slider(s) private TouchAnimator mBrightnessAnimator; + // Animator for Footer actions in QQS private TouchAnimator mQQSFooterActionsAnimator; + // Height animator for QQS tiles (height changing from QQS size to QS size) private HeightExpansionAnimator mQQSTileHeightAnimator; - private HeightExpansionAnimator mOtherTilesExpandAnimator; + // Height animator for QS tile in first page but not in QQS, to present the illusion that they + // are expanding alongside the QQS tiles + private HeightExpansionAnimator mOtherFirstPageTilesHeightAnimator; + // Pair of animators for each non first page. The creation is delayed until the user first + // scrolls to that page, in order to get the proper measures and layout. + private final SparseArray> + mNonFirstPageQSAnimators = new SparseArray<>(); private boolean mNeedsAnimatorUpdate = false; - private boolean mToShowing; private boolean mOnKeyguard; private boolean mAllowFancy; private boolean mFullRows; private int mNumQuickTiles; + private int mLastQQSTileHeight; private float mLastPosition; private final QSTileHost mHost; private final Executor mExecutor; private final TunerService mTunerService; private boolean mShowCollapsedOnKeyguard; private boolean mTranslateWhileExpanding; + private int mQQSTop; + + private int[] mTmpLoc1 = new int[2]; + private int[] mTmpLoc2 = new int[2]; @Inject public QSAnimator(QS qs, QuickQSPanel quickPanel, QuickStatusBarHeader quickStatusBarHeader, @@ -213,8 +239,19 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha updateAnimators(); } + private void addNonFirstPageAnimators(int page) { + Pair pair = createSecondaryPageAnimators(page); + mNonFirstPageQSAnimators.put(page, pair); + } + @Override - public void onPageChanged(boolean isFirst) { + public void onPageChanged(boolean isFirst, int currentPage) { + if (currentPage != INVALID_PAGE && mCurrentPage != currentPage) { + mCurrentPage = currentPage; + if (!isFirst && !mNonFirstPageQSAnimators.contains(currentPage)) { + addNonFirstPageAnimators(currentPage); + } + } if (mOnFirstPage == isFirst) return; if (!isFirst) { clearAnimationState(); @@ -230,7 +267,8 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha int yOffset, int[] temp, TouchAnimator.Builder animatorBuilderX, - TouchAnimator.Builder animatorBuilderY + TouchAnimator.Builder animatorBuilderY, + TouchAnimator.Builder qqsAnimatorBuilderY ) { getRelativePosition(temp, qqsView, commonParent); int qqsPosX = temp[0]; @@ -243,7 +281,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha animatorBuilderX.addFloat(qqsView, "translationX", 0, xDiff); animatorBuilderX.addFloat(qsView, "translationX", -xDiff, 0); int yDiff = qsPosY - qqsPosY - yOffset; - animatorBuilderY.addFloat(qqsView, "translationY", 0, yDiff); + qqsAnimatorBuilderY.addFloat(qqsView, "translationY", 0, yDiff); animatorBuilderY.addFloat(qsView, "translationY", -yDiff, 0); mAllViews.add(qqsView); mAllViews.add(qsView); @@ -253,40 +291,47 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha mNeedsAnimatorUpdate = false; TouchAnimator.Builder firstPageBuilder = new Builder(); TouchAnimator.Builder translationYBuilder = new Builder(); + TouchAnimator.Builder qqsTranslationYBuilder = new Builder(); TouchAnimator.Builder translationXBuilder = new Builder(); + TouchAnimator.Builder nonFirstPageAlphaBuilder = new Builder(); Collection tiles = mHost.getTiles(); int count = 0; - int[] loc1 = new int[2]; - int[] loc2 = new int[2]; clearAnimationState(); + mNonFirstPageQSAnimators.clear(); mAllViews.clear(); mAnimatedQsViews.clear(); mQQSTileHeightAnimator = null; - mOtherTilesExpandAnimator = null; + mOtherFirstPageTilesHeightAnimator = null; mNumQuickTiles = mQuickQsPanel.getNumQuickTiles(); QSTileLayout tileLayout = mQsPanelController.getTileLayout(); mAllViews.add((View) tileLayout); - int height = mQs.getView() != null ? mQs.getView().getMeasuredHeight() : 0; - int heightDiff = height - mQs.getHeader().getBottom() - + mQs.getHeader().getPaddingBottom(); + int heightDiff = mQs.getHeightDiff(); if (!mTranslateWhileExpanding) { heightDiff *= SHORT_PARALLAX_AMOUNT; } - firstPageBuilder.addFloat(tileLayout, "translationY", heightDiff, 0); + mQSTileLayoutTranslatorAnimator = new Builder() + .addFloat(tileLayout, "translationY", heightDiff, 0) + .build(); - int qqsTileHeight = 0; + mLastQQSTileHeight = 0; if (mQsPanelController.areThereTiles()) { for (QSTile tile : tiles) { QSTileView tileView = mQsPanelController.getTileView(tile); + if (tileView == null) { Log.e(TAG, "tileView is null " + tile.getTileSpec()); continue; } + // Only animate tiles in the first page + if (mPagedLayout != null && count >= mPagedLayout.getNumTilesFirstPage()) { + break; + } + final View tileIcon = tileView.getIcon().getIconView(); View view = mQs.getView(); @@ -297,16 +342,16 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha QSTileView quickTileView = mQuickQSPanelController.getTileView(tile); if (quickTileView == null) continue; - getRelativePosition(loc1, quickTileView, view); - getRelativePosition(loc2, tileView, view); - int yOffset = loc2[1] - loc1[1]; - int xOffset = loc2[0] - loc1[0]; + getRelativePosition(mTmpLoc1, quickTileView, view); + getRelativePosition(mTmpLoc2, tileView, view); + int yOffset = mTmpLoc2[1] - mTmpLoc1[1]; + int xOffset = mTmpLoc2[0] - mTmpLoc1[0]; // Offset the translation animation on the views // (that goes from 0 to getOffsetTranslation) int offsetWithQSBHTranslation = yOffset - mQuickStatusBarHeader.getOffsetTranslation(); - translationYBuilder.addFloat(quickTileView, "translationY", 0, + qqsTranslationYBuilder.addFloat(quickTileView, "translationY", 0, offsetWithQSBHTranslation); translationYBuilder.addFloat(tileView, "translationY", -offsetWithQSBHTranslation, 0); @@ -317,7 +362,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha if (mQQSTileHeightAnimator == null) { mQQSTileHeightAnimator = new HeightExpansionAnimator(this, quickTileView.getMeasuredHeight(), tileView.getMeasuredHeight()); - qqsTileHeight = quickTileView.getMeasuredHeight(); + mLastQQSTileHeight = quickTileView.getMeasuredHeight(); } mQQSTileHeightAnimator.addView(quickTileView); @@ -329,9 +374,10 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha view, xOffset, yOffset, - loc1, + mTmpLoc1, translationXBuilder, - translationYBuilder + translationYBuilder, + qqsTranslationYBuilder ); // Label containers @@ -341,9 +387,10 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha view, xOffset, yOffset, - loc1, + mTmpLoc1, translationXBuilder, - translationYBuilder + translationYBuilder, + qqsTranslationYBuilder ); // Secondary icon @@ -353,12 +400,15 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha view, xOffset, yOffset, - loc1, + mTmpLoc1, translationXBuilder, - translationYBuilder + translationYBuilder, + qqsTranslationYBuilder ); firstPageBuilder.addFloat(quickTileView.getSecondaryLabel(), "alpha", 0, 1); + nonFirstPageAlphaBuilder + .addFloat(quickTileView.getSecondaryLabel(), "alpha", 0, 0); mAnimatedQsViews.add(tileView); mAllViews.add(quickTileView); @@ -373,16 +423,17 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha // expanding from. SideLabelTileLayout qqsLayout = (SideLabelTileLayout) mQuickQsPanel.getTileLayout(); - getRelativePosition(loc1, qqsLayout, view); - getRelativePosition(loc2, tileView, view); - int diff = loc2[1] - (loc1[1] + qqsLayout.getPhantomTopPosition(count)); + getRelativePosition(mTmpLoc1, qqsLayout, view); + mQQSTop = mTmpLoc1[1]; + getRelativePosition(mTmpLoc2, tileView, view); + int diff = mTmpLoc2[1] - (mTmpLoc1[1] + qqsLayout.getPhantomTopPosition(count)); translationYBuilder.addFloat(tileView, "translationY", -diff, 0); - if (mOtherTilesExpandAnimator == null) { - mOtherTilesExpandAnimator = + if (mOtherFirstPageTilesHeightAnimator == null) { + mOtherFirstPageTilesHeightAnimator = new HeightExpansionAnimator( - this, qqsTileHeight, tileView.getMeasuredHeight()); + this, mLastQQSTileHeight, tileView.getMeasuredHeight()); } - mOtherTilesExpandAnimator.addView(tileView); + mOtherFirstPageTilesHeightAnimator.addView(tileView); tileView.setClipChildren(true); tileView.setClipToPadding(true); firstPageBuilder.addFloat(tileView.getSecondaryLabel(), "alpha", 0, 1); @@ -392,18 +443,19 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha mAllViews.add(tileView); count++; } + if (mCurrentPage != 0) { + addNonFirstPageAnimators(mCurrentPage); + } } if (mAllowFancy) { animateBrightnessSlider(firstPageBuilder); mFirstPageAnimator = firstPageBuilder + // Fade in the tiles/labels as we reach the final position. + .addFloat(tileLayout, "alpha", 0, 1) .setListener(this) .build(); - // Fade in the tiles/labels as we reach the final position. - Builder builder = new Builder() - .addFloat(tileLayout, "alpha", 0, 1); - mFirstPageDelayedAnimator = builder.build(); if (mQQSFooterActions.getVisibility() != View.GONE) { // only when qqs footer is present (which means split shade mode) it needs to @@ -411,9 +463,8 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha updateQQSFooterAnimation(); } - // Fade in the security footer and the divider as we reach the final position - builder = new Builder().setStartDelay(EXPANDED_TILE_DELAY); + Builder builder = new Builder().setStartDelay(EXPANDED_TILE_DELAY); builder.addFloat(mSecurityFooter.getView(), "alpha", 0, 1); if (mQsPanelController.shouldUseHorizontalLayout() && mQsPanelController.mMediaHost.hostView != null) { @@ -425,26 +476,100 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha mAllPagesDelayedAnimator = builder.build(); mAllViews.add(mSecurityFooter.getView()); translationYBuilder.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator()); + qqsTranslationYBuilder.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator()); translationXBuilder.setInterpolator(mQSExpansionPathInterpolator.getXInterpolator()); + if (mOnFirstPage) { + // Only recreate this animator if we're in the first page. That way we know that + // the first page is attached and has the proper positions/measures. + mQQSTranslationYAnimator = qqsTranslationYBuilder.build(); + } mTranslationYAnimator = translationYBuilder.build(); mTranslationXAnimator = translationXBuilder.build(); if (mQQSTileHeightAnimator != null) { mQQSTileHeightAnimator.setInterpolator( mQSExpansionPathInterpolator.getYInterpolator()); } - if (mOtherTilesExpandAnimator != null) { - mOtherTilesExpandAnimator.setInterpolator( + if (mOtherFirstPageTilesHeightAnimator != null) { + mOtherFirstPageTilesHeightAnimator.setInterpolator( mQSExpansionPathInterpolator.getYInterpolator()); } } - mNonfirstPageAnimator = new TouchAnimator.Builder() + mNonfirstPageAlphaAnimator = nonFirstPageAlphaBuilder .addFloat(mQuickQsPanel, "alpha", 1, 0) + .addFloat(tileLayout, "alpha", 0, 1) .setListener(mNonFirstPageListener) - .setEndDelay(.5f) + .setEndDelay(1 - QQS_FADE_IN_INTERVAL) .build(); - mNonfirstPageDelayedAnimator = new TouchAnimator.Builder() - .setStartDelay(.14f) - .addFloat(tileLayout, "alpha", 0, 1).build(); + } + + private Pair createSecondaryPageAnimators(int page) { + if (mPagedLayout == null) return null; + HeightExpansionAnimator animator = null; + TouchAnimator.Builder builder = new Builder() + .setInterpolator(mQSExpansionPathInterpolator.getYInterpolator()); + TouchAnimator.Builder alphaDelayedBuilder = new Builder() + .setStartDelay(QS_TILE_LABEL_FADE_OUT_START) + .setEndDelay(QS_TILE_LABEL_FADE_OUT_END); + SideLabelTileLayout qqsLayout = (SideLabelTileLayout) mQuickQsPanel.getTileLayout(); + View view = mQs.getView(); + List specs = mPagedLayout.getSpecsForPage(page); + + int row = -1; + int lastTileTop = -1; + + for (int i = 0; i < specs.size(); i++) { + QSTileView tileView = mQsPanelController.getTileView(specs.get(i)); + getRelativePosition(mTmpLoc2, tileView, view); + int diff = mTmpLoc2[1] - (mQQSTop + qqsLayout.getPhantomTopPosition(i)); + builder.addFloat(tileView, "translationY", -diff, 0); + // The different elements in the tile should be centered, so maintain them centered + int centerDiff = (tileView.getMeasuredHeight() - mLastQQSTileHeight) / 2; + builder.addFloat(tileView.getIcon(), "translationY", -centerDiff, 0); + builder.addFloat(tileView.getSecondaryIcon(), "translationY", -centerDiff, 0); + // The labels have different apparent size in QQS vs QS (no secondary label), so the + // translation needs to account for that. + int labelDiff = centerDiff - tileView.getSecondaryLabel().getMeasuredHeight() / 2; + builder.addFloat(tileView.getLabelContainer(), "translationY", -labelDiff, 0); + builder.addFloat(tileView.getSecondaryLabel(), "alpha", 0, 0.3f, 1); + + alphaDelayedBuilder.addFloat(tileView.getLabelContainer(), "alpha", 0, 1); + alphaDelayedBuilder.addFloat(tileView.getIcon(), "alpha", 0, 1); + alphaDelayedBuilder.addFloat(tileView.getSecondaryIcon(), "alpha", 0, 1); + + final int tileTop = tileView.getTop(); + if (tileTop != lastTileTop) { + row++; + lastTileTop = tileTop; + } + if (i >= mQuickQsPanel.getTileLayout().getNumVisibleTiles() && row >= 2) { + // Fade completely the tiles in rows below the ones that will merge into QQS. + // args is an array of 0s where the length is the current row index (at least third + // row) + final float[] args = new float[row]; + args[args.length - 1] = 1f; + builder.addFloat(tileView, "alpha", args); + } else { + // For all the other rows, fade them a bit + builder.addFloat(tileView, "alpha", 0.6f, 1); + } + + if (animator == null) { + animator = new HeightExpansionAnimator( + this, mLastQQSTileHeight, tileView.getMeasuredHeight()); + animator.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator()); + } + animator.addView(tileView); + + tileView.setClipChildren(true); + tileView.setClipToPadding(true); + mAllViews.add(tileView); + mAllViews.add(tileView.getSecondaryLabel()); + mAllViews.add(tileView.getIcon()); + mAllViews.add(tileView.getSecondaryIcon()); + mAllViews.add(tileView.getLabelContainer()); + } + builder.addFloat(alphaDelayedBuilder.build(), "position", 0, 1); + return new Pair<>(animator, builder.build()); } private void animateBrightnessSlider(Builder firstPageBuilder) { @@ -542,31 +667,37 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha } } mLastPosition = position; - if (mOnFirstPage && mAllowFancy) { + if (!mAllowFancy) return; + if (mOnFirstPage) { mQuickQsPanel.setAlpha(1); mFirstPageAnimator.setPosition(position); - mFirstPageDelayedAnimator.setPosition(position); mTranslationYAnimator.setPosition(position); mTranslationXAnimator.setPosition(position); - if (mQQSTileHeightAnimator != null) { - mQQSTileHeightAnimator.setPosition(position); - } - if (mOtherTilesExpandAnimator != null) { - mOtherTilesExpandAnimator.setPosition(position); + if (mOtherFirstPageTilesHeightAnimator != null) { + mOtherFirstPageTilesHeightAnimator.setPosition(position); } } else { - mNonfirstPageAnimator.setPosition(position); - mNonfirstPageDelayedAnimator.setPosition(position); + mNonfirstPageAlphaAnimator.setPosition(position); } - if (mAllowFancy) { - mAllPagesDelayedAnimator.setPosition(position); - if (mBrightnessAnimator != null) { - mBrightnessAnimator.setPosition(position); - } - if (mQQSFooterActionsAnimator != null) { - mQQSFooterActionsAnimator.setPosition(position); + for (int i = 0; i < mNonFirstPageQSAnimators.size(); i++) { + Pair pair = mNonFirstPageQSAnimators.valueAt(i); + if (pair != null) { + pair.first.setPosition(position); + pair.second.setPosition(position); } } + if (mQQSTileHeightAnimator != null) { + mQQSTileHeightAnimator.setPosition(position); + } + mQSTileLayoutTranslatorAnimator.setPosition(position); + mQQSTranslationYAnimator.setPosition(position); + mAllPagesDelayedAnimator.setPosition(position); + if (mBrightnessAnimator != null) { + mBrightnessAnimator.setPosition(position); + } + if (mQQSFooterActionsAnimator != null) { + mQQSFooterActionsAnimator.setPosition(position); + } } @Override @@ -611,8 +742,11 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha if (mQQSTileHeightAnimator != null) { mQQSTileHeightAnimator.resetViewsHeights(); } - if (mOtherTilesExpandAnimator != null) { - mOtherTilesExpandAnimator.resetViewsHeights(); + if (mOtherFirstPageTilesHeightAnimator != null) { + mOtherFirstPageTilesHeightAnimator.resetViewsHeights(); + } + for (int i = 0; i < mNonFirstPageQSAnimators.size(); i++) { + mNonFirstPageQSAnimators.valueAt(i).first.resetViewsHeights(); } final int N2 = mAnimatedQsViews.size(); for (int i = 0; i < N2; i++) { @@ -623,7 +757,9 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha @Override public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { - mExecutor.execute(mUpdateAnimators); + boolean actualChange = + left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom; + if (actualChange) mExecutor.execute(mUpdateAnimators); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index e82e9d284bdd7..bb8a1e8e22a4b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -503,6 +503,12 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca isTransitioningToFullShade ? progress : mSquishinessFraction); } + @Override + public int getHeightDiff() { + return mQSPanelScrollView.getBottom() - mHeader.getBottom() + + mHeader.getPaddingBottom(); + } + @Override public void setQsExpansion(float expansion, float panelExpansionFraction, float proposedTranslation, float squishinessFraction) { @@ -537,8 +543,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca boolean fullyExpanded = expansion == 1; boolean fullyCollapsed = expansion == 0.0f; - int heightDiff = mQSPanelScrollView.getBottom() - mHeader.getBottom() - + mHeader.getPaddingBottom(); + int heightDiff = getHeightDiff(); float panelTranslationY = translationScaleY * heightDiff; // Let the views animate their contents correctly by giving them the necessary context. diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 20c0fdd7de890..6ab1c1358bf14 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -23,6 +23,7 @@ import android.annotation.Nullable; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; +import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -106,6 +107,7 @@ public class QSPanel extends LinearLayout implements Tunable { protected QSTileLayout mTileLayout; private float mSquishinessFraction = 1f; private final ArrayMap mChildrenLayoutTop = new ArrayMap<>(); + private final Rect mClippingRect = new Rect(); public QSPanel(Context context, AttributeSet attrs) { super(context, attrs); @@ -133,8 +135,7 @@ public class QSPanel extends LinearLayout implements Tunable { mHorizontalContentContainer = new RemeasuringLinearLayout(mContext); mHorizontalContentContainer.setOrientation(LinearLayout.VERTICAL); - mHorizontalContentContainer.setClipChildren(true); - mHorizontalContentContainer.setClipToPadding(false); + setHorizontalContentContainerClipping(); LayoutParams lp = new LayoutParams(0, LayoutParams.WRAP_CONTENT, 1); int marginSize = (int) mContext.getResources().getDimension(R.dimen.qs_media_padding); @@ -148,6 +149,23 @@ public class QSPanel extends LinearLayout implements Tunable { } } + protected void setHorizontalContentContainerClipping() { + mHorizontalContentContainer.setClipChildren(true); + mHorizontalContentContainer.setClipToPadding(false); + // Don't clip on the top, that way, secondary pages tiles can animate up + mHorizontalContentContainer.addOnLayoutChangeListener( + (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { + if (left != oldLeft || right != oldRight || bottom != oldBottom) { + mClippingRect.left = left; + mClippingRect.right = right; + mClippingRect.bottom = bottom; + mHorizontalContentContainer.setClipBounds(mClippingRect); + } + }); + mClippingRect.top = -1000; + mHorizontalContentContainer.setClipBounds(mClippingRect); + } + /** * Add brightness view above the tile layout. * diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java index d470fa242f713..ff9790c3595f1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java @@ -45,6 +45,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; +import java.util.Objects; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -258,6 +259,15 @@ public abstract class QSPanelControllerBase extends ViewContr return null; } + QSTileView getTileView(String spec) { + for (QSPanelControllerBase.TileRecord r : mRecords) { + if (Objects.equals(r.tile.getTileSpec(), spec)) { + return r.tileView; + } + } + return null; + } + private String getTilesSpecs() { return mRecords.stream() .map(tileRecord -> tileRecord.tile.getTileSpec()) diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index 613e7f87371c5..f5ae019c20aba 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -46,11 +46,9 @@ public class QuickQSPanel extends QSPanel { } @Override - void initialize() { - super.initialize(); - if (mHorizontalContentContainer != null) { - mHorizontalContentContainer.setClipChildren(false); - } + protected void setHorizontalContentContainerClipping() { + mHorizontalContentContainer.setClipToPadding(false); + mHorizontalContentContainer.setClipChildren(false); } @Override