diff options
| author | 2018-10-30 20:44:52 +0800 | |
|---|---|---|
| committer | 2019-01-18 16:53:32 +0800 | |
| commit | 02655dc38c8fe9f29a11d4e82637f7fbffb18948 (patch) | |
| tree | 51e223b3cff68f3d6431e7ce71b67522d52da9c4 | |
| parent | 66f91878a06abf3307e275c265773732fd31f19e (diff) | |
Enable multi-display support for magnification controller
1) New api for a11y service to specify display id and return
per display magnification controller.
2) Updae MagnificationController to support multi-display.
3) Update AccessibilityController to support multi-display
magnifier.
Bug: 112273690
Test: atest MagnificationControllerTest
Test: atest MagnificationGestureHandlerTest
Test: atest CtsAccessibilityTestCases
Test: atest CtsAccessibilityServiceTestCases
Change-Id: I643a8f96e7d57f729daf8df7f26c9014b1dfd476
20 files changed, 1511 insertions, 800 deletions
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 35098a0ff5d9..cebe6e1211e0 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -37,6 +37,7 @@ import android.util.ArrayMap; import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import android.view.Display; import android.view.KeyEvent; import android.view.WindowManager; import android.view.WindowManagerImpl; @@ -382,7 +383,8 @@ public abstract class AccessibilityService extends Service { void init(int connectionId, IBinder windowToken); boolean onGesture(int gestureId); boolean onKeyEvent(KeyEvent event); - void onMagnificationChanged(@NonNull Region region, + /** Magnification changed callbacks for different displays */ + void onMagnificationChanged(int displayId, @NonNull Region region, float scale, float centerX, float centerY); void onSoftKeyboardShowModeChanged(int showMode); void onPerformGestureResult(int sequence, boolean completedSuccessfully); @@ -452,7 +454,9 @@ public abstract class AccessibilityService extends Service { private WindowManager mWindowManager; - private MagnificationController mMagnificationController; + /** List of magnification controllers, mapping from displayId -> MagnificationController. */ + private final SparseArray<MagnificationController> mMagnificationControllers = + new SparseArray<>(0); private SoftKeyboardController mSoftKeyboardController; private AccessibilityButtonController mAccessibilityButtonController; @@ -483,8 +487,10 @@ public abstract class AccessibilityService extends Service { * client code. */ private void dispatchServiceConnected() { - if (mMagnificationController != null) { - mMagnificationController.onServiceConnected(); + synchronized (mLock) { + for (int i = 0; i < mMagnificationControllers.size(); i++) { + mMagnificationControllers.valueAt(i).onServiceConnectedLocked(); + } } if (mSoftKeyboardController != null) { mSoftKeyboardController.onServiceConnected(); @@ -652,11 +658,34 @@ public abstract class AccessibilityService extends Service { */ @NonNull public final MagnificationController getMagnificationController() { + return getMagnificationController(Display.DEFAULT_DISPLAY); + } + + /** + * Returns the magnification controller of specified logical display, which may be used to + * query and modify the state of display magnification. + * <p> + * <strong>Note:</strong> In order to control magnification, your service + * must declare the capability by setting the + * {@link android.R.styleable#AccessibilityService_canControlMagnification} + * property in its meta-data. For more information, see + * {@link #SERVICE_META_DATA}. + * + * @param displayId The logic display id, use {@link Display#DEFAULT_DISPLAY} for + * default display. + * @return the magnification controller + * + * @hide + */ + @NonNull + public final MagnificationController getMagnificationController(int displayId) { synchronized (mLock) { - if (mMagnificationController == null) { - mMagnificationController = new MagnificationController(this, mLock); + MagnificationController controller = mMagnificationControllers.get(displayId); + if (controller == null) { + controller = new MagnificationController(this, mLock, displayId); + mMagnificationControllers.put(displayId, controller); } - return mMagnificationController; + return controller; } } @@ -765,11 +794,14 @@ public abstract class AccessibilityService extends Service { } } - private void onMagnificationChanged(@NonNull Region region, float scale, + private void onMagnificationChanged(int displayId, @NonNull Region region, float scale, float centerX, float centerY) { - if (mMagnificationController != null) { - mMagnificationController.dispatchMagnificationChanged( - region, scale, centerX, centerY); + MagnificationController controller; + synchronized (mLock) { + controller = mMagnificationControllers.get(displayId); + } + if (controller != null) { + controller.dispatchMagnificationChanged(region, scale, centerX, centerY); } } @@ -794,6 +826,7 @@ public abstract class AccessibilityService extends Service { */ public static final class MagnificationController { private final AccessibilityService mService; + private final int mDisplayId; /** * Map of listeners to their handlers. Lazily created when adding the @@ -802,19 +835,19 @@ public abstract class AccessibilityService extends Service { private ArrayMap<OnMagnificationChangedListener, Handler> mListeners; private final Object mLock; - MagnificationController(@NonNull AccessibilityService service, @NonNull Object lock) { + MagnificationController(@NonNull AccessibilityService service, @NonNull Object lock, + int displayId) { mService = service; mLock = lock; + mDisplayId = displayId; } /** * Called when the service is connected. */ - void onServiceConnected() { - synchronized (mLock) { - if (mListeners != null && !mListeners.isEmpty()) { - setMagnificationCallbackEnabled(true); - } + void onServiceConnectedLocked() { + if (mListeners != null && !mListeners.isEmpty()) { + setMagnificationCallbackEnabled(true); } } @@ -891,7 +924,7 @@ public abstract class AccessibilityService extends Service { mService.mConnectionId); if (connection != null) { try { - connection.setMagnificationCallbackEnabled(enabled); + connection.setMagnificationCallbackEnabled(mDisplayId, enabled); } catch (RemoteException re) { throw new RuntimeException(re); } @@ -952,7 +985,7 @@ public abstract class AccessibilityService extends Service { mService.mConnectionId); if (connection != null) { try { - return connection.getMagnificationScale(); + return connection.getMagnificationScale(mDisplayId); } catch (RemoteException re) { Log.w(LOG_TAG, "Failed to obtain scale", re); re.rethrowFromSystemServer(); @@ -981,7 +1014,7 @@ public abstract class AccessibilityService extends Service { mService.mConnectionId); if (connection != null) { try { - return connection.getMagnificationCenterX(); + return connection.getMagnificationCenterX(mDisplayId); } catch (RemoteException re) { Log.w(LOG_TAG, "Failed to obtain center X", re); re.rethrowFromSystemServer(); @@ -1010,7 +1043,7 @@ public abstract class AccessibilityService extends Service { mService.mConnectionId); if (connection != null) { try { - return connection.getMagnificationCenterY(); + return connection.getMagnificationCenterY(mDisplayId); } catch (RemoteException re) { Log.w(LOG_TAG, "Failed to obtain center Y", re); re.rethrowFromSystemServer(); @@ -1044,7 +1077,7 @@ public abstract class AccessibilityService extends Service { mService.mConnectionId); if (connection != null) { try { - return connection.getMagnificationRegion(); + return connection.getMagnificationRegion(mDisplayId); } catch (RemoteException re) { Log.w(LOG_TAG, "Failed to obtain magnified region", re); re.rethrowFromSystemServer(); @@ -1073,7 +1106,7 @@ public abstract class AccessibilityService extends Service { mService.mConnectionId); if (connection != null) { try { - return connection.resetMagnification(animate); + return connection.resetMagnification(mDisplayId, animate); } catch (RemoteException re) { Log.w(LOG_TAG, "Failed to reset", re); re.rethrowFromSystemServer(); @@ -1101,7 +1134,7 @@ public abstract class AccessibilityService extends Service { mService.mConnectionId); if (connection != null) { try { - return connection.setMagnificationScaleAndCenter( + return connection.setMagnificationScaleAndCenter(mDisplayId, scale, Float.NaN, Float.NaN, animate); } catch (RemoteException re) { Log.w(LOG_TAG, "Failed to set scale", re); @@ -1133,7 +1166,7 @@ public abstract class AccessibilityService extends Service { mService.mConnectionId); if (connection != null) { try { - return connection.setMagnificationScaleAndCenter( + return connection.setMagnificationScaleAndCenter(mDisplayId, Float.NaN, centerX, centerY, animate); } catch (RemoteException re) { Log.w(LOG_TAG, "Failed to set center", re); @@ -1624,9 +1657,10 @@ public abstract class AccessibilityService extends Service { } @Override - public void onMagnificationChanged(@NonNull Region region, + public void onMagnificationChanged(int displayId, @NonNull Region region, float scale, float centerX, float centerY) { - AccessibilityService.this.onMagnificationChanged(region, scale, centerX, centerY); + AccessibilityService.this.onMagnificationChanged(displayId, region, scale, + centerX, centerY); } @Override @@ -1729,13 +1763,15 @@ public abstract class AccessibilityService extends Service { mCaller.sendMessage(message); } - public void onMagnificationChanged(@NonNull Region region, + /** Magnification changed callbacks for different displays */ + public void onMagnificationChanged(int displayId, @NonNull Region region, float scale, float centerX, float centerY) { final SomeArgs args = SomeArgs.obtain(); args.arg1 = region; args.arg2 = scale; args.arg3 = centerX; args.arg4 = centerY; + args.argi1 = displayId; final Message message = mCaller.obtainMessageO(DO_ON_MAGNIFICATION_CHANGED, args); mCaller.sendMessage(message); @@ -1865,7 +1901,10 @@ public abstract class AccessibilityService extends Service { final float scale = (float) args.arg2; final float centerX = (float) args.arg3; final float centerY = (float) args.arg4; - mCallback.onMagnificationChanged(region, scale, centerX, centerY); + final int displayId = args.argi1; + args.recycle(); + mCallback.onMagnificationChanged(displayId, region, scale, + centerX, centerY); } } return; diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl index 4e96b8f11628..1dae4fca111e 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl @@ -41,7 +41,7 @@ import android.view.KeyEvent; void onKeyEvent(in KeyEvent event, int sequence); - void onMagnificationChanged(in Region region, float scale, float centerX, float centerY); + void onMagnificationChanged(int displayId, in Region region, float scale, float centerX, float centerY); void onSoftKeyboardShowModeChanged(int showMode); diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index 276131f9d0d6..8c38fe4aaf6f 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -70,20 +70,20 @@ interface IAccessibilityServiceConnection { oneway void setOnKeyEventResult(boolean handled, int sequence); - float getMagnificationScale(); + float getMagnificationScale(int displayId); - float getMagnificationCenterX(); + float getMagnificationCenterX(int displayId); - float getMagnificationCenterY(); + float getMagnificationCenterY(int displayId); - Region getMagnificationRegion(); + Region getMagnificationRegion(int displayId); - boolean resetMagnification(boolean animate); + boolean resetMagnification(int displayId, boolean animate); - boolean setMagnificationScaleAndCenter(float scale, float centerX, float centerY, + boolean setMagnificationScaleAndCenter(int displayId, float scale, float centerX, float centerY, boolean animate); - void setMagnificationCallbackEnabled(boolean enabled); + void setMagnificationCallbackEnabled(int displayId, boolean enabled); boolean setSoftKeyboardShowMode(int showMode); diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java index 3f9627ed807c..a021e3cb2d78 100644 --- a/core/java/android/app/UiAutomation.java +++ b/core/java/android/app/UiAutomation.java @@ -1240,7 +1240,7 @@ public final class UiAutomation { } @Override - public void onMagnificationChanged(@NonNull Region region, + public void onMagnificationChanged(int displayId, @NonNull Region region, float scale, float centerX, float centerY) { /* do nothing */ } diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java index 0dd768530603..683d16b1dea1 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java @@ -88,32 +88,32 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon public void setOnKeyEventResult(boolean handled, int sequence) {} - public float getMagnificationScale() { + public float getMagnificationScale(int displayId) { return 0.0f; } - public float getMagnificationCenterX() { + public float getMagnificationCenterX(int displayId) { return 0.0f; } - public float getMagnificationCenterY() { + public float getMagnificationCenterY(int displayId) { return 0.0f; } - public Region getMagnificationRegion() { + public Region getMagnificationRegion(int displayId) { return null; } - public boolean resetMagnification(boolean animate) { + public boolean resetMagnification(int displayId, boolean animate) { return false; } - public boolean setMagnificationScaleAndCenter(float scale, float centerX, float centerY, - boolean animate) { + public boolean setMagnificationScaleAndCenter(int displayId, float scale, float centerX, + float centerY, boolean animate) { return false; } - public void setMagnificationCallbackEnabled(boolean enabled) {} + public void setMagnificationCallbackEnabled(int displayId, boolean enabled) {} public boolean setSoftKeyboardShowMode(int showMode) { return false; diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index 6eba914f15bc..2c075dc809d0 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -53,6 +53,7 @@ import android.view.accessibility.AccessibilityWindowInfo; import android.view.accessibility.IAccessibilityInteractionConnection; import android.view.accessibility.IAccessibilityInteractionConnectionCallback; +import com.android.internal.annotations.GuardedBy; import com.android.internal.os.SomeArgs; import com.android.internal.util.DumpUtils; import com.android.server.accessibility.AccessibilityManagerService.RemoteAccessibilityConnection; @@ -751,7 +752,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } @Override - public float getMagnificationScale() { + public float getMagnificationScale(int displayId) { synchronized (mLock) { if (!isCalledForCurrentUserLocked()) { return 1.0f; @@ -759,14 +760,14 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } final long identity = Binder.clearCallingIdentity(); try { - return mSystemSupport.getMagnificationController().getScale(); + return mSystemSupport.getMagnificationController().getScale(displayId); } finally { Binder.restoreCallingIdentity(identity); } } @Override - public Region getMagnificationRegion() { + public Region getMagnificationRegion(int displayId) { synchronized (mLock) { final Region region = Region.obtain(); if (!isCalledForCurrentUserLocked()) { @@ -775,22 +776,22 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ MagnificationController magnificationController = mSystemSupport.getMagnificationController(); boolean registeredJustForThisCall = - registerMagnificationIfNeeded(magnificationController); + registerMagnificationIfNeeded(displayId, magnificationController); final long identity = Binder.clearCallingIdentity(); try { - magnificationController.getMagnificationRegion(region); + magnificationController.getMagnificationRegion(displayId, region); return region; } finally { Binder.restoreCallingIdentity(identity); if (registeredJustForThisCall) { - magnificationController.unregister(); + magnificationController.unregister(displayId); } } } } @Override - public float getMagnificationCenterX() { + public float getMagnificationCenterX(int displayId) { synchronized (mLock) { if (!isCalledForCurrentUserLocked()) { return 0.0f; @@ -798,21 +799,21 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ MagnificationController magnificationController = mSystemSupport.getMagnificationController(); boolean registeredJustForThisCall = - registerMagnificationIfNeeded(magnificationController); + registerMagnificationIfNeeded(displayId, magnificationController); final long identity = Binder.clearCallingIdentity(); try { - return magnificationController.getCenterX(); + return magnificationController.getCenterX(displayId); } finally { Binder.restoreCallingIdentity(identity); if (registeredJustForThisCall) { - magnificationController.unregister(); + magnificationController.unregister(displayId); } } } } @Override - public float getMagnificationCenterY() { + public float getMagnificationCenterY(int displayId) { synchronized (mLock) { if (!isCalledForCurrentUserLocked()) { return 0.0f; @@ -820,31 +821,31 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ MagnificationController magnificationController = mSystemSupport.getMagnificationController(); boolean registeredJustForThisCall = - registerMagnificationIfNeeded(magnificationController); + registerMagnificationIfNeeded(displayId, magnificationController); final long identity = Binder.clearCallingIdentity(); try { - return magnificationController.getCenterY(); + return magnificationController.getCenterY(displayId); } finally { Binder.restoreCallingIdentity(identity); if (registeredJustForThisCall) { - magnificationController.unregister(); + magnificationController.unregister(displayId); } } } } - private boolean registerMagnificationIfNeeded( + private boolean registerMagnificationIfNeeded(int displayId, MagnificationController magnificationController) { - if (!magnificationController.isRegisteredLocked() + if (!magnificationController.isRegistered(displayId) && mSecurityPolicy.canControlMagnification(this)) { - magnificationController.register(); + magnificationController.register(displayId); return true; } return false; } @Override - public boolean resetMagnification(boolean animate) { + public boolean resetMagnification(int displayId, boolean animate) { synchronized (mLock) { if (!isCalledForCurrentUserLocked()) { return false; @@ -857,16 +858,16 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ try { MagnificationController magnificationController = mSystemSupport.getMagnificationController(); - return (magnificationController.reset(animate) - || !magnificationController.isMagnifying()); + return (magnificationController.reset(displayId, animate) + || !magnificationController.isMagnifying(displayId)); } finally { Binder.restoreCallingIdentity(identity); } } @Override - public boolean setMagnificationScaleAndCenter(float scale, float centerX, float centerY, - boolean animate) { + public boolean setMagnificationScaleAndCenter(int displayId, float scale, float centerX, + float centerY, boolean animate) { synchronized (mLock) { if (!isCalledForCurrentUserLocked()) { return false; @@ -878,11 +879,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ try { MagnificationController magnificationController = mSystemSupport.getMagnificationController(); - if (!magnificationController.isRegisteredLocked()) { - magnificationController.register(); + if (!magnificationController.isRegistered(displayId)) { + magnificationController.register(displayId); } return magnificationController - .setScaleAndCenter(scale, centerX, centerY, animate, mId); + .setScaleAndCenter(displayId, scale, centerX, centerY, animate, mId); } finally { Binder.restoreCallingIdentity(identity); } @@ -890,12 +891,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } @Override - public void setMagnificationCallbackEnabled(boolean enabled) { - mInvocationHandler.setMagnificationCallbackEnabled(enabled); + public void setMagnificationCallbackEnabled(int displayId, boolean enabled) { + mInvocationHandler.setMagnificationCallbackEnabled(displayId, enabled); } - public boolean isMagnificationCallbackEnabled() { - return mInvocationHandler.mIsMagnificationCallbackEnabled; + public boolean isMagnificationCallbackEnabled(int displayId) { + return mInvocationHandler.isMagnificationCallbackEnabled(displayId); } @Override @@ -1106,10 +1107,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ InvocationHandler.MSG_CLEAR_ACCESSIBILITY_CACHE); } - public void notifyMagnificationChangedLocked(@NonNull Region region, + public void notifyMagnificationChangedLocked(int displayId, @NonNull Region region, float scale, float centerX, float centerY) { mInvocationHandler - .notifyMagnificationChangedLocked(region, scale, centerX, centerY); + .notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY); } public void notifySoftKeyboardShowModeChangedLocked(int showState) { @@ -1128,12 +1129,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ * Called by the invocation handler to notify the service that the * state of magnification has changed. */ - private void notifyMagnificationChangedInternal(@NonNull Region region, + private void notifyMagnificationChangedInternal(int displayId, @NonNull Region region, float scale, float centerX, float centerY) { final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); if (listener != null) { try { - listener.onMagnificationChanged(region, scale, centerX, centerY); + listener.onMagnificationChanged(displayId, region, scale, centerX, centerY); } catch (RemoteException re) { Slog.e(LOG_TAG, "Error sending magnification changes to " + mService, re); } @@ -1251,7 +1252,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ private static final int MSG_ON_ACCESSIBILITY_BUTTON_CLICKED = 7; private static final int MSG_ON_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED = 8; - private boolean mIsMagnificationCallbackEnabled = false; + /** List of magnification callback states, mapping from displayId -> Boolean */ + @GuardedBy("mlock") + private final SparseArray<Boolean> mMagnificationCallbackState = new SparseArray<>(0); private boolean mIsSoftKeyboardCallbackEnabled = false; public InvocationHandler(Looper looper) { @@ -1277,7 +1280,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final float scale = (float) args.arg2; final float centerX = (float) args.arg3; final float centerY = (float) args.arg4; - notifyMagnificationChangedInternal(region, scale, centerX, centerY); + final int displayId = args.argi1; + notifyMagnificationChangedInternal(displayId, region, scale, centerX, centerY); args.recycle(); } break; @@ -1301,11 +1305,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } } - public void notifyMagnificationChangedLocked(@NonNull Region region, float scale, - float centerX, float centerY) { - if (!mIsMagnificationCallbackEnabled) { - // Callback is disabled, don't bother packing args. - return; + public void notifyMagnificationChangedLocked(int displayId, @NonNull Region region, + float scale, float centerX, float centerY) { + synchronized (mLock) { + if (mMagnificationCallbackState.get(displayId) == null) { + return; + } } final SomeArgs args = SomeArgs.obtain(); @@ -1313,13 +1318,26 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ args.arg2 = scale; args.arg3 = centerX; args.arg4 = centerY; + args.argi1 = displayId; final Message msg = obtainMessage(MSG_ON_MAGNIFICATION_CHANGED, args); msg.sendToTarget(); } - public void setMagnificationCallbackEnabled(boolean enabled) { - mIsMagnificationCallbackEnabled = enabled; + public void setMagnificationCallbackEnabled(int displayId, boolean enabled) { + synchronized (mLock) { + if (enabled) { + mMagnificationCallbackState.put(displayId, true); + } else { + mMagnificationCallbackState.remove(displayId); + } + } + } + + public boolean isMagnificationCallbackEnabled(int displayId) { + synchronized (mLock) { + return mMagnificationCallbackState.get(displayId) != null; + } } public void notifySoftKeyboardShowModeChangedLocked(int showState) { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 36ca52a6e600..faf8e2efd086 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -16,6 +16,11 @@ package com.android.server.accessibility; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD; import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED; @@ -23,13 +28,9 @@ import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBIL import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK; + import static com.android.internal.util.FunctionalUtils.ignoreRemoteException; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; -import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO; -import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN; -import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD; -import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE; -import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN; import android.Manifest; import android.accessibilityservice.AccessibilityService; @@ -121,6 +122,8 @@ import com.android.server.SystemService; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; +import libcore.util.EmptyArray; + import org.xmlpull.v1.XmlPullParserException; import java.io.FileDescriptor; @@ -139,8 +142,6 @@ import java.util.Set; import java.util.function.Consumer; import java.util.function.IntSupplier; -import libcore.util.EmptyArray; - /** * This class is instantiated by the system as a system level service and can be * accessed only by the system. The task of this service is to be a centralized @@ -207,6 +208,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final WindowManagerInternal mWindowManagerService; + private final DisplayManager mDisplayManager; + private AppWidgetManagerInternal mAppWidgetService; private final SecurityPolicy mSecurityPolicy; @@ -303,10 +306,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); mMainHandler = new MainHandler(mContext.getMainLooper()); mGlobalActionPerformer = new GlobalActionPerformer(mContext, mWindowManagerService); + mDisplayManager = mContext.getSystemService(DisplayManager.class); registerBroadcastReceivers(); new AccessibilityContentObserver(mMainHandler).register( context.getContentResolver()); + registerDisplayListener(mMainHandler); } @Override @@ -522,6 +527,30 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub }, UserHandle.ALL, intentFilter, null, null); } + private void registerDisplayListener(Handler handler) { + mDisplayManager.registerDisplayListener(new DisplayManager.DisplayListener() { + @Override + public void onDisplayAdded(int displayId) { + synchronized (mLock) { + UserState userState = getCurrentUserStateLocked(); + updateMagnificationLocked(userState); + } + } + + @Override + public void onDisplayRemoved(int displayId) { + if (mMagnificationController != null) { + mMagnificationController.onDisplayRemoved(displayId); + } + } + + @Override + public void onDisplayChanged(int displayId) { + // do nothing + } + }, handler); + } + @Override public long addClient(IAccessibilityManagerClient callback, int userId) { synchronized (mLock) { @@ -967,17 +996,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * Called by the MagnificationController when the state of display * magnification changes. * + * @param displayId The logical display id. * @param region the new magnified region, may be empty if * magnification is not enabled (e.g. scale is 1) * @param scale the new scale * @param centerX the new screen-relative center X coordinate * @param centerY the new screen-relative center Y coordinate */ - public void notifyMagnificationChanged(@NonNull Region region, + public void notifyMagnificationChanged(int displayId, @NonNull Region region, float scale, float centerX, float centerY) { synchronized (mLock) { notifyClearAccessibilityCacheLocked(); - notifyMagnificationChangedLocked(region, scale, centerX, centerY); + notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY); } } @@ -1202,12 +1232,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private void notifyMagnificationChangedLocked(@NonNull Region region, + private void notifyMagnificationChangedLocked(int displayId, @NonNull Region region, float scale, float centerX, float centerY) { final UserState state = getCurrentUserStateLocked(); for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { final AccessibilityServiceConnection service = state.mBoundServices.get(i); - service.notifyMagnificationChangedLocked(region, scale, centerX, centerY); + service.notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY); } } @@ -2200,15 +2230,44 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return; } - if (!mUiAutomationManager.suppressingAccessibilityServicesLocked() - && (userState.mIsDisplayMagnificationEnabled - || userState.mIsNavBarMagnificationEnabled - || userHasListeningMagnificationServicesLocked(userState))) { - // Initialize the magnification controller if necessary - getMagnificationController(); - mMagnificationController.register(); - } else if (mMagnificationController != null) { - mMagnificationController.unregister(); + if (mUiAutomationManager.suppressingAccessibilityServicesLocked() + && mMagnificationController != null) { + mMagnificationController.unregisterAll(); + return; + } + + // register all display if global magnification is enabled. + final Display[] displays = mDisplayManager.getDisplays(); + if (userState.mIsDisplayMagnificationEnabled + || userState.mIsNavBarMagnificationEnabled) { + for (int i = 0; i < displays.length; i++) { + final Display display = displays[i]; + // Overlay display uses overlay window to simulate secondary displays in + // one display. It's not a real display and there's no input events for it. + // We should ignore it. + if (display.getType() == Display.TYPE_OVERLAY) { + continue; + } + getMagnificationController().register(display.getDisplayId()); + } + return; + } + + // register if display has listening magnification services. + for (int i = 0; i < displays.length; i++) { + final Display display = displays[i]; + // Overlay display uses overlay window to simulate secondary displays in + // one display. It's not a real display and there's no input events for it. + // We should ignore it. + if (display.getType() == Display.TYPE_OVERLAY) { + continue; + } + final int displayId = display.getDisplayId(); + if (userHasListeningMagnificationServicesLocked(userState, displayId)) { + getMagnificationController().register(displayId); + } else if (mMagnificationController != null) { + mMagnificationController.unregister(displayId); + } } } @@ -2231,12 +2290,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * Returns whether the specified user has any services that are capable of * controlling magnification and are actively listening for magnification updates. */ - private boolean userHasListeningMagnificationServicesLocked(UserState userState) { + private boolean userHasListeningMagnificationServicesLocked(UserState userState, + int displayId) { final List<AccessibilityServiceConnection> services = userState.mBoundServices; for (int i = 0, count = services.size(); i < count; i++) { final AccessibilityServiceConnection service = services.get(i); if (mSecurityPolicy.canControlMagnification(service) - && service.isMagnificationCallbackEnabled()) { + && service.isMagnificationCallbackEnabled(displayId)) { return true; } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java index 86132a8e6473..a19a84724d45 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java @@ -109,7 +109,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect UserState userState = mUserStateWeakReference.get(); if (userState == null) return; userState.removeServiceLocked(this); - mSystemSupport.getMagnificationController().resetIfNeeded(mId); + mSystemSupport.getMagnificationController().resetAllIfNeeded(mId); resetLocked(); } @@ -256,7 +256,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect userState.serviceDisconnectedLocked(this); } resetLocked(); - mSystemSupport.getMagnificationController().resetIfNeeded(mId); + mSystemSupport.getMagnificationController().resetAllIfNeeded(mId); mSystemSupport.onClientChangeLocked(false); } } diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java index 6a97fbbf30da..e784056d9e35 100644 --- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java @@ -19,7 +19,6 @@ package com.android.server.accessibility; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.content.BroadcastReceiver; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -32,6 +31,7 @@ import android.provider.Settings; import android.text.TextUtils; import android.util.MathUtils; import android.util.Slog; +import android.util.SparseArray; import android.view.Display; import android.view.MagnificationSpec; import android.view.View; @@ -39,6 +39,7 @@ import android.view.animation.DecelerateInterpolator; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.LocalServices; import com.android.server.wm.WindowManagerInternal; @@ -68,9 +69,7 @@ public class MagnificationController { private final Object mLock; - private final AccessibilityManagerService mAms; - - private final SettingsBridge mSettingsBridge; + private final ControllerContext mControllerCtx; private final ScreenStateObserver mScreenStateObserver; @@ -78,11 +77,9 @@ public class MagnificationController { private final long mMainThreadId; - private Handler mHandler; - - private final WindowManagerInternal mWindowManager; - - private final DisplayMagnification mDisplay; + /** List of display Magnification, mapping from displayId -> DisplayMagnification. */ + @GuardedBy("mLock") + private final SparseArray<DisplayMagnification> mDisplays = new SparseArray<>(0); /** * This class implements {@link WindowManagerInternal.MagnificationCallbacks} and holds @@ -107,46 +104,82 @@ public class MagnificationController { // Flag indicating that we are registered with window manager. private boolean mRegistered; private boolean mUnregisterPending; + private boolean mDeleteAfterUnregister; private final int mDisplayId; private static final int INVALID_ID = -1; private int mIdOfLastServiceToMagnify = INVALID_ID; - - DisplayMagnification(int displayId, SpecAnimationBridge specAnimation) { + DisplayMagnification(int displayId) { mDisplayId = displayId; - mSpecAnimationBridge = specAnimation; + mSpecAnimationBridge = new SpecAnimationBridge(mControllerCtx, mLock, mDisplayId); } - void register() { - synchronized (mLock) { - if (!mRegistered) { - mWindowManager.setMagnificationCallbacks(this); - mSpecAnimationBridge.setEnabled(true); - // Obtain initial state. - mWindowManager.getMagnificationRegion(mMagnificationRegion); - mMagnificationRegion.getBounds(mMagnificationBounds); - mRegistered = true; - } + /** + * Registers magnification callback and get current magnification region from + * window manager. + * + * @return true if callback registers successful. + */ + @GuardedBy("mLock") + boolean register() { + mRegistered = mControllerCtx.getWindowManager().setMagnificationCallbacks( + mDisplayId, this); + if (!mRegistered) { + Slog.w(LOG_TAG, "set magnification callbacks fail, displayId:" + mDisplayId); + return false; } + mSpecAnimationBridge.setEnabled(true); + // Obtain initial state. + mControllerCtx.getWindowManager().getMagnificationRegion( + mDisplayId, mMagnificationRegion); + mMagnificationRegion.getBounds(mMagnificationBounds); + return true; } - void unregister() { - synchronized (mLock) { - if (!isMagnifying()) { - unregisterInternalLocked(); - } else { - mUnregisterPending = true; - reset(true); - } + /** + * Unregisters magnification callback from window manager. Callbacks to + * {@link MagnificationController#unregisterCallbackLocked(int, boolean)} after + * unregistered. + * + * @param delete true if this instance should be removed from the SparseArray in + * MagnificationController after unregistered, for example, display removed. + */ + @GuardedBy("mLock") + void unregister(boolean delete) { + if (mRegistered) { + mSpecAnimationBridge.setEnabled(false); + mControllerCtx.getWindowManager().setMagnificationCallbacks( + mDisplayId, null); + mMagnificationRegion.setEmpty(); + mRegistered = false; + unregisterCallbackLocked(mDisplayId, delete); } + mUnregisterPending = false; } - boolean isRegisteredLocked() { + /** + * Reset magnification status with animation enabled. {@link #unregister(boolean)} will be + * called after animation finished. + * + * @param delete true if this instance should be removed from the SparseArray in + * MagnificationController after unregistered, for example, display removed. + */ + @GuardedBy("mLock") + void unregisterPending(boolean delete) { + mDeleteAfterUnregister = delete; + mUnregisterPending = true; + reset(true); + } + + boolean isRegistered() { return mRegistered; } + boolean isMagnifying() { + return mCurrentMagnificationSpec.scale > 1.0f; + } float getScale() { return mCurrentMagnificationSpec.scale; @@ -156,18 +189,20 @@ public class MagnificationController { return mCurrentMagnificationSpec.offsetX; } + float getOffsetY() { + return mCurrentMagnificationSpec.offsetY; + } + + @GuardedBy("mLock") float getCenterX() { - synchronized (mLock) { - return (mMagnificationBounds.width() / 2.0f - + mMagnificationBounds.left - getOffsetX()) / getScale(); - } + return (mMagnificationBounds.width() / 2.0f + + mMagnificationBounds.left - getOffsetX()) / getScale(); } + @GuardedBy("mLock") float getCenterY() { - synchronized (mLock) { - return (mMagnificationBounds.height() / 2.0f - + mMagnificationBounds.top - getOffsetY()) / getScale(); - } + return (mMagnificationBounds.height() / 2.0f + + mMagnificationBounds.top - getOffsetY()) / getScale(); } /** @@ -203,64 +238,35 @@ public class MagnificationController { return mSpecAnimationBridge.mSentMagnificationSpec.offsetY; } - boolean resetIfNeeded(boolean animate) { - synchronized (mLock) { - if (isMagnifying()) { - reset(animate); - return true; - } - return false; - } - } - - float getOffsetY() { - return mCurrentMagnificationSpec.offsetY; - } - - boolean isMagnifying() { - return mCurrentMagnificationSpec.scale > 1.0f; - } - - void unregisterInternalLocked() { - if (mRegistered) { - mSpecAnimationBridge.setEnabled(false); - mWindowManager.setMagnificationCallbacks(null); - mMagnificationRegion.setEmpty(); - - mRegistered = false; - } - mUnregisterPending = false; - } - - @Override public void onMagnificationRegionChanged(Region magnificationRegion) { final Message m = PooledLambda.obtainMessage( - DisplayMagnification.this::updateMagnificationRegion, + DisplayMagnification::updateMagnificationRegion, this, Region.obtain(magnificationRegion)); - mHandler.sendMessage(m); + mControllerCtx.getHandler().sendMessage(m); } @Override public void onRectangleOnScreenRequested(int left, int top, int right, int bottom) { final Message m = PooledLambda.obtainMessage( - DisplayMagnification.this::requestRectangleOnScreen, left, top, right, bottom); - mHandler.sendMessage(m); + DisplayMagnification::requestRectangleOnScreen, this, + left, top, right, bottom); + mControllerCtx.getHandler().sendMessage(m); } @Override public void onRotationChanged(int rotation) { // Treat as context change and reset - final Message m = PooledLambda.obtainMessage(DisplayMagnification.this::resetIfNeeded, - true); - mHandler.sendMessage(m); + final Message m = PooledLambda.obtainMessage(MagnificationController::resetIfNeeded, + MagnificationController.this, mDisplayId, true); + mControllerCtx.getHandler().sendMessage(m); } @Override public void onUserContextChanged() { - final Message m = PooledLambda.obtainMessage(DisplayMagnification.this::resetIfNeeded, - true); - mHandler.sendMessage(m); + final Message m = PooledLambda.obtainMessage(MagnificationController::resetIfNeeded, + MagnificationController.this, mDisplayId, true); + mControllerCtx.getHandler().sendMessage(m); } /** @@ -298,8 +304,9 @@ public class MagnificationController { mSpecAnimationBridge.updateSentSpecMainThread(spec, animate); } else { final Message m = PooledLambda.obtainMessage( - this.mSpecAnimationBridge::updateSentSpecMainThread, spec, animate); - mHandler.sendMessage(m); + SpecAnimationBridge::updateSentSpecMainThread, + mSpecAnimationBridge, spec, animate); + mControllerCtx.getHandler().sendMessage(m); } } @@ -313,30 +320,26 @@ public class MagnificationController { } void onMagnificationChangedLocked() { - mAms.notifyMagnificationChanged(mMagnificationRegion, + mControllerCtx.getAms().notifyMagnificationChanged(mDisplayId, mMagnificationRegion, getScale(), getCenterX(), getCenterY()); if (mUnregisterPending && !isMagnifying()) { - unregisterInternalLocked(); + unregister(mDeleteAfterUnregister); } } + @GuardedBy("mLock") boolean magnificationRegionContains(float x, float y) { - synchronized (mLock) { - return mMagnificationRegion.contains((int) x, (int) y); - - } + return mMagnificationRegion.contains((int) x, (int) y); } + @GuardedBy("mLock") void getMagnificationBounds(@NonNull Rect outBounds) { - synchronized (mLock) { - outBounds.set(mMagnificationBounds); - } + outBounds.set(mMagnificationBounds); } + @GuardedBy("mLock") void getMagnificationRegion(@NonNull Region outRegion) { - synchronized (mLock) { - outRegion.set(mMagnificationRegion); - } + outRegion.set(mMagnificationRegion); } void requestRectangleOnScreen(int left, int top, int right, int bottom) { @@ -392,94 +395,76 @@ public class MagnificationController { outFrame.scale(1.0f / scale); } - /** - * Resets magnification if last magnifying service is disabled. - * - * @param connectionId the connection ID be disabled. - * @return {@code true} on success, {@code false} on failure - */ - boolean resetIfNeeded(int connectionId) { - if (mIdOfLastServiceToMagnify == connectionId) { - return resetIfNeeded(true /*animate*/); - } - return false; - } - + @GuardedBy("mLock") void setForceShowMagnifiableBounds(boolean show) { if (mRegistered) { - mWindowManager.setForceShowMagnifiableBounds(show); + mControllerCtx.getWindowManager().setForceShowMagnifiableBounds( + mDisplayId, show); } } + @GuardedBy("mLock") boolean reset(boolean animate) { - synchronized (mLock) { - if (!mRegistered) { - return false; - } - final MagnificationSpec spec = mCurrentMagnificationSpec; - final boolean changed = !spec.isNop(); - if (changed) { - spec.clear(); - onMagnificationChangedLocked(); - } - mIdOfLastServiceToMagnify = INVALID_ID; - sendSpecToAnimation(spec, animate); - return changed; + if (!mRegistered) { + return false; } + final MagnificationSpec spec = mCurrentMagnificationSpec; + final boolean changed = !spec.isNop(); + if (changed) { + spec.clear(); + onMagnificationChangedLocked(); + } + mIdOfLastServiceToMagnify = INVALID_ID; + sendSpecToAnimation(spec, animate); + return changed; } - + @GuardedBy("mLock") boolean setScale(float scale, float pivotX, float pivotY, boolean animate, int id) { - - synchronized (mLock) { - if (!mRegistered) { - return false; - } - // Constrain scale immediately for use in the pivot calculations. - scale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE); - - final Rect viewport = mTempRect; - mMagnificationRegion.getBounds(viewport); - final MagnificationSpec spec = mCurrentMagnificationSpec; - final float oldScale = spec.scale; - final float oldCenterX - = (viewport.width() / 2.0f - spec.offsetX + viewport.left) / oldScale; - final float oldCenterY - = (viewport.height() / 2.0f - spec.offsetY + viewport.top) / oldScale; - final float normPivotX = (pivotX - spec.offsetX) / oldScale; - final float normPivotY = (pivotY - spec.offsetY) / oldScale; - final float offsetX = (oldCenterX - normPivotX) * (oldScale / scale); - final float offsetY = (oldCenterY - normPivotY) * (oldScale / scale); - final float centerX = normPivotX + offsetX; - final float centerY = normPivotY + offsetY; - mIdOfLastServiceToMagnify = id; - - return setScaleAndCenter(scale, centerX, centerY, animate, id); + if (!mRegistered) { + return false; } + // Constrain scale immediately for use in the pivot calculations. + scale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE); + + final Rect viewport = mTempRect; + mMagnificationRegion.getBounds(viewport); + final MagnificationSpec spec = mCurrentMagnificationSpec; + final float oldScale = spec.scale; + final float oldCenterX = + (viewport.width() / 2.0f - spec.offsetX + viewport.left) / oldScale; + final float oldCenterY = + (viewport.height() / 2.0f - spec.offsetY + viewport.top) / oldScale; + final float normPivotX = (pivotX - spec.offsetX) / oldScale; + final float normPivotY = (pivotY - spec.offsetY) / oldScale; + final float offsetX = (oldCenterX - normPivotX) * (oldScale / scale); + final float offsetY = (oldCenterY - normPivotY) * (oldScale / scale); + final float centerX = normPivotX + offsetX; + final float centerY = normPivotY + offsetY; + mIdOfLastServiceToMagnify = id; + return setScaleAndCenter(scale, centerX, centerY, animate, id); } + @GuardedBy("mLock") boolean setScaleAndCenter(float scale, float centerX, float centerY, boolean animate, int id) { - - synchronized (mLock) { - if (!mRegistered) { - return false; - } - if (DEBUG) { - Slog.i(LOG_TAG, - "setScaleAndCenterLocked(scale = " + scale + ", centerX = " + centerX - + ", centerY = " + centerY + ", animate = " + animate - + ", id = " + id - + ")"); - } - final boolean changed = updateMagnificationSpecLocked(scale, centerX, centerY); - sendSpecToAnimation(mCurrentMagnificationSpec, animate); - if (isMagnifying() && (id != INVALID_ID)) { - mIdOfLastServiceToMagnify = id; - } - return changed; + if (!mRegistered) { + return false; + } + if (DEBUG) { + Slog.i(LOG_TAG, + "setScaleAndCenterLocked(scale = " + scale + ", centerX = " + centerX + + ", centerY = " + centerY + ", animate = " + animate + + ", id = " + id + + ")"); } + final boolean changed = updateMagnificationSpecLocked(scale, centerX, centerY); + sendSpecToAnimation(mCurrentMagnificationSpec, animate); + if (isMagnifying() && (id != INVALID_ID)) { + mIdOfLastServiceToMagnify = id; + } + return changed; } /** @@ -527,22 +512,21 @@ public class MagnificationController { return changed; } + @GuardedBy("mLock") void offsetMagnifiedRegion(float offsetX, float offsetY, int id) { - synchronized (mLock) { - if (!mRegistered) { - return; - } + if (!mRegistered) { + return; + } - final float nonNormOffsetX = mCurrentMagnificationSpec.offsetX - offsetX; - final float nonNormOffsetY = mCurrentMagnificationSpec.offsetY - offsetY; - if (updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY)) { - onMagnificationChangedLocked(); - } - if (id != INVALID_ID) { - mIdOfLastServiceToMagnify = id; - } - sendSpecToAnimation(mCurrentMagnificationSpec, false); + final float nonNormOffsetX = mCurrentMagnificationSpec.offsetX - offsetX; + final float nonNormOffsetY = mCurrentMagnificationSpec.offsetY - offsetY; + if (updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY)) { + onMagnificationChangedLocked(); } + if (id != INVALID_ID) { + mIdOfLastServiceToMagnify = id; + } + sendSpecToAnimation(mCurrentMagnificationSpec, false); } boolean updateCurrentSpecWithOffsetsLocked(float nonNormOffsetX, float nonNormOffsetY) { @@ -593,44 +577,38 @@ public class MagnificationController { @Override public String toString() { - return "DisplayMagnification{" + - "mCurrentMagnificationSpec=" + mCurrentMagnificationSpec + - ", mMagnificationRegion=" + mMagnificationRegion + - ", mMagnificationBounds=" + mMagnificationBounds + - ", mDisplayId=" + mDisplayId + - ", mUserId=" + mUserId + - ", mIdOfLastServiceToMagnify=" + mIdOfLastServiceToMagnify + - ", mRegistered=" + mRegistered + - ", mUnregisterPending=" + mUnregisterPending + - '}'; + return "DisplayMagnification[" + + "mCurrentMagnificationSpec=" + mCurrentMagnificationSpec + + ", mMagnificationRegion=" + mMagnificationRegion + + ", mMagnificationBounds=" + mMagnificationBounds + + ", mDisplayId=" + mDisplayId + + ", mIdOfLastServiceToMagnify=" + mIdOfLastServiceToMagnify + + ", mRegistered=" + mRegistered + + ", mUnregisterPending=" + mUnregisterPending + + ']'; } - } - public MagnificationController(Context context, AccessibilityManagerService ams, Object lock) { - this(context, ams, lock, null, LocalServices.getService(WindowManagerInternal.class), - new ValueAnimator(), new SettingsBridge(context.getContentResolver())); - mHandler = new Handler(context.getMainLooper()); + /** + * MagnificationController Constructor + */ + public MagnificationController(@NonNull Context context, + @NonNull AccessibilityManagerService ams, @NonNull Object lock) { + this(new ControllerContext(context, ams, + LocalServices.getService(WindowManagerInternal.class), + new Handler(context.getMainLooper()), + context.getResources().getInteger(R.integer.config_longAnimTime)), lock); } - public MagnificationController( - Context context, - AccessibilityManagerService ams, - Object lock, - Handler handler, - WindowManagerInternal windowManagerInternal, - ValueAnimator valueAnimator, - SettingsBridge settingsBridge) { - mHandler = handler; - mWindowManager = windowManagerInternal; - mMainThreadId = context.getMainLooper().getThread().getId(); - mAms = ams; - mScreenStateObserver = new ScreenStateObserver(context, this); + /** + * Constructor for tests + */ + @VisibleForTesting + public MagnificationController(@NonNull ControllerContext ctx, @NonNull Object lock) { + mControllerCtx = ctx; mLock = lock; - mSettingsBridge = settingsBridge; - //TODO (multidisplay): Magnification is supported only for the default display. - mDisplay = new DisplayMagnification(Display.DEFAULT_DISPLAY, - new SpecAnimationBridge(context, mLock, mWindowManager, valueAnimator)); + mMainThreadId = mControllerCtx.getContext().getMainLooper().getThread().getId(); + mScreenStateObserver = new ScreenStateObserver(mControllerCtx.getContext(), this); } /** @@ -639,54 +617,114 @@ public class MagnificationController { * * This tracking imposes a cost on the system, so we avoid tracking this data unless it's * required. + * + * @param displayId The logical display id. */ - public void register() { + public void register(int displayId) { synchronized (mLock) { - mScreenStateObserver.register(); + DisplayMagnification display = mDisplays.get(displayId); + if (display == null) { + display = new DisplayMagnification(displayId); + } + if (display.isRegistered()) { + return; + } + if (display.register()) { + mDisplays.put(displayId, display); + mScreenStateObserver.registerIfNecessary(); + } } - mDisplay.register(); } /** * Stop requiring tracking the magnification region. We may remain registered while we * reset magnification. + * + * @param displayId The logical display id. */ - public void unregister() { + public void unregister(int displayId) { synchronized (mLock) { - mScreenStateObserver.unregister(); + unregisterLocked(displayId, false); } - mDisplay.unregister(); } - + /** - * Check if we are registered. Note that we may be planning to unregister at any moment. + * Stop tracking all displays' magnification region. + */ + public void unregisterAll() { + synchronized (mLock) { + // display will be removed from array after unregister, we need to clone it to + // prevent error. + final SparseArray<DisplayMagnification> displays = mDisplays.clone(); + for (int i = 0; i < displays.size(); i++) { + unregisterLocked(displays.keyAt(i), false); + } + } + } + + /** + * Remove the display magnification with given id. + * + * @param displayId The logical display id. + */ + public void onDisplayRemoved(int displayId) { + synchronized (mLock) { + unregisterLocked(displayId, true); + } + } + + /** + * Check if we are registered on specified display. Note that we may be planning to unregister + * at any moment. + * + * @return {@code true} if the controller is registered on specified display. + * {@code false} otherwise. * - * @return {@code true} if the controller is registered. {@code false} otherwise. + * @param displayId The logical display id. */ - public boolean isRegisteredLocked() { - return mDisplay.isRegisteredLocked(); + public boolean isRegistered(int displayId) { + synchronized (mLock) { + final DisplayMagnification display = mDisplays.get(displayId); + if (display == null) { + return false; + } + return display.isRegistered(); + } } /** + * @param displayId The logical display id. * @return {@code true} if magnification is active, e.g. the scale * is > 1, {@code false} otherwise */ - public boolean isMagnifying() { - return mDisplay.isMagnifying(); + public boolean isMagnifying(int displayId) { + synchronized (mLock) { + final DisplayMagnification display = mDisplays.get(displayId); + if (display == null) { + return false; + } + return display.isMagnifying(); + } } /** * Returns whether the magnification region contains the specified * screen-relative coordinates. * + * @param displayId The logical display id. * @param x the screen-relative X coordinate to check * @param y the screen-relative Y coordinate to check * @return {@code true} if the coordinate is contained within the * magnified region, or {@code false} otherwise */ - public boolean magnificationRegionContains(float x, float y) { - return mDisplay.magnificationRegionContains(x, y); - + public boolean magnificationRegionContains(int displayId, float x, float y) { + synchronized (mLock) { + final DisplayMagnification display = mDisplays.get(displayId); + if (display == null) { + return false; + } + return display.magnificationRegionContains(x, y); + } } /** @@ -694,11 +732,18 @@ public class MagnificationController { * magnification region. If magnification is not enabled, the returned * bounds will be empty. * + * @param displayId The logical display id. * @param outBounds rect to populate with the bounds of the magnified * region */ - public void getMagnificationBounds(@NonNull Rect outBounds) { - mDisplay.getMagnificationBounds(outBounds); + public void getMagnificationBounds(int displayId, @NonNull Rect outBounds) { + synchronized (mLock) { + final DisplayMagnification display = mDisplays.get(displayId); + if (display == null) { + return; + } + display.getMagnificationBounds(outBounds); + } } /** @@ -706,76 +751,122 @@ public class MagnificationController { * region. If magnification is not enabled, then the returned region * will be empty. * + * @param displayId The logical display id. * @param outRegion the region to populate */ - public void getMagnificationRegion(@NonNull Region outRegion) { - mDisplay.getMagnificationRegion(outRegion); + public void getMagnificationRegion(int displayId, @NonNull Region outRegion) { + synchronized (mLock) { + final DisplayMagnification display = mDisplays.get(displayId); + if (display == null) { + return; + } + display.getMagnificationRegion(outRegion); + } } /** * Returns the magnification scale. If an animation is in progress, * this reflects the end state of the animation. * + * @param displayId The logical display id. * @return the scale */ - public float getScale() { - return mDisplay.getScale(); + public float getScale(int displayId) { + synchronized (mLock) { + final DisplayMagnification display = mDisplays.get(displayId); + if (display == null) { + return 1.0f; + } + return display.getScale(); + } } /** * Returns the X offset of the magnification viewport. If an animation * is in progress, this reflects the end state of the animation. * + * @param displayId The logical display id. * @return the X offset */ - public float getOffsetX() { - return mDisplay.getOffsetX(); + public float getOffsetX(int displayId) { + synchronized (mLock) { + final DisplayMagnification display = mDisplays.get(displayId); + if (display == null) { + return 0.0f; + } + return display.getOffsetX(); + } } - /** * Returns the screen-relative X coordinate of the center of the * magnification viewport. * + * @param displayId The logical display id. * @return the X coordinate */ - public float getCenterX() { - return mDisplay.getCenterX(); + public float getCenterX(int displayId) { + synchronized (mLock) { + final DisplayMagnification display = mDisplays.get(displayId); + if (display == null) { + return 0.0f; + } + return display.getCenterX(); + } } /** * Returns the Y offset of the magnification viewport. If an animation * is in progress, this reflects the end state of the animation. * + * @param displayId The logical display id. * @return the Y offset */ - public float getOffsetY() { - return mDisplay.getOffsetY(); + public float getOffsetY(int displayId) { + synchronized (mLock) { + final DisplayMagnification display = mDisplays.get(displayId); + if (display == null) { + return 0.0f; + } + return display.getOffsetY(); + } } /** * Returns the screen-relative Y coordinate of the center of the * magnification viewport. * + * @param displayId The logical display id. * @return the Y coordinate */ - public float getCenterY() { - return mDisplay.getCenterY(); + public float getCenterY(int displayId) { + synchronized (mLock) { + final DisplayMagnification display = mDisplays.get(displayId); + if (display == null) { + return 0.0f; + } + return display.getCenterY(); + } } /** * Resets the magnification scale and center, optionally animating the * transition. * + * @param displayId The logical display id. * @param animate {@code true} to animate the transition, {@code false} * to transition immediately * @return {@code true} if the magnification spec changed, {@code false} if * the spec did not change */ - public boolean reset(boolean animate) { - - return mDisplay.reset(animate); - + public boolean reset(int displayId, boolean animate) { + synchronized (mLock) { + final DisplayMagnification display = mDisplays.get(displayId); + if (display == null) { + return false; + } + return display.reset(animate); + } } /** @@ -783,6 +874,7 @@ public class MagnificationController { * optionally animating the transition. If animation is disabled, the * transition is immediate. * + * @param displayId The logical display id. * @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 @@ -792,15 +884,22 @@ public class MagnificationController { * @return {@code true} if the magnification spec changed, {@code false} if * the spec did not change */ - public boolean setScale(float scale, float pivotX, float pivotY, boolean animate, int id) { - return mDisplay. - setScale(scale, pivotX, pivotY, animate, id); + public boolean setScale(int displayId, float scale, float pivotX, float pivotY, + 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); + } } /** * Sets the center of the magnified region, optionally animating the * transition. If animation is disabled, the transition is immediate. * + * @param displayId The logical display id. * @param centerX the screen-relative X coordinate around which to * center * @param centerY the screen-relative Y coordinate around which to @@ -811,9 +910,14 @@ public class MagnificationController { * @return {@code true} if the magnification spec changed, {@code false} if * the spec did not change */ - public boolean setCenter(float centerX, float centerY, boolean animate, int id) { - return mDisplay. - setScaleAndCenter(Float.NaN, centerX, centerY, animate, id); + public boolean setCenter(int displayId, float centerX, float centerY, boolean animate, int id) { + synchronized (mLock) { + final DisplayMagnification display = mDisplays.get(displayId); + if (display == null) { + return false; + } + return display.setScaleAndCenter(Float.NaN, centerX, centerY, animate, id); + } } /** @@ -821,6 +925,7 @@ public class MagnificationController { * 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 @@ -832,53 +937,66 @@ public class MagnificationController { * @return {@code true} if the magnification spec changed, {@code false} if * the spec did not change */ - public boolean setScaleAndCenter( - float scale, float centerX, float centerY, boolean animate, int id) { - return mDisplay. - setScaleAndCenter(scale, centerX, centerY, animate, id); + public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY, + boolean animate, int id) { + synchronized (mLock) { + final DisplayMagnification display = mDisplays.get(displayId); + if (display == null) { + return false; + } + return display.setScaleAndCenter(scale, centerX, centerY, animate, id); + } } /** * Offsets the magnified region. Note that the offsetX and offsetY values actually move in the * opposite direction as the offsets passed in here. * + * @param displayId The logical display id. * @param offsetX the amount in pixels to offset the region in the X direction, in current * screen pixels. * @param offsetY the amount in pixels to offset the region in the Y direction, in current * screen pixels. * @param id the ID of the service requesting the change */ - public void offsetMagnifiedRegion(float offsetX, float offsetY, int id) { - mDisplay.offsetMagnifiedRegion(offsetX, offsetY, - id); + public void offsetMagnifiedRegion(int displayId, float offsetX, float offsetY, int id) { + synchronized (mLock) { + final DisplayMagnification display = mDisplays.get(displayId); + if (display == null) { + return; + } + display.offsetMagnifiedRegion(offsetX, offsetY, id); + } } /** * Get the ID of the last service that changed the magnification spec. * + * @param displayId The logical display id. * @return The id */ - public int getIdOfLastServiceToMagnify() { - return mDisplay.getIdOfLastServiceToMagnify(); + public int getIdOfLastServiceToMagnify(int displayId) { + synchronized (mLock) { + final DisplayMagnification display = mDisplays.get(displayId); + if (display == null) { + return -1; + } + return display.getIdOfLastServiceToMagnify(); + } } /** - * Persists the current magnification scale to the current user's settings. + * Persists the default display magnification scale to the current user's settings. */ public void persistScale() { - persistScale(Display.DEFAULT_DISPLAY); - } - /** - * Persists the current magnification scale to the current user's settings. - */ - public void persistScale(int displayId) { - final float scale = mDisplay.getScale(); + // TODO: b/123047354, Need support multi-display? + final float scale = getScale(Display.DEFAULT_DISPLAY); final int userId = mUserId; new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { - mSettingsBridge.putMagnificationScale(scale, displayId, userId); + mControllerCtx.putMagnificationScale(scale, userId); return null; } }.execute(); @@ -892,7 +1010,7 @@ public class MagnificationController { * scale if none is available */ public float getPersistedScale() { - return mSettingsBridge.getMagnificationScale(Display.DEFAULT_DISPLAY, mUserId); + return mControllerCtx.getMagnificationScale(mUserId); } /** @@ -901,50 +1019,136 @@ public class MagnificationController { * @param userId the currently active user ID */ public void setUserId(int userId) { - if (mUserId != userId) { - mUserId = userId; + if (mUserId == userId) { + return; + } + mUserId = userId; + resetAllIfNeeded(false); + } - synchronized (mLock) { - if (isMagnifying()) { - reset(false); - } + /** + * Resets all displays' magnification if last magnifying service is disabled. + * + * @param connectionId + */ + public void resetAllIfNeeded(int connectionId) { + synchronized (mLock) { + for (int i = 0; i < mDisplays.size(); i++) { + resetIfNeeded(mDisplays.keyAt(i), connectionId); } } } - /** + /** * Resets magnification if magnification and auto-update are both enabled. * + * @param displayId The logical display id. * @param animate whether the animate the transition - * @return whether was {@link #isMagnifying magnifying} + * @return whether was {@link #isMagnifying(int) magnifying} */ - public boolean resetIfNeeded(boolean animate) { - return mDisplay.resetIfNeeded(animate); + boolean resetIfNeeded(int displayId, boolean animate) { + synchronized (mLock) { + final DisplayMagnification display = mDisplays.get(displayId); + if (display == null || !display.isMagnifying()) { + return false; + } + display.reset(animate); + return true; + } } /** * Resets magnification if last magnifying service is disabled. * + * @param displayId The logical display id. * @param connectionId the connection ID be disabled. * @return {@code true} on success, {@code false} on failure */ - public boolean resetIfNeeded(int connectionId) { - return mDisplay.resetIfNeeded(connectionId); + boolean resetIfNeeded(int displayId, int connectionId) { + synchronized (mLock) { + final DisplayMagnification display = mDisplays.get(displayId); + if (display == null || !display.isMagnifying() + || connectionId != display.getIdOfLastServiceToMagnify()) { + return false; + } + display.reset(true); + return true; + } } - void setForceShowMagnifiableBounds(boolean show) { - mDisplay.setForceShowMagnifiableBounds(show); + void setForceShowMagnifiableBounds(int displayId, boolean show) { + synchronized (mLock) { + final DisplayMagnification display = mDisplays.get(displayId); + if (display == null) { + return; + } + display.setForceShowMagnifiableBounds(show); + } } private void onScreenTurnedOff() { final Message m = PooledLambda.obtainMessage( - mDisplay::resetIfNeeded, false); - mHandler.sendMessage(m); + MagnificationController::resetAllIfNeeded, this, false); + mControllerCtx.getHandler().sendMessage(m); + } + + private void resetAllIfNeeded(boolean animate) { + synchronized (mLock) { + for (int i = 0; i < mDisplays.size(); i++) { + resetIfNeeded(mDisplays.keyAt(i), animate); + } + } + } + + private void unregisterLocked(int displayId, boolean delete) { + final DisplayMagnification display = mDisplays.get(displayId); + if (display == null) { + return; + } + if (!display.isRegistered()) { + if (delete) { + mDisplays.remove(displayId); + } + return; + } + if (!display.isMagnifying()) { + display.unregister(delete); + } else { + display.unregisterPending(delete); + } + } + + /** + * Callbacks from DisplayMagnification after display magnification unregistered. It will remove + * DisplayMagnification instance if delete is true, and unregister screen state if + * there is no registered display magnification. + */ + private void unregisterCallbackLocked(int displayId, boolean delete) { + if (delete) { + mDisplays.remove(displayId); + } + // unregister screen state if necessary + boolean hasRegister = false; + for (int i = 0; i < mDisplays.size(); i++) { + final DisplayMagnification display = mDisplays.valueAt(i); + hasRegister = display.isRegistered(); + if (hasRegister) { + break; + } + } + if (!hasRegister) { + mScreenStateObserver.unregister(); + } } @Override public String toString() { - return mDisplay.toString(); + StringBuilder builder = new StringBuilder(); + builder.append("MagnificationController["); + builder.append("mUserId=").append(mUserId); + builder.append(", mDisplays=").append(mDisplays); + builder.append("]"); + return builder.toString(); } /** @@ -952,7 +1156,7 @@ public class MagnificationController { * updates to the window manager. */ private static class SpecAnimationBridge implements ValueAnimator.AnimatorUpdateListener { - private final WindowManagerInternal mWindowManager; + private final ControllerContext mControllerCtx; /** * The magnification spec that was sent to the window manager. This should @@ -973,16 +1177,17 @@ public class MagnificationController { private final Object mLock; + private final int mDisplayId; + @GuardedBy("mLock") private boolean mEnabled = false; - private SpecAnimationBridge(Context context, Object lock, WindowManagerInternal wm, - ValueAnimator animator) { + private SpecAnimationBridge(ControllerContext ctx, Object lock, int displayId) { + mControllerCtx = ctx; mLock = lock; - mWindowManager = wm; - final long animationDuration = context.getResources().getInteger( - R.integer.config_longAnimTime); - mValueAnimator = animator; + mDisplayId = displayId; + final long animationDuration = mControllerCtx.getAnimationDuration(); + mValueAnimator = mControllerCtx.newValueAnimator(); mValueAnimator.setDuration(animationDuration); mValueAnimator.setInterpolator(new DecelerateInterpolator(2.5f)); mValueAnimator.setFloatValues(0.0f, 1.0f); @@ -999,7 +1204,8 @@ public class MagnificationController { mEnabled = enabled; if (!mEnabled) { mSentMagnificationSpec.clear(); - mWindowManager.setMagnificationSpec(mSentMagnificationSpec); + mControllerCtx.getWindowManager().setMagnificationSpec( + mDisplayId, mSentMagnificationSpec); } } } @@ -1031,7 +1237,8 @@ public class MagnificationController { } mSentMagnificationSpec.setTo(spec); - mWindowManager.setMagnificationSpec(spec); + mControllerCtx.getWindowManager().setMagnificationSpec( + mDisplayId, mSentMagnificationSpec); } } @@ -1054,9 +1261,7 @@ public class MagnificationController { mTmpMagnificationSpec.offsetY = mStartMagnificationSpec.offsetY + (mEndMagnificationSpec.offsetY - mStartMagnificationSpec.offsetY) * fract; - synchronized (mLock) { - setMagnificationSpecLocked(mTmpMagnificationSpec); - } + setMagnificationSpecLocked(mTmpMagnificationSpec); } } } @@ -1072,7 +1277,7 @@ public class MagnificationController { mController = controller; } - public void register() { + public void registerIfNecessary() { if (!mRegistered) { mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_SCREEN_OFF)); mRegistered = true; @@ -1092,26 +1297,97 @@ public class MagnificationController { } } - // Extra class to get settings so tests can mock it - public static class SettingsBridge { - private final ContentResolver mContentResolver; + /** + * This class holds resources used between the classes in MagnificationController, and + * functions for tests to mock it. + */ + @VisibleForTesting + public static class ControllerContext { + private final Context mContext; + private final AccessibilityManagerService mAms; + private final WindowManagerInternal mWindowManager; + private final Handler mHandler; + private final Long mAnimationDuration; + + /** + * Constructor for ControllerContext. + */ + public ControllerContext(@NonNull Context context, + @NonNull AccessibilityManagerService ams, + @NonNull WindowManagerInternal windowManager, + @NonNull Handler handler, + long animationDuration) { + mContext = context; + mAms = ams; + mWindowManager = windowManager; + mHandler = handler; + mAnimationDuration = animationDuration; + } + + /** + * @return A context. + */ + @NonNull + public Context getContext() { + return mContext; + } + + /** + * @return AccessibilityManagerService + */ + @NonNull + public AccessibilityManagerService getAms() { + return mAms; + } + + /** + * @return WindowManagerInternal + */ + @NonNull + public WindowManagerInternal getWindowManager() { + return mWindowManager; + } - public SettingsBridge(ContentResolver contentResolver) { - mContentResolver = contentResolver; + /** + * @return Handler for main looper + */ + @NonNull + public Handler getHandler() { + return mHandler; } - public void putMagnificationScale(float value, int displayId, int userId) { - Settings.Secure.putFloatForUser(mContentResolver, - Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE + ( - Display.DEFAULT_DISPLAY == displayId ? "" : displayId), - value, userId); + /** + * Create a new ValueAnimator. + * + * @return ValueAnimator + */ + @NonNull + public ValueAnimator newValueAnimator() { + return new ValueAnimator(); } - public float getMagnificationScale(int displayId, int userId) { - return Settings.Secure.getFloatForUser(mContentResolver, - Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE - + (Display.DEFAULT_DISPLAY == displayId ? "" : displayId), + /** + * Write Settings of magnification scale. + */ + public void putMagnificationScale(float value, int userId) { + Settings.Secure.putFloatForUser(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, value, userId); + } + + /** + * Get Settings of magnification scale. + */ + public float getMagnificationScale(int userId) { + return Settings.Secure.getFloatForUser(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, DEFAULT_MAGNIFICATION_SCALE, userId); } + + /** + * @return Configuration of animation duration. + */ + public long getAnimationDuration() { + return mAnimationDuration; + } } } diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java index 80049e80e1a9..35abfd4c843e 100644 --- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java @@ -43,6 +43,7 @@ import android.util.Log; import android.util.MathUtils; import android.util.Slog; import android.util.TypedValue; +import android.view.Display; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.MotionEvent; @@ -251,14 +252,16 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { mScreenStateReceiver.unregister(); } // Check if need to reset when MagnificationGestureHandler is the last magnifying service. - mMagnificationController.resetIfNeeded( + mMagnificationController.resetAllIfNeeded( AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); clearAndTransitionToStateDetecting(); } void notifyShortcutTriggered() { if (mDetectShortcutTrigger) { - boolean wasMagnifying = mMagnificationController.resetIfNeeded(/* animate */ true); + // TODO: multi-display support for magnification gesture handler + boolean wasMagnifying = mMagnificationController.resetIfNeeded(Display.DEFAULT_DISPLAY, + /* animate */ true); if (wasMagnifying) { clearAndTransitionToStateDetecting(); } else { @@ -419,8 +422,9 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { Slog.i(LOG_TAG, "Panned content by scrollX: " + distanceX + " scrollY: " + distanceY); } - mMagnificationController.offsetMagnifiedRegion(distanceX, distanceY, - AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); + // TODO: multi-display support for magnification gesture handler + mMagnificationController.offsetMagnifiedRegion(Display.DEFAULT_DISPLAY, distanceX, + distanceY, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); return /* event consumed: */ true; } @@ -436,7 +440,8 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { return mScaling; } - final float initialScale = mMagnificationController.getScale(); + // TODO: multi-display support for magnification gesture handler + final float initialScale = mMagnificationController.getScale(Display.DEFAULT_DISPLAY); final float targetScale = initialScale * detector.getScaleFactor(); // Don't allow a gesture to move the user further outside the @@ -458,7 +463,8 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { final float pivotX = detector.getFocusX(); final float pivotY = detector.getFocusY(); if (DEBUG_PANNING_SCALING) Slog.i(LOG_TAG, "Scaled content to: " + scale + "x"); - mMagnificationController.setScale(scale, pivotX, pivotY, false, + // TODO: multi-display support for magnification gesture handler + mMagnificationController.setScale(Display.DEFAULT_DISPLAY, scale, pivotX, pivotY, false, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); return /* handled: */ true; } @@ -518,8 +524,10 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { } final float eventX = event.getX(); final float eventY = event.getY(); - if (mMagnificationController.magnificationRegionContains(eventX, eventY)) { - mMagnificationController.setCenter(eventX, eventY, + // TODO: multi-display support for magnification gesture handler + if (mMagnificationController.magnificationRegionContains( + Display.DEFAULT_DISPLAY, eventX, eventY)) { + mMagnificationController.setCenter(Display.DEFAULT_DISPLAY, eventX, eventY, /* animate */ mLastMoveOutsideMagnifiedRegion, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); mLastMoveOutsideMagnifiedRegion = false; @@ -657,8 +665,9 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE); + // TODO: multi-display support for magnification gesture handler if (!mMagnificationController.magnificationRegionContains( - event.getX(), event.getY())) { + Display.DEFAULT_DISPLAY, event.getX(), event.getY())) { transitionToDelegatingStateAndClear(); @@ -671,7 +680,8 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { // If magnified, delay an ACTION_DOWN for mMultiTapMaxDelay // to ensure reachability of // STATE_PANNING_SCALING(triggerable with ACTION_POINTER_DOWN) - || mMagnificationController.isMagnifying()) { + // TODO: multi-display support for magnification gesture handler + || mMagnificationController.isMagnifying(Display.DEFAULT_DISPLAY)) { afterMultiTapTimeoutTransitionToDelegatingState(); @@ -683,7 +693,8 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { } break; case ACTION_POINTER_DOWN: { - if (mMagnificationController.isMagnifying()) { + // TODO: multi-display support for magnification gesture handler + if (mMagnificationController.isMagnifying(Display.DEFAULT_DISPLAY)) { transitionTo(mPanningScalingState); clear(); } else { @@ -712,8 +723,9 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { mHandler.removeMessages(MESSAGE_ON_TRIPLE_TAP_AND_HOLD); + // TODO: multi-display support for magnification gesture handler if (!mMagnificationController.magnificationRegionContains( - event.getX(), event.getY())) { + Display.DEFAULT_DISPLAY, event.getX(), event.getY())) { transitionToDelegatingStateAndClear(); @@ -864,7 +876,8 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { clear(); // Toggle zoom - if (mMagnificationController.isMagnifying()) { + // TODO: multi-display support for magnification gesture handler + if (mMagnificationController.isMagnifying(Display.DEFAULT_DISPLAY)) { zoomOff(); } else { zoomOn(up.getX(), up.getY()); @@ -876,8 +889,9 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { if (DEBUG_DETECTING) Slog.i(LOG_TAG, "onTripleTapAndHold()"); clear(); + // TODO: multi-display support for magnification gesture handler mViewportDraggingState.mZoomedInBeforeDrag = - mMagnificationController.isMagnifying(); + mMagnificationController.isMagnifying(Display.DEFAULT_DISPLAY); zoomOn(down.getX(), down.getY()); @@ -904,7 +918,8 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { if (DEBUG_DETECTING) Slog.i(LOG_TAG, "setShortcutTriggered(" + state + ")"); mShortcutTriggered = state; - mMagnificationController.setForceShowMagnifiableBounds(state); + // TODO: multi-display support for magnification gesture handler + mMagnificationController.setForceShowMagnifiableBounds(Display.DEFAULT_DISPLAY, state); } } @@ -914,7 +929,8 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { final float scale = MathUtils.constrain( mMagnificationController.getPersistedScale(), MIN_SCALE, MAX_SCALE); - mMagnificationController.setScaleAndCenter( + // TODO: multi-display support for magnification gesture handler + mMagnificationController.setScaleAndCenter(Display.DEFAULT_DISPLAY, scale, centerX, centerY, /* animate */ true, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); @@ -922,8 +938,8 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { private void zoomOff() { if (DEBUG_DETECTING) Slog.i(LOG_TAG, "zoomOff()"); - - mMagnificationController.reset(/* animate */ true); + // TODO: multi-display support for magnification gesture handler + mMagnificationController.reset(Display.DEFAULT_DISPLAY, /* animate */ true); } private static MotionEvent recycleAndNullify(@Nullable MotionEvent event) { diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index caebf1528270..545b69b2fd54 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -49,6 +49,7 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.TypedValue; +import android.view.Display; import android.view.MagnificationSpec; import android.view.Surface; import android.view.Surface.OutOfResourcesException; @@ -83,23 +84,36 @@ final class AccessibilityController { mService = service; } - private DisplayMagnifier mDisplayMagnifier; + private SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>(); private WindowsForAccessibilityObserver mWindowsForAccessibilityObserver; - public void setMagnificationCallbacksLocked(MagnificationCallbacks callbacks) { + public boolean setMagnificationCallbacksLocked(int displayId, + MagnificationCallbacks callbacks) { + boolean result = false; if (callbacks != null) { - if (mDisplayMagnifier != null) { + if (mDisplayMagnifiers.get(displayId) != null) { throw new IllegalStateException("Magnification callbacks already set!"); } - mDisplayMagnifier = new DisplayMagnifier(mService, callbacks); + final DisplayContent dc = mService.mRoot.getDisplayContent(displayId); + if (dc != null) { + final Display display = dc.getDisplay(); + if (display != null && display.getType() != Display.TYPE_OVERLAY) { + mDisplayMagnifiers.put(displayId, new DisplayMagnifier( + mService, dc, display, callbacks)); + result = true; + } + } } else { - if (mDisplayMagnifier == null) { + final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); + if (displayMagnifier == null) { throw new IllegalStateException("Magnification callbacks already cleared!"); } - mDisplayMagnifier.destroyLocked(); - mDisplayMagnifier = null; + displayMagnifier.destroyLocked(); + mDisplayMagnifiers.remove(displayId); + result = true; } + return result; } public void setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback) { @@ -129,58 +143,72 @@ final class AccessibilityController { } } - public void setMagnificationSpecLocked(MagnificationSpec spec) { - if (mDisplayMagnifier != null) { - mDisplayMagnifier.setMagnificationSpecLocked(spec); + public void setMagnificationSpecLocked(int displayId, MagnificationSpec spec) { + final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); + if (displayMagnifier != null) { + displayMagnifier.setMagnificationSpecLocked(spec); } - if (mWindowsForAccessibilityObserver != null) { + // TODO: support multi-display for windows observer + if (mWindowsForAccessibilityObserver != null && displayId == Display.DEFAULT_DISPLAY) { mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); } } - public void getMagnificationRegionLocked(Region outMagnificationRegion) { - if (mDisplayMagnifier != null) { - mDisplayMagnifier.getMagnificationRegionLocked(outMagnificationRegion); + public void getMagnificationRegionLocked(int displayId, Region outMagnificationRegion) { + final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); + if (displayMagnifier != null) { + displayMagnifier.getMagnificationRegionLocked(outMagnificationRegion); } } - public void onRectangleOnScreenRequestedLocked(Rect rectangle) { - if (mDisplayMagnifier != null) { - mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle); + public void onRectangleOnScreenRequestedLocked(int displayId, Rect rectangle) { + final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); + if (displayMagnifier != null) { + displayMagnifier.onRectangleOnScreenRequestedLocked(rectangle); } // Not relevant for the window observer. } - public void onWindowLayersChangedLocked() { - if (mDisplayMagnifier != null) { - mDisplayMagnifier.onWindowLayersChangedLocked(); + public void onWindowLayersChangedLocked(int displayId) { + final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); + if (displayMagnifier != null) { + displayMagnifier.onWindowLayersChangedLocked(); } - if (mWindowsForAccessibilityObserver != null) { + // TODO: support multi-display for windows observer + if (mWindowsForAccessibilityObserver != null && displayId == Display.DEFAULT_DISPLAY) { mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); } } public void onRotationChangedLocked(DisplayContent displayContent) { - if (mDisplayMagnifier != null) { - mDisplayMagnifier.onRotationChangedLocked(displayContent); + final int displayId = displayContent.getDisplayId(); + final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); + if (displayMagnifier != null) { + displayMagnifier.onRotationChangedLocked(displayContent); } - if (mWindowsForAccessibilityObserver != null) { + // TODO: support multi-display for windows observer + if (mWindowsForAccessibilityObserver != null && displayId == Display.DEFAULT_DISPLAY) { mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); } } public void onAppWindowTransitionLocked(WindowState windowState, int transition) { - if (mDisplayMagnifier != null) { - mDisplayMagnifier.onAppWindowTransitionLocked(windowState, transition); + final int displayId = windowState.getDisplayId(); + final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); + if (displayMagnifier != null) { + displayMagnifier.onAppWindowTransitionLocked(windowState, transition); } // Not relevant for the window observer. } public void onWindowTransitionLocked(WindowState windowState, int transition) { - if (mDisplayMagnifier != null) { - mDisplayMagnifier.onWindowTransitionLocked(windowState, transition); + final int displayId = windowState.getDisplayId(); + final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); + if (displayMagnifier != null) { + displayMagnifier.onWindowTransitionLocked(windowState, transition); } - if (mWindowsForAccessibilityObserver != null) { + // TODO: support multi-display for windows observer + if (mWindowsForAccessibilityObserver != null && displayId == Display.DEFAULT_DISPLAY) { mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); } } @@ -197,7 +225,6 @@ final class AccessibilityController { } } - public void onSomeWindowResizedOrMovedLocked() { // Not relevant for the display magnifier. @@ -207,29 +234,34 @@ final class AccessibilityController { } /** NOTE: This has to be called within a surface transaction. */ - public void drawMagnifiedRegionBorderIfNeededLocked() { - if (mDisplayMagnifier != null) { - mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked(); + public void drawMagnifiedRegionBorderIfNeededLocked(int displayId) { + final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); + if (displayMagnifier != null) { + displayMagnifier.drawMagnifiedRegionBorderIfNeededLocked(); } // Not relevant for the window observer. } public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) { - if (mDisplayMagnifier != null) { - return mDisplayMagnifier.getMagnificationSpecForWindowLocked(windowState); + final int displayId = windowState.getDisplayId(); + final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); + if (displayMagnifier != null) { + return displayMagnifier.getMagnificationSpecForWindowLocked(windowState); } return null; } public boolean hasCallbacksLocked() { - return (mDisplayMagnifier != null + // TODO: support multi-display for windows observer + return (mDisplayMagnifiers.size() > 0 || mWindowsForAccessibilityObserver != null); } - public void setForceShowMagnifiableBoundsLocked(boolean show) { - if (mDisplayMagnifier != null) { - mDisplayMagnifier.setForceShowMagnifiableBoundsLocked(show); - mDisplayMagnifier.showMagnificationBoundsIfNeeded(); + public void setForceShowMagnifiableBoundsLocked(int displayId, boolean show) { + final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); + if (displayMagnifier != null) { + displayMagnifier.setForceShowMagnifiableBoundsLocked(show); + displayMagnifier.showMagnificationBoundsIfNeeded(); } } @@ -263,6 +295,8 @@ final class AccessibilityController { private final WindowManagerService mService; private final MagnifiedViewport mMagnifedViewport; private final Handler mHandler; + private final DisplayContent mDisplayContent; + private final Display mDisplay; private final MagnificationCallbacks mCallbacks; @@ -271,10 +305,14 @@ final class AccessibilityController { private boolean mForceShowMagnifiableBounds = false; public DisplayMagnifier(WindowManagerService windowManagerService, + DisplayContent displayContent, + Display display, MagnificationCallbacks callbacks) { mContext = windowManagerService.mContext; mService = windowManagerService; mCallbacks = callbacks; + mDisplayContent = displayContent; + mDisplay = display; mHandler = new MyHandler(mService.mH.getLooper()); mMagnifedViewport = new MagnifiedViewport(); mLongAnimationDuration = mContext.getResources().getInteger( @@ -285,7 +323,7 @@ final class AccessibilityController { mMagnifedViewport.updateMagnificationSpecLocked(spec); mMagnifedViewport.recomputeBoundsLocked(); - mService.applyMagnificationSpec(spec); + mService.applyMagnificationSpecLocked(mDisplay.getDisplayId(), spec); mService.scheduleAnimationLocked(); } @@ -482,7 +520,7 @@ final class AccessibilityController { if (mContext.getResources().getConfiguration().isScreenRound()) { mCircularPath = new Path(); - mWindowManager.getDefaultDisplay().getRealSize(mTempPoint); + mDisplay.getRealSize(mTempPoint); final int centerXY = mTempPoint.x / 2; mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW); } else { @@ -512,7 +550,7 @@ final class AccessibilityController { } public void recomputeBoundsLocked() { - mWindowManager.getDefaultDisplay().getRealSize(mTempPoint); + mDisplay.getRealSize(mTempPoint); final int screenWidth = mTempPoint.x; final int screenHeight = mTempPoint.y; @@ -671,9 +709,8 @@ final class AccessibilityController { } private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) { - final DisplayContent dc = mService.getDefaultDisplayContentLocked(); mTempLayer = 0; - dc.forAllWindows((w) -> { + mDisplayContent.forAllWindows((w) -> { if (w.isOnScreen() && w.isVisibleLw() && (w.mAttrs.alpha != 0) && !w.mWinAnimator.mEnterAnimationPending) { @@ -703,8 +740,9 @@ final class AccessibilityController { public ViewportWindow(Context context) { SurfaceControl surfaceControl = null; try { - mWindowManager.getDefaultDisplay().getRealSize(mTempPoint); - surfaceControl = mService.getDefaultDisplayContentLocked().makeOverlay() + mDisplay.getRealSize(mTempPoint); + surfaceControl = mDisplayContent + .makeOverlay() .setName(SURFACE_TITLE) .setBufferSize(mTempPoint.x, mTempPoint.y) // not a typo .setFormat(PixelFormat.TRANSLUCENT) diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 801c1e727483..76b9b02fac86 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -25,7 +25,6 @@ import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; -import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; import static android.view.WindowManager.LayoutParams.FLAG_SECURE; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; @@ -568,9 +567,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree delayed = runningAppAnimation = true; } final WindowState window = findMainWindow(); - //TODO (multidisplay): Magnification is supported only for the default display. - if (window != null && accessibilityController != null - && getDisplayContent().getDisplayId() == DEFAULT_DISPLAY) { + if (window != null && accessibilityController != null) { accessibilityController.onAppWindowTransitionLocked(window, transit); } changed = true; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 740d47240bd0..19076fd47e02 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1483,11 +1483,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } - // TODO (multi-display): Magnification is supported only for the default display. // Announce rotation only if we will not animate as we already have the // windows in final state. Otherwise, we make this call at the rotation end. - if (screenRotationAnimation == null && mWmService.mAccessibilityController != null - && isDefaultDisplay) { + if (screenRotationAnimation == null && mWmService.mAccessibilityController != null) { mWmService.mAccessibilityController.onRotationChangedLocked(this); } } diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index b8a0739b9c83..b8db98b3c2f3 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -167,13 +167,11 @@ public class WindowAnimator { screenRotationAnimation.kill(); displayAnimator.mScreenRotationAnimation = null; - //TODO (multidisplay): Accessibility supported only for the default // display. - if (accessibilityController != null && dc.isDefaultDisplay) { + if (accessibilityController != null) { // We just finished rotation animation which means we did not // announce the rotation and waited for it to end, announce now. - accessibilityController.onRotationChangedLocked( - mService.getDefaultDisplayContentLocked()); + accessibilityController.onRotationChangedLocked(dc); } } } @@ -197,9 +195,8 @@ public class WindowAnimator { screenRotationAnimation.updateSurfaces(mTransaction); } orAnimating(dc.getDockedDividerController().animate(mCurrentTime)); - //TODO (multidisplay): Magnification is supported only for the default display. - if (accessibilityController != null && dc.isDefaultDisplay) { - accessibilityController.drawMagnifiedRegionBorderIfNeededLocked(); + if (accessibilityController != null) { + accessibilityController.drawMagnifiedRegionBorderIfNeededLocked(displayId); } } diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 1691dc0e3806..5267e7e55793 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -212,34 +212,40 @@ public abstract class WindowManagerInternal { * and has access to the raw window data while the accessibility layer serves * as a controller. * + * @param displayId The logical display id. * @param callbacks The callbacks to invoke. + * @return {@code false} if display id is not valid. */ - public abstract void setMagnificationCallbacks(@Nullable MagnificationCallbacks callbacks); + public abstract boolean setMagnificationCallbacks(int displayId, + @Nullable MagnificationCallbacks callbacks); /** * Set by the accessibility layer to specify the magnification and panning to * be applied to all windows that should be magnified. * + * @param displayId The logical display id. * @param spec The MagnficationSpec to set. * - * @see #setMagnificationCallbacks(MagnificationCallbacks) + * @see #setMagnificationCallbacks(int, MagnificationCallbacks) */ - public abstract void setMagnificationSpec(MagnificationSpec spec); + public abstract void setMagnificationSpec(int displayId, MagnificationSpec spec); /** * Set by the accessibility framework to indicate whether the magnifiable regions of the display * should be shown. * + * @param displayId The logical display id. * @param show {@code true} to show magnifiable region bounds, {@code false} to hide */ - public abstract void setForceShowMagnifiableBounds(boolean show); + public abstract void setForceShowMagnifiableBounds(int displayId, boolean show); /** * Obtains the magnification regions. * + * @param displayId The logical display id. * @param magnificationRegion the current magnification region */ - public abstract void getMagnificationRegion(@NonNull Region magnificationRegion); + public abstract void getMagnificationRegion(int displayId, @NonNull Region magnificationRegion); /** * Gets the magnification and translation applied to a window given its token. @@ -251,7 +257,7 @@ public abstract class WindowManagerInternal { * * @return The magnification spec for the window. * - * @see #setMagnificationCallbacks(MagnificationCallbacks) + * @see #setMagnificationCallbacks(int, MagnificationCallbacks) */ public abstract MagnificationSpec getCompatibleMagnificationSpecForWindow( IBinder windowToken); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 90506e744250..839f43e54f68 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1845,9 +1845,9 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mGlobalLock) { if (mAccessibilityController != null) { WindowState window = mWindowMap.get(token); - //TODO (multidisplay): Magnification is supported only for the default display. - if (window != null && window.getDisplayId() == DEFAULT_DISPLAY) { - mAccessibilityController.onRectangleOnScreenRequestedLocked(rectangle); + if (window != null) { + mAccessibilityController.onRectangleOnScreenRequestedLocked( + window.getDisplayId(), rectangle); } } } @@ -2236,8 +2236,7 @@ public class WindowManagerService extends IWindowManager.Stub win.mDestroying = true; win.destroySurface(false, stopped); } - // TODO(multidisplay): Magnification is supported only for the default display. - if (mAccessibilityController != null && win.getDisplayId() == DEFAULT_DISPLAY) { + if (mAccessibilityController != null) { mAccessibilityController.onWindowTransitionLocked(win, transit); } @@ -6813,10 +6812,10 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void setMagnificationSpec(MagnificationSpec spec) { + public void setMagnificationSpec(int displayId, MagnificationSpec spec) { synchronized (mGlobalLock) { if (mAccessibilityController != null) { - mAccessibilityController.setMagnificationSpecLocked(spec); + mAccessibilityController.setMagnificationSpecLocked(displayId, spec); } else { throw new IllegalStateException("Magnification callbacks not set!"); } @@ -6827,10 +6826,10 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void setForceShowMagnifiableBounds(boolean show) { + public void setForceShowMagnifiableBounds(int displayId, boolean show) { synchronized (mGlobalLock) { if (mAccessibilityController != null) { - mAccessibilityController.setForceShowMagnifiableBoundsLocked(show); + mAccessibilityController.setForceShowMagnifiableBoundsLocked(displayId, show); } else { throw new IllegalStateException("Magnification callbacks not set!"); } @@ -6838,10 +6837,11 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void getMagnificationRegion(@NonNull Region magnificationRegion) { + public void getMagnificationRegion(int displayId, @NonNull Region magnificationRegion) { synchronized (mGlobalLock) { if (mAccessibilityController != null) { - mAccessibilityController.getMagnificationRegionLocked(magnificationRegion); + mAccessibilityController.getMagnificationRegionLocked(displayId, + magnificationRegion); } else { throw new IllegalStateException("Magnification callbacks not set!"); } @@ -6869,16 +6869,19 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void setMagnificationCallbacks(@Nullable MagnificationCallbacks callbacks) { + public boolean setMagnificationCallbacks(int displayId, + @Nullable MagnificationCallbacks callbacks) { synchronized (mGlobalLock) { if (mAccessibilityController == null) { mAccessibilityController = new AccessibilityController( WindowManagerService.this); } - mAccessibilityController.setMagnificationCallbacksLocked(callbacks); + boolean result = mAccessibilityController.setMagnificationCallbacksLocked( + displayId, callbacks); if (!mAccessibilityController.hasCallbacksLocked()) { mAccessibilityController = null; } + return result; } } @@ -7257,8 +7260,12 @@ public class WindowManagerService extends IWindowManager.Stub }, false /* traverseTopToBottom */); } - public void applyMagnificationSpec(MagnificationSpec spec) { - getDefaultDisplayContentLocked().applyMagnificationSpec(spec); + /** Called from Accessibility Controller to apply magnification spec */ + public void applyMagnificationSpecLocked(int displayId, MagnificationSpec spec) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent != null) { + displayContent.applyMagnificationSpec(spec); + } } SurfaceControl.Builder makeSurfaceBuilder(SurfaceSession s) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index cd29b3c29248..d9366f0beeda 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1607,8 +1607,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mWmService.mAccessibilityController; final int winTransit = TRANSIT_EXIT; mWinAnimator.applyAnimationLocked(winTransit, false /* isEntrance */); - //TODO (multidisplay): Magnification is supported only for the default - if (accessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) { + if (accessibilityController != null) { accessibilityController.onWindowTransitionLocked(this, winTransit); } } @@ -1625,8 +1624,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (isVisibleNow()) { mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false); - //TODO (multidisplay): Magnification is supported only for the default - if (mWmService.mAccessibilityController != null && isDefaultDisplay()) { + if (mWmService.mAccessibilityController != null) { mWmService.mAccessibilityController.onWindowTransitionLocked(this, TRANSIT_EXIT); } changed = true; @@ -1915,9 +1913,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP setDisplayLayoutNeeded(); mWmService.requestTraversal(); } - //TODO (multidisplay): Magnification is supported only for the default display. - if (mWmService.mAccessibilityController != null - && displayId == DEFAULT_DISPLAY) { + if (mWmService.mAccessibilityController != null) { mWmService.mAccessibilityController.onWindowTransitionLocked(this, transit); } } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index fb5c556e1643..6b4d6d2bfb3c 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -16,7 +16,6 @@ package com.android.server.wm; -import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; import static android.view.WindowManager.LayoutParams.FLAG_SCALED; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; @@ -1308,9 +1307,7 @@ class WindowStateAnimator { transit = WindowManagerPolicy.TRANSIT_SHOW; } applyAnimationLocked(transit, true); - //TODO (multidisplay): Magnification is supported only for the default display. - if (mService.mAccessibilityController != null - && mWin.getDisplayId() == DEFAULT_DISPLAY) { + if (mService.mAccessibilityController != null) { mService.mAccessibilityController.onWindowTransitionLocked(mWin, transit); } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java index feffeef3f044..773b8778c7bf 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java @@ -20,7 +20,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; @@ -36,19 +38,15 @@ import android.animation.ValueAnimator; import android.content.BroadcastReceiver; import android.content.Context; import android.content.IntentFilter; -import android.content.res.Resources; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.Region; -import android.os.Handler; import android.os.Looper; -import android.os.Message; import android.view.MagnificationSpec; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; -import com.android.internal.R; import com.android.server.wm.WindowManagerInternal; import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks; @@ -81,48 +79,36 @@ public class MagnificationControllerTest { static final Region OTHER_REGION = new Region(OTHER_MAGNIFICATION_BOUNDS); static final int SERVICE_ID_1 = 1; static final int SERVICE_ID_2 = 2; + static final int DISPLAY_0 = 0; + static final int DISPLAY_1 = 1; + static final int DISPLAY_COUNT = 2; + static final int INVALID_DISPLAY = 2; + final MagnificationController.ControllerContext mMockControllerCtx = + mock(MagnificationController.ControllerContext.class); final Context mMockContext = mock(Context.class); final AccessibilityManagerService mMockAms = mock(AccessibilityManagerService.class); final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class); - final MessageCapturingHandler mMessageCapturingHandler = - new MessageCapturingHandler(null); + final MessageCapturingHandler mMessageCapturingHandler = new MessageCapturingHandler(null); - final ValueAnimator mMockValueAnimator = mock(ValueAnimator.class); - MagnificationController.SettingsBridge mMockSettingsBridge; + ValueAnimator mMockValueAnimator; + ValueAnimator.AnimatorUpdateListener mTargetAnimationListener; MagnificationController mMagnificationController; - ValueAnimator.AnimatorUpdateListener mTargetAnimationListener; @Before public void setUp() { Looper looper = InstrumentationRegistry.getContext().getMainLooper(); // Pretending ID of the Thread associated with looper as main thread ID in controller when(mMockContext.getMainLooper()).thenReturn(looper); - Resources mockResources = mock(Resources.class); - when(mMockContext.getResources()).thenReturn(mockResources); - when(mockResources.getInteger(R.integer.config_longAnimTime)) - .thenReturn(1000); - mMockSettingsBridge = mock(MagnificationController.SettingsBridge.class); - mMagnificationController = new MagnificationController(mMockContext, mMockAms, new Object(), - mMessageCapturingHandler, mMockWindowManager, mMockValueAnimator, - mMockSettingsBridge); - - doAnswer(new Answer<Void>() { - @Override - public Void answer(InvocationOnMock invocationOnMock) throws Throwable { - Object[] args = invocationOnMock.getArguments(); - Region regionArg = (Region) args[0]; - regionArg.set(INITIAL_MAGNIFICATION_REGION); - return null; - } - }).when(mMockWindowManager).getMagnificationRegion((Region) anyObject()); - - ArgumentCaptor<ValueAnimator.AnimatorUpdateListener> listenerArgumentCaptor = - ArgumentCaptor.forClass(ValueAnimator.AnimatorUpdateListener.class); - verify(mMockValueAnimator).addUpdateListener(listenerArgumentCaptor.capture()); - mTargetAnimationListener = listenerArgumentCaptor.getValue(); - Mockito.reset(mMockValueAnimator); // Ignore other initialization + when(mMockControllerCtx.getContext()).thenReturn(mMockContext); + when(mMockControllerCtx.getAms()).thenReturn(mMockAms); + when(mMockControllerCtx.getWindowManager()).thenReturn(mMockWindowManager); + when(mMockControllerCtx.getHandler()).thenReturn(mMessageCapturingHandler); + when(mMockControllerCtx.getAnimationDuration()).thenReturn(1000L); + initMockWindowManager(); + + mMagnificationController = new MagnificationController(mMockControllerCtx, new Object()); } @After @@ -133,88 +119,132 @@ public class MagnificationControllerTest { @Test public void testRegister_WindowManagerAndContextRegisterListeners() { - mMagnificationController.register(); + register(DISPLAY_0); + register(DISPLAY_1); + register(INVALID_DISPLAY); verify(mMockContext).registerReceiver( (BroadcastReceiver) anyObject(), (IntentFilter) anyObject()); - verify(mMockWindowManager).setMagnificationCallbacks((MagnificationCallbacks) anyObject()); - assertTrue(mMagnificationController.isRegisteredLocked()); + verify(mMockWindowManager).setMagnificationCallbacks( + eq(DISPLAY_0), (MagnificationCallbacks) anyObject()); + verify(mMockWindowManager).setMagnificationCallbacks( + eq(DISPLAY_1), (MagnificationCallbacks) anyObject()); + verify(mMockWindowManager).setMagnificationCallbacks( + eq(INVALID_DISPLAY), (MagnificationCallbacks) anyObject()); + assertTrue(mMagnificationController.isRegistered(DISPLAY_0)); + assertTrue(mMagnificationController.isRegistered(DISPLAY_1)); + assertFalse(mMagnificationController.isRegistered(INVALID_DISPLAY)); } @Test public void testRegister_WindowManagerAndContextUnregisterListeners() { - mMagnificationController.register(); - mMagnificationController.unregister(); - + register(DISPLAY_0); + register(DISPLAY_1); + mMagnificationController.unregister(DISPLAY_0); + verify(mMockContext, times(0)).unregisterReceiver((BroadcastReceiver) anyObject()); + mMagnificationController.unregister(DISPLAY_1); verify(mMockContext).unregisterReceiver((BroadcastReceiver) anyObject()); - verify(mMockWindowManager).setMagnificationCallbacks(null); - assertFalse(mMagnificationController.isRegisteredLocked()); + verify(mMockWindowManager).setMagnificationCallbacks(eq(DISPLAY_0), eq(null)); + verify(mMockWindowManager).setMagnificationCallbacks(eq(DISPLAY_1), eq(null)); + assertFalse(mMagnificationController.isRegistered(DISPLAY_0)); + assertFalse(mMagnificationController.isRegistered(DISPLAY_1)); } @Test public void testInitialState_noMagnificationAndMagnificationRegionReadFromWindowManager() { - mMagnificationController.register(); + for (int i = 0; i < DISPLAY_COUNT; i++) { + initialState_noMagnificationAndMagnificationRegionReadFromWindowManager(i); + resetMockWindowManager(); + } + } + + private void initialState_noMagnificationAndMagnificationRegionReadFromWindowManager( + int displayId) { + register(displayId); MagnificationSpec expectedInitialSpec = getMagnificationSpec(1.0f, 0.0f, 0.0f); Region initialMagRegion = new Region(); Rect initialBounds = new Rect(); - assertEquals(expectedInitialSpec, getCurrentMagnificationSpec()); - mMagnificationController.getMagnificationRegion(initialMagRegion); - mMagnificationController.getMagnificationBounds(initialBounds); + assertEquals(expectedInitialSpec, getCurrentMagnificationSpec(displayId)); + mMagnificationController.getMagnificationRegion(displayId, initialMagRegion); + mMagnificationController.getMagnificationBounds(displayId, initialBounds); assertEquals(INITIAL_MAGNIFICATION_REGION, initialMagRegion); assertEquals(INITIAL_MAGNIFICATION_BOUNDS, initialBounds); assertEquals(INITIAL_MAGNIFICATION_BOUNDS.centerX(), - mMagnificationController.getCenterX(), 0.0f); + mMagnificationController.getCenterX(displayId), 0.0f); assertEquals(INITIAL_MAGNIFICATION_BOUNDS.centerY(), - mMagnificationController.getCenterY(), 0.0f); + mMagnificationController.getCenterY(displayId), 0.0f); } @Test public void testNotRegistered_publicMethodsShouldBeBenign() { - assertFalse(mMagnificationController.isMagnifying()); - assertFalse(mMagnificationController.magnificationRegionContains(100, 100)); - assertFalse(mMagnificationController.reset(true)); - assertFalse(mMagnificationController.setScale(2, 100, 100, true, 0)); - assertFalse(mMagnificationController.setCenter(100, 100, false, 1)); - assertFalse(mMagnificationController.setScaleAndCenter(1.5f, 100, 100, false, 2)); - assertTrue(mMagnificationController.getIdOfLastServiceToMagnify() < 0); - - mMagnificationController.getMagnificationRegion(new Region()); - mMagnificationController.getMagnificationBounds(new Rect()); - mMagnificationController.getScale(); - mMagnificationController.getOffsetX(); - mMagnificationController.getOffsetY(); - mMagnificationController.getCenterX(); - mMagnificationController.getCenterY(); - mMagnificationController.offsetMagnifiedRegion(50, 50, 1); - mMagnificationController.unregister(); + for (int i = 0; i < DISPLAY_COUNT; i++) { + notRegistered_publicMethodsShouldBeBenign(i); + resetMockWindowManager(); + } + } + + private void notRegistered_publicMethodsShouldBeBenign(int displayId) { + assertFalse(mMagnificationController.isMagnifying(displayId)); + assertFalse(mMagnificationController.magnificationRegionContains(displayId, 100, 100)); + assertFalse(mMagnificationController.reset(displayId, true)); + assertFalse(mMagnificationController.setScale(displayId, 2, 100, 100, true, 0)); + assertFalse(mMagnificationController.setCenter(displayId, 100, 100, false, 1)); + assertFalse(mMagnificationController.setScaleAndCenter(displayId, + 1.5f, 100, 100, false, 2)); + assertTrue(mMagnificationController.getIdOfLastServiceToMagnify(displayId) < 0); + + mMagnificationController.getMagnificationRegion(displayId, new Region()); + mMagnificationController.getMagnificationBounds(displayId, new Rect()); + mMagnificationController.getScale(displayId); + mMagnificationController.getOffsetX(displayId); + mMagnificationController.getOffsetY(displayId); + mMagnificationController.getCenterX(displayId); + mMagnificationController.getCenterY(displayId); + mMagnificationController.offsetMagnifiedRegion(displayId, 50, 50, 1); + mMagnificationController.unregister(displayId); } @Test public void testSetScale_noAnimation_shouldGoStraightToWindowManagerAndUpdateState() { - mMagnificationController.register(); + for (int i = 0; i < DISPLAY_COUNT; i++) { + setScale_noAnimation_shouldGoStraightToWindowManagerAndUpdateState(i); + resetMockWindowManager(); + } + } + + private void setScale_noAnimation_shouldGoStraightToWindowManagerAndUpdateState(int displayId) { + register(displayId); final float scale = 2.0f; final PointF center = INITIAL_MAGNIFICATION_BOUNDS_CENTER; final PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, center, scale); assertTrue(mMagnificationController - .setScale(scale, center.x, center.y, false, SERVICE_ID_1)); + .setScale(displayId, scale, center.x, center.y, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); final MagnificationSpec expectedSpec = getMagnificationSpec(scale, offsets); - verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedSpec))); - assertThat(getCurrentMagnificationSpec(), closeTo(expectedSpec)); - assertEquals(center.x, mMagnificationController.getCenterX(), 0.0); - assertEquals(center.y, mMagnificationController.getCenterY(), 0.0); + verify(mMockWindowManager).setMagnificationSpec( + eq(displayId), argThat(closeTo(expectedSpec))); + assertThat(getCurrentMagnificationSpec(displayId), closeTo(expectedSpec)); + assertEquals(center.x, mMagnificationController.getCenterX(displayId), 0.0); + assertEquals(center.y, mMagnificationController.getCenterY(displayId), 0.0); verify(mMockValueAnimator, times(0)).start(); } @Test public void testSetScale_withPivotAndAnimation_stateChangesAndAnimationHappens() { - mMagnificationController.register(); - MagnificationSpec startSpec = getCurrentMagnificationSpec(); + for (int i = 0; i < DISPLAY_COUNT; i++) { + setScale_withPivotAndAnimation_stateChangesAndAnimationHappens(i); + resetMockWindowManager(); + } + } + + private void setScale_withPivotAndAnimation_stateChangesAndAnimationHappens(int displayId) { + register(displayId); + MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId); float scale = 2.0f; PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; assertTrue(mMagnificationController - .setScale(scale, pivotPoint.x, pivotPoint.y, true, SERVICE_ID_1)); + .setScale(displayId, scale, pivotPoint.x, pivotPoint.y, true, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); // New center should be halfway between original center and pivot @@ -223,467 +253,645 @@ public class MagnificationControllerTest { PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale); MagnificationSpec endSpec = getMagnificationSpec(scale, offsets); - assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.5); - assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.5); - assertThat(getCurrentMagnificationSpec(), closeTo(endSpec)); + assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.5); + assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.5); + assertThat(getCurrentMagnificationSpec(displayId), closeTo(endSpec)); verify(mMockValueAnimator).start(); // Initial value when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f); mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); - verify(mMockWindowManager).setMagnificationSpec(startSpec); + verify(mMockWindowManager).setMagnificationSpec(eq(displayId), eq(startSpec)); // Intermediate point Mockito.reset(mMockWindowManager); float fraction = 0.5f; when(mMockValueAnimator.getAnimatedFraction()).thenReturn(fraction); mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); - verify(mMockWindowManager).setMagnificationSpec( + verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(getInterpolatedMagSpec(startSpec, endSpec, fraction)))); // Final value Mockito.reset(mMockWindowManager); when(mMockValueAnimator.getAnimatedFraction()).thenReturn(1.0f); mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); - verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(endSpec))); + verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec))); } @Test public void testSetCenter_whileMagnifying_noAnimation_centerMoves() { - mMagnificationController.register(); + for (int i = 0; i < DISPLAY_COUNT; i++) { + setCenter_whileMagnifying_noAnimation_centerMoves(i); + resetMockWindowManager(); + } + } + + private void setCenter_whileMagnifying_noAnimation_centerMoves(int displayId) { + register(displayId); // First zoom in float scale = 2.0f; - assertTrue(mMagnificationController.setScale(scale, + assertTrue(mMagnificationController.setScale(displayId, scale, INITIAL_MAGNIFICATION_BOUNDS.centerX(), INITIAL_MAGNIFICATION_BOUNDS.centerY(), false, SERVICE_ID_1)); Mockito.reset(mMockWindowManager); PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; assertTrue(mMagnificationController - .setCenter(newCenter.x, newCenter.y, false, SERVICE_ID_1)); + .setCenter(displayId, newCenter.x, newCenter.y, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); PointF expectedOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale); MagnificationSpec expectedSpec = getMagnificationSpec(scale, expectedOffsets); - verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedSpec))); - assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.0); - assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.0); + verify(mMockWindowManager).setMagnificationSpec( + eq(displayId), argThat(closeTo(expectedSpec))); + assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.0); + assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.0); verify(mMockValueAnimator, times(0)).start(); } @Test public void testSetScaleAndCenter_animated_stateChangesAndAnimationHappens() { - mMagnificationController.register(); - MagnificationSpec startSpec = getCurrentMagnificationSpec(); + for (int i = 0; i < DISPLAY_COUNT; i++) { + setScaleAndCenter_animated_stateChangesAndAnimationHappens(i); + resetMockWindowManager(); + } + } + + private void setScaleAndCenter_animated_stateChangesAndAnimationHappens(int displayId) { + register(displayId); + MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId); float scale = 2.5f; PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale); MagnificationSpec endSpec = getMagnificationSpec(scale, offsets); - assertTrue(mMagnificationController.setScaleAndCenter(scale, newCenter.x, newCenter.y, - true, SERVICE_ID_1)); + assertTrue(mMagnificationController.setScaleAndCenter(displayId, scale, newCenter.x, + newCenter.y, true, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); - assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.5); - assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.5); - assertThat(getCurrentMagnificationSpec(), closeTo(endSpec)); - verify(mMockAms).notifyMagnificationChanged( + assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.5); + assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.5); + assertThat(getCurrentMagnificationSpec(displayId), closeTo(endSpec)); + verify(mMockAms).notifyMagnificationChanged(displayId, INITIAL_MAGNIFICATION_REGION, scale, newCenter.x, newCenter.y); verify(mMockValueAnimator).start(); // Initial value when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f); mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); - verify(mMockWindowManager).setMagnificationSpec(startSpec); + verify(mMockWindowManager).setMagnificationSpec(eq(displayId), eq(startSpec)); // Intermediate point Mockito.reset(mMockWindowManager); float fraction = 0.33f; when(mMockValueAnimator.getAnimatedFraction()).thenReturn(fraction); mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); - verify(mMockWindowManager).setMagnificationSpec( + verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(getInterpolatedMagSpec(startSpec, endSpec, fraction)))); // Final value Mockito.reset(mMockWindowManager); when(mMockValueAnimator.getAnimatedFraction()).thenReturn(1.0f); mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); - verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(endSpec))); + verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec))); } @Test public void testSetScaleAndCenter_scaleOutOfBounds_cappedAtLimits() { - mMagnificationController.register(); - MagnificationSpec startSpec = getCurrentMagnificationSpec(); + for (int i = 0; i < DISPLAY_COUNT; i++) { + setScaleAndCenter_scaleOutOfBounds_cappedAtLimits(i); + resetMockWindowManager(); + } + } + + private void setScaleAndCenter_scaleOutOfBounds_cappedAtLimits(int displayId) { + register(displayId); + MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId); PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, MagnificationController.MAX_SCALE); MagnificationSpec endSpec = getMagnificationSpec( MagnificationController.MAX_SCALE, offsets); - assertTrue(mMagnificationController.setScaleAndCenter( + assertTrue(mMagnificationController.setScaleAndCenter(displayId, MagnificationController.MAX_SCALE + 1.0f, newCenter.x, newCenter.y, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); - assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.5); - assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.5); - verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(endSpec))); + assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.5); + assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.5); + verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec))); Mockito.reset(mMockWindowManager); // Verify that we can't zoom below 1x - assertTrue(mMagnificationController.setScaleAndCenter(0.5f, + assertTrue(mMagnificationController.setScaleAndCenter(displayId, 0.5f, INITIAL_MAGNIFICATION_BOUNDS_CENTER.x, INITIAL_MAGNIFICATION_BOUNDS_CENTER.y, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); assertEquals(INITIAL_MAGNIFICATION_BOUNDS_CENTER.x, - mMagnificationController.getCenterX(), 0.5); + mMagnificationController.getCenterX(displayId), 0.5); assertEquals(INITIAL_MAGNIFICATION_BOUNDS_CENTER.y, - mMagnificationController.getCenterY(), 0.5); - verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(startSpec))); + mMagnificationController.getCenterY(displayId), 0.5); + verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(startSpec))); } @Test public void testSetScaleAndCenter_centerOutOfBounds_cappedAtLimits() { - mMagnificationController.register(); + for (int i = 0; i < DISPLAY_COUNT; i++) { + setScaleAndCenter_centerOutOfBounds_cappedAtLimits(i); + resetMockWindowManager(); + } + } + + private void setScaleAndCenter_centerOutOfBounds_cappedAtLimits(int displayId) { + register(displayId); float scale = 2.0f; // Off the edge to the top and left - assertTrue(mMagnificationController.setScaleAndCenter( + assertTrue(mMagnificationController.setScaleAndCenter(displayId, scale, -100f, -200f, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); PointF newCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER; PointF newOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale); - assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.5); - assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.5); - verify(mMockWindowManager).setMagnificationSpec( + assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.5); + assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.5); + verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(getMagnificationSpec(scale, newOffsets)))); Mockito.reset(mMockWindowManager); // Off the edge to the bottom and right - assertTrue(mMagnificationController.setScaleAndCenter(scale, + assertTrue(mMagnificationController.setScaleAndCenter(displayId, scale, INITIAL_MAGNIFICATION_BOUNDS.right + 1, INITIAL_MAGNIFICATION_BOUNDS.bottom + 1, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; newOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale); - assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.5); - assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.5); - verify(mMockWindowManager).setMagnificationSpec( + assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.5); + assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.5); + verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(getMagnificationSpec(scale, newOffsets)))); } @Test public void testMagnificationRegionChanged_serviceNotified() { - mMagnificationController.register(); - MagnificationCallbacks callbacks = getMagnificationCallbacks(); + for (int i = 0; i < DISPLAY_COUNT; i++) { + magnificationRegionChanged_serviceNotified(i); + resetMockWindowManager(); + } + } + + private void magnificationRegionChanged_serviceNotified(int displayId) { + register(displayId); + MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId); callbacks.onMagnificationRegionChanged(OTHER_REGION); mMessageCapturingHandler.sendAllMessages(); - verify(mMockAms).notifyMagnificationChanged(OTHER_REGION, 1.0f, + verify(mMockAms).notifyMagnificationChanged(displayId, OTHER_REGION, 1.0f, OTHER_MAGNIFICATION_BOUNDS.centerX(), OTHER_MAGNIFICATION_BOUNDS.centerY()); } @Test public void testOffsetMagnifiedRegion_whileMagnifying_offsetsMove() { - mMagnificationController.register(); + for (int i = 0; i < DISPLAY_COUNT; i++) { + offsetMagnifiedRegion_whileMagnifying_offsetsMove(i); + resetMockWindowManager(); + } + } + + private void offsetMagnifiedRegion_whileMagnifying_offsetsMove(int displayId) { + register(displayId); PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER; float scale = 2.0f; PointF startOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, startCenter, scale); // First zoom in assertTrue(mMagnificationController - .setScaleAndCenter(scale, startCenter.x, startCenter.y, false, SERVICE_ID_1)); + .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, false, + SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); Mockito.reset(mMockWindowManager); PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; PointF newOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale); - mMagnificationController.offsetMagnifiedRegion( - startOffsets.x - newOffsets.x, startOffsets.y - newOffsets.y, SERVICE_ID_1); + mMagnificationController.offsetMagnifiedRegion(displayId, + startOffsets.x - newOffsets.x, startOffsets.y - newOffsets.y, + SERVICE_ID_1); mMessageCapturingHandler.sendAllMessages(); MagnificationSpec expectedSpec = getMagnificationSpec(scale, newOffsets); - verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedSpec))); - assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.0); - assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.0); + verify(mMockWindowManager).setMagnificationSpec(eq(displayId), + argThat(closeTo(expectedSpec))); + assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.0); + assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.0); verify(mMockValueAnimator, times(0)).start(); } @Test public void testOffsetMagnifiedRegion_whileNotMagnifying_hasNoEffect() { - mMagnificationController.register(); + for (int i = 0; i < DISPLAY_COUNT; i++) { + offsetMagnifiedRegion_whileNotMagnifying_hasNoEffect(i); + resetMockWindowManager(); + } + } + + private void offsetMagnifiedRegion_whileNotMagnifying_hasNoEffect(int displayId) { + register(displayId); Mockito.reset(mMockWindowManager); - MagnificationSpec startSpec = getCurrentMagnificationSpec(); - mMagnificationController.offsetMagnifiedRegion(10, 10, SERVICE_ID_1); - assertThat(getCurrentMagnificationSpec(), closeTo(startSpec)); - mMagnificationController.offsetMagnifiedRegion(-10, -10, SERVICE_ID_1); - assertThat(getCurrentMagnificationSpec(), closeTo(startSpec)); + MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId); + mMagnificationController.offsetMagnifiedRegion(displayId, 10, 10, SERVICE_ID_1); + assertThat(getCurrentMagnificationSpec(displayId), closeTo(startSpec)); + mMagnificationController.offsetMagnifiedRegion(displayId, -10, -10, SERVICE_ID_1); + assertThat(getCurrentMagnificationSpec(displayId), closeTo(startSpec)); verifyNoMoreInteractions(mMockWindowManager); } @Test public void testOffsetMagnifiedRegion_whileMagnifyingButAtEdge_hasNoEffect() { - mMagnificationController.register(); + for (int i = 0; i < DISPLAY_COUNT; i++) { + offsetMagnifiedRegion_whileMagnifyingButAtEdge_hasNoEffect(i); + resetMockWindowManager(); + } + } + + private void offsetMagnifiedRegion_whileMagnifyingButAtEdge_hasNoEffect(int displayId) { + register(displayId); float scale = 2.0f; // Upper left edges PointF ulCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER; assertTrue(mMagnificationController - .setScaleAndCenter(scale, ulCenter.x, ulCenter.y, false, SERVICE_ID_1)); + .setScaleAndCenter(displayId, scale, ulCenter.x, ulCenter.y, false, + SERVICE_ID_1)); Mockito.reset(mMockWindowManager); - MagnificationSpec ulSpec = getCurrentMagnificationSpec(); - mMagnificationController.offsetMagnifiedRegion(-10, -10, SERVICE_ID_1); - assertThat(getCurrentMagnificationSpec(), closeTo(ulSpec)); + MagnificationSpec ulSpec = getCurrentMagnificationSpec(displayId); + mMagnificationController.offsetMagnifiedRegion(displayId, -10, -10, + SERVICE_ID_1); + assertThat(getCurrentMagnificationSpec(displayId), closeTo(ulSpec)); verifyNoMoreInteractions(mMockWindowManager); // Lower right edges PointF lrCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; assertTrue(mMagnificationController - .setScaleAndCenter(scale, lrCenter.x, lrCenter.y, false, SERVICE_ID_1)); + .setScaleAndCenter(displayId, scale, lrCenter.x, lrCenter.y, false, + SERVICE_ID_1)); Mockito.reset(mMockWindowManager); - MagnificationSpec lrSpec = getCurrentMagnificationSpec(); - mMagnificationController.offsetMagnifiedRegion(10, 10, SERVICE_ID_1); - assertThat(getCurrentMagnificationSpec(), closeTo(lrSpec)); + MagnificationSpec lrSpec = getCurrentMagnificationSpec(displayId); + mMagnificationController.offsetMagnifiedRegion(displayId, 10, 10, + SERVICE_ID_1); + assertThat(getCurrentMagnificationSpec(displayId), closeTo(lrSpec)); verifyNoMoreInteractions(mMockWindowManager); } @Test public void testGetIdOfLastServiceToChange_returnsCorrectValue() { - mMagnificationController.register(); + for (int i = 0; i < DISPLAY_COUNT; i++) { + getIdOfLastServiceToChange_returnsCorrectValue(i); + resetMockWindowManager(); + } + } + + private void getIdOfLastServiceToChange_returnsCorrectValue(int displayId) { + register(displayId); PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER; assertTrue(mMagnificationController - .setScale(2.0f, startCenter.x, startCenter.y, false, SERVICE_ID_1)); - assertEquals(SERVICE_ID_1, mMagnificationController.getIdOfLastServiceToMagnify()); + .setScale(displayId, 2.0f, startCenter.x, startCenter.y, false, + SERVICE_ID_1)); + assertEquals(SERVICE_ID_1, mMagnificationController.getIdOfLastServiceToMagnify(displayId)); assertTrue(mMagnificationController - .setScale(1.5f, startCenter.x, startCenter.y, false, SERVICE_ID_2)); - assertEquals(SERVICE_ID_2, mMagnificationController.getIdOfLastServiceToMagnify()); + .setScale(displayId, 1.5f, startCenter.x, startCenter.y, false, + SERVICE_ID_2)); + assertEquals(SERVICE_ID_2, mMagnificationController.getIdOfLastServiceToMagnify(displayId)); } @Test public void testResetIfNeeded_resetsOnlyIfLastMagnifyingServiceIsDisabled() { - mMagnificationController.register(); + for (int i = 0; i < DISPLAY_COUNT; i++) { + resetIfNeeded_resetsOnlyIfLastMagnifyingServiceIsDisabled(i); + resetMockWindowManager(); + } + } + + private void resetIfNeeded_resetsOnlyIfLastMagnifyingServiceIsDisabled(int displayId) { + register(displayId); PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER; mMagnificationController - .setScale(2.0f, startCenter.x, startCenter.y, false, SERVICE_ID_1); + .setScale(displayId, 2.0f, startCenter.x, startCenter.y, false, + SERVICE_ID_1); mMagnificationController - .setScale(1.5f, startCenter.x, startCenter.y, false, SERVICE_ID_2); - assertFalse(mMagnificationController.resetIfNeeded(SERVICE_ID_1)); - assertTrue(mMagnificationController.isMagnifying()); - assertTrue(mMagnificationController.resetIfNeeded(SERVICE_ID_2)); - assertFalse(mMagnificationController.isMagnifying()); + .setScale(displayId, 1.5f, startCenter.x, startCenter.y, false, + SERVICE_ID_2); + assertFalse(mMagnificationController.resetIfNeeded(displayId, SERVICE_ID_1)); + assertTrue(mMagnificationController.isMagnifying(displayId)); + assertTrue(mMagnificationController.resetIfNeeded(displayId, SERVICE_ID_2)); + assertFalse(mMagnificationController.isMagnifying(displayId)); } @Test public void testSetUserId_resetsOnlyIfIdChanges() { + for (int i = 0; i < DISPLAY_COUNT; i++) { + testSetUserId_resetsOnlyIfIdChanges(i); + resetMockWindowManager(); + } + } + + private void testSetUserId_resetsOnlyIfIdChanges(int displayId) { final int userId1 = 1; final int userId2 = 2; - mMagnificationController.register(); + register(displayId); mMagnificationController.setUserId(userId1); PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER; float scale = 2.0f; - mMagnificationController.setScale(scale, startCenter.x, startCenter.y, false, SERVICE_ID_1); + mMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, false, + SERVICE_ID_1); mMagnificationController.setUserId(userId1); - assertTrue(mMagnificationController.isMagnifying()); + assertTrue(mMagnificationController.isMagnifying(displayId)); mMagnificationController.setUserId(userId2); - assertFalse(mMagnificationController.isMagnifying()); + assertFalse(mMagnificationController.isMagnifying(displayId)); } @Test public void testResetIfNeeded_doesWhatItSays() { - mMagnificationController.register(); - zoomIn2xToMiddle(); + for (int i = 0; i < DISPLAY_COUNT; i++) { + testResetIfNeeded_doesWhatItSays(i); + resetMockWindowManager(); + } + } + + private void testResetIfNeeded_doesWhatItSays(int displayId) { + register(displayId); + zoomIn2xToMiddle(displayId); mMessageCapturingHandler.sendAllMessages(); reset(mMockAms); - assertTrue(mMagnificationController.resetIfNeeded(false)); - verify(mMockAms).notifyMagnificationChanged( + assertTrue(mMagnificationController.resetIfNeeded(displayId, false)); + verify(mMockAms).notifyMagnificationChanged(eq(displayId), eq(INITIAL_MAGNIFICATION_REGION), eq(1.0f), anyFloat(), anyFloat()); - assertFalse(mMagnificationController.isMagnifying()); - assertFalse(mMagnificationController.resetIfNeeded(false)); + assertFalse(mMagnificationController.isMagnifying(displayId)); + assertFalse(mMagnificationController.resetIfNeeded(displayId, false)); } @Test public void testTurnScreenOff_resetsMagnification() { - mMagnificationController.register(); + register(DISPLAY_0); + register(DISPLAY_1); ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); verify(mMockContext).registerReceiver( broadcastReceiverCaptor.capture(), (IntentFilter) anyObject()); BroadcastReceiver br = broadcastReceiverCaptor.getValue(); - zoomIn2xToMiddle(); + zoomIn2xToMiddle(DISPLAY_0); + zoomIn2xToMiddle(DISPLAY_1); mMessageCapturingHandler.sendAllMessages(); br.onReceive(mMockContext, null); mMessageCapturingHandler.sendAllMessages(); - assertFalse(mMagnificationController.isMagnifying()); + assertFalse(mMagnificationController.isMagnifying(DISPLAY_0)); + assertFalse(mMagnificationController.isMagnifying(DISPLAY_1)); } @Test public void testUserContextChange_resetsMagnification() { - mMagnificationController.register(); - MagnificationCallbacks callbacks = getMagnificationCallbacks(); - zoomIn2xToMiddle(); + for (int i = 0; i < DISPLAY_COUNT; i++) { + contextChange_resetsMagnification(i); + resetMockWindowManager(); + } + } + + private void contextChange_resetsMagnification(int displayId) { + register(displayId); + MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId); + zoomIn2xToMiddle(displayId); mMessageCapturingHandler.sendAllMessages(); callbacks.onUserContextChanged(); mMessageCapturingHandler.sendAllMessages(); - assertFalse(mMagnificationController.isMagnifying()); + assertFalse(mMagnificationController.isMagnifying(displayId)); } @Test public void testRotation_resetsMagnification() { - mMagnificationController.register(); - MagnificationCallbacks callbacks = getMagnificationCallbacks(); - zoomIn2xToMiddle(); + for (int i = 0; i < DISPLAY_COUNT; i++) { + rotation_resetsMagnification(i); + resetMockWindowManager(); + } + } + + private void rotation_resetsMagnification(int displayId) { + register(displayId); + MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId); + zoomIn2xToMiddle(displayId); mMessageCapturingHandler.sendAllMessages(); - assertTrue(mMagnificationController.isMagnifying()); + assertTrue(mMagnificationController.isMagnifying(displayId)); callbacks.onRotationChanged(0); mMessageCapturingHandler.sendAllMessages(); - assertFalse(mMagnificationController.isMagnifying()); + assertFalse(mMagnificationController.isMagnifying(displayId)); } @Test public void testBoundsChange_whileMagnifyingWithCompatibleSpec_noSpecChange() { + for (int i = 0; i < DISPLAY_COUNT; i++) { + boundsChange_whileMagnifyingWithCompatibleSpec_noSpecChange(i); + resetMockWindowManager(); + } + } + + private void boundsChange_whileMagnifyingWithCompatibleSpec_noSpecChange(int displayId) { // Going from a small region to a large one leads to no issues - mMagnificationController.register(); - zoomIn2xToMiddle(); + register(displayId); + zoomIn2xToMiddle(displayId); mMessageCapturingHandler.sendAllMessages(); - MagnificationSpec startSpec = getCurrentMagnificationSpec(); - MagnificationCallbacks callbacks = getMagnificationCallbacks(); + MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId); + MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId); Mockito.reset(mMockWindowManager); callbacks.onMagnificationRegionChanged(OTHER_REGION_COMPAT); mMessageCapturingHandler.sendAllMessages(); - assertThat(getCurrentMagnificationSpec(), closeTo(startSpec)); + assertThat(getCurrentMagnificationSpec(displayId), closeTo(startSpec)); verifyNoMoreInteractions(mMockWindowManager); } @Test public void testBoundsChange_whileZoomingWithCompatibleSpec_noSpecChange() { - mMagnificationController.register(); + for (int i = 0; i < DISPLAY_COUNT; i++) { + boundsChange_whileZoomingWithCompatibleSpec_noSpecChange(i); + resetMockWindowManager(); + } + } + + private void boundsChange_whileZoomingWithCompatibleSpec_noSpecChange(int displayId) { + register(displayId); PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER; float scale = 2.0f; // setting animate parameter to true is differ from zoomIn2xToMiddle() - mMagnificationController.setScale(scale, startCenter.x, startCenter.y, true, SERVICE_ID_1); - MagnificationSpec startSpec = getCurrentMagnificationSpec(); - MagnificationCallbacks callbacks = getMagnificationCallbacks(); + mMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, true, + SERVICE_ID_1); + MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId); + MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId); Mockito.reset(mMockWindowManager); callbacks.onMagnificationRegionChanged(OTHER_REGION_COMPAT); mMessageCapturingHandler.sendAllMessages(); - assertThat(getCurrentMagnificationSpec(), closeTo(startSpec)); + assertThat(getCurrentMagnificationSpec(displayId), closeTo(startSpec)); verifyNoMoreInteractions(mMockWindowManager); } @Test public void testBoundsChange_whileMagnifyingWithIncompatibleSpec_offsetsConstrained() { + for (int i = 0; i < DISPLAY_COUNT; i++) { + boundsChange_whileMagnifyingWithIncompatibleSpec_offsetsConstrained(i); + resetMockWindowManager(); + } + } + + private void boundsChange_whileMagnifyingWithIncompatibleSpec_offsetsConstrained( + int displayId) { // In a large region, pan to the farthest point possible - mMagnificationController.register(); - MagnificationCallbacks callbacks = getMagnificationCallbacks(); + register(displayId); + MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId); callbacks.onMagnificationRegionChanged(OTHER_REGION); mMessageCapturingHandler.sendAllMessages(); PointF startCenter = OTHER_BOUNDS_LOWER_RIGHT_2X_CENTER; float scale = 2.0f; - mMagnificationController.setScale(scale, startCenter.x, startCenter.y, false, SERVICE_ID_1); + mMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, false, + SERVICE_ID_1); mMessageCapturingHandler.sendAllMessages(); - MagnificationSpec startSpec = getCurrentMagnificationSpec(); - verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(startSpec))); + MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId); + verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(startSpec))); Mockito.reset(mMockWindowManager); callbacks.onMagnificationRegionChanged(INITIAL_MAGNIFICATION_REGION); mMessageCapturingHandler.sendAllMessages(); - MagnificationSpec endSpec = getCurrentMagnificationSpec(); + MagnificationSpec endSpec = getCurrentMagnificationSpec(displayId); assertThat(endSpec, CoreMatchers.not(closeTo(startSpec))); PointF expectedOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER, scale); assertThat(endSpec, closeTo(getMagnificationSpec(scale, expectedOffsets))); - verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(endSpec))); + verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec))); } @Test public void testBoundsChange_whileZoomingWithIncompatibleSpec_jumpsToCompatibleSpec() { - mMagnificationController.register(); - MagnificationCallbacks callbacks = getMagnificationCallbacks(); + for (int i = 0; i < DISPLAY_COUNT; i++) { + boundsChange_whileZoomingWithIncompatibleSpec_jumpsToCompatibleSpec(i); + resetMockWindowManager(); + } + } + + private void boundsChange_whileZoomingWithIncompatibleSpec_jumpsToCompatibleSpec( + int displayId) { + register(displayId); + MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId); callbacks.onMagnificationRegionChanged(OTHER_REGION); mMessageCapturingHandler.sendAllMessages(); PointF startCenter = OTHER_BOUNDS_LOWER_RIGHT_2X_CENTER; float scale = 2.0f; - mMagnificationController.setScale(scale, startCenter.x, startCenter.y, true, SERVICE_ID_1); + mMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, true, + SERVICE_ID_1); mMessageCapturingHandler.sendAllMessages(); - MagnificationSpec startSpec = getCurrentMagnificationSpec(); + MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId); when(mMockValueAnimator.isRunning()).thenReturn(true); callbacks.onMagnificationRegionChanged(INITIAL_MAGNIFICATION_REGION); mMessageCapturingHandler.sendAllMessages(); verify(mMockValueAnimator).cancel(); - MagnificationSpec endSpec = getCurrentMagnificationSpec(); + MagnificationSpec endSpec = getCurrentMagnificationSpec(displayId); assertThat(endSpec, CoreMatchers.not(closeTo(startSpec))); PointF expectedOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER, scale); assertThat(endSpec, closeTo(getMagnificationSpec(scale, expectedOffsets))); - verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(endSpec))); + verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec))); } @Test public void testRequestRectOnScreen_rectAlreadyOnScreen_doesNothing() { - mMagnificationController.register(); - zoomIn2xToMiddle(); + for (int i = 0; i < DISPLAY_COUNT; i++) { + requestRectOnScreen_rectAlreadyOnScreen_doesNothing(i); + resetMockWindowManager(); + } + } + + private void requestRectOnScreen_rectAlreadyOnScreen_doesNothing(int displayId) { + register(displayId); + zoomIn2xToMiddle(displayId); mMessageCapturingHandler.sendAllMessages(); - MagnificationSpec startSpec = getCurrentMagnificationSpec(); - MagnificationCallbacks callbacks = getMagnificationCallbacks(); + MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId); + MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId); Mockito.reset(mMockWindowManager); int centerX = (int) INITIAL_MAGNIFICATION_BOUNDS_CENTER.x; int centerY = (int) INITIAL_MAGNIFICATION_BOUNDS_CENTER.y; callbacks.onRectangleOnScreenRequested(centerX - 1, centerY - 1, centerX + 1, centerY - 1); mMessageCapturingHandler.sendAllMessages(); - assertThat(getCurrentMagnificationSpec(), closeTo(startSpec)); + assertThat(getCurrentMagnificationSpec(displayId), closeTo(startSpec)); verifyNoMoreInteractions(mMockWindowManager); } @Test public void testRequestRectOnScreen_rectCanFitOnScreen_pansToGetRectOnScreen() { - mMagnificationController.register(); - zoomIn2xToMiddle(); + for (int i = 0; i < DISPLAY_COUNT; i++) { + requestRectOnScreen_rectCanFitOnScreen_pansToGetRectOnScreen(i); + resetMockWindowManager(); + } + } + + private void requestRectOnScreen_rectCanFitOnScreen_pansToGetRectOnScreen(int displayId) { + register(displayId); + zoomIn2xToMiddle(displayId); mMessageCapturingHandler.sendAllMessages(); - MagnificationCallbacks callbacks = getMagnificationCallbacks(); + MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId); Mockito.reset(mMockWindowManager); callbacks.onRectangleOnScreenRequested(0, 0, 1, 1); mMessageCapturingHandler.sendAllMessages(); MagnificationSpec expectedEndSpec = getMagnificationSpec(2.0f, 0, 0); - assertThat(getCurrentMagnificationSpec(), closeTo(expectedEndSpec)); - verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedEndSpec))); + assertThat(getCurrentMagnificationSpec(displayId), closeTo(expectedEndSpec)); + verify(mMockWindowManager).setMagnificationSpec(eq(displayId), + argThat(closeTo(expectedEndSpec))); } @Test public void testRequestRectOnScreen_garbageInput_doesNothing() { - mMagnificationController.register(); - zoomIn2xToMiddle(); + for (int i = 0; i < DISPLAY_COUNT; i++) { + requestRectOnScreen_garbageInput_doesNothing(i); + resetMockWindowManager(); + } + } + + private void requestRectOnScreen_garbageInput_doesNothing(int displayId) { + register(displayId); + zoomIn2xToMiddle(displayId); mMessageCapturingHandler.sendAllMessages(); - MagnificationSpec startSpec = getCurrentMagnificationSpec(); - MagnificationCallbacks callbacks = getMagnificationCallbacks(); + MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId); + MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId); Mockito.reset(mMockWindowManager); callbacks.onRectangleOnScreenRequested(0, 0, -50, -50); mMessageCapturingHandler.sendAllMessages(); - assertThat(getCurrentMagnificationSpec(), closeTo(startSpec)); + assertThat(getCurrentMagnificationSpec(displayId), closeTo(startSpec)); verifyNoMoreInteractions(mMockWindowManager); } @Test public void testRequestRectOnScreen_rectTooWide_pansToGetStartOnScreenBasedOnLocale() { + for (int i = 0; i < DISPLAY_COUNT; i++) { + requestRectOnScreen_rectTooWide_pansToGetStartOnScreenBasedOnLocale(i); + resetMockWindowManager(); + } + } + + private void requestRectOnScreen_rectTooWide_pansToGetStartOnScreenBasedOnLocale( + int displayId) { Locale.setDefault(new Locale("en", "us")); - mMagnificationController.register(); - zoomIn2xToMiddle(); + register(displayId); + zoomIn2xToMiddle(displayId); mMessageCapturingHandler.sendAllMessages(); - MagnificationCallbacks callbacks = getMagnificationCallbacks(); - MagnificationSpec startSpec = getCurrentMagnificationSpec(); + MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId); + MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId); Mockito.reset(mMockWindowManager); Rect wideRect = new Rect(0, 50, 100, 51); callbacks.onRectangleOnScreenRequested( wideRect.left, wideRect.top, wideRect.right, wideRect.bottom); mMessageCapturingHandler.sendAllMessages(); MagnificationSpec expectedEndSpec = getMagnificationSpec(2.0f, 0, startSpec.offsetY); - assertThat(getCurrentMagnificationSpec(), closeTo(expectedEndSpec)); - verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedEndSpec))); + assertThat(getCurrentMagnificationSpec(displayId), closeTo(expectedEndSpec)); + verify(mMockWindowManager).setMagnificationSpec(eq(displayId), + argThat(closeTo(expectedEndSpec))); Mockito.reset(mMockWindowManager); // Repeat with RTL @@ -692,50 +900,66 @@ public class MagnificationControllerTest { wideRect.left, wideRect.top, wideRect.right, wideRect.bottom); mMessageCapturingHandler.sendAllMessages(); expectedEndSpec = getMagnificationSpec(2.0f, -100, startSpec.offsetY); - assertThat(getCurrentMagnificationSpec(), closeTo(expectedEndSpec)); - verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedEndSpec))); + assertThat(getCurrentMagnificationSpec(displayId), closeTo(expectedEndSpec)); + verify(mMockWindowManager).setMagnificationSpec(eq(displayId), + argThat(closeTo(expectedEndSpec))); } @Test public void testRequestRectOnScreen_rectTooTall_pansMinimumToGetTopOnScreen() { - mMagnificationController.register(); - zoomIn2xToMiddle(); + for (int i = 0; i < DISPLAY_COUNT; i++) { + requestRectOnScreen_rectTooTall_pansMinimumToGetTopOnScreen(i); + resetMockWindowManager(); + } + } + + private void requestRectOnScreen_rectTooTall_pansMinimumToGetTopOnScreen(int displayId) { + register(displayId); + zoomIn2xToMiddle(displayId); mMessageCapturingHandler.sendAllMessages(); - MagnificationCallbacks callbacks = getMagnificationCallbacks(); - MagnificationSpec startSpec = getCurrentMagnificationSpec(); + MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId); + MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId); Mockito.reset(mMockWindowManager); Rect tallRect = new Rect(50, 0, 51, 100); callbacks.onRectangleOnScreenRequested( tallRect.left, tallRect.top, tallRect.right, tallRect.bottom); mMessageCapturingHandler.sendAllMessages(); MagnificationSpec expectedEndSpec = getMagnificationSpec(2.0f, startSpec.offsetX, 0); - assertThat(getCurrentMagnificationSpec(), closeTo(expectedEndSpec)); - verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedEndSpec))); + assertThat(getCurrentMagnificationSpec(displayId), closeTo(expectedEndSpec)); + verify(mMockWindowManager).setMagnificationSpec(eq(displayId), + argThat(closeTo(expectedEndSpec))); } @Test public void testChangeMagnification_duringAnimation_animatesToNewValue() { - mMagnificationController.register(); - MagnificationSpec startSpec = getCurrentMagnificationSpec(); + for (int i = 0; i < DISPLAY_COUNT; i++) { + changeMagnification_duringAnimation_animatesToNewValue(i); + resetMockWindowManager(); + } + } + + private void changeMagnification_duringAnimation_animatesToNewValue(int displayId) { + register(displayId); + MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId); float scale = 2.5f; PointF firstCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; MagnificationSpec firstEndSpec = getMagnificationSpec( scale, computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, firstCenter, scale)); - assertTrue(mMagnificationController.setScaleAndCenter(scale, firstCenter.x, firstCenter.y, - true, SERVICE_ID_1)); + assertTrue(mMagnificationController.setScaleAndCenter(displayId, + scale, firstCenter.x, firstCenter.y, true, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); - assertEquals(firstCenter.x, mMagnificationController.getCenterX(), 0.5); - assertEquals(firstCenter.y, mMagnificationController.getCenterY(), 0.5); - assertThat(getCurrentMagnificationSpec(), closeTo(firstEndSpec)); + assertEquals(firstCenter.x, mMagnificationController.getCenterX(displayId), 0.5); + assertEquals(firstCenter.y, mMagnificationController.getCenterY(displayId), 0.5); + assertThat(getCurrentMagnificationSpec(displayId), closeTo(firstEndSpec)); verify(mMockValueAnimator, times(1)).start(); // Initial value when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f); mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); - verify(mMockWindowManager).setMagnificationSpec(startSpec); - verify(mMockAms).notifyMagnificationChanged( + verify(mMockWindowManager).setMagnificationSpec(eq(displayId), eq(startSpec)); + verify(mMockAms).notifyMagnificationChanged(displayId, INITIAL_MAGNIFICATION_REGION, scale, firstCenter.x, firstCenter.y); Mockito.reset(mMockWindowManager); @@ -745,32 +969,34 @@ public class MagnificationControllerTest { mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); MagnificationSpec intermediateSpec1 = getInterpolatedMagSpec(startSpec, firstEndSpec, fraction); - verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(intermediateSpec1))); + verify(mMockWindowManager).setMagnificationSpec(eq(displayId), + argThat(closeTo(intermediateSpec1))); Mockito.reset(mMockWindowManager); PointF newCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER; MagnificationSpec newEndSpec = getMagnificationSpec( scale, computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale)); - assertTrue(mMagnificationController.setCenter( + assertTrue(mMagnificationController.setCenter(displayId, newCenter.x, newCenter.y, true, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); // Animation should have been restarted verify(mMockValueAnimator, times(2)).start(); - verify(mMockAms).notifyMagnificationChanged( + verify(mMockAms).notifyMagnificationChanged(displayId, INITIAL_MAGNIFICATION_REGION, scale, newCenter.x, newCenter.y); // New starting point should be where we left off when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f); mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); - verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(intermediateSpec1))); + verify(mMockWindowManager).setMagnificationSpec(eq(displayId), + argThat(closeTo(intermediateSpec1))); Mockito.reset(mMockWindowManager); // Second intermediate point fraction = 0.5f; when(mMockValueAnimator.getAnimatedFraction()).thenReturn(fraction); mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); - verify(mMockWindowManager).setMagnificationSpec( + verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(getInterpolatedMagSpec(intermediateSpec1, newEndSpec, fraction)))); Mockito.reset(mMockWindowManager); @@ -778,21 +1004,54 @@ public class MagnificationControllerTest { Mockito.reset(mMockWindowManager); when(mMockValueAnimator.getAnimatedFraction()).thenReturn(1.0f); mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); - verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(newEndSpec))); + verify(mMockWindowManager).setMagnificationSpec(eq(displayId), + argThat(closeTo(newEndSpec))); + } + + private void initMockWindowManager() { + for (int i = 0; i < DISPLAY_COUNT; i++) { + when(mMockWindowManager.setMagnificationCallbacks(eq(i), any())).thenReturn(true); + } + doAnswer(new Answer<Void>() { + @Override + public Void answer(InvocationOnMock invocationOnMock) throws Throwable { + Object[] args = invocationOnMock.getArguments(); + Region regionArg = (Region) args[1]; + regionArg.set(INITIAL_MAGNIFICATION_REGION); + return null; + } + }).when(mMockWindowManager).getMagnificationRegion(anyInt(), (Region) anyObject()); + } + + private void resetMockWindowManager() { + Mockito.reset(mMockWindowManager); + initMockWindowManager(); + } + + private void register(int displayId) { + mMockValueAnimator = mock(ValueAnimator.class); + when(mMockControllerCtx.newValueAnimator()).thenReturn(mMockValueAnimator); + mMagnificationController.register(displayId); + ArgumentCaptor<ValueAnimator.AnimatorUpdateListener> listenerArgumentCaptor = + ArgumentCaptor.forClass(ValueAnimator.AnimatorUpdateListener.class); + verify(mMockValueAnimator).addUpdateListener(listenerArgumentCaptor.capture()); + mTargetAnimationListener = listenerArgumentCaptor.getValue(); + Mockito.reset(mMockValueAnimator); // Ignore other initialization } - private void zoomIn2xToMiddle() { + private void zoomIn2xToMiddle(int displayId) { PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER; float scale = 2.0f; - mMagnificationController.setScale(scale, startCenter.x, startCenter.y, false, SERVICE_ID_1); - assertTrue(mMagnificationController.isMagnifying()); + mMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, false, + SERVICE_ID_1); + assertTrue(mMagnificationController.isMagnifying(displayId)); } - private MagnificationCallbacks getMagnificationCallbacks() { + private MagnificationCallbacks getMagnificationCallbacks(int displayId) { ArgumentCaptor<MagnificationCallbacks> magnificationCallbacksCaptor = ArgumentCaptor.forClass(MagnificationCallbacks.class); verify(mMockWindowManager) - .setMagnificationCallbacks(magnificationCallbacksCaptor.capture()); + .setMagnificationCallbacks(eq(displayId), magnificationCallbacksCaptor.capture()); return magnificationCallbacksCaptor.getValue(); } @@ -823,9 +1082,10 @@ public class MagnificationControllerTest { return spec; } - private MagnificationSpec getCurrentMagnificationSpec() { - return getMagnificationSpec(mMagnificationController.getScale(), - mMagnificationController.getOffsetX(), mMagnificationController.getOffsetY()); + private MagnificationSpec getCurrentMagnificationSpec(int displayId) { + return getMagnificationSpec(mMagnificationController.getScale(displayId), + mMagnificationController.getOffsetX(displayId), + mMagnificationController.getOffsetY(displayId)); } private MagSpecMatcher closeTo(MagnificationSpec spec) { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java index 032074a7e398..5083110342f4 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java @@ -26,16 +26,19 @@ import static com.android.server.testutils.TestUtils.strictMock; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.content.Context; +import android.os.Handler; import android.os.Message; import android.util.DebugUtils; import android.view.InputDevice; @@ -104,14 +107,9 @@ public class MagnificationGestureHandlerTest { public static final float DEFAULT_X = 301; public static final float DEFAULT_Y = 299; + private static final int DISPLAY_0 = 0; + private Context mContext; - final AccessibilityManagerService mMockAms = mock(AccessibilityManagerService.class); - final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class); - final MessageCapturingHandler mMessageCapturingHandler = - new MessageCapturingHandler(null); - final ValueAnimator mMockValueAnimator = mock(ValueAnimator.class); - MagnificationController.SettingsBridge mMockSettingsBridge = - mock(MagnificationController.SettingsBridge.class); MagnificationController mMagnificationController; private OffsettableClock mClock; @@ -123,18 +121,26 @@ public class MagnificationGestureHandlerTest { @Before public void setUp() { mContext = InstrumentationRegistry.getContext(); - mMagnificationController = new MagnificationController(mContext, mMockAms, new Object(), - mMessageCapturingHandler, mMockWindowManager, mMockValueAnimator, - mMockSettingsBridge) { + final MagnificationController.ControllerContext mockController = + mock(MagnificationController.ControllerContext.class); + final WindowManagerInternal mockWindowManager = mock(WindowManagerInternal.class); + when(mockController.getContext()).thenReturn(mContext); + when(mockController.getAms()).thenReturn(mock(AccessibilityManagerService.class)); + when(mockController.getWindowManager()).thenReturn(mockWindowManager); + when(mockController.getHandler()).thenReturn(new Handler(mContext.getMainLooper())); + when(mockController.newValueAnimator()).thenReturn(new ValueAnimator()); + when(mockController.getAnimationDuration()).thenReturn(1000L); + when(mockWindowManager.setMagnificationCallbacks(eq(DISPLAY_0), any())).thenReturn(true); + mMagnificationController = new MagnificationController(mockController, new Object()) { @Override - public boolean magnificationRegionContains(float x, float y) { + public boolean magnificationRegionContains(int displayId, float x, float y) { return true; } @Override - void setForceShowMagnifiableBounds(boolean show) {} + void setForceShowMagnifiableBounds(int displayId, boolean show) {} }; - mMagnificationController.register(); + mMagnificationController.register(DISPLAY_0); mClock = new OffsettableClock.Stopped(); boolean detectTripleTap = true; @@ -144,7 +150,7 @@ public class MagnificationGestureHandlerTest { @After public void tearDown() { - mMagnificationController.unregister(); + mMagnificationController.unregister(DISPLAY_0); } @NonNull @@ -509,7 +515,7 @@ public class MagnificationGestureHandlerTest { } private boolean isZoomed() { - return mMgh.mMagnificationController.isMagnifying(); + return mMgh.mMagnificationController.isMagnifying(DISPLAY_0); } private int tapCount() { |