summaryrefslogtreecommitdiff
path: root/core/java
diff options
context:
space:
mode:
authorJun Mukai <mukai@google.com>2015-12-18 23:30:10 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2015-12-18 23:30:10 +0000
commit56cd51a70411d60fac0b3987f5c5543e6a3ea8ef (patch)
tree00d0bcda1d09fdbe4c6acf3a78d6c099fac7020e /core/java
parentea5a94669c3f9757e2cb5f813a0c01e23ba1f20f (diff)
parent347e5d498f4c216d588e98776a386d8bdf93d05c (diff)
Merge "Introduce pointer capture API."
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/hardware/input/IInputManager.aidl2
-rw-r--r--core/java/android/hardware/input/InputManager.java18
-rw-r--r--core/java/android/view/MotionEvent.java33
-rw-r--r--core/java/android/view/View.java51
-rw-r--r--core/java/android/view/ViewRootImpl.java40
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) {