Update pointer icon when View.setPointerIcon is called
Currently the updated pointer icon is only displayed after
the next mouse move.
Bug:27107871
Change-Id: Ieed57b07fe44699735179cf57968a9bb08981396
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 1703ed1..6a2cc80 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -260,4 +260,6 @@
* Returns true if the move started successfully; false otherwise.
*/
boolean startMovingTask(IWindow window, float startX, float startY);
+
+ void updatePointerIcon(IWindow window);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index bba5a17..5ae426c2 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -21581,6 +21581,13 @@
*/
public void setPointerIcon(PointerIcon pointerIcon) {
mPointerIcon = pointerIcon;
+ if (mAttachInfo == null) {
+ return;
+ }
+ try {
+ mAttachInfo.mSession.updatePointerIcon(mAttachInfo.mWindow);
+ } catch (RemoteException e) {
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 0c04d4d..9a3aaa5 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -378,7 +378,9 @@
private void cleanUpDragLw() {
broadcastDragEndedLw();
- restorePointerIconLw();
+ if (isFromSource(InputDevice.SOURCE_MOUSE)) {
+ mService.restorePointerIconLocked(mDisplay, mCurrentX, mCurrentY);
+ }
// stop intercepting input
unregister();
@@ -416,7 +418,7 @@
void notifyLocationLw(float x, float y) {
// Tell the affected window
- WindowState touchedWin = getTouchedWinAtPointLw(x, y);
+ WindowState touchedWin = mService.getTouchableWinAtPointLocked(mDisplay, x, y);
if (touchedWin == null) {
if (DEBUG_DRAG) Slog.d(TAG_WM, "No touched win at x=" + x + " y=" + y);
return;
@@ -461,10 +463,6 @@
mTargetWindow = touchedWin;
}
- WindowState getDropTargetWinLw(float x, float y) {
- return getTouchedWinAtPointLw(x, y);
- }
-
// Tell the drop target about the data. Returns 'true' if we can immediately
// dispatch the global drag-ended message, 'false' if we need to wait for a
// result from the recipient.
@@ -512,77 +510,17 @@
return false;
}
- // Find the visible, touch-deliverable window under the given point
- private WindowState getTouchedWinAtPointLw(float xf, float yf) {
- WindowState touchedWin = null;
- final int x = (int) xf;
- final int y = (int) yf;
-
- final WindowList windows = mService.getWindowListLocked(mDisplay);
- if (windows == null) {
- return null;
- }
- final int N = windows.size();
- for (int i = N - 1; i >= 0; i--) {
- WindowState child = windows.get(i);
- final int flags = child.mAttrs.flags;
- if (!child.isVisibleLw()) {
- // not visible == don't tell about drags
- continue;
- }
- if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
- // not touchable == don't tell about drags
- continue;
- }
-
- child.getVisibleBounds(mTmpRect);
- if (!mTmpRect.contains(x, y)) {
- // outside of this window's activity stack == don't tell about drags
- continue;
- }
-
- child.getTouchableRegion(mTmpRegion);
-
- final int touchFlags = flags &
- (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
- if (mTmpRegion.contains(x, y) || touchFlags == 0) {
- // Found it
- touchedWin = child;
- break;
- }
- }
-
- return touchedWin;
- }
-
private static DragEvent obtainDragEvent(WindowState win, int action,
float x, float y, Object localState,
ClipDescription description, ClipData data,
IDropPermissions dropPermissions,
boolean result) {
- final float winX = translateToWindowX(win, x);
- final float winY = translateToWindowY(win, y);
+ final float winX = win.translateToWindowX(x);
+ final float winY = win.translateToWindowY(y);
return DragEvent.obtain(action, winX, winY, localState, description, data,
dropPermissions, result);
}
- private static float translateToWindowX(WindowState win, float x) {
- float winX = x - win.mFrame.left;
- if (win.mEnforceSizeCompat) {
- winX *= win.mGlobalScale;
- }
- return winX;
- }
-
- private static float translateToWindowY(WindowState win, float y) {
- float winY = y - win.mFrame.top;
- if (win.mEnforceSizeCompat) {
- winY *= win.mGlobalScale;
- }
- return winY;
- }
-
boolean stepAnimationLocked(long currentTimeMs) {
if (mAnimation == null) {
return false;
@@ -638,21 +576,4 @@
InputManager.getInstance().setPointerIconShape(PointerIcon.STYLE_GRAB);
}
}
-
- private void restorePointerIconLw() {
- if (isFromSource(InputDevice.SOURCE_MOUSE)) {
- WindowState touchWin = getTouchedWinAtPointLw(mCurrentX, mCurrentY);
- if (touchWin != null) {
- try {
- touchWin.mClient.updatePointerIcon(
- translateToWindowX(touchWin, mCurrentX),
- translateToWindowY(touchWin, mCurrentY));
- return;
- } catch (RemoteException e) {
- Slog.w(TAG_WM, "unable to restore pointer icon");
- }
- }
- InputManager.getInstance().setPointerIconShape(PointerIcon.STYLE_DEFAULT);
- }
- }
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index a8d974f..25de75a 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -528,6 +528,16 @@
}
}
+ @Override
+ public void updatePointerIcon(IWindow window) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mService.updatePointerIcon(window);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
void windowAddedLocked() {
if (mSurfaceSession == null) {
if (WindowManagerService.localLOGV) Slog.v(
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d54e9e3..c8f5dda 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -41,6 +41,7 @@
import android.graphics.Region;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
+import android.hardware.input.InputManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -81,6 +82,7 @@
import android.view.Display;
import android.view.DisplayInfo;
import android.view.Gravity;
+import android.view.PointerIcon;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IApplicationToken;
import android.view.IDockedStackListener;
@@ -466,6 +468,7 @@
final float[] mTmpFloats = new float[9];
final Rect mTmpRect = new Rect();
final Rect mTmpRect2 = new Rect();
+ final Region mTmpRegion = new Region();
boolean mDisplayReady;
boolean mSafeMode;
@@ -758,7 +761,7 @@
}
private boolean completeDropLw(float x, float y) {
- WindowState dropTargetWin = mDragState.getDropTargetWinLw(x, y);
+ WindowState dropTargetWin = getTouchableWinAtPointLocked(mDragState.mDisplay, x, y);
DropPermissionsHandler dropPermissions = null;
if (dropTargetWin != null &&
@@ -10170,6 +10173,7 @@
if (displayId == Display.DEFAULT_DISPLAY) {
displayContent.mTapDetector = new TaskTapPointerEventListener(this, displayContent);
registerPointerEventListener(displayContent.mTapDetector);
+ registerPointerEventListener(mMousePositionTracker);
}
return displayContent;
@@ -10262,6 +10266,7 @@
displayContent.close();
if (displayId == Display.DEFAULT_DISPLAY) {
unregisterPointerEventListener(displayContent.mTapDetector);
+ unregisterPointerEventListener(mMousePositionTracker);
}
}
mAnimator.removeDisplayLocked(displayId);
@@ -10461,6 +10466,128 @@
}
}
+ /**
+ * Find the visible, touch-deliverable window under the given point
+ */
+ WindowState getTouchableWinAtPointLocked(Display display, float xf, float yf) {
+ WindowState touchedWin = null;
+ final int x = (int) xf;
+ final int y = (int) yf;
+
+ final WindowList windows = getWindowListLocked(display);
+ if (windows == null) {
+ return null;
+ }
+ final int N = windows.size();
+ for (int i = N - 1; i >= 0; i--) {
+ WindowState child = windows.get(i);
+ final int flags = child.mAttrs.flags;
+ if (!child.isVisibleLw()) {
+ continue;
+ }
+ if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
+ continue;
+ }
+
+ child.getTouchableRegion(mTmpRegion);
+
+ final int touchFlags = flags &
+ (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
+ if (mTmpRegion.contains(x, y) || touchFlags == 0) {
+ touchedWin = child;
+ break;
+ }
+ }
+
+ return touchedWin;
+ }
+
+ private MousePositionTracker mMousePositionTracker = new MousePositionTracker();
+
+ private static class MousePositionTracker implements PointerEventListener {
+ private boolean mLatestEventWasMouse;
+ private float mLatestMouseX;
+ private float mLatestMouseY;
+
+ void updatePosition(float x, float y) {
+ synchronized (this) {
+ mLatestEventWasMouse = true;
+ mLatestMouseX = x;
+ mLatestMouseY = y;
+ }
+ }
+
+ @Override
+ public void onPointerEvent(MotionEvent motionEvent) {
+ if (motionEvent.isFromSource(InputDevice.SOURCE_MOUSE)) {
+ updatePosition(motionEvent.getRawX(), motionEvent.getRawY());
+ } else {
+ synchronized (this) {
+ mLatestEventWasMouse = false;
+ }
+ }
+ }
+ };
+
+ void updatePointerIcon(IWindow client) {
+ float mouseX, mouseY;
+
+ synchronized(mMousePositionTracker) {
+ if (!mMousePositionTracker.mLatestEventWasMouse) {
+ return;
+ }
+ mouseX = mMousePositionTracker.mLatestMouseX;
+ mouseY = mMousePositionTracker.mLatestMouseY;
+ }
+
+ synchronized (mWindowMap) {
+ if (mDragState != null) {
+ // Drag cursor overrides the app cursor.
+ return;
+ }
+ WindowState callingWin = windowForClientLocked(null, client, false);
+ if (callingWin == null) {
+ Slog.w(TAG_WM, "Bad requesting window " + client);
+ return;
+ }
+ final DisplayContent displayContent = callingWin.getDisplayContent();
+ if (displayContent == null) {
+ return;
+ }
+ Display display = displayContent.getDisplay();
+ WindowState windowUnderPointer = getTouchableWinAtPointLocked(display, mouseX, mouseY);
+ if (windowUnderPointer != callingWin) {
+ return;
+ }
+ try {
+ windowUnderPointer.mClient.updatePointerIcon(
+ windowUnderPointer.translateToWindowX(mouseX),
+ windowUnderPointer.translateToWindowY(mouseY));
+ } catch (RemoteException e) {
+ Slog.w(TAG_WM, "unable to update pointer icon");
+ }
+ }
+ }
+
+ void restorePointerIconLocked(Display display, float latestX, float latestY) {
+ // Mouse position tracker has not been getting updates while dragging, update it now.
+ mMousePositionTracker.updatePosition(latestX, latestY);
+
+ WindowState windowUnderPointer = getTouchableWinAtPointLocked(display, latestX, latestY);
+ if (windowUnderPointer != null) {
+ try {
+ windowUnderPointer.mClient.updatePointerIcon(
+ windowUnderPointer.translateToWindowX(latestX),
+ windowUnderPointer.translateToWindowY(latestY));
+ } catch (RemoteException e) {
+ Slog.w(TAG_WM, "unable to restore pointer icon");
+ }
+ } else {
+ InputManager.getInstance().setPointerIconShape(PointerIcon.STYLE_DEFAULT);
+ }
+ }
+
private final class LocalService extends WindowManagerInternal {
@Override
public void requestTraversalFromDisplayManager() {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 5b9206d..37c8a7e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2483,4 +2483,20 @@
mReplacingWindow = null;
mAnimateReplacingWindow = false;
}
+
+ float translateToWindowX(float x) {
+ float winX = x - mFrame.left;
+ if (mEnforceSizeCompat) {
+ winX *= mGlobalScale;
+ }
+ return winX;
+ }
+
+ float translateToWindowY(float y) {
+ float winY = y - mFrame.top;
+ if (mEnforceSizeCompat) {
+ winY *= mGlobalScale;
+ }
+ return winY;
+ }
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index 7b8e29a..fe05b0e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -236,4 +236,9 @@
public void prepareToReplaceChildren(IBinder appToken) {
// pass for now.
}
+
+ @Override
+ public void updatePointerIcon(IWindow window) {
+ // pass for now.
+ }
}