diff options
| author | 2024-10-21 23:30:23 +0000 | |
|---|---|---|
| committer | 2024-10-21 23:30:23 +0000 | |
| commit | 56ec45a3d9e33ea3385892f0af58336a18d50b80 (patch) | |
| tree | 936e094f6c80cac3be33b3442545510cda6e8042 | |
| parent | b34540fef192e6af4fde487bca3d3173ee424ac9 (diff) | |
| parent | 2b5c3c6cddf1ba710f5e51ab51ea9166f79327da (diff) | |
Merge changes from topic "355734856-magnification-enlarge-cursor" into main
* changes:
Fullscreen Magnification updates pointer icon on non-transient change
Enlarge pointer icon for magnification
12 files changed, 462 insertions, 76 deletions
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java index a77ba624a68f..ce1a292fb069 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java @@ -64,6 +64,7 @@ import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityManagerService; import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.accessibility.Flags; +import com.android.server.input.InputManagerInternal; import com.android.server.wm.WindowManagerInternal; import java.util.ArrayList; @@ -396,7 +397,7 @@ public class FullScreenMagnificationController implements mCurrentMagnificationSpec.offsetX, mCurrentMagnificationSpec.offsetY)) { sendSpecToAnimation(mCurrentMagnificationSpec, null); } - onMagnificationChangedLocked(); + onMagnificationChangedLocked(/* isScaleTransient= */ false); } magnified.recycle(); } @@ -474,8 +475,16 @@ public class FullScreenMagnificationController implements return mIdOfLastServiceToMagnify; } + /** + * This is invoked whenever magnification change happens. + * + * @param isScaleTransient represents that if the scale is being changed and the changed + * value may be short lived and be updated again soon. + * Calling the method usually notifies input manager to update the + * cursor scale, but setting this value {@code true} prevents it. + */ @GuardedBy("mLock") - void onMagnificationChangedLocked() { + void onMagnificationChangedLocked(boolean isScaleTransient) { final float scale = getScale(); final float centerX = getCenterX(); final float centerY = getCenterY(); @@ -498,6 +507,10 @@ public class FullScreenMagnificationController implements } else { hideThumbnail(); } + + if (!isScaleTransient) { + notifyScaleForInput(mDisplayId, scale); + } } @GuardedBy("mLock") @@ -611,8 +624,9 @@ public class FullScreenMagnificationController implements * Directly Zooms out the scale to 1f with animating the transition. This method is * triggered only by service automatically, such as when user context changed. */ + @GuardedBy("mLock") void zoomOutFromService() { - setScaleAndCenter(1.0f, Float.NaN, Float.NaN, + setScaleAndCenter(1.0f, Float.NaN, Float.NaN, /* isScaleTransient= */ false, transformToStubCallback(true), AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); mZoomedOutFromService = true; @@ -640,7 +654,7 @@ public class FullScreenMagnificationController implements setActivated(false); if (changed) { spec.clear(); - onMagnificationChangedLocked(); + onMagnificationChangedLocked(/* isScaleTransient= */ false); } mIdOfLastServiceToMagnify = INVALID_SERVICE_ID; sendSpecToAnimation(spec, animationCallback); @@ -651,7 +665,7 @@ public class FullScreenMagnificationController implements } @GuardedBy("mLock") - boolean setScale(float scale, float pivotX, float pivotY, + boolean setScale(float scale, float pivotX, float pivotY, boolean isScaleTransient, boolean animate, int id) { if (!mRegistered) { return false; @@ -674,12 +688,14 @@ public class FullScreenMagnificationController implements final float centerX = normPivotX + offsetX; final float centerY = normPivotY + offsetY; mIdOfLastServiceToMagnify = id; - return setScaleAndCenter(scale, centerX, centerY, transformToStubCallback(animate), id); + return setScaleAndCenter(scale, centerX, centerY, isScaleTransient, + transformToStubCallback(animate), id); } @GuardedBy("mLock") boolean setScaleAndCenter(float scale, float centerX, float centerY, - MagnificationAnimationCallback animationCallback, int id) { + boolean isScaleTransient, MagnificationAnimationCallback animationCallback, + int id) { if (!mRegistered) { return false; } @@ -696,7 +712,7 @@ public class FullScreenMagnificationController implements + animationCallback + ", id = " + id + ")"); } boolean changed = setActivated(true); - changed |= updateMagnificationSpecLocked(scale, centerX, centerY); + changed |= updateMagnificationSpecLocked(scale, centerX, centerY, isScaleTransient); sendSpecToAnimation(mCurrentMagnificationSpec, animationCallback); if (isActivated() && (id != INVALID_SERVICE_ID)) { mIdOfLastServiceToMagnify = id; @@ -773,7 +789,9 @@ public class FullScreenMagnificationController implements * @return {@code true} if the magnification spec changed or {@code false} * otherwise */ - boolean updateMagnificationSpecLocked(float scale, float centerX, float centerY) { + @GuardedBy("mLock") + boolean updateMagnificationSpecLocked(float scale, float centerX, float centerY, + boolean isScaleTransient) { // Handle defaults. if (Float.isNaN(centerX)) { centerX = getCenterX(); @@ -801,7 +819,7 @@ public class FullScreenMagnificationController implements changed |= updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY); if (changed) { - onMagnificationChangedLocked(); + onMagnificationChangedLocked(isScaleTransient); } return changed; @@ -816,7 +834,7 @@ public class FullScreenMagnificationController implements final float nonNormOffsetX = mCurrentMagnificationSpec.offsetX - offsetX; final float nonNormOffsetY = mCurrentMagnificationSpec.offsetY - offsetY; if (updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY)) { - onMagnificationChangedLocked(); + onMagnificationChangedLocked(/* isScaleTransient= */ false); } if (id != INVALID_SERVICE_ID) { mIdOfLastServiceToMagnify = id; @@ -861,7 +879,7 @@ public class FullScreenMagnificationController implements } synchronized (mLock) { mCurrentMagnificationSpec.setTo(lastSpecSent); - onMagnificationChangedLocked(); + onMagnificationChangedLocked(/* isScaleTransient= */ false); } } }); @@ -955,6 +973,7 @@ public class FullScreenMagnificationController implements context, traceManager, LocalServices.getService(WindowManagerInternal.class), + LocalServices.getService(InputManagerInternal.class), new Handler(context.getMainLooper()), context.getResources().getInteger(R.integer.config_longAnimTime)), lock, @@ -1464,20 +1483,24 @@ public class FullScreenMagnificationController implements * @param scale the target scale, must be >= 1 * @param pivotX the screen-relative X coordinate around which to scale * @param pivotY the screen-relative Y coordinate around which to scale + * @param isScaleTransient {@code true} if the scale is for a short time and potentially changed + * soon. {@code false} otherwise. * @param animate {@code true} to animate the transition, {@code false} * to transition immediately * @param id the ID of the service requesting the change * @return {@code true} if the magnification spec changed, {@code false} if * the spec did not change */ + @SuppressWarnings("GuardedBy") + // errorprone cannot recognize an inner class guarded by an outer class member. public boolean setScale(int displayId, float scale, float pivotX, float pivotY, - boolean animate, int id) { + boolean isScaleTransient, boolean animate, int id) { synchronized (mLock) { final DisplayMagnification display = mDisplays.get(displayId); if (display == null) { return false; } - return display.setScale(scale, pivotX, pivotY, animate, id); + return display.setScale(scale, pivotX, pivotY, isScaleTransient, animate, id); } } @@ -1496,6 +1519,8 @@ public class FullScreenMagnificationController implements * @return {@code true} if the magnification spec changed, {@code false} if * the spec did not change */ + @SuppressWarnings("GuardedBy") + // errorprone cannot recognize an inner class guarded by an outer class member. public boolean setCenter(int displayId, float centerX, float centerY, boolean animate, int id) { synchronized (mLock) { final DisplayMagnification display = mDisplays.get(displayId); @@ -1503,7 +1528,7 @@ public class FullScreenMagnificationController implements return false; } return display.setScaleAndCenter(Float.NaN, centerX, centerY, - animate ? STUB_ANIMATION_CALLBACK : null, id); + /* isScaleTransient= */ false, animate ? STUB_ANIMATION_CALLBACK : null, id); } } @@ -1526,7 +1551,32 @@ public class FullScreenMagnificationController implements */ public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY, boolean animate, int id) { - return setScaleAndCenter(displayId, scale, centerX, centerY, + return setScaleAndCenter(displayId, scale, centerX, centerY, /* isScaleTransient= */ false, + transformToStubCallback(animate), id); + } + + /** + * Sets the scale and center of the magnified region, optionally + * animating the transition. If animation is disabled, the transition + * is immediate. + * + * @param displayId The logical display id. + * @param scale the target scale, or {@link Float#NaN} to leave unchanged + * @param centerX the screen-relative X coordinate around which to + * center and scale, or {@link Float#NaN} to leave unchanged + * @param centerY the screen-relative Y coordinate around which to + * center and scale, or {@link Float#NaN} to leave unchanged + * @param isScaleTransient {@code true} if the scale is for a short time and potentially changed + * soon. {@code false} otherwise. + * @param animate {@code true} to animate the transition, {@code false} + * to transition immediately + * @param id the ID of the service requesting the change + * @return {@code true} if the magnification spec changed, {@code false} if + * the spec did not change + */ + public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY, + boolean isScaleTransient, boolean animate, int id) { + return setScaleAndCenter(displayId, scale, centerX, centerY, isScaleTransient, transformToStubCallback(animate), id); } @@ -1541,20 +1591,25 @@ public class FullScreenMagnificationController implements * center and scale, or {@link Float#NaN} to leave unchanged * @param centerY the screen-relative Y coordinate around which to * center and scale, or {@link Float#NaN} to leave unchanged + * @param isScaleTransient {@code true} if the scale is for a short time and potentially changed + * soon. {@code false} otherwise. * @param animationCallback Called when the animation result is valid. * {@code null} to transition immediately * @param id the ID of the service requesting the change * @return {@code true} if the magnification spec changed, {@code false} if * the spec did not change */ + @SuppressWarnings("GuardedBy") + // errorprone cannot recognize an inner class guarded by an outer class member. public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY, - MagnificationAnimationCallback animationCallback, int id) { + boolean isScaleTransient, MagnificationAnimationCallback animationCallback, int id) { synchronized (mLock) { final DisplayMagnification display = mDisplays.get(displayId); if (display == null) { return false; } - return display.setScaleAndCenter(scale, centerX, centerY, animationCallback, id); + return display.setScaleAndCenter(scale, centerX, centerY, isScaleTransient, + animationCallback, id); } } @@ -1569,6 +1624,8 @@ public class FullScreenMagnificationController implements * screen pixels. * @param id the ID of the service requesting the change */ + @SuppressWarnings("GuardedBy") + // errorprone cannot recognize an inner class guarded by an outer class member. public void offsetMagnifiedRegion(int displayId, float offsetX, float offsetY, int id) { synchronized (mLock) { final DisplayMagnification display = mDisplays.get(displayId); @@ -1640,6 +1697,8 @@ public class FullScreenMagnificationController implements */ public void persistScale(int displayId) { final float scale = getScale(displayId); + notifyScaleForInput(displayId, scale); + if (scale < MagnificationConstants.PERSISTED_SCALE_MIN_VALUE) { return; } @@ -1665,6 +1724,8 @@ public class FullScreenMagnificationController implements * * @param displayId The logical display id. */ + @SuppressWarnings("GuardedBy") + // errorprone cannot recognize an inner class guarded by an outer class member. private void zoomOutFromService(int displayId) { synchronized (mLock) { final DisplayMagnification display = mDisplays.get(displayId); @@ -1691,6 +1752,20 @@ public class FullScreenMagnificationController implements } /** + * Notifies input manager that magnification scale changed non-transiently + * so that pointer cursor is scaled as well. + * + * @param displayId The logical display id. + * @param scale The new scale factor. + */ + public void notifyScaleForInput(int displayId, float scale) { + if (Flags.magnificationEnlargePointer()) { + mControllerCtx.getInputManager() + .setAccessibilityPointerIconScaleFactor(displayId, scale); + } + } + + /** * Resets all displays' magnification if last magnifying service is disabled. * * @param connectionId @@ -2166,6 +2241,7 @@ public class FullScreenMagnificationController implements private final Context mContext; private final AccessibilityTraceManager mTrace; private final WindowManagerInternal mWindowManager; + private final InputManagerInternal mInputManager; private final Handler mHandler; private final Long mAnimationDuration; @@ -2175,11 +2251,13 @@ public class FullScreenMagnificationController implements public ControllerContext(@NonNull Context context, @NonNull AccessibilityTraceManager traceManager, @NonNull WindowManagerInternal windowManager, + @NonNull InputManagerInternal inputManager, @NonNull Handler handler, long animationDuration) { mContext = context; mTrace = traceManager; mWindowManager = windowManager; + mInputManager = inputManager; mHandler = handler; mAnimationDuration = animationDuration; } @@ -2209,6 +2287,14 @@ public class FullScreenMagnificationController implements } /** + * @return InputManagerInternal + */ + @NonNull + public InputManagerInternal getInputManager() { + return mInputManager; + } + + /** * @return Handler for main looper */ @NonNull diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java index 963334b07ea6..c6a966f47952 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java @@ -617,7 +617,8 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH } if (DEBUG_PANNING_SCALING) Slog.i(mLogTag, "Scaled content to: " + scale + "x"); - mFullScreenMagnificationController.setScale(mDisplayId, scale, pivotX, pivotY, false, + mFullScreenMagnificationController.setScale(mDisplayId, scale, pivotX, pivotY, + /* isScaleTransient= */ true, /* animate= */ false, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); checkShouldDetectPassPersistedScale(); @@ -1974,6 +1975,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH /* scale= */ scale, /* centerX= */ mPivotEdge.x, /* centerY= */ mPivotEdge.y, + /* isScaleTransient= */ true, /* animate= */ true, /* id= */ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); if (scale == 1.0f) { diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java index 1489d16c3764..d40e7476f7ec 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java @@ -176,7 +176,8 @@ public class MagnificationController implements MagnificationConnectionManager.C public void onPerformScaleAction(int displayId, float scale, boolean updatePersistence) { if (getFullScreenMagnificationController().isActivated(displayId)) { getFullScreenMagnificationController().setScaleAndCenter(displayId, scale, - Float.NaN, Float.NaN, false, MAGNIFICATION_GESTURE_HANDLER_ID); + Float.NaN, Float.NaN, /* isScaleTransient= */ !updatePersistence, false, + MAGNIFICATION_GESTURE_HANDLER_ID); if (updatePersistence) { getFullScreenMagnificationController().persistScale(displayId); } @@ -371,7 +372,7 @@ public class MagnificationController implements MagnificationConnectionManager.C } screenMagnificationController.setScaleAndCenter(displayId, targetScale, magnificationCenter.x, magnificationCenter.y, - magnificationAnimationCallback, id); + /* isScaleTransient= */ false, magnificationAnimationCallback, id); } else { if (screenMagnificationController.isRegistered(displayId)) { screenMagnificationController.reset(displayId, false); diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java index 99f7f12567b4..c888eef7f5df 100644 --- a/services/core/java/com/android/server/input/InputManagerInternal.java +++ b/services/core/java/com/android/server/input/InputManagerInternal.java @@ -262,4 +262,12 @@ public abstract class InputManagerInternal { */ public abstract void handleKeyGestureInKeyGestureController(int deviceId, int[] keycodes, int modifierState, @KeyGestureEvent.KeyGestureType int event); + + /** + * Sets the magnification scale factor for pointer icons. + * + * @param displayId the ID of the display where the new scale factor is applied. + * @param scaleFactor the new scale factor to be applied for pointer icons. + */ + public abstract void setAccessibilityPointerIconScaleFactor(int displayId, float scaleFactor); } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 8acf583e0765..98e5319cde30 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -3506,6 +3506,11 @@ public class InputManagerService extends IInputManager.Stub int modifierState, @KeyGestureEvent.KeyGestureType int gestureType) { mKeyGestureController.handleKeyGesture(deviceId, keycodes, modifierState, gestureType); } + + @Override + public void setAccessibilityPointerIconScaleFactor(int displayId, float scaleFactor) { + InputManagerService.this.setAccessibilityPointerIconScaleFactor(displayId, scaleFactor); + } } @Override @@ -3688,6 +3693,10 @@ public class InputManagerService extends IInputManager.Stub mPointerIconCache.setPointerScale(scale); } + void setAccessibilityPointerIconScaleFactor(int displayId, float scaleFactor) { + mPointerIconCache.setAccessibilityScaleFactor(displayId, scaleFactor); + } + interface KeyboardBacklightControllerInterface { default void incrementKeyboardBacklight(int deviceId) {} default void decrementKeyboardBacklight(int deviceId) {} diff --git a/services/core/java/com/android/server/input/PointerIconCache.java b/services/core/java/com/android/server/input/PointerIconCache.java index 297cd68d5d3d..e16031cb664a 100644 --- a/services/core/java/com/android/server/input/PointerIconCache.java +++ b/services/core/java/com/android/server/input/PointerIconCache.java @@ -27,6 +27,7 @@ import android.hardware.display.DisplayManager; import android.os.Handler; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseDoubleArray; import android.util.SparseIntArray; import android.view.ContextThemeWrapper; import android.view.Display; @@ -34,6 +35,7 @@ import android.view.DisplayInfo; import android.view.PointerIcon; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.UiThread; import java.util.Objects; @@ -51,7 +53,7 @@ final class PointerIconCache { private final NativeInputManagerService mNative; // We use the UI thread for loading pointer icons. - private final Handler mUiThreadHandler = UiThread.getHandler(); + private final Handler mUiThreadHandler; @GuardedBy("mLoadedPointerIconsByDisplayAndType") private final SparseArray<SparseArray<PointerIcon>> mLoadedPointerIconsByDisplayAndType = @@ -70,6 +72,9 @@ final class PointerIconCache { POINTER_ICON_VECTOR_STYLE_STROKE_WHITE; @GuardedBy("mLoadedPointerIconsByDisplayAndType") private float mPointerIconScale = DEFAULT_POINTER_SCALE; + // Note that android doesn't have SparseFloatArray, so this falls back to use double instead. + @GuardedBy("mLoadedPointerIconsByDisplayAndType") + private final SparseDoubleArray mAccessibilityScaleFactorPerDisplay = new SparseDoubleArray(); private final DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() { @@ -86,6 +91,7 @@ final class PointerIconCache { mLoadedPointerIconsByDisplayAndType.remove(displayId); mDisplayContexts.remove(displayId); mDisplayDensities.delete(displayId); + mAccessibilityScaleFactorPerDisplay.delete(displayId); } } @@ -96,8 +102,15 @@ final class PointerIconCache { }; /* package */ PointerIconCache(Context context, NativeInputManagerService nativeService) { + this(context, nativeService, UiThread.getHandler()); + } + + @VisibleForTesting + /* package */ PointerIconCache(Context context, NativeInputManagerService nativeService, + Handler handler) { mContext = context; mNative = nativeService; + mUiThreadHandler = handler; } public void systemRunning() { @@ -134,6 +147,11 @@ final class PointerIconCache { mUiThreadHandler.post(() -> handleSetPointerScale(scale)); } + /** Set the scale for accessibility (magnification) for vector pointer icons. */ + public void setAccessibilityScaleFactor(int displayId, float scaleFactor) { + mUiThreadHandler.post(() -> handleAccessibilityScaleFactor(displayId, scaleFactor)); + } + /** * Get a loaded system pointer icon. This will fetch the icon from the cache, or load it if * it isn't already cached. @@ -155,8 +173,10 @@ final class PointerIconCache { /* force= */ true); theme.applyStyle(PointerIcon.vectorStrokeStyleToResource(mPointerIconStrokeStyle), /* force= */ true); + final float scale = mPointerIconScale + * (float) mAccessibilityScaleFactorPerDisplay.get(displayId, 1f); icon = PointerIcon.getLoadedSystemIcon(new ContextThemeWrapper(context, theme), - type, mUseLargePointerIcons, mPointerIconScale); + type, mUseLargePointerIcons, scale); iconsByType.put(type, icon); } return Objects.requireNonNull(icon); @@ -261,6 +281,19 @@ final class PointerIconCache { mNative.reloadPointerIcons(); } + @android.annotation.UiThread + private void handleAccessibilityScaleFactor(int displayId, float scale) { + synchronized (mLoadedPointerIconsByDisplayAndType) { + if (mAccessibilityScaleFactorPerDisplay.get(displayId, 1f) == scale) { + return; + } + mAccessibilityScaleFactorPerDisplay.put(displayId, scale); + // Clear cached icons on the display. + mLoadedPointerIconsByDisplayAndType.remove(displayId); + } + mNative.reloadPointerIcons(); + } + // Updates the cached display density for the given displayId, and returns true if // the cached density changed. @GuardedBy("mLoadedPointerIconsByDisplayAndType") diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java index 7829fcc4b44d..8df18a8c178b 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java @@ -480,7 +480,7 @@ public class MagnificationProcessorTest { if (config.getMode() == MAGNIFICATION_MODE_FULLSCREEN) { mFullScreenMagnificationControllerStub.resetAndStubMethods(); mMockFullScreenMagnificationController.setScaleAndCenter(displayId, config.getScale(), - config.getCenterX(), config.getCenterY(), false, SERVICE_ID); + config.getCenterX(), config.getCenterY(), true, false, SERVICE_ID); mMagnificationManagerStub.deactivateIfNeed(); } else if (config.getMode() == MAGNIFICATION_MODE_WINDOW) { mMagnificationManagerStub.resetAndStubMethods(); @@ -531,6 +531,9 @@ public class MagnificationProcessorTest { }; doAnswer(enableMagnificationStubAnswer).when( mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), anyFloat(), + anyFloat(), anyFloat(), anyBoolean(), anyBoolean(), eq(SERVICE_ID)); + doAnswer(enableMagnificationStubAnswer).when( + mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), anyFloat(), anyFloat(), anyFloat(), anyBoolean(), eq(SERVICE_ID)); Answer disableMagnificationStubAnswer = invocation -> { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java index c4b4afd13a60..5985abc344a2 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java @@ -18,6 +18,7 @@ package com.android.server.accessibility.magnification; import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN; +import static com.android.server.accessibility.Flags.FLAG_MAGNIFICATION_ENLARGE_POINTER; import static com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback; import static com.android.server.accessibility.magnification.MockMagnificationConnection.TEST_DISPLAY; import static com.android.window.flags.Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER; @@ -76,6 +77,7 @@ import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.accessibility.Flags; import com.android.server.accessibility.test.MessageCapturingHandler; +import com.android.server.input.InputManagerInternal; import com.android.server.wm.WindowManagerInternal; import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks; @@ -126,6 +128,7 @@ public class FullScreenMagnificationControllerTest { final Resources mMockResources = mock(Resources.class); final AccessibilityTraceManager mMockTraceManager = mock(AccessibilityTraceManager.class); final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class); + final InputManagerInternal mMockInputManager = mock(InputManagerInternal.class); private final MagnificationAnimationCallback mAnimationCallback = mock( MagnificationAnimationCallback.class); private final MagnificationInfoChangedCallback mRequestObserver = mock( @@ -163,6 +166,7 @@ public class FullScreenMagnificationControllerTest { when(mMockControllerCtx.getContext()).thenReturn(mMockContext); when(mMockControllerCtx.getTraceManager()).thenReturn(mMockTraceManager); when(mMockControllerCtx.getWindowManager()).thenReturn(mMockWindowManager); + when(mMockControllerCtx.getInputManager()).thenReturn(mMockInputManager); when(mMockControllerCtx.getHandler()).thenReturn(mMessageCapturingHandler); when(mMockControllerCtx.getAnimationDuration()).thenReturn(1000L); mResolver = new MockContentResolver(); @@ -285,10 +289,11 @@ public class FullScreenMagnificationControllerTest { mFullScreenMagnificationController.magnificationRegionContains(displayId, 100, 100)); assertFalse(mFullScreenMagnificationController.reset(displayId, true)); - assertFalse(mFullScreenMagnificationController.setScale(displayId, 2, 100, 100, true, 0)); + assertFalse( + mFullScreenMagnificationController.setScale(displayId, 2, 100, 100, true, true, 0)); assertFalse(mFullScreenMagnificationController.setCenter(displayId, 100, 100, false, 1)); assertFalse(mFullScreenMagnificationController.setScaleAndCenter(displayId, - 1.5f, 100, 100, false, 2)); + 1.5f, 100, 100, true, false, 2)); assertTrue(mFullScreenMagnificationController.getIdOfLastServiceToMagnify(displayId) < 0); mFullScreenMagnificationController.getMagnificationRegion(displayId, new Region()); @@ -313,7 +318,7 @@ public class FullScreenMagnificationControllerTest { final float scale = 2.0f; final PointF center = INITIAL_MAGNIFICATION_BOUNDS_CENTER; assertFalse(mFullScreenMagnificationController - .setScale(TEST_DISPLAY, scale, center.x, center.y, false, SERVICE_ID_1)); + .setScale(TEST_DISPLAY, scale, center.x, center.y, true, false, SERVICE_ID_1)); assertFalse(mFullScreenMagnificationController.isActivated(TEST_DISPLAY)); } @@ -331,7 +336,7 @@ public class FullScreenMagnificationControllerTest { final PointF center = INITIAL_MAGNIFICATION_BOUNDS_CENTER; final PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, center, scale); assertTrue(mFullScreenMagnificationController - .setScale(displayId, scale, center.x, center.y, false, SERVICE_ID_1)); + .setScale(displayId, scale, center.x, center.y, true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); final MagnificationSpec expectedSpec = getMagnificationSpec(scale, offsets); @@ -357,7 +362,7 @@ public class FullScreenMagnificationControllerTest { float scale = 2.0f; PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; assertTrue(mFullScreenMagnificationController - .setScale(displayId, scale, pivotPoint.x, pivotPoint.y, true, SERVICE_ID_1)); + .setScale(displayId, scale, pivotPoint.x, pivotPoint.y, true, true, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); // New center should be halfway between original center and pivot @@ -405,7 +410,7 @@ public class FullScreenMagnificationControllerTest { float scale = 2.0f; assertTrue(mFullScreenMagnificationController.setScale(displayId, scale, INITIAL_MAGNIFICATION_BOUNDS.centerX(), INITIAL_MAGNIFICATION_BOUNDS.centerY(), - false, SERVICE_ID_1)); + true, false, SERVICE_ID_1)); Mockito.reset(mMockWindowManager); PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; @@ -440,7 +445,7 @@ public class FullScreenMagnificationControllerTest { MagnificationSpec endSpec = getMagnificationSpec(scale, offsets); assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, scale, - newCenter.x, newCenter.y, mAnimationCallback, SERVICE_ID_1)); + newCenter.x, newCenter.y, true, mAnimationCallback, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5); @@ -486,11 +491,11 @@ public class FullScreenMagnificationControllerTest { final PointF center = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; final float targetScale = 2.0f; assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, - targetScale, center.x, center.y, false, SERVICE_ID_1)); + targetScale, center.x, center.y, true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); assertFalse(mFullScreenMagnificationController.setScaleAndCenter(displayId, - targetScale, center.x, center.y, mAnimationCallback, SERVICE_ID_1)); + targetScale, center.x, center.y, true, mAnimationCallback, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); verify(mMockValueAnimator, never()).start(); @@ -516,7 +521,7 @@ public class FullScreenMagnificationControllerTest { assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, MagnificationScaleProvider.MAX_SCALE + 1.0f, - newCenter.x, newCenter.y, false, SERVICE_ID_1)); + newCenter.x, newCenter.y, true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5); @@ -527,7 +532,7 @@ public class FullScreenMagnificationControllerTest { // Verify that we can't zoom below 1x assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, 0.5f, INITIAL_MAGNIFICATION_BOUNDS_CENTER.x, INITIAL_MAGNIFICATION_BOUNDS_CENTER.y, - false, SERVICE_ID_1)); + true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); assertEquals(INITIAL_MAGNIFICATION_BOUNDS_CENTER.x, @@ -551,7 +556,7 @@ public class FullScreenMagnificationControllerTest { // Off the edge to the top and left assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, - scale, -100f, -200f, false, SERVICE_ID_1)); + scale, -100f, -200f, true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); PointF newCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER; @@ -565,7 +570,7 @@ public class FullScreenMagnificationControllerTest { // Off the edge to the bottom and right assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, scale, INITIAL_MAGNIFICATION_BOUNDS.right + 1, INITIAL_MAGNIFICATION_BOUNDS.bottom + 1, - false, SERVICE_ID_1)); + true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; newOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale); @@ -619,7 +624,7 @@ public class FullScreenMagnificationControllerTest { PointF startOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, startCenter, scale); // First zoom in assertTrue(mFullScreenMagnificationController - .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, false, + .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); Mockito.reset(mMockWindowManager); @@ -673,7 +678,7 @@ public class FullScreenMagnificationControllerTest { // Upper left edges PointF ulCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER; assertTrue(mFullScreenMagnificationController - .setScaleAndCenter(displayId, scale, ulCenter.x, ulCenter.y, false, + .setScaleAndCenter(displayId, scale, ulCenter.x, ulCenter.y, true, false, SERVICE_ID_1)); Mockito.reset(mMockWindowManager); MagnificationSpec ulSpec = getCurrentMagnificationSpec(displayId); @@ -685,7 +690,7 @@ public class FullScreenMagnificationControllerTest { // Lower right edges PointF lrCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; assertTrue(mFullScreenMagnificationController - .setScaleAndCenter(displayId, scale, lrCenter.x, lrCenter.y, false, + .setScaleAndCenter(displayId, scale, lrCenter.x, lrCenter.y, true, false, SERVICE_ID_1)); Mockito.reset(mMockWindowManager); MagnificationSpec lrSpec = getCurrentMagnificationSpec(displayId); @@ -710,7 +715,7 @@ public class FullScreenMagnificationControllerTest { float scale = 2.0f; // First zoom in assertTrue(mFullScreenMagnificationController - .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, false, + .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); @@ -753,7 +758,7 @@ public class FullScreenMagnificationControllerTest { PointF startOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, startCenter, scale); // First zoom in assertTrue(mFullScreenMagnificationController - .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, false, + .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, true, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); @@ -786,12 +791,12 @@ public class FullScreenMagnificationControllerTest { register(displayId); PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER; assertTrue(mFullScreenMagnificationController - .setScale(displayId, 2.0f, startCenter.x, startCenter.y, false, + .setScale(displayId, 2.0f, startCenter.x, startCenter.y, true, false, SERVICE_ID_1)); assertEquals(SERVICE_ID_1, mFullScreenMagnificationController.getIdOfLastServiceToMagnify(displayId)); assertTrue(mFullScreenMagnificationController - .setScale(displayId, 1.5f, startCenter.x, startCenter.y, false, + .setScale(displayId, 1.5f, startCenter.x, startCenter.y, true, false, SERVICE_ID_2)); assertEquals(SERVICE_ID_2, mFullScreenMagnificationController.getIdOfLastServiceToMagnify(displayId)); @@ -809,10 +814,10 @@ public class FullScreenMagnificationControllerTest { register(displayId); PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER; mFullScreenMagnificationController - .setScale(displayId, 2.0f, startCenter.x, startCenter.y, false, + .setScale(displayId, 2.0f, startCenter.x, startCenter.y, true, false, SERVICE_ID_1); mFullScreenMagnificationController - .setScale(displayId, 1.5f, startCenter.x, startCenter.y, false, + .setScale(displayId, 1.5f, startCenter.x, startCenter.y, true, false, SERVICE_ID_2); assertFalse(mFullScreenMagnificationController.resetIfNeeded(displayId, SERVICE_ID_1)); checkActivatedAndMagnifying(/* activated= */ true, /* magnifying= */ true, displayId); @@ -873,7 +878,7 @@ public class FullScreenMagnificationControllerTest { float scale = 2.5f; PointF firstCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, - scale, firstCenter.x, firstCenter.y, mAnimationCallback, SERVICE_ID_1)); + scale, firstCenter.x, firstCenter.y, true, mAnimationCallback, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); Mockito.reset(mMockValueAnimator); // Stubs the logic after the animation is started. @@ -1076,7 +1081,7 @@ public class FullScreenMagnificationControllerTest { float scale = 2.0f; // setting animate parameter to true is differ from zoomIn2xToMiddle() mFullScreenMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, - true, SERVICE_ID_1); + true, true, SERVICE_ID_1); MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId); MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId); Mockito.reset(mMockWindowManager); @@ -1107,7 +1112,7 @@ public class FullScreenMagnificationControllerTest { PointF startCenter = OTHER_BOUNDS_LOWER_RIGHT_2X_CENTER; float scale = 2.0f; mFullScreenMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, - false, SERVICE_ID_1); + true, false, SERVICE_ID_1); mMessageCapturingHandler.sendAllMessages(); MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId); verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(startSpec))); @@ -1148,7 +1153,7 @@ public class FullScreenMagnificationControllerTest { PointF startCenter = OTHER_BOUNDS_LOWER_RIGHT_2X_CENTER; float scale = 2.0f; mFullScreenMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, - true, SERVICE_ID_1); + true, true, SERVICE_ID_1); mMessageCapturingHandler.sendAllMessages(); MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId); when(mMockValueAnimator.isRunning()).thenReturn(true); @@ -1335,7 +1340,7 @@ public class FullScreenMagnificationControllerTest { scale, computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, firstCenter, scale)); assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, - scale, firstCenter.x, firstCenter.y, true, SERVICE_ID_1)); + scale, firstCenter.x, firstCenter.y, true, true, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); assertEquals(firstCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5); @@ -1404,7 +1409,7 @@ public class FullScreenMagnificationControllerTest { register(DISPLAY_0); final float scale = 1.0f; mFullScreenMagnificationController.setScaleAndCenter( - DISPLAY_0, scale, Float.NaN, Float.NaN, true, SERVICE_ID_1); + DISPLAY_0, scale, Float.NaN, Float.NaN, true, true, SERVICE_ID_1); checkActivatedAndMagnifying(/* activated= */ true, /* magnifying= */ false, DISPLAY_0); verify(mMockWindowManager).setFullscreenMagnificationActivated(DISPLAY_0, true); @@ -1449,7 +1454,7 @@ public class FullScreenMagnificationControllerTest { PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; mFullScreenMagnificationController.setScale(TEST_DISPLAY, 1.0f, pivotPoint.x, pivotPoint.y, - false, SERVICE_ID_1); + true, false, SERVICE_ID_1); mFullScreenMagnificationController.persistScale(TEST_DISPLAY); // persistScale may post a task to a background thread. Let's wait for it completes. @@ -1464,10 +1469,10 @@ public class FullScreenMagnificationControllerTest { register(DISPLAY_1); final PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; mFullScreenMagnificationController.setScale(DISPLAY_0, 3.0f, pivotPoint.x, pivotPoint.y, - false, SERVICE_ID_1); + true, false, SERVICE_ID_1); mFullScreenMagnificationController.persistScale(DISPLAY_0); mFullScreenMagnificationController.setScale(DISPLAY_1, 4.0f, pivotPoint.x, pivotPoint.y, - false, SERVICE_ID_1); + true, false, SERVICE_ID_1); mFullScreenMagnificationController.persistScale(DISPLAY_1); // persistScale may post a task to a background thread. Let's wait for it completes. @@ -1479,6 +1484,101 @@ public class FullScreenMagnificationControllerTest { } @Test + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + public void persistScale_setValue_notifyInput() { + register(TEST_DISPLAY); + + PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; + mFullScreenMagnificationController.setScale(TEST_DISPLAY, 4.0f, pivotPoint.x, pivotPoint.y, + /* isScaleTransient= */ true, /* animate= */ false, SERVICE_ID_1); + verify(mMockInputManager, never()).setAccessibilityPointerIconScaleFactor(anyInt(), + anyFloat()); + + mFullScreenMagnificationController.persistScale(TEST_DISPLAY); + + // persistScale may post a task to a background thread. Let's wait for it completes. + waitForBackgroundThread(); + Assert.assertEquals(mFullScreenMagnificationController.getPersistedScale(TEST_DISPLAY), + 4.0f); + verify(mMockInputManager).setAccessibilityPointerIconScaleFactor(TEST_DISPLAY, 4.0f); + } + + @Test + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + public void setScale_setNonTransientScale_notifyInput() { + register(TEST_DISPLAY); + + PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; + mFullScreenMagnificationController.setScale(TEST_DISPLAY, 4.0f, pivotPoint.x, pivotPoint.y, + /* isScaleTransient= */ false, /* animate= */ false, SERVICE_ID_1); + + verify(mMockInputManager).setAccessibilityPointerIconScaleFactor(TEST_DISPLAY, 4.0f); + } + + @Test + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + public void setScaleAndCenter_setTransientScale_notNotifyInput() { + register(TEST_DISPLAY); + + PointF point = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; + mFullScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, 3.0f, point.x, + point.y, /* isScaleTransient= */ true, /* animate= */ false, SERVICE_ID_1); + + verify(mRequestObserver).onFullScreenMagnificationChanged(anyInt(), any(Region.class), + any(MagnificationConfig.class)); + verify(mMockInputManager, never()).setAccessibilityPointerIconScaleFactor(anyInt(), + anyFloat()); + } + + @Test + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + public void setScaleAndCenter_setNonTransientScale_notifyInput() { + register(TEST_DISPLAY); + + PointF point = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; + mFullScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, 3.0f, point.x, + point.y, /* isScaleTransient= */ false, /* animate= */ false, SERVICE_ID_1); + + verify(mMockInputManager).setAccessibilityPointerIconScaleFactor(TEST_DISPLAY, 3.0f); + } + + @Test + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + public void setCenter_notNotifyInput() { + register(TEST_DISPLAY); + + PointF point = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; + mFullScreenMagnificationController.setScale(TEST_DISPLAY, 2.0f, point.x, point.y, + /* isScaleTransient= */ true, /* animate= */ false, SERVICE_ID_1); + mFullScreenMagnificationController.setCenter(TEST_DISPLAY, point.x, point.y, false, + SERVICE_ID_1); + + // Note that setCenter doesn't change scale, so it's not necessary to notify the input + // manager, but we currently do. The input manager skips redundant computation if the + // notified scale is the same as the previous call. + verify(mMockInputManager).setAccessibilityPointerIconScaleFactor(TEST_DISPLAY, + 2.0f); + } + + @Test + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + public void offsetMagnifiedRegion_notNotifyInput() { + register(TEST_DISPLAY); + + PointF point = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; + mFullScreenMagnificationController.setScale(TEST_DISPLAY, 2.0f, point.x, point.y, + /* isScaleTransient= */ true, /* animate= */ false, SERVICE_ID_1); + mFullScreenMagnificationController.offsetMagnifiedRegion(TEST_DISPLAY, 100, 50, + SERVICE_ID_1); + + // Note that setCenter doesn't change scale, so it's not necessary to notify the input + // manager, but we currently do. The input manager skips redundant computation if the + // notified scale is the same as the previous call. + verify(mMockInputManager).setAccessibilityPointerIconScaleFactor(TEST_DISPLAY, + 2.0f); + } + + @Test public void testOnContextChanged_alwaysOnFeatureDisabled_resetMagnification() { setScaleToMagnifying(); @@ -1535,7 +1635,7 @@ public class FullScreenMagnificationControllerTest { PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; mFullScreenMagnificationController.setScale(DISPLAY_0, scale, pivotPoint.x, pivotPoint.y, - false, SERVICE_ID_1); + true, false, SERVICE_ID_1); } private void initMockWindowManager() { @@ -1578,7 +1678,7 @@ public class FullScreenMagnificationControllerTest { PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER; float scale = 2.0f; mFullScreenMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, - false, SERVICE_ID_1); + true, false, SERVICE_ID_1); checkActivatedAndMagnifying(/* activated= */ true, /* magnifying= */ true, displayId); } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java index e5831b326de5..9f5dd93054c3 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java @@ -90,6 +90,7 @@ import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.accessibility.EventStreamTransformation; import com.android.server.accessibility.Flags; import com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback; +import com.android.server.input.InputManagerInternal; import com.android.server.testutils.OffsettableClock; import com.android.server.testutils.TestHandler; import com.android.server.wm.WindowManagerInternal; @@ -227,9 +228,11 @@ public class FullScreenMagnificationGestureHandlerTest { final FullScreenMagnificationController.ControllerContext mockController = mock(FullScreenMagnificationController.ControllerContext.class); final WindowManagerInternal mockWindowManager = mock(WindowManagerInternal.class); + final InputManagerInternal mockInputManager = mock(InputManagerInternal.class); when(mockController.getContext()).thenReturn(mContext); when(mockController.getTraceManager()).thenReturn(mMockTraceManager); when(mockController.getWindowManager()).thenReturn(mockWindowManager); + when(mockController.getInputManager()).thenReturn(mockInputManager); when(mockController.getHandler()).thenReturn(new Handler(mContext.getMainLooper())); when(mockController.newValueAnimator()).thenReturn(new ValueAnimator()); when(mockController.getAnimationDuration()).thenReturn(1000L); @@ -1343,7 +1346,7 @@ public class FullScreenMagnificationGestureHandlerTest { Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, persistedScale, UserHandle.USER_SYSTEM); mFullScreenMagnificationController.setScale(DISPLAY_0, scale, DEFAULT_X, - DEFAULT_Y, /* animate= */ false, + DEFAULT_Y, true, /* animate= */ false, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); mMgh.transitionTo(mMgh.mPanningScalingState); @@ -1364,7 +1367,7 @@ public class FullScreenMagnificationGestureHandlerTest { Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, persistedScale, UserHandle.USER_SYSTEM); mFullScreenMagnificationController.setScale(DISPLAY_0, scale, DEFAULT_X, - DEFAULT_Y, /* animate= */ false, + DEFAULT_Y, true, /* animate= */ false, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); mMgh.transitionTo(mMgh.mPanningScalingState); @@ -1401,7 +1404,7 @@ public class FullScreenMagnificationGestureHandlerTest { mFullScreenMagnificationController.getPersistedScale(DISPLAY_0); mFullScreenMagnificationController.setScale(DISPLAY_0, persistedScale, DEFAULT_X, - DEFAULT_Y, /* animate= */ false, + DEFAULT_Y, true, /* animate= */ false, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); mMgh.transitionTo(mMgh.mPanningScalingState); @@ -1438,7 +1441,7 @@ public class FullScreenMagnificationGestureHandlerTest { (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f; float scale = 5.6f; // value is unimportant but unique among tests to increase coverage. mFullScreenMagnificationController.setScaleAndCenter( - DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1); + DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1); centerX = mFullScreenMagnificationController.getCenterX(DISPLAY_0); centerY = mFullScreenMagnificationController.getCenterY(DISPLAY_0); @@ -1530,7 +1533,7 @@ public class FullScreenMagnificationGestureHandlerTest { (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f; float scale = 6.2f; // value is unimportant but unique among tests to increase coverage. mFullScreenMagnificationController.setScaleAndCenter( - DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1); + DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1); MotionEvent event = mouseEvent(centerX, centerY, ACTION_HOVER_MOVE); send(event, InputDevice.SOURCE_MOUSE); fastForward(20); @@ -1571,7 +1574,7 @@ public class FullScreenMagnificationGestureHandlerTest { (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f; float scale = 4.0f; // value is unimportant but unique among tests to increase coverage. mFullScreenMagnificationController.setScaleAndCenter( - DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1); + DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1); // HOVER_MOVE should change magnifier viewport. MotionEvent event = motionEvent(centerX + 20, centerY, ACTION_HOVER_MOVE); @@ -1615,7 +1618,7 @@ public class FullScreenMagnificationGestureHandlerTest { (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f; float scale = 5.3f; // value is unimportant but unique among tests to increase coverage. mFullScreenMagnificationController.setScaleAndCenter( - DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1); + DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1); MotionEvent event = motionEvent(centerX, centerY, ACTION_HOVER_MOVE); send(event, source); fastForward(20); @@ -1649,7 +1652,7 @@ public class FullScreenMagnificationGestureHandlerTest { (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f; float scale = 2.7f; // value is unimportant but unique among tests to increase coverage. mFullScreenMagnificationController.setScaleAndCenter( - DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1); + DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1); MotionEvent event = motionEvent(centerX, centerY, ACTION_HOVER_MOVE); send(event, source); fastForward(20); @@ -1685,7 +1688,7 @@ public class FullScreenMagnificationGestureHandlerTest { (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f; float scale = 3.8f; // value is unimportant but unique among tests to increase coverage. mFullScreenMagnificationController.setScaleAndCenter( - DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1); + DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1); centerX = mFullScreenMagnificationController.getCenterX(DISPLAY_0); centerY = mFullScreenMagnificationController.getCenterY(DISPLAY_0); @@ -1722,7 +1725,7 @@ public class FullScreenMagnificationGestureHandlerTest { (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f; float scale = 4.0f; // value is unimportant but unique among tests to increase coverage. mFullScreenMagnificationController.setScaleAndCenter( - DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1); + DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1); centerX = mFullScreenMagnificationController.getCenterX(DISPLAY_0); centerY = mFullScreenMagnificationController.getCenterY(DISPLAY_0); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java index 25281774cd95..8164ef9314d9 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java @@ -76,6 +76,7 @@ import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityManagerService; import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.accessibility.test.MessageCapturingHandler; +import com.android.server.input.InputManagerInternal; import com.android.server.wm.WindowManagerInternal; import com.android.window.flags.Flags; @@ -154,6 +155,8 @@ public class MagnificationControllerTest { private WindowManagerInternal mWindowManagerInternal; @Mock private WindowManagerInternal.AccessibilityControllerInternal mA11yController; + @Mock + private InputManagerInternal mInputManagerInternal; @Mock private DisplayManagerInternal mDisplayManagerInternal; @@ -200,6 +203,7 @@ public class MagnificationControllerTest { when(mControllerCtx.getContext()).thenReturn(mContext); when(mControllerCtx.getTraceManager()).thenReturn(mTraceManager); when(mControllerCtx.getWindowManager()).thenReturn(mWindowManagerInternal); + when(mControllerCtx.getInputManager()).thenReturn(mInputManagerInternal); when(mControllerCtx.getHandler()).thenReturn(mMessageCapturingHandler); when(mControllerCtx.getAnimationDuration()).thenReturn(1000L); when(mControllerCtx.newValueAnimator()).thenReturn(mValueAnimator); @@ -417,7 +421,7 @@ public class MagnificationControllerTest { assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY)); verify(mScreenMagnificationController, never()).setScaleAndCenter(TEST_DISPLAY, DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y, - true, MAGNIFICATION_GESTURE_HANDLER_ID); + true, true, MAGNIFICATION_GESTURE_HANDLER_ID); verify(mTransitionCallBack).onResult(TEST_DISPLAY, false); } @@ -467,7 +471,7 @@ public class MagnificationControllerTest { assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY)); verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), eq(DEFAULT_SCALE), eq(MAGNIFIED_CENTER_X), eq(MAGNIFIED_CENTER_Y), - any(MagnificationAnimationCallback.class), eq(TEST_SERVICE_ID)); + eq(false), any(MagnificationAnimationCallback.class), eq(TEST_SERVICE_ID)); } @Test @@ -484,7 +488,7 @@ public class MagnificationControllerTest { verify(mScreenMagnificationController, never()).setScaleAndCenter(anyInt(), anyFloat(), anyFloat(), anyFloat(), - anyBoolean(), anyInt()); + anyBoolean(), anyBoolean(), anyInt()); } @Test @@ -546,7 +550,7 @@ public class MagnificationControllerTest { config, animate, TEST_SERVICE_ID); verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), /* scale= */ anyFloat(), /* centerX= */ anyFloat(), /* centerY= */ anyFloat(), - mCallbackArgumentCaptor.capture(), /* id= */ anyInt()); + anyBoolean(), mCallbackArgumentCaptor.capture(), /* id= */ anyInt()); mCallbackArgumentCaptor.getValue().onResult(true); mMockConnection.invokeCallbacks(); @@ -616,7 +620,7 @@ public class MagnificationControllerTest { @Test public void magnifyThroughExternalRequest_showMagnificationButton() { mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, DEFAULT_SCALE, - MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y, false, TEST_SERVICE_ID); + MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y, true, false, TEST_SERVICE_ID); // The first time is trigger when fullscreen mode is activated. // The second time is triggered when magnification spec is changed. @@ -638,7 +642,7 @@ public class MagnificationControllerTest { mMagnificationController.onPerformScaleAction(TEST_DISPLAY, newScale, updatePersistence); verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), eq(newScale), - anyFloat(), anyFloat(), anyBoolean(), anyInt()); + anyFloat(), anyFloat(), anyBoolean(), anyBoolean(), anyInt()); verify(mScreenMagnificationController).persistScale(eq(TEST_DISPLAY)); } @@ -681,7 +685,7 @@ public class MagnificationControllerTest { final MagnificationConfig config = obtainMagnificationConfig(MODE_FULLSCREEN); mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, config.getScale(), config.getCenterX(), config.getCenterY(), - true, TEST_SERVICE_ID); + true, true, TEST_SERVICE_ID); // The notify method is triggered when setting magnification enabled. // The setScaleAndCenter call should not trigger notify method due to same scale and center. @@ -930,7 +934,7 @@ public class MagnificationControllerTest { public void onWindowModeActivated_fullScreenIsActivatedByExternal_fullScreenIsDisabled() { mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y, - true, TEST_SERVICE_ID); + true, true, TEST_SERVICE_ID); mMagnificationController.onWindowMagnificationActivationState(TEST_DISPLAY, true); @@ -1317,7 +1321,8 @@ public class MagnificationControllerTest { } if (mode == MODE_FULLSCREEN) { mScreenMagnificationController.setScaleAndCenter(displayId, DEFAULT_SCALE, centerX, - centerY, true, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); + centerY, true, true, + AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); } else { mMagnificationConnectionManager.enableWindowMagnification(displayId, DEFAULT_SCALE, centerX, centerY, null, TEST_SERVICE_ID); diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp index 6742cbe1f19a..168141bf6e7d 100644 --- a/tests/Input/Android.bp +++ b/tests/Input/Android.bp @@ -41,6 +41,7 @@ android_test { "hamcrest-library", "junit-params", "kotlin-test", + "mockito-kotlin-nodeps", "mockito-target-extended-minus-junit4", "platform-test-annotations", "platform-screenshot-diff-core", diff --git a/tests/Input/src/com/android/server/input/PointerIconCacheTest.kt b/tests/Input/src/com/android/server/input/PointerIconCacheTest.kt new file mode 100644 index 000000000000..47e7ac720a08 --- /dev/null +++ b/tests/Input/src/com/android/server/input/PointerIconCacheTest.kt @@ -0,0 +1,135 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.input + +import android.content.Context +import android.content.ContextWrapper +import android.os.Handler +import android.os.test.TestLooper +import android.platform.test.annotations.Presubmit +import android.view.Display +import android.view.PointerIcon +import androidx.test.platform.app.InstrumentationRegistry +import junit.framework.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.Mock +import org.mockito.junit.MockitoJUnit +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +/** + * Tests for {@link PointerIconCache}. + */ +@Presubmit +class PointerIconCacheTest { + + @get:Rule + val rule = MockitoJUnit.rule()!! + + @Mock + private lateinit var native: NativeInputManagerService + @Mock + private lateinit var defaultDisplay: Display + + private lateinit var context: Context + private lateinit var testLooper: TestLooper + private lateinit var cache: PointerIconCache + + @Before + fun setup() { + whenever(defaultDisplay.displayId).thenReturn(Display.DEFAULT_DISPLAY) + + context = object : ContextWrapper(InstrumentationRegistry.getInstrumentation().context) { + override fun getDisplay() = defaultDisplay + } + + testLooper = TestLooper() + cache = PointerIconCache(context, native, Handler(testLooper.looper)) + } + + @Test + fun testSetPointerScale() { + val defaultBitmap = getDefaultIcon().bitmap + cache.setPointerScale(2f) + + testLooper.dispatchAll() + verify(native).reloadPointerIcons() + + val bitmap = + cache.getLoadedPointerIcon(Display.DEFAULT_DISPLAY, PointerIcon.TYPE_ARROW).bitmap + + assertEquals(defaultBitmap.height * 2, bitmap.height) + assertEquals(defaultBitmap.width * 2, bitmap.width) + } + + @Test + fun testSetAccessibilityScaleFactor() { + val defaultBitmap = getDefaultIcon().bitmap + cache.setAccessibilityScaleFactor(Display.DEFAULT_DISPLAY, 4f) + + testLooper.dispatchAll() + verify(native).reloadPointerIcons() + + val bitmap = + cache.getLoadedPointerIcon(Display.DEFAULT_DISPLAY, PointerIcon.TYPE_ARROW).bitmap + + assertEquals(defaultBitmap.height * 4, bitmap.height) + assertEquals(defaultBitmap.width * 4, bitmap.width) + } + + @Test + fun testSetAccessibilityScaleFactorOnSecondaryDisplay() { + val defaultBitmap = getDefaultIcon().bitmap + val secondaryDisplayId = Display.DEFAULT_DISPLAY + 1 + cache.setAccessibilityScaleFactor(secondaryDisplayId, 4f) + + testLooper.dispatchAll() + verify(native).reloadPointerIcons() + + val bitmap = + cache.getLoadedPointerIcon(Display.DEFAULT_DISPLAY, PointerIcon.TYPE_ARROW).bitmap + assertEquals(defaultBitmap.height, bitmap.height) + assertEquals(defaultBitmap.width, bitmap.width) + + val bitmapSecondary = + cache.getLoadedPointerIcon(secondaryDisplayId, PointerIcon.TYPE_ARROW).bitmap + assertEquals(defaultBitmap.height * 4, bitmapSecondary.height) + assertEquals(defaultBitmap.width * 4, bitmapSecondary.width) + } + + @Test + fun testSetPointerScaleAndAccessibilityScaleFactor() { + val defaultBitmap = getDefaultIcon().bitmap + cache.setPointerScale(2f) + cache.setAccessibilityScaleFactor(Display.DEFAULT_DISPLAY, 3f) + + testLooper.dispatchAll() + verify(native, times(2)).reloadPointerIcons() + + val bitmap = + cache.getLoadedPointerIcon(Display.DEFAULT_DISPLAY, PointerIcon.TYPE_ARROW).bitmap + + assertEquals(defaultBitmap.height * 6, bitmap.height) + assertEquals(defaultBitmap.width * 6, bitmap.width) + } + + private fun getDefaultIcon() = + PointerIcon.getLoadedSystemIcon(context, PointerIcon.TYPE_ARROW, false, 1f) +} |