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

Commit cea45132 authored by Gilles Debunne's avatar Gilles Debunne
Browse files

Handle animations in Views' rectangle clipping methods.

getChildVisibleRect and getLocationInWindow do not take the new
mTransformationInfo View attribute.

As a result, these methods return invalid value during an animation
Bug 5638710

Changes in Patch Set 2:
- temporary allocations removed using static thread local variables (method
calls are NOT reentrant).

- scroll should be handled *before* applying the transformation matrix.
Fixed the call order in View#getLocationInWindow()

Patch set 4: fix from comments.
Patch set 5: <p>s

Change-Id: I15dc44c0659305d9029c59a47aba3a738bb35ae1
parent 88f10c6c
Loading
Loading
Loading
Loading
+31 −18
Original line number Diff line number Diff line
@@ -8059,9 +8059,9 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
    /**
     * If some part of this view is not clipped by any of its parents, then
     * return that area in r in global (root) coordinates. To convert r to local
     * coordinates, offset it by -globalOffset (e.g. r.offset(-globalOffset.x,
     * -globalOffset.y)) If the view is completely clipped or translated out,
     * return false.
     * coordinates (without taking possible View rotations into account), offset
     * it by -globalOffset (e.g. r.offset(-globalOffset.x, -globalOffset.y)).
     * If the view is completely clipped or translated out, return false.
     *
     * @param r If true is returned, r holds the global coordinates of the
     *        visible portion of this view.
@@ -12152,35 +12152,48 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
     * @param location an array of two integers in which to hold the coordinates
     */
    public void getLocationInWindow(int[] location) {
        // When the view is not attached to a window, this method does not make sense
        if (mAttachInfo == null) return;
        if (location == null || location.length < 2) {
            throw new IllegalArgumentException("location must be an array of "
                    + "two integers");
            throw new IllegalArgumentException("location must be an array of two integers");
        }
        location[0] = mLeft;
        location[1] = mTop;
        if (mTransformationInfo != null) {
            location[0] += (int) (mTransformationInfo.mTranslationX + 0.5f);
            location[1] += (int) (mTransformationInfo.mTranslationY + 0.5f);
        float[] position = mAttachInfo.mTmpTransformLocation;
        position[0] = position[1] = 0.0f;
        if (!hasIdentityMatrix()) {
            getMatrix().mapPoints(position);
        }
        position[0] += mLeft;
        position[1] += mTop;
        ViewParent viewParent = mParent;
        while (viewParent instanceof View) {
            final View view = (View) viewParent;
            location[0] += view.mLeft - view.mScrollX;
            location[1] += view.mTop - view.mScrollY;
            if (view.mTransformationInfo != null) {
                location[0] += (int) (view.mTransformationInfo.mTranslationX + 0.5f);
                location[1] += (int) (view.mTransformationInfo.mTranslationY + 0.5f);
            position[0] -= view.mScrollX;
            position[1] -= view.mScrollY;
            if (!view.hasIdentityMatrix()) {
                view.getMatrix().mapPoints(position);
            }
            position[0] += view.mLeft;
            position[1] += view.mTop;
            viewParent = view.mParent;
        }
        if (viewParent instanceof ViewRootImpl) {
            // *cough*
            final ViewRootImpl vr = (ViewRootImpl) viewParent;
            location[1] -= vr.mCurScrollY;
            position[1] -= vr.mCurScrollY;
        }
        location[0] = (int) (position[0] + 0.5f);
        location[1] = (int) (position[1] + 0.5f);
    }
    /**
+31 −4
Original line number Diff line number Diff line
@@ -4182,15 +4182,42 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
     * {@inheritDoc}
     */
    public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
        // The View is not attached to a window, 'visible' does not make sense, return false
        if (mAttachInfo == null) return false;

        final RectF rect = mAttachInfo.mTmpTransformRect;
        rect.set(r);

        if (!child.hasIdentityMatrix()) {
           child.getMatrix().mapRect(rect);
        }

        int dx = child.mLeft - mScrollX;
        int dy = child.mTop - mScrollY;

        rect.offset(dx, dy);

        if (offset != null) {
            if (!child.hasIdentityMatrix()) {
                float[] position = mAttachInfo.mTmpTransformLocation;
                position[0] = offset.x;
                position[1] = offset.y;
                child.getMatrix().mapPoints(position);
                offset.x = (int) (position[0] + 0.5f);
                offset.y = (int) (position[1] + 0.5f);
            }
            offset.x += dx;
            offset.y += dy;
        }
        r.offset(dx, dy);
        return r.intersect(0, 0, mRight - mLeft, mBottom - mTop) &&
               (mParent == null || mParent.getChildVisibleRect(this, r, offset));

        if (rect.intersect(0, 0, mRight - mLeft, mBottom - mTop)) {
            if (mParent == null) return true;
            r.set((int) (rect.left + 0.5f), (int) (rect.top + 0.5f),
                    (int) (rect.right + 0.5f), (int) (rect.bottom + 0.5f));
            return mParent.getChildVisibleRect(this, r, offset);
        }

        return false;
    }

    /**
+36 −16
Original line number Diff line number Diff line
@@ -63,16 +63,16 @@ public interface ViewParent {
    /**
     * All or part of a child is dirty and needs to be redrawn.
     *
     * The location array is an array of two int values which respectively
     * define the left and the top position of the dirty child.
     * <p>The location array is an array of two int values which respectively
     * define the left and the top position of the dirty child.</p>
     *
     * This method must return the parent of this ViewParent if the specified
     * <p>This method must return the parent of this ViewParent if the specified
     * rectangle must be invalidated in the parent. If the specified rectangle
     * does not require invalidation in the parent or if the parent does not
     * exist, this method must return null.
     * exist, this method must return null.</p>
     *
     * When this method returns a non-null value, the location array must
     * have been updated with the left and top coordinates of this ViewParent.
     * <p>When this method returns a non-null value, the location array must
     * have been updated with the left and top coordinates of this ViewParent.</p>
     *
     * @param location An array of 2 ints containing the left and top
     *        coordinates of the child to invalidate
@@ -115,6 +115,26 @@ public interface ViewParent {
     */
    public void clearChildFocus(View child);

    /**
     * Compute the visible part of a rectangular region defined in terms of a child view's
     * coordinates.
     *
     * <p>Returns the clipped visible part of the rectangle <code>r</code>, defined in the
     * <code>child</code>'s local coordinate system. <code>r</code> is modified by this method to
     * contain the result, expressed in the global (root) coordinate system.</p>
     *
     * <p>The resulting rectangle is always axis aligned. If a rotation is applied to a node in the
     * View hierarchy, the result is the axis-aligned bounding box of the visible rectangle.</p>
     *
     * @param child A child View, whose rectangular visible region we want to compute
     * @param r The input rectangle, defined in the child coordinate system. Will be overwritten to
     * contain the resulting visible rectangle, expressed in global (root) coordinates
     * @param offset The input coordinates of a point, defined in the child coordinate system.
     * As with the <code>r</code> parameter, this will be overwritten to contain the global (root)
     * coordinates of that point.
     * A <code>null</code> value is valid (in case you are not interested in this result)
     * @return true if the resulting rectangle is not empty, false otherwise
     */
    public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset);

    /**
@@ -143,11 +163,11 @@ public interface ViewParent {

    /**
     * Bring up a context menu for the specified view or its ancestors.
     * <p>
     * In most cases, a subclass does not need to override this.  However, if
     *
     * <p>In most cases, a subclass does not need to override this.  However, if
     * the subclass is added directly to the window manager (for example,
     * {@link ViewManager#addView(View, android.view.ViewGroup.LayoutParams)})
     * then it should override this and show the context menu.
     * then it should override this and show the context menu.</p>
     * 
     * @param originalView The source view where the context menu was first invoked
     * @return true if a context menu was displayed
@@ -164,11 +184,11 @@ public interface ViewParent {

    /**
     * Start an action mode for the specified view.
     * <p>
     * In most cases, a subclass does not need to override this. However, if the
     *
     * <p>In most cases, a subclass does not need to override this. However, if the
     * subclass is added directly to the window manager (for example,
     * {@link ViewManager#addView(View, android.view.ViewGroup.LayoutParams)})
     * then it should override this and start the action mode.
     * then it should override this and start the action mode.</p>
     *
     * @param originalView The source view where the action mode was first invoked
     * @param callback The callback that will handle lifecycle events for the action mode
@@ -188,10 +208,10 @@ public interface ViewParent {
     * Called when a child does not want this parent and its ancestors to
     * intercept touch events with
     * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
     * <p>
     * This parent should pass this call onto its parents. This parent must obey
     *
     * <p>This parent should pass this call onto its parents. This parent must obey
     * this request for the duration of the touch (that is, only clear the flag
     * after this parent has received an up or a cancel.
     * after this parent has received an up or a cancel.</p>
     * 
     * @param disallowIntercept True if the child does not want the parent to
     *            intercept touch events.
@@ -234,7 +254,7 @@ public interface ViewParent {
     *       the sending. The parent can optionally add a record for itself before
     *       dispatching the request to its parent. A parent can also choose not to
     *       respect the request for sending the event. The accessibility event is sent
     *       by the topmost view in the view tree.
     *       by the topmost view in the view tree.</p>
     *
     * @param child The child which requests sending the event.
     * @param event The event to be sent.