Notifies magnification change for the transitioning target mode

When the magnification settings mode or config mode transitions,
MagnificationController would
1. Disables the current magnification mode. Then
2. Starts to enable the target magnification mode.

To prevent the magnification change callback send twice during the
mode transition, MagnificationController should only notify the
change of the transitioning target mode and ignore the notifcation
change for the disabling magnification mode.

And when WindowMagnification resets or disables by
WindowMagnificationController#deleteWindowMagnification(),
it should notify source bounds empty when magnification is deleted.

And when FullscreenMagnification resets and the animator is animating,
FullScreenMagnificationController should notify magnification
change.

Bug: 209701404
Bug: 210636326
Test: atest AccessibilityMagnificationTest,
    atest WindowMagnificationControllerTest,
    atest com.android.server.accessibility.magnification
Change-Id: Ief718614d80177a4a076eddc708a7ecfc4dfb73f
This commit is contained in:
mincheli
2022-01-28 14:20:57 +08:00
committed by Minche Li
parent 240171e237
commit d560a73ca9
5 changed files with 264 additions and 33 deletions

View File

@@ -353,6 +353,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
mSourceBounds.setEmpty();
updateSystemUIStateIfNeeded();
mContext.unregisterComponentCallbacks(this);
// Notify source bounds empty when magnification is deleted.
mWindowMagnifierCallback.onSourceBoundsChanged(mDisplayId, new Rect());
}
@Override

View File

