Merge "Refactor PagedTileLayout to not overflow screen"

This commit is contained in:
Fabian Kozynski
2018-09-20 14:33:55 +00:00
committed by Android (Google) Code Review
8 changed files with 125 additions and 125 deletions

View File

@@ -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"

View File

@@ -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">

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
* Copyright (c) 2017, 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.
*/
-->
<resources>
<!-- The number of rows in the QuickSettings -->
<integer name="quick_settings_num_rows">2</integer>
</resources>

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
* Copyright (c) 2017, 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.
*/
-->
<resources>
<!-- The number of rows in the QuickSettings -->
<integer name="quick_settings_num_rows">3</integer>
</resources>

View File

@@ -107,7 +107,7 @@
<integer name="quick_settings_num_columns">3</integer>
<!-- The number of rows in the QuickSettings -->
<integer name="quick_settings_num_rows">1</integer>
<integer name="quick_settings_max_rows">3</integer>
<!-- The number of columns that the top level tiles span in the QuickSettings -->
<integer name="quick_settings_user_time_settings_tile_span">1</integer>

View File

@@ -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<TileRecord> mTiles = new ArrayList<>();
private final ArrayList<TilePage> 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;
}
}
@@ -401,7 +402,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
@Override
public int getCount() {
return mNumPages;
return mPages.size();
}
@Override

View File

@@ -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));

View File

@@ -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<TileRecord> 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;