diff options
| author | 2015-12-03 01:13:31 -0800 | |
|---|---|---|
| committer | 2015-12-18 10:22:43 -0800 | |
| commit | 347e5d498f4c216d588e98776a386d8bdf93d05c (patch) | |
| tree | 8f9b85983dcee4deeefc7fcbaa64124b6ea176f8 | |
| parent | 92e432c30e2304272c2f5b1b33366f32c3d763cf (diff) | |
Introduce pointer capture API.
This depends on I4189eb4d93f50c2865b7a325727be5ceebcc71f8 of
frameworks/native.
Bug: 5452473
Change-Id: Ie21e521f3e5c581f976dc0feb5d84bfa48b046cd
| -rw-r--r-- | api/current.txt | 5 | ||||
| -rw-r--r-- | api/system-current.txt | 5 | ||||
| -rw-r--r-- | api/test-current.txt | 5 | ||||
| -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 | ||||
| -rw-r--r-- | libs/input/PointerController.cpp | 29 | ||||
| -rw-r--r-- | libs/input/PointerController.h | 6 | ||||
| -rw-r--r-- | services/core/java/com/android/server/input/InputManagerService.java | 6 | ||||
| -rw-r--r-- | services/core/jni/com_android_server_input_InputManagerService.cpp | 17 |
12 files changed, 216 insertions, 1 deletions
diff --git a/api/current.txt b/api/current.txt index 5421f05b198b..e39444dd4ad6 100644 --- a/api/current.txt +++ b/api/current.txt @@ -40244,6 +40244,8 @@ package android.view { field public static final int AXIS_LTRIGGER = 17; // 0x11 field public static final int AXIS_ORIENTATION = 8; // 0x8 field public static final int AXIS_PRESSURE = 2; // 0x2 + field public static final int AXIS_RELATIVE_X = 27; // 0x1b + field public static final int AXIS_RELATIVE_Y = 28; // 0x1c field public static final int AXIS_RTRIGGER = 18; // 0x12 field public static final int AXIS_RUDDER = 20; // 0x14 field public static final int AXIS_RX = 12; // 0xc @@ -40779,6 +40781,7 @@ package android.view { method public boolean hasNestedScrollingParent(); method public boolean hasOnClickListeners(); method public boolean hasOverlappingRendering(); + method public boolean hasPointerCapture(); method public boolean hasTransientState(); method public boolean hasWindowFocus(); method public static android.view.View inflate(android.content.Context, int, android.view.ViewGroup); @@ -40905,6 +40908,7 @@ package android.view { method public void postOnAnimation(java.lang.Runnable); method public void postOnAnimationDelayed(java.lang.Runnable, long); method public void refreshDrawableState(); + method public void releasePointerCapture(); method public boolean removeCallbacks(java.lang.Runnable); method public void removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void removeOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); @@ -41005,6 +41009,7 @@ package android.view { method public void setPaddingRelative(int, int, int, int); method public void setPivotX(float); method public void setPivotY(float); + method public void setPointerCapture(); method public void setPointerIcon(android.view.PointerIcon); method public void setPressed(boolean); method public final void setRight(int); diff --git a/api/system-current.txt b/api/system-current.txt index 3071fd978239..e430b9f515fe 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -42599,6 +42599,8 @@ package android.view { field public static final int AXIS_LTRIGGER = 17; // 0x11 field public static final int AXIS_ORIENTATION = 8; // 0x8 field public static final int AXIS_PRESSURE = 2; // 0x2 + field public static final int AXIS_RELATIVE_X = 27; // 0x1b + field public static final int AXIS_RELATIVE_Y = 28; // 0x1c field public static final int AXIS_RTRIGGER = 18; // 0x12 field public static final int AXIS_RUDDER = 20; // 0x14 field public static final int AXIS_RX = 12; // 0xc @@ -43134,6 +43136,7 @@ package android.view { method public boolean hasNestedScrollingParent(); method public boolean hasOnClickListeners(); method public boolean hasOverlappingRendering(); + method public boolean hasPointerCapture(); method public boolean hasTransientState(); method public boolean hasWindowFocus(); method public static android.view.View inflate(android.content.Context, int, android.view.ViewGroup); @@ -43260,6 +43263,7 @@ package android.view { method public void postOnAnimation(java.lang.Runnable); method public void postOnAnimationDelayed(java.lang.Runnable, long); method public void refreshDrawableState(); + method public void releasePointerCapture(); method public boolean removeCallbacks(java.lang.Runnable); method public void removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void removeOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); @@ -43360,6 +43364,7 @@ package android.view { method public void setPaddingRelative(int, int, int, int); method public void setPivotX(float); method public void setPivotY(float); + method public void setPointerCapture(); method public void setPointerIcon(android.view.PointerIcon); method public void setPressed(boolean); method public final void setRight(int); diff --git a/api/test-current.txt b/api/test-current.txt index f418d365832b..88ddc21a92b9 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -40246,6 +40246,8 @@ package android.view { field public static final int AXIS_LTRIGGER = 17; // 0x11 field public static final int AXIS_ORIENTATION = 8; // 0x8 field public static final int AXIS_PRESSURE = 2; // 0x2 + field public static final int AXIS_RELATIVE_X = 27; // 0x1b + field public static final int AXIS_RELATIVE_Y = 28; // 0x1c field public static final int AXIS_RTRIGGER = 18; // 0x12 field public static final int AXIS_RUDDER = 20; // 0x14 field public static final int AXIS_RX = 12; // 0xc @@ -40781,6 +40783,7 @@ package android.view { method public boolean hasNestedScrollingParent(); method public boolean hasOnClickListeners(); method public boolean hasOverlappingRendering(); + method public boolean hasPointerCapture(); method public boolean hasTransientState(); method public boolean hasWindowFocus(); method public static android.view.View inflate(android.content.Context, int, android.view.ViewGroup); @@ -40907,6 +40910,7 @@ package android.view { method public void postOnAnimation(java.lang.Runnable); method public void postOnAnimationDelayed(java.lang.Runnable, long); method public void refreshDrawableState(); + method public void releasePointerCapture(); method public boolean removeCallbacks(java.lang.Runnable); method public void removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void removeOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); @@ -41007,6 +41011,7 @@ package android.view { method public void setPaddingRelative(int, int, int, int); method public void setPivotX(float); method public void setPivotY(float); + method public void setPointerCapture(); method public void setPointerIcon(android.view.PointerIcon); method public void setPressed(boolean); method public final void setRight(int); 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 faf26405c199..f758fef9f518 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 @@ -3004,6 +3008,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) { @@ -3081,6 +3110,10 @@ public final class ViewRootImpl implements ViewParent, mView = null; mAttachInfo.mRootView = null; + if (mCapturingView != null) { + releasePointerCapture(mCapturingView); + } + mSurface.release(); if (mInputQueueCallback != null && mInputQueue != null) { @@ -3390,6 +3423,8 @@ public final class ViewRootImpl implements ViewParent, .softInputMode &= ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; mHasHadWindowFocus = true; + } else if (mCapturingView != null) { + releasePointerCapture(mCapturingView); } } } break; @@ -4236,7 +4271,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) { diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index 529849e99fb6..212c6a0d2c3c 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -90,6 +90,7 @@ PointerController::PointerController(const sp<PointerControllerPolicyInterface>& mLocked.lastFrameUpdatedTime = 0; mLocked.buttonState = 0; + mLocked.iconDetached = false; mPolicy->loadPointerIcon(&mLocked.pointerIcon); @@ -184,6 +185,10 @@ void PointerController::setPosition(float x, float y) { } void PointerController::setPositionLocked(float x, float y) { + if (mLocked.iconDetached) { + return; + } + float minX, minY, maxX, maxY; if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) { if (x <= minX) { @@ -217,6 +222,10 @@ void PointerController::fade(Transition transition) { // Remove the inactivity timeout, since we are fading now. removeInactivityTimeoutLocked(); + if (mLocked.iconDetached) { + return; + } + // Start fading. if (transition == TRANSITION_IMMEDIATE) { mLocked.pointerFadeDirection = 0; @@ -234,6 +243,10 @@ void PointerController::unfade(Transition transition) { // Always reset the inactivity timer. resetInactivityTimeoutLocked(); + if (mLocked.iconDetached) { + return; + } + // Start unfading. if (transition == TRANSITION_IMMEDIATE) { mLocked.pointerFadeDirection = 0; @@ -349,6 +362,22 @@ void PointerController::reloadPointerResources() { updatePointerLocked(); } +void PointerController::detachPointerIcon(bool detached) { + AutoMutex _l(mLock); + + if (mLocked.iconDetached == detached) { + return; + } + + mLocked.iconDetached = detached; + if (detached) { + mLocked.pointerFadeDirection = -1; + } else { + mLocked.pointerFadeDirection = 1; + } + startAnimationLocked(); +} + void PointerController::setDisplayViewport(int32_t width, int32_t height, int32_t orientation) { AutoMutex _l(mLock); diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index 9ba37b338276..c1381f32a37f 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -111,6 +111,10 @@ public: void setInactivityTimeout(InactivityTimeout inactivityTimeout); void reloadPointerResources(); + /* Detach or attach the pointer icon status. When detached, the pointer icon disappears + * and the icon location does not change at all. */ + void detachPointerIcon(bool detached); + private: static const size_t MAX_RECYCLED_SPRITES = 12; static const size_t MAX_SPOTS = 12; @@ -180,6 +184,8 @@ private: int32_t buttonState; + bool iconDetached; + Vector<Spot*> spots; Vector<sp<Sprite> > recycledSprites; } mLocked; diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 68b38179e26d..9f74fc655fac 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -214,6 +214,7 @@ public class InputManagerService extends IInputManager.Stub private static native void nativeSetPointerIconShape(long ptr, int iconId); private static native void nativeReloadPointerIcons(long ptr); private static native void nativeSetCustomPointerIcon(long ptr, PointerIcon icon); + private static native void nativeSetPointerIconDetached(long ptr, boolean detached); // Input event injection constants defined in InputDispatcher.h. private static final int INPUT_EVENT_INJECTION_SUCCEEDED = 0; @@ -1274,6 +1275,11 @@ public class InputManagerService extends IInputManager.Stub nativeSetFocusedApplication(mPtr, application); } + @Override + public void setPointerIconDetached(boolean detached) { + nativeSetPointerIconDetached(mPtr, detached); + } + public void setInputDispatchMode(boolean enabled, boolean frozen) { nativeSetInputDispatchMode(mPtr, enabled, frozen); } diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 4c8474ab29f1..a5237ca89520 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -207,6 +207,7 @@ public: void setPointerIconShape(int32_t iconId); void reloadPointerIcons(); void setCustomPointerIcon(const SpriteIcon& icon); + void setPointerIconDetached(bool detached); /* --- InputReaderPolicyInterface implementation --- */ @@ -711,6 +712,14 @@ void NativeInputManager::setFocusedApplication(JNIEnv* env, jobject applicationH mInputManager->getDispatcher()->setFocusedApplication(applicationHandle); } +void NativeInputManager::setPointerIconDetached(bool detached) { + AutoMutex _l(mLock); + sp<PointerController> controller = mLocked.pointerController.promote(); + if (controller != NULL) { + controller->detachPointerIcon(detached); + } +} + void NativeInputManager::setInputDispatchMode(bool enabled, bool frozen) { mInputManager->getDispatcher()->setInputDispatchMode(enabled, frozen); } @@ -1317,6 +1326,12 @@ static void nativeSetFocusedApplication(JNIEnv* env, jclass /* clazz */, im->setFocusedApplication(env, applicationHandleObj); } +static void nativeSetPointerIconDetached(JNIEnv* env, jclass /* clazz */, jlong ptr, + jboolean detached) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + im->setPointerIconDetached(detached); +} + static void nativeSetInputDispatchMode(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, jboolean enabled, jboolean frozen) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); @@ -1497,6 +1512,8 @@ static const JNINativeMethod gInputManagerMethods[] = { (void*) nativeSetInputWindows }, { "nativeSetFocusedApplication", "(JLcom/android/server/input/InputApplicationHandle;)V", (void*) nativeSetFocusedApplication }, + { "nativeSetPointerIconDetached", "(JZ)V", + (void*) nativeSetPointerIconDetached }, { "nativeSetInputDispatchMode", "(JZZ)V", (void*) nativeSetInputDispatchMode }, { "nativeSetSystemUiVisibility", "(JI)V", |