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

Commit 1557fd78 authored by Philip Milne's avatar Philip Milne
Browse files

Fix for bug 6110465.

Add layout bound metadata to 9-patch files and make layouts take them into account.

This CL contains a proposed API for dealing with layout bounds.

This solution exposes:

1. Class: Insets - for storing layout Insets (and later possibly padding).
2. Methods: View:(get/set)LayoutInsets() - for storing layoutBounds.
3. Methods: ViewGroup:(get/set)LayoutMode() - for controlling layoutMode.

It also iuncudes the changes to GridLayout to support layout bounds.

Change-Id: I60c836b6530b61c5abf37f93ee9c44aad73573f1
parent c58a6d2d
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -24079,6 +24079,7 @@ package android.view {
    method public android.view.View getFocusedChild();
    method public android.view.animation.LayoutAnimationController getLayoutAnimation();
    method public android.view.animation.Animation.AnimationListener getLayoutAnimationListener();
    method public int getLayoutMode();
    method public android.animation.LayoutTransition getLayoutTransition();
    method public int getPersistentDrawingCache();
    method public int indexOfChild(android.view.View);
@@ -24126,6 +24127,7 @@ package android.view {
    method public void setDescendantFocusability(int);
    method public void setLayoutAnimation(android.view.animation.LayoutAnimationController);
    method public void setLayoutAnimationListener(android.view.animation.Animation.AnimationListener);
    method public void setLayoutMode(int);
    method public void setLayoutTransition(android.animation.LayoutTransition);
    method public void setMotionEventSplittingEnabled(boolean);
    method public void setOnHierarchyChangeListener(android.view.ViewGroup.OnHierarchyChangeListener);
@@ -24138,9 +24140,11 @@ package android.view {
    method public void startViewTransition(android.view.View);
    method public void updateViewLayout(android.view.View, android.view.ViewGroup.LayoutParams);
    field protected static final int CLIP_TO_PADDING_MASK = 34; // 0x22
    field public static final int COMPONENT_BOUNDS = 0; // 0x0
    field public static final int FOCUS_AFTER_DESCENDANTS = 262144; // 0x40000
    field public static final int FOCUS_BEFORE_DESCENDANTS = 131072; // 0x20000
    field public static final int FOCUS_BLOCK_DESCENDANTS = 393216; // 0x60000
    field public static final int LAYOUT_BOUNDS = 1; // 0x1
    field public static final int PERSISTENT_ALL_CACHES = 3; // 0x3
    field public static final int PERSISTENT_ANIMATION_CACHE = 1; // 0x1
    field public static final int PERSISTENT_NO_CACHE = 0; // 0x0
+30 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Camera;
import android.graphics.Canvas;
import android.graphics.Insets;
import android.graphics.Interpolator;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
@@ -2597,6 +2598,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
    @ViewDebug.ExportedProperty(category = "padding")
    protected int mPaddingBottom;
    /**
     * The layout insets in pixels, that is the distance in pixels between the
     * visible edges of this view its bounds.
     */
    private Insets mLayoutInsets;
    /**
     * Briefly describes the view and is primarily used for accessibility support.
     */
@@ -13272,6 +13279,29 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
        return mUserPaddingRelative;
    }
    /**
     * @hide
     */
    public Insets getLayoutInsets() {
        if (mLayoutInsets == null) {
            if (mBackground == null) {
                mLayoutInsets = Insets.NONE;
            } else {
                Rect insetRect = new Rect();
                boolean hasInsets = mBackground.getLayoutInsets(insetRect);
                mLayoutInsets = hasInsets ? Insets.of(insetRect) : Insets.NONE;
            }
        }
        return mLayoutInsets;
    }
    /**
     * @hide
     */
    public void setLayoutInsets(Insets layoutInsets) {
        mLayoutInsets = layoutInsets;
    }
    /**
     * Changes the selection state of this view. A view can be selected or not.
     * Note that selection is not the same as focus. Views are typically
+70 −0
Original line number Diff line number Diff line
@@ -169,6 +169,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
     */
    protected int mGroupFlags;

    /*
     * THe layout mode: either {@link #UNDEFINED_LAYOUT_MODE}, {@link #COMPONENT_BOUNDS} or
     * {@link #LAYOUT_BOUNDS}
     */
    private int mLayoutMode = UNDEFINED_LAYOUT_MODE;

    /**
     * NOTE: If you change the flags below make sure to reflect the changes
     *       the DisplayList class
@@ -334,6 +340,24 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
     */
    public static final int PERSISTENT_ALL_CACHES = 0x3;

    // Layout Modes

    private static final int UNDEFINED_LAYOUT_MODE = -1;

    /**
     * This constant is a {@link #setLayoutMode(int) layoutMode}.
     * Component bounds are the raw values of {@link #getLeft() left}, {@link #getTop() top},
     * {@link #getRight() right} and {@link #getBottom() bottom}.
     */
    public static final int COMPONENT_BOUNDS = 0;

    /**
     * This constant is a {@link #setLayoutMode(int) layoutMode}.
     * Layout bounds are derived by offsetting the component bounds using
     * {@link View#getLayoutInsets()}.
     */
    public static final int LAYOUT_BOUNDS = 1;

    /**
     * We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL
     * are set at the same time.
@@ -4367,6 +4391,52 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
    }

    /**
     * Returns the basis of alignment during the layout of this view group:
     * either {@link #COMPONENT_BOUNDS} or {@link #LAYOUT_BOUNDS}.
     *
     * @return whether or not this view group should use the component or layout bounds during
     * layout operations
     *
     * @see #setLayoutMode(int)
     */
    public int getLayoutMode() {
        if (mLayoutMode == UNDEFINED_LAYOUT_MODE) {
            ViewParent parent = getParent();
            if (parent instanceof ViewGroup) {
                ViewGroup viewGroup = (ViewGroup) parent;
                return viewGroup.getLayoutMode();
            } else {
                int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
                boolean preJellyBean = targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN;
                return preJellyBean ? COMPONENT_BOUNDS : LAYOUT_BOUNDS;
            }

        }
        return mLayoutMode;
    }

    /**
     * Sets the basis of alignment during alignment of this view group.
     * Valid values are either {@link #COMPONENT_BOUNDS} or {@link #LAYOUT_BOUNDS}.
     * <p>
     * The default is to query the property of the parent if this view group has a parent.
     * If this ViewGroup is the root of the view hierarchy the default
     * value is {@link #LAYOUT_BOUNDS} for target SDK's greater than JellyBean,
     * {@link #LAYOUT_BOUNDS} otherwise.
     *
     * @return whether or not this view group should use the component or layout bounds during
     * layout operations
     *
     * @see #getLayoutMode()
     */
    public void setLayoutMode(int layoutMode) {
        if (mLayoutMode != layoutMode) {
            mLayoutMode = layoutMode;
            requestLayout();
        }
    }

    /**
     * Returns a new set of layout parameters based on the supplied attributes set.
     *
+33 −15
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
@@ -559,9 +560,9 @@ public class GridLayout extends ViewGroup {
        int flags = (gravity & mask) >> shift;
        switch (flags) {
            case (AXIS_SPECIFIED | AXIS_PULL_BEFORE):
                return LEADING;
                return horizontal ? LEFT : TOP;
            case (AXIS_SPECIFIED | AXIS_PULL_AFTER):
                return TRAILING;
                return horizontal ? RIGHT : BOTTOM;
            case (AXIS_SPECIFIED | AXIS_PULL_BEFORE | AXIS_PULL_AFTER):
                return FILL;
            case AXIS_SPECIFIED:
@@ -1042,12 +1043,15 @@ public class GridLayout extends ViewGroup {
            int rightMargin = getMargin(c, true, false);
            int bottomMargin = getMargin(c, false, false);

            int sumMarginsX = leftMargin + rightMargin;
            int sumMarginsY = topMargin + bottomMargin;

            // Alignment offsets: the location of the view relative to its alignment group.
            int alignmentOffsetX = boundsX.getOffset(c, hAlign, leftMargin + pWidth + rightMargin);
            int alignmentOffsetY = boundsY.getOffset(c, vAlign, topMargin + pHeight + bottomMargin);
            int alignmentOffsetX = boundsX.getOffset(this, c, hAlign, pWidth + sumMarginsX, true);
            int alignmentOffsetY = boundsY.getOffset(this, c, vAlign, pHeight + sumMarginsY, false);

            int width = hAlign.getSizeInCell(c, pWidth, cellWidth - leftMargin - rightMargin);
            int height = vAlign.getSizeInCell(c, pHeight, cellHeight - topMargin - bottomMargin);
            int width = hAlign.getSizeInCell(c, pWidth, cellWidth - sumMarginsX);
            int height = vAlign.getSizeInCell(c, pHeight, cellHeight - sumMarginsY);

            int dx = x1 + gravityOffsetX + alignmentOffsetX;

@@ -1181,7 +1185,7 @@ public class GridLayout extends ViewGroup {
                View c = getChildAt(i);
                LayoutParams lp = getLayoutParams(c);
                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
                groupBounds.getValue(i).include(c, spec, GridLayout.this, this);
                groupBounds.getValue(i).include(GridLayout.this, c, spec, this);
            }
        }

@@ -2138,16 +2142,30 @@ public class GridLayout extends ViewGroup {
            return before + after;
        }

        protected int getOffset(View c, Alignment alignment, int size) {
            return before - alignment.getAlignmentValue(c, size);
        private int getAlignmentValue(GridLayout gl, View c, int size, Alignment a, boolean horiz) {
            boolean useLayoutBounds = gl.getLayoutMode() == LAYOUT_BOUNDS;
            if (!useLayoutBounds) {
                return a.getAlignmentValue(c, size);
            } else {
                Insets insets = c.getLayoutInsets();
                int leadingInset = horiz ? insets.left : insets.top; // RTL?
                int trailingInset = horiz ? insets.right : insets.bottom; // RTL?
                int totalInset = leadingInset + trailingInset;
                return leadingInset + a.getAlignmentValue(c, size - totalInset);
            }
        }

        protected int getOffset(GridLayout gl, View c, Alignment a, int size, boolean horizontal) {
            return before - getAlignmentValue(gl, c, size, a, horizontal);
        }

        protected final void include(View c, Spec spec, GridLayout gridLayout, Axis axis) {
        protected final void include(GridLayout gl, View c, Spec spec, Axis axis) {
            this.flexibility &= spec.getFlexibility();
            int size = gridLayout.getMeasurementIncludingMargin(c, axis.horizontal);
            Alignment alignment = gridLayout.getAlignment(spec.alignment, axis.horizontal);
            boolean horizontal = axis.horizontal;
            int size = gl.getMeasurementIncludingMargin(c, horizontal);
            Alignment alignment = gl.getAlignment(spec.alignment, horizontal);
            // todo test this works correctly when the returned value is UNDEFINED
            int before = alignment.getAlignmentValue(c, size);
            int before = getAlignmentValue(gl, c, size, alignment, horizontal);
            include(before, size - before);
        }

@@ -2614,8 +2632,8 @@ public class GridLayout extends ViewGroup {
                }

                @Override
                protected int getOffset(View c, Alignment alignment, int size) {
                    return max(0, super.getOffset(c, alignment, size));
                protected int getOffset(GridLayout gl, View c, Alignment a, int size, boolean hrz) {
                    return max(0, super.getOffset(gl, c, a, size, hrz));
                }
            };
        }
+114 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.graphics;

/**
 * An Insets instance holds four integer offsets which describe changes to the four
 * edges of a Rectangle. By convention, positive values move edges towards the
 * centre of the rectangle.
 * <p>
 * Insets are immutable so may be treated as values.
 *
 * @hide
 */
public class Insets {
    public static final Insets NONE = new Insets(0, 0, 0, 0);

    public final int left;
    public final int top;
    public final int right;
    public final int bottom;

    private Insets(int left, int top, int right, int bottom) {
        this.left = left;
        this.top = top;
        this.right = right;
        this.bottom = bottom;
    }

    // Factory methods

    /**
     * Return an Insets instance with the appropriate values.
     *
     * @param left the left inset
     * @param top the top inset
     * @param right the right inset
     * @param bottom the bottom inset
     *
     * @return Insets instance with the appropriate values
     */
    public static Insets of(int left, int top, int right, int bottom) {
        if (left == 0 && top == 0 && right == 0 && bottom == 0) {
            return NONE;
        }
        return new Insets(left, top, right, bottom);
    }

    /**
     * Return an Insets instance with the appropriate values.
     *
     * @param r the rectangle from which to take the values
     *
     * @return an Insets instance with the appropriate values
     */
    public static Insets of(Rect r) {
        return of(r.left, r.top, r.right, r.bottom);
    }

    /**
     * Two Insets instances are equal iff they belong to the same class and their fields are
     * pairwise equal.
     *
     * @param o the object to compare this instance with.
     *
     * @return true iff this object is equal {@code o}
     */
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Insets insets = (Insets) o;

        if (bottom != insets.bottom) return false;
        if (left != insets.left) return false;
        if (right != insets.right) return false;
        if (top != insets.top) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = left;
        result = 31 * result + top;
        result = 31 * result + right;
        result = 31 * result + bottom;
        return result;
    }

    @Override
    public String toString() {
        return "Insets{" +
                "left=" + left +
                ", top=" + top +
                ", right=" + right +
                ", bottom=" + bottom +
                '}';
    }
}
Loading