11abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/*
21abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Copyright (C) 2015 The Android Open Source Project
31abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *
41abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Licensed under the Apache License, Version 2.0 (the "License");
51abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * you may not use this file except in compliance with the License.
61abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * You may obtain a copy of the License at
71abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *
81abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *      http://www.apache.org/licenses/LICENSE-2.0
91abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *
101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Unless required by applicable law or agreed to in writing, software
111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * distributed under the License is distributed on an "AS IS" BASIS,
121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * See the License for the specific language governing permissions and
141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * limitations under the License.
151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */
161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkopackage com.android.tv.tuner.layout;
181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
191abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.content.Context;
201abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.content.res.TypedArray;
2165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.graphics.Point;
221abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.graphics.Rect;
2365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.hardware.display.DisplayManager;
241abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.util.AttributeSet;
251abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.util.Log;
2665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.view.Display;
271abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.view.View;
281abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.view.ViewGroup;
291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
3065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.R;
311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
321abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.Arrays;
331abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.Comparator;
341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/**
361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * A layout that scales its children using the given percentage value.
371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */
381abddd9f6225298066094e20a6c29061b6af4590Nick Chalkopublic class ScaledLayout extends ViewGroup {
391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final String TAG = "ScaledLayout";
401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final boolean DEBUG = false;
411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final Comparator<Rect> mRectTopLeftSorter = new Comparator<Rect>() {
421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        @Override
431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        public int compare(Rect lhs, Rect rhs) {
441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (lhs.top != rhs.top) {
451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return lhs.top - rhs.top;
461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            } else {
471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return lhs.left - rhs.left;
481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    };
511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private Rect[] mRectArray;
5365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private final int mMaxWidth;
5465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private final int mMaxHeight;
551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public ScaledLayout(Context context) {
571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        this(context, null);
581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public ScaledLayout(Context context, AttributeSet attrs) {
611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        this(context, attrs, 0);
621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public ScaledLayout(Context context, AttributeSet attrs, int defStyle) {
651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        super(context, attrs, defStyle);
6665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        Point size = new Point();
6765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        DisplayManager displayManager = (DisplayManager) getContext()
6865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                .getSystemService(Context.DISPLAY_SERVICE);
6965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
7065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        display.getRealSize(size);
7165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        mMaxWidth = size.x;
7265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        mMaxHeight = size.y;
731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    /**
761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * ScaledLayoutParams stores the four scale factors.
771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * <br>
781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * Vertical coordinate system:   ({@code scaleStartRow} * 100) % ~ ({@code scaleEndRow} * 100) %
791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * Horizontal coordinate system: ({@code scaleStartCol} * 100) % ~ ({@code scaleEndCol} * 100) %
801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * <br>
811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * In XML, for example,
821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * <pre>
831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * {@code
841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * <View
851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     *     app:layout_scaleStartRow="0.1"
861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     *     app:layout_scaleEndRow="0.5"
871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     *     app:layout_scaleStartCol="0.4"
881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     *     app:layout_scaleEndCol="1" />
891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * }
901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * </pre>
911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     */
921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public static class ScaledLayoutParams extends ViewGroup.LayoutParams {
931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        public static final float SCALE_UNSPECIFIED = -1;
9465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        public final float scaleStartRow;
9565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        public final float scaleEndRow;
9665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        public final float scaleStartCol;
9765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        public final float scaleEndCol;
981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        public ScaledLayoutParams(float scaleStartRow, float scaleEndRow,
1001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                float scaleStartCol, float scaleEndCol) {
1011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            super(MATCH_PARENT, MATCH_PARENT);
1021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            this.scaleStartRow = scaleStartRow;
1031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            this.scaleEndRow = scaleEndRow;
1041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            this.scaleStartCol = scaleStartCol;
1051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            this.scaleEndCol = scaleEndCol;
1061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
1071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        public ScaledLayoutParams(Context context, AttributeSet attrs) {
1091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            super(MATCH_PARENT, MATCH_PARENT);
1101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            TypedArray array =
1111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                context.obtainStyledAttributes(attrs, R.styleable.utScaledLayout);
1121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            scaleStartRow =
1131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                array.getFloat(R.styleable.utScaledLayout_layout_scaleStartRow, SCALE_UNSPECIFIED);
1141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            scaleEndRow =
1151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                array.getFloat(R.styleable.utScaledLayout_layout_scaleEndRow, SCALE_UNSPECIFIED);
1161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            scaleStartCol =
1171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                array.getFloat(R.styleable.utScaledLayout_layout_scaleStartCol, SCALE_UNSPECIFIED);
1181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            scaleEndCol =
1191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                array.getFloat(R.styleable.utScaledLayout_layout_scaleEndCol, SCALE_UNSPECIFIED);
1201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            array.recycle();
1211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
1221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
1251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public LayoutParams generateLayoutParams(AttributeSet attrs) {
1261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return new ScaledLayoutParams(getContext(), attrs);
1271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
1301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    protected boolean checkLayoutParams(LayoutParams p) {
1311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return (p instanceof ScaledLayoutParams);
1321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
1351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
1371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
1381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int width = widthSpecSize - getPaddingLeft() - getPaddingRight();
1391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int height = heightSpecSize - getPaddingTop() - getPaddingBottom();
1401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (DEBUG) {
1411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.d(TAG, String.format("onMeasure width: %d, height: %d", width, height));
1421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
1431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int count = getChildCount();
1441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mRectArray = new Rect[count];
1451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (int i = 0; i < count; ++i) {
1461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            View child = getChildAt(i);
1471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            ViewGroup.LayoutParams params = child.getLayoutParams();
1481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            float scaleStartRow, scaleEndRow, scaleStartCol, scaleEndCol;
1491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (!(params instanceof ScaledLayoutParams)) {
1501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                throw new RuntimeException(
1511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        "A child of ScaledLayout cannot have the UNSPECIFIED scale factors");
1521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
1531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            scaleStartRow = ((ScaledLayoutParams) params).scaleStartRow;
1541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            scaleEndRow = ((ScaledLayoutParams) params).scaleEndRow;
1551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            scaleStartCol = ((ScaledLayoutParams) params).scaleStartCol;
1561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            scaleEndCol = ((ScaledLayoutParams) params).scaleEndCol;
1571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (scaleStartRow < 0 || scaleStartRow > 1) {
1581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                throw new RuntimeException("A child of ScaledLayout should have a range of "
1591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        + "scaleStartRow between 0 and 1");
1601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
1611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (scaleEndRow < scaleStartRow || scaleStartRow > 1) {
1621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                throw new RuntimeException("A child of ScaledLayout should have a range of "
1631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        + "scaleEndRow between scaleStartRow and 1");
1641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
1651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (scaleEndCol < 0 || scaleEndCol > 1) {
1661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                throw new RuntimeException("A child of ScaledLayout should have a range of "
1671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        + "scaleStartCol between 0 and 1");
1681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
1691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (scaleEndCol < scaleStartCol || scaleEndCol > 1) {
1701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                throw new RuntimeException("A child of ScaledLayout should have a range of "
1711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        + "scaleEndCol between scaleStartCol and 1");
1721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
1731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (DEBUG) {
1741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.d(TAG, String.format("onMeasure child scaleStartRow: %f scaleEndRow: %f "
1751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        + "scaleStartCol: %f scaleEndCol: %f",
1761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        scaleStartRow, scaleEndRow, scaleStartCol, scaleEndCol));
1771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
1781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mRectArray[i] = new Rect((int) (scaleStartCol * width), (int) (scaleStartRow * height),
1791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    (int) (scaleEndCol * width), (int) (scaleEndRow * height));
18065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            int scaleWidth = (int) (width * (scaleEndCol - scaleStartCol));
1811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int childWidthSpec = MeasureSpec.makeMeasureSpec(
18265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    scaleWidth > mMaxWidth ? mMaxWidth : scaleWidth, MeasureSpec.EXACTLY);
1831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
1841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            child.measure(childWidthSpec, childHeightSpec);
1851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // If the height of the measured child view is bigger than the height of the calculated
1871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // region by the given ScaleLayoutParams, the height of the region should be increased
1881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // to fit the size of the child view.
1891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (child.getMeasuredHeight() > mRectArray[i].height()) {
1901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                int overflowedHeight = child.getMeasuredHeight() - mRectArray[i].height();
1911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                overflowedHeight = (overflowedHeight + 1) / 2;
1921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                mRectArray[i].bottom += overflowedHeight;
1931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                mRectArray[i].top -= overflowedHeight;
1941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (mRectArray[i].top < 0) {
1951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    mRectArray[i].bottom -= mRectArray[i].top;
1961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    mRectArray[i].top = 0;
1971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
1981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (mRectArray[i].bottom > height) {
1991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    mRectArray[i].top -= mRectArray[i].bottom - height;
2001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    mRectArray[i].bottom = height;
2011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
2021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
20365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            int scaleHeight = (int) (height * (scaleEndRow - scaleStartRow));
2041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            childHeightSpec = MeasureSpec.makeMeasureSpec(
20565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    scaleHeight > mMaxHeight ? mMaxHeight : scaleHeight, MeasureSpec.EXACTLY);
2061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            child.measure(childWidthSpec, childHeightSpec);
2071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // Avoid overlapping rectangles.
2101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // Step 1. Sort rectangles by position (top-left).
2111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int visibleRectCount = 0;
2121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int[] visibleRectGroup = new int[count];
2131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Rect[] visibleRectArray = new Rect[count];
2141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (int i = 0; i < count; ++i) {
2151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (getChildAt(i).getVisibility() == View.VISIBLE) {
2161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                visibleRectGroup[visibleRectCount] = visibleRectCount;
2171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                visibleRectArray[visibleRectCount] = mRectArray[i];
2181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                ++visibleRectCount;
2191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
2201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Arrays.sort(visibleRectArray, 0, visibleRectCount, mRectTopLeftSorter);
2221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // Step 2. Move down if there are overlapping rectangles.
2241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (int i = 0; i < visibleRectCount - 1; ++i) {
2251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            for (int j = i + 1; j < visibleRectCount; ++j) {
2261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (Rect.intersects(visibleRectArray[i], visibleRectArray[j])) {
2271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    visibleRectGroup[j] = visibleRectGroup[i];
2281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    visibleRectArray[j].set(visibleRectArray[j].left,
2291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                            visibleRectArray[i].bottom,
2301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                            visibleRectArray[j].right,
2311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                            visibleRectArray[i].bottom + visibleRectArray[j].height());
2321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
2331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
2341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // Step 3. Move up if there is any overflowed rectangle.
2371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (int i = visibleRectCount - 1; i >= 0; --i) {
2381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (visibleRectArray[i].bottom > height) {
2391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                int overflowedHeight = visibleRectArray[i].bottom - height;
2401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                for (int j = 0; j <= i; ++j) {
2411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    if (visibleRectGroup[i] == visibleRectGroup[j]) {
2421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        visibleRectArray[j].set(visibleRectArray[j].left,
2431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                visibleRectArray[j].top - overflowedHeight,
2441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                visibleRectArray[j].right,
2451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                visibleRectArray[j].bottom - overflowedHeight);
2461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    }
2471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
2481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
2491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        setMeasuredDimension(widthSpecSize, heightSpecSize);
2511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
2521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
2541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    protected void onLayout(boolean changed, int l, int t, int r, int b) {
2551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int paddingLeft = getPaddingLeft();
2561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int paddingTop = getPaddingTop();
2571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int count = getChildCount();
2581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (int i = 0; i < count; ++i) {
2591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            View child = getChildAt(i);
2601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (child.getVisibility() != GONE) {
2611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                int childLeft = paddingLeft + mRectArray[i].left;
2621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                int childTop = paddingTop + mRectArray[i].top;
2631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                int childBottom = paddingLeft + mRectArray[i].bottom;
2641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                int childRight = paddingTop + mRectArray[i].right;
2651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (DEBUG) {
2661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    Log.d(TAG, String.format("layoutChild bottom: %d left: %d right: %d top: %d",
2671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                            childBottom, childLeft,
2681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                            childRight, childTop));
2691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
2701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                child.layout(childLeft, childTop, childRight, childBottom);
2711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
2721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
2731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
2741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko}
275