From 802279f701daff7fc361735dd3425a764ef37dc2 Mon Sep 17 00:00:00 2001 From: Fabian Kozynski Date: Fri, 7 Sep 2018 13:44:54 -0400 Subject: [PATCH] Refactored layout of QQS and transition animation. QuickQSPanel.HeaderTileLayout is reworked to work in displays of different densities by inheriting from TileLayout. The maximum number of tiles shown is still determined by the same parameter, but it can show less tiles if there is not enough space due to low density/small screen. This is calculated during onMeasure using the available space. QSAnimator is modified to accomodate the following three cases: * If the tile is present in QQS and QS, animate as before. * If the tile is present in QQS and not in QS, fade translate to right. * If the tile is not present in QQS and is in QS, fade in from the right. This is a new upload of ag/4898401 (reverted in ag/4968215), addressing b/113994014. Fixes: 80141553 Fixes: 110404680 Test: Visual, different densities and orientations. Tested battery saver tile. Change-Id: Iffb7a45df7d20e67beb9c75b6f71b3bbc7c49b7d --- .../android/systemui/qs/PagedTileLayout.java | 6 + .../com/android/systemui/qs/QSAnimator.java | 33 ++-- .../src/com/android/systemui/qs/QSPanel.java | 2 + .../com/android/systemui/qs/QuickQSPanel.java | 177 ++++++++---------- .../com/android/systemui/qs/TileLayout.java | 25 ++- 5 files changed, 123 insertions(+), 120 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index f1b7eeca5c07b..ca1b48901c9de 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java @@ -261,6 +261,12 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { return mPages.get(0).mColumns; } + public int getNumVisibleTiles() { + if (mPages.size() == 0) return 0; + TilePage currentPage = mPages.get(getCurrentItem()); + return currentPage.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 diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index 2a4bb607673e7..f6073671dd60e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -169,6 +169,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha QSTileLayout tileLayout = mQsPanel.getTileLayout(); mAllViews.add((View) tileLayout); int height = mQs.getView() != null ? mQs.getView().getMeasuredHeight() : 0; + int width = mQs.getView() != null ? mQs.getView().getMeasuredWidth() : 0; int heightDiff = height - mQs.getHeader().getBottom() + mQs.getHeader().getPaddingBottom(); firstPageBuilder.addFloat(tileLayout, "translationY", heightDiff, 0); @@ -181,7 +182,9 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha } final View tileIcon = tileView.getIcon().getIconView(); View view = mQs.getView(); - if (count < mNumQuickTiles && mAllowFancy) { + + // This case: less tiles to animate in small displays. + if (count < mQuickQsPanel.getTileLayout().getNumVisibleTiles() && mAllowFancy) { // Quick tiles. QSTileView quickTileView = mQuickQsPanel.getTileView(tile); if (quickTileView == null) continue; @@ -192,18 +195,26 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha final int xDiff = loc2[0] - loc1[0]; final int yDiff = loc2[1] - loc1[1]; lastXDiff = loc1[0] - lastX; - // Move the quick tile right from its location to the new one. - translationXBuilder.addFloat(quickTileView, "translationX", 0, xDiff); - translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff); - // Counteract the parent translation on the tile. So we have a static base to - // animate the label position off from. - //firstPageBuilder.addFloat(tileView, "translationY", mQsPanel.getHeight(), 0); + if (count < tileLayout.getNumVisibleTiles()) { + // Move the quick tile right from its location to the new one. + translationXBuilder.addFloat(quickTileView, "translationX", 0, xDiff); + translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff); - // Move the real tile from the quick tile position to its final - // location. - translationXBuilder.addFloat(tileView, "translationX", -xDiff, 0); - translationYBuilder.addFloat(tileView, "translationY", -yDiff, 0); + // Counteract the parent translation on the tile. So we have a static base to + // animate the label position off from. + //firstPageBuilder.addFloat(tileView, "translationY", mQsPanel.getHeight(), 0); + + // Move the real tile from the quick tile position to its final + // location. + translationXBuilder.addFloat(tileView, "translationX", -xDiff, 0); + translationYBuilder.addFloat(tileView, "translationY", -yDiff, 0); + + } else { // These tiles disappear when expanding + firstPageBuilder.addFloat(quickTileView, "alpha", 1, 0); + translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff); + translationXBuilder.addFloat(quickTileView, "translationX", 0, xDiff + width); + } mQuickQsViews.add(tileView.getIconWithBackground()); mAllViews.add(tileView.getIcon()); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 8697015742378..762fd75f48dc3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -665,5 +665,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne void setListening(boolean listening); default void setExpansion(float expansion) {} + + int getNumVisibleTiles(); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index 1c50f797e1a79..556786a16cafa 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -18,18 +18,17 @@ package com.android.systemui.qs; import android.content.Context; import android.content.res.Configuration; +import android.graphics.Rect; import android.util.AttributeSet; import android.view.Gravity; import android.view.View; import android.widget.LinearLayout; -import android.widget.Space; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.qs.QSTile.SignalState; import com.android.systemui.plugins.qs.QSTile.State; -import com.android.systemui.plugins.qs.QSTileView; import com.android.systemui.qs.customize.QSCustomizer; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; @@ -43,6 +42,7 @@ import java.util.Collection; public class QuickQSPanel extends QSPanel { public static final String NUM_QUICK_TILES = "sysui_qqs_count"; + private static final String TAG = "QuickQSPanel"; private boolean mDisabledByPolicy; private static int mDefaultMaxTiles; @@ -178,121 +178,95 @@ public class QuickQSPanel extends QSPanel { super.setVisibility(visibility); } - private static class HeaderTileLayout extends LinearLayout implements QSTileLayout { + private static class HeaderTileLayout extends TileLayout { - protected final ArrayList mRecords = new ArrayList<>(); private boolean mListening; - /** Size of the QS tile (width & height). */ - private int mTileDimensionSize; public HeaderTileLayout(Context context) { super(context); setClipChildren(false); setClipToPadding(false); - - mTileDimensionSize = mContext.getResources().getDimensionPixelSize( - R.dimen.qs_quick_tile_size); - updateLayoutParams(); } @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - updateLayoutParams(); + updateResources(); + } + + @Override + public void onFinishInflate(){ + updateResources(); } private void updateLayoutParams() { - setGravity(Gravity.CENTER); int width = getResources().getDimensionPixelSize(R.dimen.qs_quick_layout_width); - LayoutParams lp = new LayoutParams(width, LayoutParams.MATCH_PARENT); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(width, LayoutParams.MATCH_PARENT); lp.gravity = Gravity.CENTER_HORIZONTAL; setLayoutParams(lp); } - /** - * Returns {@link LayoutParams} based on the given {@code spaceWidth}. If the width is 0, - * then we're going to have the space expand to take up as much space as possible. If the - * width is non-zero, we want the inter-tile spacers to be fixed. - */ - private LayoutParams generateSpaceLayoutParams() { - LayoutParams lp = new LayoutParams(0, mTileDimensionSize); - lp.weight = 1; - lp.gravity = Gravity.CENTER; - return lp; - } - - @Override - public void setListening(boolean listening) { - if (mListening == listening) return; - mListening = listening; - for (TileRecord record : mRecords) { - record.tile.setListening(this, mListening); - } - } - - @Override - public void addTile(TileRecord tile) { - if (getChildCount() != 0) { - addView(new Space(mContext), getChildCount(), generateSpaceLayoutParams()); - } - - addView(tile.tileView, getChildCount(), generateTileLayoutParams()); - mRecords.add(tile); - tile.tile.setListening(this, mListening); - } - private LayoutParams generateTileLayoutParams() { - LayoutParams lp = new LayoutParams(mTileDimensionSize, mTileDimensionSize); - lp.gravity = Gravity.CENTER; + LayoutParams lp = new LayoutParams(mCellWidth, mCellHeight); return lp; } @Override - public void removeTile(TileRecord tile) { - int childIndex = getChildIndex(tile.tileView); - // Remove the tile. - removeViewAt(childIndex); - if (getChildCount() != 0) { - // Remove its spacer as well. - removeViewAt(childIndex); - } - mRecords.remove(tile); - tile.tile.setListening(this, false); - } - - private int getChildIndex(QSTileView tileView) { - final int childViewCount = getChildCount(); - for (int i = 0; i < childViewCount; i++) { - if (getChildAt(i) == tileView) { - return i; - } - } - return -1; + protected void addTileView(TileRecord tile) { + addView(tile.tileView, getChildCount(), generateTileLayoutParams()); } @Override - public int getOffsetTop(TileRecord tile) { - return 0; + protected void onLayout(boolean changed, int l, int t, int r, int b) { + // We only care about clipping on the right side + Rect bounds = new Rect(0, 0, r - l, 10000); + setClipBounds(bounds); + + calculateColumns(); + + for (int i = 0; i < mRecords.size(); i++) { + mRecords.get(i).tileView.setVisibility( i < mColumns ? View.VISIBLE : View.GONE); + } + + setAccessibilityOrder(); + layoutTileRecords(mColumns); } @Override public boolean updateResources() { - // No resources here. + mCellWidth = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size); + mCellHeight = mCellWidth; + + updateLayoutParams(); + return false; } - @Override - public boolean hasOverlappingRendering() { - return false; - } + private boolean calculateColumns() { + int prevNumColumns = mColumns; + int maxTiles = mRecords.size(); - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - if (hideOverflowingChildren(widthMeasureSpec)) { - return; // Rely on visibility change to trigger remeasure. + if (maxTiles == 0){ // Early return during setup + mColumns = 0; + return true; } + final int availableWidth = getMeasuredWidth() - getPaddingStart() - getPaddingEnd(); + final int leftoverWithespace = availableWidth - maxTiles * mCellWidth; + final int smallestHorizontalMarginNeeded = leftoverWithespace / (maxTiles - 1); + + if (smallestHorizontalMarginNeeded > 0){ + mCellMarginHorizontal = smallestHorizontalMarginNeeded; + mColumns = maxTiles; + } else{ + mColumns = mCellWidth == 0 ? 1 : + Math.min(maxTiles, availableWidth / mCellWidth ); + mCellMarginHorizontal = (availableWidth - mColumns * mCellWidth) / (mColumns - 1); + } + return mColumns != prevNumColumns; + } + + private void setAccessibilityOrder() { if (mRecords != null && mRecords.size() > 0) { View previousView = this; for (TileRecord record : mRecords) { @@ -306,31 +280,28 @@ public class QuickQSPanel extends QSPanel { } } - /** - * Hide child views that would otherwise be clipped. - * @return {@code true} if any child visibilities have changed. - */ - private boolean hideOverflowingChildren(int widthMeasureSpec) { - if (getChildCount() == 0) { - return false; + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + // Measure each QS tile. + for (TileRecord record : mRecords) { + if (record.tileView.getVisibility() == GONE) continue; + record.tileView.measure(exactly(mCellWidth), exactly(mCellHeight)); } - boolean childVisibilityChanged = false; - int widthRemaining = MeasureSpec.getSize(widthMeasureSpec) - - getChildAt(0).getMeasuredWidth() - getPaddingStart() - getPaddingEnd(); - for (int i = 2; i < getChildCount(); i += 2) { - View tileChild = getChildAt(i); - LayoutParams lp = (LayoutParams) tileChild.getLayoutParams(); - // All Space views have 0 width; only tiles contribute to the total width. - widthRemaining = widthRemaining - - tileChild.getMeasuredWidth() - lp.getMarginEnd() - lp.getMarginStart(); - int newVisibility = widthRemaining < 0 ? View.GONE : View.VISIBLE; - if (tileChild.getVisibility() != newVisibility) { - tileChild.setVisibility(newVisibility); - getChildAt(i - 1).setVisibility(newVisibility); // Hide spacer as well. - childVisibilityChanged = true; - } - } - return childVisibilityChanged; + + int height = mCellHeight; + if (height < 0) height = 0; + + setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), height); + } + + @Override + public int getNumVisibleTiles() { + return mColumns; + } + + @Override + protected int getColumnStart(int column) { + return getPaddingStart() + column * (mCellWidth + mCellMarginHorizontal); } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java index 45d63e0c1359f..c67165ea95a14 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java @@ -56,6 +56,10 @@ public class TileLayout extends ViewGroup implements QSTileLayout { public void addTile(TileRecord tile) { mRecords.add(tile); tile.tile.setListening(this, mListening); + addTileView(tile); + } + + protected void addTileView(TileRecord tile) { addView(tile.tileView); } @@ -120,19 +124,18 @@ public class TileLayout extends ViewGroup implements QSTileLayout { return false; } - private static int exactly(int size) { + protected static int exactly(int size) { return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY); } - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - final int w = getWidth(); + + protected void layoutTileRecords(int numRecords) { final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; int row = 0; int column = 0; // Layout each QS tile. - for (int i = 0; i < mRecords.size(); i++, column++) { + for (int i = 0; i < numRecords; i++, column++) { // If we reached the last column available to layout a tile, wrap back to the next row. if (column == mColumns) { column = 0; @@ -147,12 +150,22 @@ public class TileLayout extends ViewGroup implements QSTileLayout { } } + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + layoutTileRecords(mRecords.size()); + } + private int getRowTop(int row) { return row * (mCellHeight + mCellMarginVertical) + mCellMarginTop; } - private int getColumnStart(int column) { + protected int getColumnStart(int column) { return getPaddingStart() + mSidePadding + mCellMarginHorizontal / 2 + column * (mCellWidth + mCellMarginHorizontal); } + + @Override + public int getNumVisibleTiles() { + return mRecords.size(); + } }