diff options
| author | Jun Mukai <mukai@google.com> | 2015-12-18 23:30:10 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2015-12-18 23:30:10 +0000 |
| commit | 56cd51a70411d60fac0b3987f5c5543e6a3ea8ef (patch) | |
| tree | 00d0bcda1d09fdbe4c6acf3a78d6c099fac7020e /core/java | |
| parent | ea5a94669c3f9757e2cb5f813a0c01e23ba1f20f (diff) | |
| parent | 347e5d498f4c216d588e98776a386d8bdf93d05c (diff) | |
Merge "Introduce pointer capture API."
Diffstat (limited to 'core/java')
| -rw-r--r-- | core/java/android/hardware/input/IInputManager.aidl | 2 | ||||
| -rw-r--r-- | core/java/android/hardware/input/InputManager.java | 18 | ||||
| -rw-r--r-- | core/java/android/view/MotionEvent.java | 33 | ||||
| -rw-r--r-- | core/java/android/view/View.java | 51 | ||||
| -rw-r--r-- | core/java/android/view/ViewRootImpl.java | 40 |
5 files changed, 143 insertions, 1 deletions
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index ff33bd959175..b8f464d76b0d 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -73,4 +73,6 @@ interface IInputManager { void setPointerIconShape(int shapeId); void setCustomPointerIcon(in PointerIcon icon); + + void setPointerIconDetached(boolean detached); } diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index fab47181cfef..16b872233f06 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -829,6 +829,24 @@ public final class InputManager { } } + /** + * Update the pointer icon status. When detached, the pointer icon disappears, and further + * mouse location will be stuck at the current point. Mouse movement events will still arrive, + * and movement should be handled through {@link MotionEvent.AXIS_RELATIVE_X} and + * {@link MotionEvent.AXIS_RELATIVE_Y}. + * + * @param detached true if the icon will be detached from the actual mouse movement. + * + * @hide + */ + public void setPointerIconDetached(boolean detached) { + try { + mIm.setPointerIconDetached(detached); + } catch (RemoteException ex) { + // Do nothing. + } + } + private void populateInputDevicesLocked() { if (mInputDevicesChangedListener == null) { final InputDevicesChangedListener listener = new InputDevicesChangedListener(); diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index 0195decfcf16..7a544b8f0bb3 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -977,6 +977,37 @@ public final class MotionEvent extends InputEvent implements Parcelable { public static final int AXIS_SCROLL = 26; /** + * Axis constant: The movement of x position of a motion event. + * <p> + * <ul> + * <li>For a mouse, reports a difference of x position between the previous position. + * This is useful when pointer is captured, in that case the mouse pointer doesn't change + * the location but this axis reports the difference which allows the app to see + * how the mouse is moved. + * </ul> + * </p> + * + * @see #getAxisValue(int, int) + * @see #getHistoricalAxisValue(int, int, int) + * @see MotionEvent.PointerCoords#getAxisValue(int, int) + * @see InputDevice#getMotionRange + */ + public static final int AXIS_RELATIVE_X = 27; + + /** + * Axis constant: The movement of y position of a motion event. + * <p> + * This is similar to {@link #AXIS_RELATIVE_X} but for y-axis. + * </p> + * + * @see #getAxisValue(int, int) + * @see #getHistoricalAxisValue(int, int, int) + * @see MotionEvent.PointerCoords#getAxisValue(int, int) + * @see InputDevice#getMotionRange + */ + public static final int AXIS_RELATIVE_Y = 28; + + /** * Axis constant: Generic 1 axis of a motion event. * The interpretation of a generic axis is device-specific. * @@ -1187,6 +1218,8 @@ public final class MotionEvent extends InputEvent implements Parcelable { names.append(AXIS_DISTANCE, "AXIS_DISTANCE"); names.append(AXIS_TILT, "AXIS_TILT"); names.append(AXIS_SCROLL, "AXIS_SCROLL"); + names.append(AXIS_RELATIVE_X, "AXIS_REALTIVE_X"); + names.append(AXIS_RELATIVE_Y, "AXIS_REALTIVE_Y"); names.append(AXIS_GENERIC_1, "AXIS_GENERIC_1"); names.append(AXIS_GENERIC_2, "AXIS_GENERIC_2"); names.append(AXIS_GENERIC_3, "AXIS_GENERIC_3"); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index afa6c783f150..2d7ea2e6e3a8 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -14716,6 +14716,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, destroyDrawingCache(); cleanupDraw(); + releasePointerCapture(); mCurrentAnimation = null; } @@ -21212,6 +21213,56 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPointerIcon = pointerIcon; } + /** + * Request capturing further mouse events. + * + * When the view captures, the mouse pointer icon will disappear and will not change its + * position. Further mouse events will come to the capturing view, and the mouse movements + * will can be detected through {@link MotionEvent#AXIS_RELATIVE_X} and + * {@link MotionEvent#AXIS_RELATIVE_Y}. Non-mouse events (touchscreens, or stylus) will not + * be affected. + * + * The capture will be released through {@link #releasePointerCapture()}, or will be lost + * automatically when the view or containing window disappear. + * + * @return true when succeeds. + * @see #releasePointerCapture() + * @see #hasPointerCapture() + */ + public void setPointerCapture() { + final ViewRootImpl viewRootImpl = getViewRootImpl(); + if (viewRootImpl != null) { + viewRootImpl.setPointerCapture(this); + } + } + + + /** + * Release the current capture of mouse events. + * + * If the view does not have the capture, it will do nothing. + * @see #setPointerCapture() + * @see #hasPointerCapture() + */ + public void releasePointerCapture() { + final ViewRootImpl viewRootImpl = getViewRootImpl(); + if (viewRootImpl != null) { + viewRootImpl.releasePointerCapture(this); + } + } + + /** + * Checks the capture status of mouse events. + * + * @return true if the view has the capture. + * @see #setPointerCapture() + * @see #hasPointerCapture() + */ + public boolean hasPointerCapture() { + final ViewRootImpl viewRootImpl = getViewRootImpl(); + return (viewRootImpl != null) && viewRootImpl.hasPointerCapture(this); + } + // // Properties // diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 0ed007dd9b8a..f2b4fb3a042f 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -39,6 +39,7 @@ import android.graphics.Region; import android.graphics.drawable.Drawable; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; +import android.hardware.input.InputManager; import android.media.AudioManager; import android.os.Binder; import android.os.Build; @@ -174,6 +175,9 @@ public final class ViewRootImpl implements ViewParent, View mAccessibilityFocusedHost; AccessibilityNodeInfo mAccessibilityFocusedVirtualView; + // The view which captures mouse input, or null when no one is capturing. + View mCapturingView; + int mViewVisibility; boolean mAppVisible = true; // For recents to freeform transition we need to keep drawing after the app receives information @@ -3012,6 +3016,31 @@ public final class ViewRootImpl implements ViewParent, } } + void setPointerCapture(View view) { + if (!mAttachInfo.mHasWindowFocus) { + Log.w(TAG, "Can't set capture if it's not focused."); + return; + } + if (mCapturingView == view) { + return; + } + mCapturingView = view; + InputManager.getInstance().setPointerIconDetached(true); + } + + void releasePointerCapture(View view) { + if (mCapturingView != view || mCapturingView == null) { + return; + } + + mCapturingView = null; + InputManager.getInstance().setPointerIconDetached(false); + } + + boolean hasPointerCapture(View view) { + return view != null && mCapturingView == view; + } + @Override public void requestChildFocus(View child, View focused) { if (DEBUG_INPUT_RESIZE) { @@ -3089,6 +3118,10 @@ public final class ViewRootImpl implements ViewParent, mView = null; mAttachInfo.mRootView = null; + if (mCapturingView != null) { + releasePointerCapture(mCapturingView); + } + mSurface.release(); if (mInputQueueCallback != null && mInputQueue != null) { @@ -3398,6 +3431,8 @@ public final class ViewRootImpl implements ViewParent, .softInputMode &= ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; mHasHadWindowFocus = true; + } else if (mCapturingView != null) { + releasePointerCapture(mCapturingView); } } } break; @@ -4244,7 +4279,10 @@ public final class ViewRootImpl implements ViewParent, } mAttachInfo.mUnbufferedDispatchRequested = false; - boolean handled = mView.dispatchPointerEvent(event); + final View eventTarget = + (event.isFromSource(InputDevice.SOURCE_MOUSE) && mCapturingView != null) ? + mCapturingView : mView; + boolean handled = eventTarget.dispatchPointerEvent(event); if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) { mUnbufferedInputDispatch = true; if (mConsumeBatchedInputScheduled) { |
