Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 40c9f0d7 authored by Fabian Kozynski's avatar Fabian Kozynski
Browse files

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.

Bug: 80141553
Bug: 110404680
Change-Id: I7be192b9ad6e66f97b3041bd8d5ac57ad71e6fdd
Test: Visual, different densities and orientations.
parent 2d50001f
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -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
+25 −14
Original line number Diff line number Diff line
@@ -155,6 +155,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
        TouchAnimator.Builder translationYBuilder = new Builder();

        if (mQsPanel.getHost() == null) return;
        if (mQuickQsPanel.getTileLayout().getNumVisibleTiles() < 1) return;
        Collection<QSTile> tiles = mQsPanel.getHost().getTiles();
        int count = 0;
        int[] loc1 = new int[2];
@@ -169,6 +170,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 +183,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,6 +196,8 @@ 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;

                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);
@@ -205,6 +211,12 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
                    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());
                mAllViews.add(quickTileView);
@@ -218,10 +230,9 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
                final int xDiff = loc2[0] - loc1[0];
                final int yDiff = loc2[1] - loc1[1];

                firstPageBuilder.addFloat(tileView, "translationY", heightDiff, 0);
                translationXBuilder.addFloat(tileView, "translationX", -xDiff, 0);
                firstPageBuilder.addFloat(tileView, "translationY", -heightDiff, 0);
                translationYBuilder.addFloat(tileView, "translationY", -yDiff, 0);
                translationYBuilder.addFloat(tileIcon, "translationY", -yDiff, 0);
                translationXBuilder.addFloat(tileView, "translationX", -xDiff, 0);

                mAllViews.add(tileIcon);
            } else {
+2 −0
Original line number Diff line number Diff line
@@ -663,5 +663,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
        void setListening(boolean listening);

        default void setExpansion(float expansion) {}

        int getNumVisibleTiles();
    }
}
+71 −100
Original line number Diff line number Diff line
@@ -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;
        private LayoutParams generateTileLayoutParams() {
            LayoutParams lp = new LayoutParams(mCellWidth, mCellHeight);
            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());
            }

        protected void addTileView(TileRecord tile) {
            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;
            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);
        }
        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);

        private int getChildIndex(QSTileView tileView) {
            final int childViewCount = getChildCount();
            for (int i = 0; i < childViewCount; i++) {
                if (getChildAt(i) == tileView) {
                    return i;
                }
            }
            return -1;
            calculateColumns();

            for (int i = 0; i < mRecords.size(); i++) {
                mRecords.get(i).tileView.setVisibility( i < mColumns ? View.VISIBLE : View.GONE);
            }

        @Override
        public int getOffsetTop(TileRecord tile) {
            return 0;
            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();

            if (maxTiles == 0){ // Early return during setup
                mColumns = 0;
                return true;
            }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            if (hideOverflowingChildren(widthMeasureSpec)) {
                return; // Rely on visibility change to trigger remeasure.
            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);
        }
    }
}
+19 −6
Original line number Diff line number Diff line
@@ -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();
    }
}