diff --git a/packages/SystemUI/res/layout/qs_paged_page.xml b/packages/SystemUI/res/layout/qs_paged_page.xml index 07f0c83e7b2e7..a8960d9b94371 100644 --- a/packages/SystemUI/res/layout/qs_paged_page.xml +++ b/packages/SystemUI/res/layout/qs_paged_page.xml @@ -19,7 +19,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/tile_page" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="match_parent" android:paddingStart="@dimen/notification_side_paddings" android:paddingEnd="@dimen/notification_side_paddings" android:clipChildren="false" diff --git a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml index e96a09baa9dbb..11a01871b7828 100644 --- a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml +++ b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml @@ -19,6 +19,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_weight="1" android:clipChildren="false" android:clipToPadding="false" android:paddingBottom="@dimen/qs_paged_tile_layout_padding_bottom"> diff --git a/packages/SystemUI/res/values-h320dp/config.xml b/packages/SystemUI/res/values-h320dp/config.xml deleted file mode 100644 index a9c19db0f46f2..0000000000000 --- a/packages/SystemUI/res/values-h320dp/config.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - 2 - diff --git a/packages/SystemUI/res/values-h600dp/config.xml b/packages/SystemUI/res/values-h600dp/config.xml deleted file mode 100644 index 8616e3e627793..0000000000000 --- a/packages/SystemUI/res/values-h600dp/config.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - 3 - diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 11bd392b7d521..47a6c523210fd 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -107,7 +107,7 @@ 3 - 1 + 3 1 diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index ca1b48901c9de..cf9f58cb95985 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java @@ -6,14 +6,12 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.content.Context; -import android.content.res.Configuration; -import android.content.res.Resources; + import androidx.viewpager.widget.PagerAdapter; import androidx.viewpager.widget.ViewPager; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; -import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.Interpolator; @@ -41,14 +39,12 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { return t * t * t + 1.0f; }; - private final ArrayList mTiles = new ArrayList<>(); private final ArrayList mPages = new ArrayList<>(); private PageIndicator mPageIndicator; private float mPageIndicatorPosition; - private int mNumPages; private PageListener mPageListener; private boolean mListening; @@ -56,6 +52,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { private AnimatorSet mBounceAnimatorSet; private float mLastExpansion; + private boolean mDistributeTiles = false; public PagedTileLayout(Context context, AttributeSet attrs) { super(context, attrs); @@ -122,7 +119,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { public void setPageIndicator(PageIndicator indicator) { mPageIndicator = indicator; - mPageIndicator.setNumPages(mNumPages); + mPageIndicator.setNumPages(mPages.size()); mPageIndicator.setLocation(mPageIndicatorPosition); } @@ -136,13 +133,15 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { @Override public void addTile(TileRecord tile) { mTiles.add(tile); - postDistributeTiles(); + mDistributeTiles = true; + requestLayout(); } @Override public void removeTile(TileRecord tile) { if (mTiles.remove(tile)) { - postDistributeTiles(); + mDistributeTiles = true; + requestLayout(); } } @@ -175,44 +174,50 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { mPageListener = listener; } - private void postDistributeTiles() { - removeCallbacks(mDistribute); - post(mDistribute); - } - private void distributeTiles() { + emptyAndInflateOrRemovePages(); + + final int tileCount = mPages.get(0).maxTiles(); if (DEBUG) Log.d(TAG, "Distributing tiles"); - final int NP = mPages.size(); - for (int i = 0; i < NP; i++) { - mPages.get(i).removeAllViews(); - } int index = 0; final int NT = mTiles.size(); for (int i = 0; i < NT; i++) { TileRecord tile = mTiles.get(i); - if (mPages.get(index).isFull()) { - if (++index == mPages.size()) { - if (DEBUG) Log.d(TAG, "Adding page for " - + tile.tile.getClass().getSimpleName()); - mPages.add((TilePage) LayoutInflater.from(getContext()) - .inflate(R.layout.qs_paged_page, this, false)); - } + if (mPages.get(index).mRecords.size() == tileCount) index++; + if (DEBUG) { + Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to " + + index); } - if (DEBUG) Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to " - + index); mPages.get(index).addTile(tile); } - if (mNumPages != index + 1) { - mNumPages = index + 1; - while (mPages.size() > mNumPages) { - mPages.remove(mPages.size() - 1); - } - if (DEBUG) Log.d(TAG, "Size: " + mNumPages); - mPageIndicator.setNumPages(mNumPages); - setAdapter(mAdapter); - mAdapter.notifyDataSetChanged(); - setCurrentItem(0, false); + } + + private void emptyAndInflateOrRemovePages() { + final int nTiles = mTiles.size(); + int numPages = nTiles / mPages.get(0).maxTiles(); + // Add one more not full page if needed + numPages += (nTiles % mPages.get(0).maxTiles() == 0 ? 0 : 1); + + final int NP = mPages.size(); + for (int i = 0; i < NP; i++) { + mPages.get(i).removeAllViews(); } + if (NP == numPages) { + return; + } + while (mPages.size() < numPages) { + if (DEBUG) Log.d(TAG, "Adding page"); + mPages.add((TilePage) LayoutInflater.from(getContext()) + .inflate(R.layout.qs_paged_page, this, false)); + } + while (mPages.size() > numPages) { + if (DEBUG) Log.d(TAG, "Removing page"); + mPages.remove(mPages.size() - 1); + } + mPageIndicator.setNumPages(mPages.size()); + setAdapter(mAdapter); + mAdapter.notifyDataSetChanged(); + setCurrentItem(0, false); } @Override @@ -222,20 +227,39 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { setPadding(0, 0, 0, getContext().getResources().getDimensionPixelSize( R.dimen.qs_paged_tile_layout_padding_bottom)); - boolean changed = false; for (int i = 0; i < mPages.size(); i++) { changed |= mPages.get(i).updateResources(); } if (changed) { - distributeTiles(); + mDistributeTiles = true; + requestLayout(); } return changed; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + + final int nTiles = mTiles.size(); + if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { + + // Only change the pages if the number of rows or columns (from updateResources) has + // changed or the tiles have changed + if (mPages.get(0).updateMaxRows(heightMeasureSpec, nTiles) || mDistributeTiles) { + mDistributeTiles = false; + distributeTiles(); + } + + final int nRows = mPages.get(0).mRows; + for (int i = 0; i < mPages.size(); i++) { + TilePage t = mPages.get(i); + t.mRows = nRows; + } + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + // The ViewPager likes to eat all of the space, instead force it to wrap to the max height // of the pages. int maxHeight = 0; @@ -249,13 +273,6 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { setMeasuredDimension(getMeasuredWidth(), maxHeight + getPaddingBottom()); } - private final Runnable mDistribute = new Runnable() { - @Override - public void run() { - distributeTiles(); - } - }; - public int getColumnCount() { if (mPages.size() == 0) return 0; return mPages.get(0).mColumns; @@ -346,33 +363,17 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { }; public static class TilePage extends TileLayout { - private int mMaxRows = 3; + public TilePage(Context context, AttributeSet attrs) { super(context, attrs); - updateResources(); - } - - @Override - public boolean updateResources() { - final int rows = getRows(); - boolean changed = rows != mMaxRows; - if (changed) { - mMaxRows = rows; - requestLayout(); - } - return super.updateResources() || changed; - } - - private int getRows() { - return Math.max(1, getResources().getInteger(R.integer.quick_settings_num_rows)); - } - - public void setMaxRows(int maxRows) { - mMaxRows = maxRows; } public boolean isFull() { - return mRecords.size() >= mColumns * mMaxRows; + return mRecords.size() >= mColumns * mRows; + } + + public int maxTiles() { + return mColumns * mRows; } } @@ -398,7 +399,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { @Override public int getCount() { - return mNumPages; + return mPages.size(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java index feff5d4a3cc10..1451e71e2df5d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java @@ -23,6 +23,7 @@ import android.content.Context; import android.content.res.Configuration; import android.graphics.Point; import android.util.AttributeSet; +import android.util.Log; import android.view.View; import android.widget.FrameLayout; @@ -91,15 +92,24 @@ public class QSContainerImpl extends FrameLayout { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - // Since we control our own bottom, be whatever size we want. - // Otherwise the QSPanel ends up with 0 height when the window is only the - // size of the status bar. - mQSPanel.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec( - MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED)); + // QSPanel will show as many rows as it can (up to TileLayout.MAX_ROWS) such that the + // bottom and footer are inside the screen. + Configuration config = getResources().getConfiguration(); + boolean navBelow = config.smallestScreenWidthDp >= 600 + || config.orientation != Configuration.ORIENTATION_LANDSCAPE; + MarginLayoutParams layoutParams = (MarginLayoutParams) mQSPanel.getLayoutParams(); + + // The footer is pinned to the bottom of QSPanel (same bottoms), therefore we don't need to + // subtract its height. We do not care if the collapsed notifications fit in the screen. + int maxQs = getDisplayHeight() - layoutParams.topMargin - layoutParams.bottomMargin + - getPaddingBottom(); + if (navBelow) { + maxQs -= getResources().getDimensionPixelSize(R.dimen.navigation_bar_height); + } + mQSPanel.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(maxQs, MeasureSpec.AT_MOST)); int width = mQSPanel.getMeasuredWidth(); - LayoutParams layoutParams = (LayoutParams) mQSPanel.getLayoutParams(); int height = layoutParams.topMargin + layoutParams.bottomMargin - + mQSPanel.getMeasuredHeight(); + + mQSPanel.getMeasuredHeight() + getPaddingBottom(); super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java index c67165ea95a14..01ff72e6d152b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java @@ -24,10 +24,12 @@ public class TileLayout extends ViewGroup implements QSTileLayout { protected int mCellMarginHorizontal; protected int mCellMarginVertical; protected int mSidePadding; + protected int mRows = 1; protected final ArrayList mRecords = new ArrayList<>(); private int mCellMarginTop; private boolean mListening; + protected int mMaxAllowedRows = 3; public TileLayout(Context context) { this(context, null); @@ -86,6 +88,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout { mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical); mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top); mSidePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_layout_margin_side); + mMaxAllowedRows = Math.max(1, getResources().getInteger(R.integer.quick_settings_max_rows)); if (mColumns != columns) { mColumns = columns; requestLayout(); @@ -96,10 +99,16 @@ public class TileLayout extends ViewGroup implements QSTileLayout { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + // If called with AT_MOST, it will limit the number of rows. If called with UNSPECIFIED + // it will show all its tiles. In this case, the tiles have to be entered before the + // container is measured. Any change in the tiles, should trigger a remeasure. final int numTiles = mRecords.size(); final int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingStart() - getPaddingEnd(); - final int numRows = (numTiles + mColumns - 1) / mColumns; + final int heightMode = MeasureSpec.getMode(heightMeasureSpec); + if (heightMode == MeasureSpec.UNSPECIFIED) { + mRows = (numTiles + mColumns - 1) / mColumns; + } mCellWidth = (width - mSidePadding * 2 - (mCellMarginHorizontal * mColumns)) / mColumns; // Measure each QS tile. @@ -112,13 +121,35 @@ public class TileLayout extends ViewGroup implements QSTileLayout { // Only include the top margin in our measurement if we have more than 1 row to show. // Otherwise, don't add the extra margin buffer at top. - int height = (mCellHeight + mCellMarginVertical) * numRows + - (numRows != 0 ? (mCellMarginTop - mCellMarginVertical) : 0); + int height = (mCellHeight + mCellMarginVertical) * mRows + + (mRows != 0 ? (mCellMarginTop - mCellMarginVertical) : 0); if (height < 0) height = 0; setMeasuredDimension(width, height); } + /** + * Determines the maximum number of rows that can be shown based on height. Clips at a minimum + * of 1 and a maximum of mMaxAllowedRows. + * + * @param heightMeasureSpec Available height. + * @param tilesCount Upper limit on the number of tiles to show. to prevent empty rows. + */ + public boolean updateMaxRows(int heightMeasureSpec, int tilesCount) { + final int availableHeight = MeasureSpec.getSize(heightMeasureSpec) - mCellMarginTop; + final int previousRows = mRows; + mRows = availableHeight / (mCellHeight + mCellMarginVertical); + if (mRows >= mMaxAllowedRows) { + mRows = mMaxAllowedRows; + } else if (mRows <= 1) { + mRows = 1; + } + if (mRows > (tilesCount + mColumns - 1) / mColumns) { + mRows = (tilesCount + mColumns - 1) / mColumns; + } + return previousRows != mRows; + } + @Override public boolean hasOverlappingRendering() { return false; @@ -135,7 +166,8 @@ public class TileLayout extends ViewGroup implements QSTileLayout { int column = 0; // Layout each QS tile. - for (int i = 0; i < numRecords; i++, column++) { + final int tilesToLayout = Math.min(numRecords, mRows * mColumns); + for (int i = 0; i < tilesToLayout; i++, column++) { // If we reached the last column available to layout a tile, wrap back to the next row. if (column == mColumns) { column = 0;