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

Commit 59a422e9 authored by Jeff Brown's avatar Jeff Brown
Browse files

Ensure that touch and hover targets are cleared when needed.

When views are removed from a view or a view is detached from
a window, we need to update the touch and hover targets appropriately.

Failing to do this resulted in a NPE while dispatching an
ACTION_HOVER_EXIT to a view that had previously been removed.
Removed views should not get input events.

Change-Id: I4af4f8e2c4028347d3f570894fd1b3b366d11455
parent 00710e90
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -7338,7 +7338,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
                sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
                // If the window does not have input focus we take away accessibility
                // focus as soon as the user stop hovering over the view.
                if (!mAttachInfo.mHasWindowFocus) {
                if (mAttachInfo != null && !mAttachInfo.mHasWindowFocus) {
                    getViewRootImpl().setAccessibilityFocusedHost(null);
                }
            }
+78 −0
Original line number Diff line number Diff line
@@ -1569,6 +1569,43 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        return handled;
    }

    private void exitHoverTargets() {
        if (mHoveredSelf || mFirstHoverTarget != null) {
            final long now = SystemClock.uptimeMillis();
            MotionEvent event = MotionEvent.obtain(now, now,
                    MotionEvent.ACTION_HOVER_EXIT, 0.0f, 0.0f, 0);
            event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
            dispatchHoverEvent(event);
            event.recycle();
        }
    }

    private void cancelHoverTarget(View view) {
        HoverTarget predecessor = null;
        HoverTarget target = mFirstHoverTarget;
        while (target != null) {
            final HoverTarget next = target.next;
            if (target.child == view) {
                if (predecessor == null) {
                    mFirstHoverTarget = next;
                } else {
                    predecessor.next = next;
                }
                target.recycle();

                final long now = SystemClock.uptimeMillis();
                MotionEvent event = MotionEvent.obtain(now, now,
                        MotionEvent.ACTION_HOVER_EXIT, 0.0f, 0.0f, 0);
                event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
                view.dispatchHoverEvent(event);
                event.recycle();
                return;
            }
            predecessor = target;
            target = next;
        }
    }

    /** @hide */
    @Override
    protected boolean hasHoveredChild() {
@@ -1997,6 +2034,32 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        }
    }

    private void cancelTouchTarget(View view) {
        TouchTarget predecessor = null;
        TouchTarget target = mFirstTouchTarget;
        while (target != null) {
            final TouchTarget next = target.next;
            if (target.child == view) {
                if (predecessor == null) {
                    mFirstTouchTarget = next;
                } else {
                    predecessor.next = next;
                }
                target.recycle();

                final long now = SystemClock.uptimeMillis();
                MotionEvent event = MotionEvent.obtain(now, now,
                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
                event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
                view.dispatchTouchEvent(event);
                event.recycle();
                return;
            }
            predecessor = target;
            target = next;
        }
    }

    /**
     * Returns true if a child view can receive pointer events.
     * @hide
@@ -2416,6 +2479,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        // first send it an ACTION_CANCEL motion event.
        cancelAndClearTouchTargets(null);

        // Similarly, set ACTION_EXIT to all hover targets and clear them.
        exitHoverTargets();

        // In case view is detached while transition is running
        mLayoutSuppressed = false;

@@ -3453,6 +3519,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
            clearChildFocus = true;
        }

        cancelTouchTarget(view);
        cancelHoverTarget(view);

        if (view.getAnimation() != null ||
                (mTransitioningViews != null && mTransitioningViews.contains(view))) {
            addDisappearingView(view);
@@ -3533,6 +3602,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
                clearChildFocus = view;
            }

            cancelTouchTarget(view);
            cancelHoverTarget(view);

            if (view.getAnimation() != null ||
                (mTransitioningViews != null && mTransitioningViews.contains(view))) {
                addDisappearingView(view);
@@ -3603,6 +3675,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
                clearChildFocus = view;
            }

            cancelTouchTarget(view);
            cancelHoverTarget(view);

            if (view.getAnimation() != null ||
                    (mTransitioningViews != null && mTransitioningViews.contains(view))) {
                addDisappearingView(view);
@@ -3648,6 +3723,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
            child.clearFocus();
        }

        cancelTouchTarget(child);
        cancelHoverTarget(child);

        if ((animate && child.getAnimation() != null) ||
                (mTransitioningViews != null && mTransitioningViews.contains(child))) {
            addDisappearingView(child);