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

Commit b6deefa2 authored by Prabir Pradhan's avatar Prabir Pradhan
Browse files

Refresh pointer icons locally from ViewRootImpl

The previous pipeline for refreshing pointer icon from a View would
call into WindowManagerService, which would look up the cursor position
and call back into the app to updatePointerIcon(x, y). This
implmenetation requires WM to know the cursor position, and adds
unnecessary binder roundtrips.

When PointerChoreographer is enabled, we will store the last input event
used to resolve the pointer icon locally in the app, and will use that
whenever we need to force the pointer icon to resolve. We can do this
because the hit test in InputDispatcher will verify that the pointer is
indeed being sent to the app before allowing the icon to be changed.

Bug: 293587049
Test: manual with test app that calls View#setPointerIcon every second,
observe that the icon changes without needing to generate new events.

Change-Id: I5344fe7023d0caaff001215c4195947f94d24ae6
parent 7282d556
Loading
Loading
Loading
Loading
+14 −6
Original line number Diff line number Diff line
@@ -29901,6 +29901,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     */
    public void setPointerIcon(PointerIcon pointerIcon) {
        mMousePointerIcon = pointerIcon;
        if (com.android.input.flags.Flags.enablePointerChoreographer()) {
            final ViewRootImpl viewRootImpl = getViewRootImpl();
            if (viewRootImpl == null) {
                return;
            }
            viewRootImpl.refreshPointerIcon();
        } else {
            if (mAttachInfo == null || mAttachInfo.mHandlingPointerEvent) {
                return;
            }
@@ -29909,6 +29916,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            } catch (RemoteException e) {
            }
        }
    }
    /**
     * Gets the mouse pointer icon for the current view.
+51 −10
Original line number Diff line number Diff line
@@ -1061,6 +1061,9 @@ public final class ViewRootImpl implements ViewParent,
        sToolkitSetFrameRateReadOnlyFlagValue = toolkitSetFrameRateReadOnly();
    }

    // The latest input event from the gesture that was used to resolve the pointer icon.
    private MotionEvent mPointerIconEvent = null;

    public ViewRootImpl(Context context, Display display) {
        this(context, display, WindowManagerGlobal.getWindowSession(), new WindowLayout());
    }
@@ -6090,6 +6093,7 @@ public final class ViewRootImpl implements ViewParent,
    private static final int MSG_DECOR_VIEW_GESTURE_INTERCEPTION = 38;
    private static final int MSG_TOUCH_BOOST_TIMEOUT = 39;
    private static final int MSG_CHECK_INVALIDATION_IDLE = 40;
    private static final int MSG_REFRESH_POINTER_ICON = 41;

    final class ViewRootHandler extends Handler {
        @Override
@@ -6155,6 +6159,8 @@ public final class ViewRootImpl implements ViewParent,
                    return "MSG_WINDOW_TOUCH_MODE_CHANGED";
                case MSG_KEEP_CLEAR_RECTS_CHANGED:
                    return "MSG_KEEP_CLEAR_RECTS_CHANGED";
                case MSG_REFRESH_POINTER_ICON:
                    return "MSG_REFRESH_POINTER_ICON";
            }
            return super.getMessageName(message);
        }
@@ -6411,6 +6417,12 @@ public final class ViewRootImpl implements ViewParent,
                                FRAME_RATE_IDLENESS_REEVALUATE_TIME);
                    }
                    break;
                case MSG_REFRESH_POINTER_ICON:
                    if (mPointerIconEvent == null) {
                        break;
                    }
                    updatePointerIcon(mPointerIconEvent);
                    break;
            }
        }
    }
@@ -7399,23 +7411,42 @@ public final class ViewRootImpl implements ViewParent,
            if (event.getPointerCount() != 1) {
                return;
            }
            final int action = event.getActionMasked();
            final boolean needsStylusPointerIcon = event.isStylusPointer()
                    && event.isHoverEvent()
                    && mIsStylusPointerIconEnabled;
            if (needsStylusPointerIcon || event.isFromSource(InputDevice.SOURCE_MOUSE)) {
                if (event.getActionMasked() == MotionEvent.ACTION_HOVER_ENTER
                        || event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) {
            if (!needsStylusPointerIcon && !event.isFromSource(InputDevice.SOURCE_MOUSE)) {
                return;
            }

            if (action == MotionEvent.ACTION_HOVER_ENTER
                    || action == MotionEvent.ACTION_HOVER_EXIT) {
                // Other apps or the window manager may change the icon type outside of
                // this app, therefore the icon type has to be reset on enter/exit event.
                mPointerIconType = null;
            }

                if (event.getActionMasked() != MotionEvent.ACTION_HOVER_EXIT) {
                    if (!updatePointerIcon(event) &&
                            event.getActionMasked() == MotionEvent.ACTION_HOVER_MOVE) {
            if (action != MotionEvent.ACTION_HOVER_EXIT) {
                // Resolve the pointer icon
                if (!updatePointerIcon(event) && action == MotionEvent.ACTION_HOVER_MOVE) {
                    mPointerIconType = null;
                }
            }

            // Keep track of the newest event used to resolve the pointer icon.
            switch (action) {
                case MotionEvent.ACTION_HOVER_EXIT:
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_POINTER_UP:
                case MotionEvent.ACTION_CANCEL:
                    if (mPointerIconEvent != null) {
                        mPointerIconEvent.recycle();
                    }
                    mPointerIconEvent = null;
                    break;
                default:
                    mPointerIconEvent = MotionEvent.obtain(event);
                    break;
            }
        }

@@ -7456,6 +7487,16 @@ public final class ViewRootImpl implements ViewParent,
        updatePointerIcon(event);
    }


    /**
     * If there is pointer that is showing a PointerIcon in this window, refresh the icon for that
     * pointer. This will resolve the PointerIcon through the view hierarchy.
     */
    public void refreshPointerIcon() {
        mHandler.removeMessages(MSG_REFRESH_POINTER_ICON);
        mHandler.sendEmptyMessage(MSG_REFRESH_POINTER_ICON);
    }

    private boolean updatePointerIcon(MotionEvent event) {
        final int pointerIndex = 0;
        final float x = event.getX(pointerIndex);