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:
@@ -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) {
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user