From fd8666df526a6fe317e4f3bb98a9dc2cbca872e5 Mon Sep 17 00:00:00 2001 From: Andrii Kulian Date: Fri, 5 Oct 2018 16:58:39 -0700 Subject: [PATCH] Load correct resources for pointer icon on external screen When mouse pointer changes displays, it should also reload the icon from new resources. Otherwise, if the densities of the previous and new displays are different, the size of the pointer sprite will look too small or too large. - Add getDisplayContext to get the corresponding Context by displayId. - Cache system pointer icons per display, clear if display removed. - Fix icon moved to default when not resetting out of task bound. Bug: 113559891 Test: Enable mouse pointer on default display, move to other screen Change-Id: Ic42d0ec32d9c979281e13c83b9e8b57134fd4f0d --- core/java/android/view/PointerIcon.java | 54 +++++++++++++++++-- libs/input/PointerController.cpp | 8 +-- libs/input/PointerController.h | 6 +-- .../server/input/InputManagerService.java | 24 ++++++++- .../wm/TaskTapPointerEventListener.java | 22 ++++++-- ...droid_server_input_InputManagerService.cpp | 45 ++++++++++------ 6 files changed, 126 insertions(+), 33 deletions(-) diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java index cf11fd04efdfe..c3d13bd46eb3e 100644 --- a/core/java/android/view/PointerIcon.java +++ b/core/java/android/view/PointerIcon.java @@ -31,6 +31,7 @@ import android.graphics.RectF; import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.hardware.display.DisplayManager; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -144,7 +145,8 @@ public final class PointerIcon implements Parcelable { public static final int TYPE_DEFAULT = TYPE_ARROW; private static final PointerIcon gNullIcon = new PointerIcon(TYPE_NULL); - private static final SparseArray gSystemIcons = new SparseArray(); + private static final SparseArray> gSystemIconsByDisplay = + new SparseArray>(); private static boolean sUseLargeIcons = false; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) @@ -163,6 +165,12 @@ public final class PointerIcon implements Parcelable { @UnsupportedAppUsage private int mDurationPerFrame; + /** + * Listener for displays lifecycle. + * @hide + */ + private static DisplayManager.DisplayListener sDisplayListener; + private PointerIcon(int type) { mType = type; } @@ -211,7 +219,19 @@ public final class PointerIcon implements Parcelable { return gNullIcon; } - PointerIcon icon = gSystemIcons.get(type); + if (sDisplayListener == null) { + registerDisplayListener(context); + } + + final int displayId = context.getDisplayId(); + SparseArray systemIcons = gSystemIconsByDisplay.get(displayId); + if (systemIcons == null) { + systemIcons = new SparseArray<>(); + gSystemIconsByDisplay.put(displayId, systemIcons); + } + + PointerIcon icon = systemIcons.get(type); + // Reload if not in the same display. if (icon != null) { return icon; } @@ -240,7 +260,7 @@ public final class PointerIcon implements Parcelable { } else { icon.loadResource(context, context.getResources(), resourceId); } - gSystemIcons.append(type, icon); + systemIcons.append(type, icon); return icon; } @@ -250,7 +270,7 @@ public final class PointerIcon implements Parcelable { */ public static void setUseLargeIcons(boolean use) { sUseLargeIcons = use; - gSystemIcons.clear(); + gSystemIconsByDisplay.clear(); } /** @@ -576,4 +596,30 @@ public final class PointerIcon implements Parcelable { return 0; } } + + /** + * Manage system icon cache handled by display lifecycle. + * @param context The context. + */ + private static void registerDisplayListener(@NonNull Context context) { + sDisplayListener = new DisplayManager.DisplayListener() { + @Override + public void onDisplayAdded(int displayId) { + } + + @Override + public void onDisplayRemoved(int displayId) { + gSystemIconsByDisplay.remove(displayId); + } + + @Override + public void onDisplayChanged(int displayId) { + gSystemIconsByDisplay.remove(displayId); + } + }; + + DisplayManager displayManager = context.getSystemService(DisplayManager.class); + displayManager.registerDisplayListener(sDisplayListener, null /* handler */); + } + } diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index b4f19c99c6fea..d742cc34b57ec 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -255,7 +255,7 @@ void PointerController::setPresentation(Presentation presentation) { if (presentation == PRESENTATION_POINTER && mLocked.additionalMouseResources.empty()) { mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, - &mLocked.animationResources); + &mLocked.animationResources, mLocked.viewport.displayId); } if (mLocked.presentation != presentation) { @@ -727,14 +727,14 @@ void PointerController::fadeOutAndReleaseAllSpotsLocked() { } void PointerController::loadResourcesLocked() REQUIRES(mLock) { - mPolicy->loadPointerResources(&mResources); + mPolicy->loadPointerResources(&mResources, mLocked.viewport.displayId); if (mLocked.presentation == PRESENTATION_POINTER) { mLocked.additionalMouseResources.clear(); mLocked.animationResources.clear(); - mPolicy->loadPointerIcon(&mLocked.pointerIcon); + mPolicy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId); mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, - &mLocked.animationResources); + &mLocked.animationResources, mLocked.viewport.displayId); } mLocked.pointerIconChanged = true; diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index a32cc42a33420..be057867890d3 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -62,10 +62,10 @@ protected: virtual ~PointerControllerPolicyInterface() { } public: - virtual void loadPointerIcon(SpriteIcon* icon) = 0; - virtual void loadPointerResources(PointerResources* outResources) = 0; + virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) = 0; + virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) = 0; virtual void loadAdditionalMouseResources(std::map* outResources, - std::map* outAnimationResources) = 0; + std::map* outAnimationResources, int32_t displayId) = 0; virtual int32_t getDefaultPointerIconId() = 0; virtual int32_t getCustomPointerIconId() = 0; }; diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 979de66f1dc88..669ff2b7bad5b 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -138,6 +138,9 @@ public class InputManagerService extends IInputManager.Stub private final Context mContext; private final InputManagerHandler mHandler; + // Context cache used for loading pointer resources. + private Context mDisplayContext; + private final File mDoubleTouchGestureEnableFile; private WindowManagerCallbacks mWindowManagerCallbacks; @@ -1923,8 +1926,25 @@ public class InputManagerService extends IInputManager.Stub } // Native callback. - private PointerIcon getPointerIcon() { - return PointerIcon.getDefaultIcon(mContext); + private PointerIcon getPointerIcon(int displayId) { + return PointerIcon.getDefaultIcon(getContextForDisplay(displayId)); + } + + private Context getContextForDisplay(int displayId) { + if (mDisplayContext != null && mDisplayContext.getDisplay().getDisplayId() == displayId) { + return mDisplayContext; + } + + if (mContext.getDisplay().getDisplayId() == displayId) { + mDisplayContext = mContext; + return mDisplayContext; + } + + // Create and cache context for non-default display. + final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); + final Display display = displayManager.getDisplay(displayId); + mDisplayContext = mContext.createDisplayContext(display); + return mDisplayContext; } // Native callback. diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java index b2194190f4f40..685e9bfa6025a 100644 --- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java +++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java @@ -31,6 +31,10 @@ import android.view.WindowManagerPolicyConstants.PointerEventListener; import com.android.server.wm.WindowManagerService.H; +/** + * 1. Adjust the top most focus display if touch down on some display. + * 2. Adjust the pointer icon when cursor moves to the task bounds. + */ public class TaskTapPointerEventListener implements PointerEventListener { private final Region mTouchExcludeRegion = new Region(); @@ -80,8 +84,7 @@ public class TaskTapPointerEventListener implements PointerEventListener { if (motionEvent.getDisplayId() != getDisplayId()) { return; } - final int action = motionEvent.getAction(); - switch (action & MotionEvent.ACTION_MASK) { + switch (motionEvent.getActionMasked()) { case MotionEvent.ACTION_DOWN: { final int x = (int) motionEvent.getX(); final int y = (int) motionEvent.getY(); @@ -97,7 +100,7 @@ public class TaskTapPointerEventListener implements PointerEventListener { } } break; - + case MotionEvent.ACTION_HOVER_ENTER: case MotionEvent.ACTION_HOVER_MOVE: { final int x = (int) motionEvent.getX(); final int y = (int) motionEvent.getY(); @@ -125,6 +128,7 @@ public class TaskTapPointerEventListener implements PointerEventListener { mPointerIconType = iconType; if (mPointerIconType == TYPE_NOT_SPECIFIED) { // Find the underlying window and ask it restore the pointer icon. + mService.mH.removeMessages(H.RESTORE_POINTER_ICON); mService.mH.obtainMessage(H.RESTORE_POINTER_ICON, x, y, mDisplayContent).sendToTarget(); } else { @@ -133,6 +137,18 @@ public class TaskTapPointerEventListener implements PointerEventListener { } } break; + case MotionEvent.ACTION_HOVER_EXIT: { + final int x = (int) motionEvent.getX(); + final int y = (int) motionEvent.getY(); + if (mPointerIconType != TYPE_NOT_SPECIFIED) { + mPointerIconType = TYPE_NOT_SPECIFIED; + // Find the underlying window and ask it to restore the pointer icon. + mService.mH.removeMessages(H.RESTORE_POINTER_ICON); + mService.mH.obtainMessage(H.RESTORE_POINTER_ICON, + x, y, mDisplayContent).sendToTarget(); + } + } + break; } } diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 641200769cf00..90c9cc2b0a356 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -111,6 +111,7 @@ static struct { jmethodID getKeyboardLayoutOverlay; jmethodID getDeviceAlias; jmethodID getTouchCalibrationForInputDevice; + jmethodID getContextForDisplay; } gServiceClassInfo; static struct { @@ -260,17 +261,16 @@ public: /* --- PointerControllerPolicyInterface implementation --- */ - virtual void loadPointerIcon(SpriteIcon* icon); - virtual void loadPointerResources(PointerResources* outResources); + virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId); + virtual void loadPointerResources(PointerResources* outResources, int32_t displayId); virtual void loadAdditionalMouseResources(std::map* outResources, - std::map* outAnimationResources); + std::map* outAnimationResources, int32_t displayId); virtual int32_t getDefaultPointerIconId(); virtual int32_t getCustomPointerIconId(); private: sp mInputManager; - jobject mContextObj; jobject mServiceObj; sp mLooper; @@ -329,7 +329,6 @@ NativeInputManager::NativeInputManager(jobject contextObj, mLooper(looper), mInteractive(true) { JNIEnv* env = jniEnv(); - mContextObj = env->NewGlobalRef(contextObj); mServiceObj = env->NewGlobalRef(serviceObj); { @@ -351,7 +350,6 @@ NativeInputManager::NativeInputManager(jobject contextObj, NativeInputManager::~NativeInputManager() { JNIEnv* env = jniEnv(); - env->DeleteGlobalRef(mContextObj); env->DeleteGlobalRef(mServiceObj); } @@ -1202,19 +1200,22 @@ bool NativeInputManager::checkInjectEventsPermissionNonReentrant( return result; } -void NativeInputManager::loadPointerIcon(SpriteIcon* icon) { +void NativeInputManager::loadPointerIcon(SpriteIcon* icon, int32_t displayId) { ATRACE_CALL(); JNIEnv* env = jniEnv(); ScopedLocalRef pointerIconObj(env, env->CallObjectMethod( - mServiceObj, gServiceClassInfo.getPointerIcon)); + mServiceObj, gServiceClassInfo.getPointerIcon, displayId)); if (checkAndClearExceptionFromCallback(env, "getPointerIcon")) { return; } + ScopedLocalRef displayContext(env, env->CallObjectMethod( + mServiceObj, gServiceClassInfo.getContextForDisplay, displayId)); + PointerIcon pointerIcon; status_t status = android_view_PointerIcon_load(env, pointerIconObj.get(), - mContextObj, &pointerIcon); + displayContext.get(), &pointerIcon); if (!status && !pointerIcon.isNullIcon()) { *icon = SpriteIcon(pointerIcon.bitmap, pointerIcon.hotSpotX, pointerIcon.hotSpotY); } else { @@ -1222,28 +1223,34 @@ void NativeInputManager::loadPointerIcon(SpriteIcon* icon) { } } -void NativeInputManager::loadPointerResources(PointerResources* outResources) { +void NativeInputManager::loadPointerResources(PointerResources* outResources, int32_t displayId) { ATRACE_CALL(); JNIEnv* env = jniEnv(); - loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_SPOT_HOVER, + ScopedLocalRef displayContext(env, env->CallObjectMethod( + mServiceObj, gServiceClassInfo.getContextForDisplay, displayId)); + + loadSystemIconAsSprite(env, displayContext.get(), POINTER_ICON_STYLE_SPOT_HOVER, &outResources->spotHover); - loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_SPOT_TOUCH, + loadSystemIconAsSprite(env, displayContext.get(), POINTER_ICON_STYLE_SPOT_TOUCH, &outResources->spotTouch); - loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_SPOT_ANCHOR, + loadSystemIconAsSprite(env, displayContext.get(), POINTER_ICON_STYLE_SPOT_ANCHOR, &outResources->spotAnchor); } void NativeInputManager::loadAdditionalMouseResources(std::map* outResources, - std::map* outAnimationResources) { + std::map* outAnimationResources, int32_t displayId) { ATRACE_CALL(); JNIEnv* env = jniEnv(); + ScopedLocalRef displayContext(env, env->CallObjectMethod( + mServiceObj, gServiceClassInfo.getContextForDisplay, displayId)); + for (int iconId = POINTER_ICON_STYLE_CONTEXT_MENU; iconId <= POINTER_ICON_STYLE_GRABBING; ++iconId) { PointerIcon pointerIcon; loadSystemIconAsSpriteWithPointerIcon( - env, mContextObj, iconId, &pointerIcon, &((*outResources)[iconId])); + env, displayContext.get(), iconId, &pointerIcon, &((*outResources)[iconId])); if (!pointerIcon.bitmapFrames.empty()) { PointerAnimation& animationData = (*outAnimationResources)[iconId]; size_t numFrames = pointerIcon.bitmapFrames.size() + 1; @@ -1258,7 +1265,7 @@ void NativeInputManager::loadAdditionalMouseResources(std::map