Introduce pointer capture API.
This depends on I4189eb4d93f50c2865b7a325727be5ceebcc71f8 of frameworks/native. Bug: 5452473 Change-Id: Ie21e521f3e5c581f976dc0feb5d84bfa48b046cd
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -73,4 +73,6 @@ interface IInputManager {
|
||||
|
||||
void setPointerIconShape(int shapeId);
|
||||
void setCustomPointerIcon(in PointerIcon icon);
|
||||
|
||||
void setPointerIconDetached(boolean detached);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -976,6 +976,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");
|
||||
|
||||
@@ -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
|
||||
//
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user