Fix QS expansion animations

* Fix gap between tiles when expanding
* Fix second page collapsing animation: tiles now will shrink and at the
end be replaced by QQS tiles. We need to create the animators as we move
through the pages, as PagedTileLayout only has the current page and the
(up to) two adjacent ones. This means that if the page has not been
added, we don't have correct positions and measures.

See bug for video
Fixes: 207895040
Test: manual

Change-Id: Id2b14b11ace4199ed07f7675b6575cb4b71e93d5
This commit is contained in:
Fabian Kozynski
2021-12-14 09:21:36 -05:00
parent f8660097ff
commit a74a7c079e
8 changed files with 284 additions and 86 deletions

View File

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

View File

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

View File

@@ -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<String> getSpecsForPage(int page) {
ArrayList<String> 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<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
@@ -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);
}
}

View File

@@ -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<Pair<HeightExpansionAnimator, TouchAnimator>>
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<HeightExpansionAnimator, TouchAnimator> 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<QSTile> 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<HeightExpansionAnimator, TouchAnimator> 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<String> 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<HeightExpansionAnimator, TouchAnimator> 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

View File

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

View File

@@ -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<View, Integer> 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.
*

View File

@@ -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<T extends QSPanel> 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())

View File

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