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
This commit is contained in:
Fabian Kozynski
2018-09-07 13:44:54 -04:00
parent b2fa27cc65
commit 802279f701
5 changed files with 123 additions and 120 deletions

View File

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

View File

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

View File

@@ -665,5 +665,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
void setListening(boolean listening);
default void setExpansion(float expansion) {}
int getNumVisibleTiles();
}
}

View File

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

View File

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