@@ -292,6 +292,22 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
assertFalse(hasMagnificationOverlapFlag());
}
@Test
public void deleteWindowMagnification_notifySourceBoundsChanged() {
mInstrumentation.runOnMainSync(
() -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN,
Float.NaN,
Float.NaN));
mInstrumentation.runOnMainSync(
() -> mWindowMagnificationController.deleteWindowMagnification());
// The first time is for notifying magnification enabled and the second time is for
// notifying magnification disabled.
verify(mWindowMagnifierCallback, times(2)).onSourceBoundsChanged(
(eq(mContext.getDisplayId())), any());
}
@Test
public void moveMagnifier_schedulesFrame() {
mInstrumentation.runOnMainSync(() -> {

View File

@@ -356,13 +356,6 @@ public class FullScreenMagnificationController implements
mSpecAnimationBridge, spec, animationCallback);
mControllerCtx.getHandler().sendMessage(m);
}
final boolean lastMagnificationActivated = mMagnificationActivated;
mMagnificationActivated = spec.scale > 1.0f;
if (mMagnificationActivated != lastMagnificationActivated) {
mMagnificationInfoChangedCallback.onFullScreenMagnificationActivationState(
mDisplayId, mMagnificationActivated);
}
}
/**
@@ -376,9 +369,17 @@ public class FullScreenMagnificationController implements
@GuardedBy("mLock")
void onMagnificationChangedLocked() {
final float scale = getScale();
final boolean lastMagnificationActivated = mMagnificationActivated;
mMagnificationActivated = scale > 1.0f;
if (mMagnificationActivated != lastMagnificationActivated) {
mMagnificationInfoChangedCallback.onFullScreenMagnificationActivationState(
mDisplayId, mMagnificationActivated);
}
final MagnificationConfig config = new MagnificationConfig.Builder()
.setMode(MAGNIFICATION_MODE_FULLSCREEN)
.setScale(getScale())
.setScale(scale)
.setCenterX(getCenterX())
.setCenterY(getCenterY()).build();
mMagnificationInfoChangedCallback.onFullScreenMagnificationChanged(mDisplayId,

View File

@@ -16,6 +16,7 @@
package com.android.server.accessibility.magnification;
import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN;
import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_WINDOW;
import static android.content.pm.PackageManager.FEATURE_WINDOW_MAGNIFICATION;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
@@ -111,6 +112,15 @@ public class MagnificationController implements WindowMagnificationManager.Callb
@GuardedBy("mLock")
private final SparseLongArray mFullScreenModeEnabledTimeArray = new SparseLongArray();
/**
* The transitioning magnification modes on the displays. The controller notifies
* magnification change depending on the target config mode.
* If the target mode is null, it means the config mode of the display is not
* transitioning.
*/
@GuardedBy("mLock")
private final SparseArray<Integer> mTransitionModes = new SparseArray();
@GuardedBy("mLock")
private final SparseArray<WindowManagerInternal.AccessibilityControllerInternal
.UiChangesForAccessibilityCallbacks> mAccessibilityCallbacksDelegateArray =
@@ -213,6 +223,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
final PointF currentCenter = getCurrentMagnificationCenterLocked(displayId, targetMode);
final DisableMagnificationCallback animationCallback =
getDisableMagnificationEndRunnableLocked(displayId);
if (currentCenter == null && animationCallback == null) {
transitionCallBack.onResult(displayId, true);
return;
@@ -233,6 +244,9 @@ public class MagnificationController implements WindowMagnificationManager.Callb
transitionCallBack.onResult(displayId, true);
return;
}
setTransitionState(displayId, targetMode);
final FullScreenMagnificationController screenMagnificationController =
getFullScreenMagnificationController();
final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationMgr();
@@ -286,26 +300,51 @@ public class MagnificationController implements WindowMagnificationManager.Callb
Slog.w(TAG, "Discard previous animation request");
animationCallback.setExpiredAndRemoveFromListLocked();
}
final FullScreenMagnificationController screenMagnificationController =
getFullScreenMagnificationController();
final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationMgr();
final float targetScale = Float.isNaN(config.getScale())
? getTargetModeScaleFromCurrentMagnification(displayId, targetMode)
: config.getScale();
if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
screenMagnificationController.reset(displayId, false);
windowMagnificationMgr.enableWindowMagnification(displayId,
targetScale, magnificationCenter.x, magnificationCenter.y,
animate ? STUB_ANIMATION_CALLBACK : null, id);
} else if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
windowMagnificationMgr.disableWindowMagnification(displayId, false, null);
if (!screenMagnificationController.isRegistered(displayId)) {
screenMagnificationController.register(displayId);
try {
setTransitionState(displayId, targetMode);
if (targetMode == MAGNIFICATION_MODE_WINDOW) {
screenMagnificationController.reset(displayId, false);
windowMagnificationMgr.enableWindowMagnification(displayId,
targetScale, magnificationCenter.x, magnificationCenter.y,
animate ? STUB_ANIMATION_CALLBACK : null, id);
} else if (targetMode == MAGNIFICATION_MODE_FULLSCREEN) {
windowMagnificationMgr.disableWindowMagnification(displayId, false, null);
if (!screenMagnificationController.isRegistered(displayId)) {
screenMagnificationController.register(displayId);
}
screenMagnificationController.setScaleAndCenter(displayId, targetScale,
magnificationCenter.x, magnificationCenter.y, animate,
id);
}
screenMagnificationController.setScaleAndCenter(displayId, targetScale,
magnificationCenter.x, magnificationCenter.y, animate,
id);
} finally {
// Reset transition state after enabling target mode.
setTransitionState(displayId, null);
}
}
}
/**
* Sets magnification config mode transition state. Called when the mode transition starts and
* ends. If the targetMode and the display id are null, it resets all
* the transition state.
*
* @param displayId The logical display id
* @param targetMode The transition target mode. It is not transitioning, if the target mode
* is set null
*/
private void setTransitionState(Integer displayId, Integer targetMode) {
synchronized (mLock) {
if (targetMode == null && displayId == null) {
mTransitionModes.clear();
} else {
mTransitionModes.put(displayId, targetMode);
}
}
}
@@ -413,18 +452,57 @@ public class MagnificationController implements WindowMagnificationManager.Callb
@Override
public void onSourceBoundsChanged(int displayId, Rect bounds) {
final MagnificationConfig config = new MagnificationConfig.Builder()
.setMode(MAGNIFICATION_MODE_WINDOW)
.setScale(getWindowMagnificationMgr().getScale(displayId))
.setCenterX(bounds.exactCenterX())
.setCenterY(bounds.exactCenterY()).build();
mAms.notifyMagnificationChanged(displayId, new Region(bounds), config);
if (shouldNotifyMagnificationChange(displayId, MAGNIFICATION_MODE_WINDOW)) {
final MagnificationConfig config = new MagnificationConfig.Builder()
.setMode(MAGNIFICATION_MODE_WINDOW)
.setScale(getWindowMagnificationMgr().getScale(displayId))
.setCenterX(bounds.exactCenterX())
.setCenterY(bounds.exactCenterY()).build();
mAms.notifyMagnificationChanged(displayId, new Region(bounds), config);
}
}
@Override
public void onFullScreenMagnificationChanged(int displayId, @NonNull Region region,
@NonNull MagnificationConfig config) {
mAms.notifyMagnificationChanged(displayId, region, config);
if (shouldNotifyMagnificationChange(displayId, MAGNIFICATION_MODE_FULLSCREEN)) {
mAms.notifyMagnificationChanged(displayId, region, config);
}
}
/**
* Should notify magnification change for the given display under the conditions below
*
* <ol>
* <li> 1. No mode transitioning and the change mode is active. </li>
* <li> 2. No mode transitioning and all the modes are inactive. </li>
* <li> 3. It is mode transitioning and the change mode is the transition mode. </li>
* </ol>
*
* @param displayId The logical display id
* @param changeMode The mode that has magnification spec change
*/
private boolean shouldNotifyMagnificationChange(int displayId, int changeMode) {
synchronized (mLock) {
final boolean fullScreenMagnifying = mFullScreenMagnificationController != null
&& mFullScreenMagnificationController.isMagnifying(displayId);
final boolean windowEnabled = mWindowMagnificationMgr != null
&& mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId);
final Integer transitionMode = mTransitionModes.get(displayId);
if (((changeMode == MAGNIFICATION_MODE_FULLSCREEN && fullScreenMagnifying)
|| (changeMode == MAGNIFICATION_MODE_WINDOW && windowEnabled))
&& (transitionMode == null)) {
return true;
}
if ((!fullScreenMagnifying && !windowEnabled)
&& (transitionMode == null)) {
return true;
}
if (transitionMode != null && changeMode == transitionMode) {
return true;
}
}
return false;
}
private void disableFullScreenMagnificationIfNeeded(int displayId) {
@@ -740,9 +818,32 @@ public class MagnificationController implements WindowMagnificationManager.Callb
return;
}
setExpiredAndRemoveFromListLocked();
setTransitionState(mDisplayId, null);
if (success) {
adjustCurrentCenterIfNeededLocked();
applyMagnificationModeLocked(mTargetMode);
} else {
// Notify magnification change if magnification is inactive when the
// transition is failed. This is for the failed transition from
// full-screen to window mode. Disable magnification callback helps to send
// magnification inactive change since FullScreenMagnificationController
// would not notify magnification change if the spec is not changed.
final FullScreenMagnificationController screenMagnificationController =
getFullScreenMagnificationController();
if (mCurrentMode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
&& !screenMagnificationController.isMagnifying(mDisplayId)) {
MagnificationConfig.Builder configBuilder =
new MagnificationConfig.Builder();
Region region = new Region();
configBuilder.setMode(MAGNIFICATION_MODE_FULLSCREEN)
.setScale(screenMagnificationController.getScale(mDisplayId))
.setCenterX(screenMagnificationController.getCenterX(mDisplayId))
.setCenterY(screenMagnificationController.getCenterY(mDisplayId));
screenMagnificationController.getMagnificationRegion(mDisplayId,
region);
mAms.notifyMagnificationChanged(mDisplayId, region, configBuilder.build());
}
}
updateMagnificationButton(mDisplayId, mTargetMode);
if (mTransitionCallBack != null) {
@@ -770,6 +871,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
return;
}
setExpiredAndRemoveFromListLocked();
setTransitionState(mDisplayId, null);
applyMagnificationModeLocked(mCurrentMode);
updateMagnificationButton(mDisplayId, mCurrentMode);
if (mTransitionCallBack != null) {

View File

@@ -197,6 +197,31 @@ public class MagnificationControllerTest {
assertEquals(MAGNIFIED_CENTER_Y, mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 0);
}
@Test
public void transitionToWindowModeFailedByReset_fullScreenMagnifying_notifyTransitionFailed()
throws RemoteException {
setMagnificationEnabled(MODE_FULLSCREEN);
mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_WINDOW,
mTransitionCallBack);
verify(mScreenMagnificationController).reset(eq(TEST_DISPLAY),
mCallbackArgumentCaptor.capture());
// The transition is interrupted and failed by calling reset.
mCallbackArgumentCaptor.getValue().onResult(false);
verify(mTransitionCallBack).onResult(TEST_DISPLAY, false);
final ArgumentCaptor<MagnificationConfig> configCaptor = ArgumentCaptor.forClass(
MagnificationConfig.class);
// The first time is for notifying full-screen enabled and the second time is for notifying
// the target mode transitions failed.
verify(mService, times(2)).notifyMagnificationChanged(eq(TEST_DISPLAY), any(Region.class),
configCaptor.capture());
final MagnificationConfig actualConfig = configCaptor.getValue();
assertEquals(MODE_FULLSCREEN, actualConfig.getMode(), 0);
assertEquals(1.0f, actualConfig.getScale(), 0);
}
@Test
public void transitionToWindowMode_disablingWindowMode_enablingWindowWithFormerCenter()
throws RemoteException {
@@ -478,6 +503,92 @@ public class MagnificationControllerTest {
assertEquals(config.getScale(), actualConfig.getScale(), 0);
}
@Test
public void transitionMagnificationMode_windowEnabled_notifyTargetMagnificationChanged()
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_FULLSCREEN, mTransitionCallBack);
mMockConnection.invokeCallbacks();
final ArgumentCaptor<MagnificationConfig> configCaptor = ArgumentCaptor.forClass(
MagnificationConfig.class);
// The first time is for notifying window enabled and the second time is for notifying
// the target mode transitions.
verify(mService, times(2)).notifyMagnificationChanged(eq(TEST_DISPLAY), any(Region.class),
configCaptor.capture());
final MagnificationConfig actualConfig = configCaptor.getValue();
assertEquals(MODE_FULLSCREEN, actualConfig.getMode(), 0);
}
@Test
public void transitionConfigMode_windowEnabled_notifyTargetMagnificationChanged()
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
final MagnificationConfig config = obtainMagnificationConfig(MODE_FULLSCREEN);
mMagnificationController.transitionMagnificationConfigMode(TEST_DISPLAY,
config, true, TEST_SERVICE_ID);
mMockConnection.invokeCallbacks();
final ArgumentCaptor<MagnificationConfig> configCaptor = ArgumentCaptor.forClass(
MagnificationConfig.class);
// The first time is for notifying window enabled and the second time is for notifying
// the target mode transitions.
verify(mService, times(2)).notifyMagnificationChanged(eq(TEST_DISPLAY), any(Region.class),
configCaptor.capture());
final MagnificationConfig actualConfig = configCaptor.getValue();
assertEquals(config.getCenterX(), actualConfig.getCenterX(), 0);
assertEquals(config.getCenterY(), actualConfig.getCenterY(), 0);
assertEquals(config.getScale(), actualConfig.getScale(), 0);
}
@Test
public void transitionMagnificationMode_fullScreenEnabled_notifyTargetMagnificationChanged()
throws RemoteException {
setMagnificationEnabled(MODE_FULLSCREEN);
mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_WINDOW, mTransitionCallBack);
verify(mScreenMagnificationController).reset(eq(TEST_DISPLAY),
mCallbackArgumentCaptor.capture());
mCallbackArgumentCaptor.getValue().onResult(true);
mMockConnection.invokeCallbacks();
final ArgumentCaptor<MagnificationConfig> configCaptor = ArgumentCaptor.forClass(
MagnificationConfig.class);
// The first time is for notifying full-screen enabled and the second time is for notifying
// the target mode transitions.
verify(mService, times(2)).notifyMagnificationChanged(eq(TEST_DISPLAY), any(Region.class),
configCaptor.capture());
final MagnificationConfig actualConfig = configCaptor.getValue();
assertEquals(MODE_WINDOW, actualConfig.getMode(), 0);
}
@Test
public void transitionConfigMode_fullScreenEnabled_notifyTargetMagnificationChanged()
throws RemoteException {
setMagnificationEnabled(MODE_FULLSCREEN);
final MagnificationConfig config = obtainMagnificationConfig(MODE_WINDOW);
mMagnificationController.transitionMagnificationConfigMode(TEST_DISPLAY,
config, true, TEST_SERVICE_ID);
mMockConnection.invokeCallbacks();
final ArgumentCaptor<MagnificationConfig> configCaptor = ArgumentCaptor.forClass(
MagnificationConfig.class);
// The first time is for notifying full-screen enabled and the second time is for notifying
// the target mode transitions.
verify(mService, times(2)).notifyMagnificationChanged(eq(TEST_DISPLAY), any(Region.class),
configCaptor.capture());
final MagnificationConfig actualConfig = configCaptor.getValue();
assertEquals(config.getCenterX(), actualConfig.getCenterX(), 0);
assertEquals(config.getCenterY(), actualConfig.getCenterY(), 0);
assertEquals(config.getScale(), actualConfig.getScale(), 0);
}
@Test
public void onAccessibilityActionPerformed_magnifierEnabled_showMagnificationButton()
throws RemoteException {
@@ -743,7 +854,7 @@ public class MagnificationControllerTest {
}
@Test
public void disableWindowMode_windowModeInActive_removeMagnificationButton()
public void disableWindowMode_windowEnabled_removeMagnificationButton()
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
@@ -753,7 +864,7 @@ public class MagnificationControllerTest {
}
@Test
public void onFullScreenDeactivated_fullscreenModeInActive_removeMagnificationButton()
public void onFullScreenDeactivated_fullScreenEnabled_removeMagnificationButton()
throws RemoteException {
setMagnificationEnabled(MODE_FULLSCREEN);
mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY,
@@ -766,7 +877,7 @@ public class MagnificationControllerTest {
}
@Test
public void transitionToFullScreenMode_fullscreenModeInActive_showMagnificationButton()
public void transitionToFullScreenMode_windowEnabled_showMagnificationButton()
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
@@ -779,7 +890,7 @@ public class MagnificationControllerTest {
}
@Test
public void transitionToWindow_fullscreenModeInActive_showMagnificationButton()
public void transitionToWindow_fullScreenEnabled_showMagnificationButton()
throws RemoteException {
setMagnificationEnabled(MODE_FULLSCREEN);
@@ -1018,7 +1129,6 @@ public class MagnificationControllerTest {
reset();
}
final MagnificationConfig config = new MagnificationConfig.Builder().setMode(
MODE_FULLSCREEN).setScale(mScale).setCenterX(mCenterX).setCenterY(
mCenterY).build();