summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Ryan Lin <ryanlwlin@google.com> 2020-09-16 01:13:03 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2020-09-16 01:13:03 +0000
commit28a10589ef7f6d6fff051ac6082dda1d2444d397 (patch)
tree45319aa0e867076622a4cbdf15986d1cb3387cc8
parentf50b1540d88e122de84e67255060152e53a1de57 (diff)
parent379e238c6ba2780f67205665292ec909b92a6ca5 (diff)
Merge changes Ic12d6d90,Iecd5e1bc
* changes: Implement transitn animation while switching the mode Change magnification animation callback (2/2)
-rw-r--r--core/java/android/view/accessibility/IRemoteMagnificationAnimationCallback.aidl33
-rw-r--r--core/java/android/view/accessibility/IWindowMagnificationConnection.aidl11
-rw-r--r--core/java/android/view/accessibility/MagnificationAnimationCallback.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java48
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java128
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java34
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationTransitionController.java237
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java36
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java100
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java15
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationTransitionControllerTest.java279
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java100
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java30
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java29
16 files changed, 953 insertions, 172 deletions
diff --git a/core/java/android/view/accessibility/IRemoteMagnificationAnimationCallback.aidl b/core/java/android/view/accessibility/IRemoteMagnificationAnimationCallback.aidl
new file mode 100644
index 000000000000..946ea16575bd
--- /dev/null
+++ b/core/java/android/view/accessibility/IRemoteMagnificationAnimationCallback.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.accessibility;
+
+/**
+ * A callback for magnification animation result.
+ * @hide
+ */
+
+ oneway interface IRemoteMagnificationAnimationCallback {
+
+ /**
+ * Called when the animation is finished or interrupted during animating.
+ *
+ * @param success {@code true} if animating successfully with given spec or the spec did not
+ * change. Otherwise {@code false}
+ */
+ void onResult(boolean success);
+}
diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
index ae853e952d25..ddf68fcb1311 100644
--- a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
+++ b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
@@ -18,8 +18,8 @@ package android.view.accessibility;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.os.RemoteCallback;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
/**
* Interface for interaction between {@link AccessibilityManagerService}
@@ -38,10 +38,10 @@ oneway interface IWindowMagnificationConnection {
* or {@link Float#NaN} to leave unchanged.
* @param centerY the screen-relative Y coordinate around which to center,
* or {@link Float#NaN} to leave unchanged.
- * @param endCallback The callback called when the animation is completed.
+ * @param callback The callback called when the animation is completed or interrupted.
*/
void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
- in RemoteCallback endCallback);
+ in IRemoteMagnificationAnimationCallback callback);
/**
* Sets the scale of the window magnifier on specified display.
@@ -55,9 +55,10 @@ oneway interface IWindowMagnificationConnection {
* Disables window magnification on specified display with animation.
*
* @param displayId The logical display id.
- * @param endCallback The callback called when the animation is completed.
+ * @param callback The callback called when the animation is completed or interrupted.
*/
- void disableWindowMagnification(int displayId, in RemoteCallback endCallback);
+ void disableWindowMagnification(int displayId,
+ in IRemoteMagnificationAnimationCallback callback);
/**
* Moves the window magnifier on the specified display. It has no effect while animating.
diff --git a/core/java/android/view/accessibility/MagnificationAnimationCallback.java b/core/java/android/view/accessibility/MagnificationAnimationCallback.java
index 491f7fb32a8c..bc9fb0a3e5e0 100644
--- a/core/java/android/view/accessibility/MagnificationAnimationCallback.java
+++ b/core/java/android/view/accessibility/MagnificationAnimationCallback.java
@@ -28,4 +28,4 @@ public interface MagnificationAnimationCallback {
* change. Otherwise {@code false}
*/
void onResult(boolean success);
-}
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index e10d2be4cb5e..911bf9ef757b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -24,11 +24,11 @@ import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Handler;
-import android.os.RemoteCallback;
import android.os.RemoteException;
import android.util.Log;
import android.view.SurfaceControl;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.accessibility.IWindowMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
@@ -101,10 +101,10 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall
@MainThread
void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
- @Nullable RemoteCallback endCallback) {
+ @Nullable IRemoteMagnificationAnimationCallback callback) {
//TODO: b/144080869 support multi-display.
mWindowMagnificationAnimationController.enableWindowMagnification(scale, centerX, centerY,
- endCallback != null ? () -> endCallback.sendResult(null) : null);
+ callback);
}
@MainThread
@@ -120,10 +120,10 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall
}
@MainThread
- void disableWindowMagnification(int displayId, @Nullable RemoteCallback endCallback) {
+ void disableWindowMagnification(int displayId,
+ @Nullable IRemoteMagnificationAnimationCallback callback) {
//TODO: b/144080869 support multi-display.
- mWindowMagnificationAnimationController.deleteWindowMagnification(
- endCallback != null ? () -> endCallback.sendResult(null) : null);
+ mWindowMagnificationAnimationController.deleteWindowMagnification(callback);
}
@Override
@@ -182,10 +182,10 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall
@Override
public void enableWindowMagnification(int displayId, float scale, float centerX,
- float centerY, RemoteCallback remoteCallback) {
+ float centerY, IRemoteMagnificationAnimationCallback callback) {
mHandler.post(
() -> mWindowMagnification.enableWindowMagnification(displayId, scale, centerX,
- centerY, remoteCallback));
+ centerY, callback));
}
@Override
@@ -194,9 +194,10 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall
}
@Override
- public void disableWindowMagnification(int displayId, RemoteCallback remoteCallback) {
+ public void disableWindowMagnification(int displayId,
+ IRemoteMagnificationAnimationCallback callback) {
mHandler.post(() -> mWindowMagnification.disableWindowMagnification(displayId,
- remoteCallback));
+ callback));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
index 2f2e3eaddd3b..24d83884f093 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
@@ -22,7 +22,9 @@ import android.annotation.IntDef;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
+import android.os.RemoteException;
import android.util.Log;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.animation.AccelerateInterpolator;
import com.android.internal.annotations.VisibleForTesting;
@@ -38,7 +40,7 @@ import java.lang.annotation.RetentionPolicy;
class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUpdateListener,
Animator.AnimatorListener {
- private static final String TAG = "WindowMagnificationBridge";
+ private static final String TAG = "WindowMagnificationAnimationController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@Retention(RetentionPolicy.SOURCE)
@@ -61,7 +63,7 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
private final Context mContext;
// Called when the animation is ended successfully without cancelling or mStartSpec and
// mEndSpec are equal.
- private Runnable mAnimationEndCallback;
+ private IRemoteMagnificationAnimationCallback mAnimationCallback;
// The flag to ignore the animation end callback.
private boolean mEndAnimationCanceled = false;
@MagnificationState
@@ -93,14 +95,16 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
* or {@link Float#NaN} to leave unchanged.
* @param centerY The screen-relative Y coordinate around which to center,
* or {@link Float#NaN} to leave unchanged.
- * @param animationEndCallback Called when the transition is complete or the given arguments
- * are as same as current values.
+ * @param animationCallback Called when the transition is complete, the given arguments
+ * are as same as current values, or the transition is interrupted
+ * due to the new transition request.
*
* @see #onAnimationUpdate(ValueAnimator)
*/
void enableWindowMagnification(float scale, float centerX, float centerY,
- @Nullable Runnable animationEndCallback) {
- mAnimationEndCallback = animationEndCallback;
+ @Nullable IRemoteMagnificationAnimationCallback animationCallback) {
+ sendAnimationCallback(false);
+ mAnimationCallback = animationCallback;
setupEnableAnimationSpecs(scale, centerX, centerY);
if (mEndSpec.equals(mStartSpec)) {
if (mState == STATE_DISABLED) {
@@ -108,7 +112,7 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
} else if (mState == STATE_ENABLING || mState == STATE_DISABLING) {
mValueAnimator.cancel();
}
- sendCallbackIfNeeded();
+ sendAnimationCallback(true);
setState(STATE_ENABLED);
} else {
if (mState == STATE_DISABLING) {
@@ -160,14 +164,17 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
* Wraps {@link WindowMagnificationController#deleteWindowMagnification()}} with transition
* animation. If the window magnification is enabling, it runs the animation in reverse.
*
- * @param animationEndCallback Called when the transition is complete or the window
- * magnification is disabled already.
+ * @param animationCallback Called when the transition is complete, the given arguments
+ * are as same as current values, or the transition is interrupted
+ * due to the new transition request.
*/
- void deleteWindowMagnification(@Nullable Runnable animationEndCallback) {
- mAnimationEndCallback = animationEndCallback;
+ void deleteWindowMagnification(
+ @Nullable IRemoteMagnificationAnimationCallback animationCallback) {
+ sendAnimationCallback(false);
+ mAnimationCallback = animationCallback;
if (mState == STATE_DISABLED || mState == STATE_DISABLING) {
if (mState == STATE_DISABLED) {
- sendCallbackIfNeeded();
+ sendAnimationCallback(true);
}
return;
}
@@ -220,7 +227,7 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
} else {
setState(STATE_ENABLED);
}
- sendCallbackIfNeeded();
+ sendAnimationCallback(true);
}
@Override
@@ -236,10 +243,17 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
public void onAnimationRepeat(Animator animation) {
}
- private void sendCallbackIfNeeded() {
- if (mAnimationEndCallback != null) {
- mAnimationEndCallback.run();
- mAnimationEndCallback = null;
+ private void sendAnimationCallback(boolean success) {
+ if (mAnimationCallback != null) {
+ try {
+ mAnimationCallback.onResult(success);
+ if (DEBUG) {
+ Log.d(TAG, "sendAnimationCallback success = " + success);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "sendAnimationCallback failed : " + e);
+ }
+ mAnimationCallback = null;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
index 9079338cd502..1e969c226ff1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
@@ -23,13 +23,13 @@ import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import android.content.Context;
-import android.os.RemoteCallback;
import android.os.RemoteException;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.Display;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.accessibility.IWindowMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
@@ -41,7 +41,6 @@ import com.android.systemui.statusbar.CommandQueue;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -66,8 +65,7 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase {
@Mock
private ModeSwitchesController mModeSwitchesController;
@Mock
- private RemoteCallback mRemoteCallback;
- private ArgumentCaptor<Runnable> mRunnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+ private IRemoteMagnificationAnimationCallback mAnimationCallback;
private IWindowMagnificationConnection mIWindowMagnificationConnection;
private WindowMagnification mWindowMagnification;
@@ -92,22 +90,21 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase {
@Test
public void enableWindowMagnification_passThrough() throws RemoteException {
mIWindowMagnificationConnection.enableWindowMagnification(TEST_DISPLAY, 3.0f, Float.NaN,
- Float.NaN, mRemoteCallback);
+ Float.NaN, mAnimationCallback);
waitForIdleSync();
verify(mWindowMagnificationAnimationController).enableWindowMagnification(eq(3.0f),
- eq(Float.NaN), eq(Float.NaN), mRunnableCaptor.capture());
- verifyRunnableWrapsRemoteCallback(mRunnableCaptor.getValue());
+ eq(Float.NaN), eq(Float.NaN), eq(mAnimationCallback));
}
@Test
public void disableWindowMagnification_deleteWindowMagnification() throws RemoteException {
- mIWindowMagnificationConnection.disableWindowMagnification(TEST_DISPLAY, mRemoteCallback);
+ mIWindowMagnificationConnection.disableWindowMagnification(TEST_DISPLAY,
+ mAnimationCallback);
waitForIdleSync();
verify(mWindowMagnificationAnimationController).deleteWindowMagnification(
- mRunnableCaptor.capture());
- verifyRunnableWrapsRemoteCallback(mRunnableCaptor.getValue());
+ mAnimationCallback);
}
@Test
@@ -143,10 +140,5 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase {
verify(mModeSwitchesController).removeButton(TEST_DISPLAY);
}
-
- private void verifyRunnableWrapsRemoteCallback(Runnable runnable) {
- runnable.run();
- verify(mRemoteCallback).sendResult(null);
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index d74c62b90fda..33b1d94df80e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -18,6 +18,7 @@ package com.android.systemui.accessibility;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.never;
@@ -28,9 +29,11 @@ import android.annotation.Nullable;
import android.app.Instrumentation;
import android.content.Context;
import android.os.Handler;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.testing.AndroidTestingRunner;
import android.view.SurfaceControl;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.animation.AccelerateInterpolator;
import androidx.test.InstrumentationRegistry;
@@ -75,9 +78,9 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
@Mock
WindowMagnifierCallback mWindowMagnifierCallback;
@Mock
- Runnable mAnimationEndCallback;
+ IRemoteMagnificationAnimationCallback mAnimationCallback;
@Mock
- Runnable mAnimationEndCallback2;
+ IRemoteMagnificationAnimationCallback mAnimationCallback2;
private SpyWindowMagnificationController mController;
private WindowMagnificationController mSpyController;
private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
@@ -105,8 +108,9 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
}
@Test
- public void enableWindowMagnification_disabled_expectedValuesAndInvokeCallback() {
- enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationEndCallback);
+ public void enableWindowMagnification_disabled_expectedValuesAndInvokeCallback()
+ throws RemoteException {
+ enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback);
verify(mSpyController, atLeast(2)).enableWindowMagnification(
mScaleCaptor.capture(),
@@ -115,28 +119,30 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
verifyStartValue(mCenterXCaptor, DEFAULT_CENTER_X);
verifyStartValue(mCenterYCaptor, DEFAULT_CENTER_Y);
verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
- verify(mAnimationEndCallback).run();
+ verify(mAnimationCallback).onResult(true);
}
@Test
- public void enableWindowMagnificationWithScaleOne_disabled_NoAnimationAndInvokeCallback() {
+ public void enableWindowMagnificationWithScaleOne_disabled_NoAnimationAndInvokeCallback()
+ throws RemoteException {
mInstrumentation.runOnMainSync(
() -> {
mWindowMagnificationAnimationController.enableWindowMagnification(1,
- DEFAULT_CENTER_X, DEFAULT_CENTER_Y, mAnimationEndCallback);
+ DEFAULT_CENTER_X, DEFAULT_CENTER_Y, mAnimationCallback);
});
SystemClock.sleep(mWaitingAnimationPeriod);
verify(mSpyController).enableWindowMagnification(1, DEFAULT_CENTER_X,
DEFAULT_CENTER_Y);
- verify(mAnimationEndCallback).run();
+ verify(mAnimationCallback).onResult(true);
}
@Test
- public void enableWindowMagnification_enabling_expectedValuesAndInvokeCallback() {
+ public void enableWindowMagnification_enabling_expectedValuesAndInvokeCallback()
+ throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
- mAnimationEndCallback);
+ mAnimationCallback);
final float targetScale = DEFAULT_SCALE + 1.0f;
final float targetCenterX = DEFAULT_CENTER_X + 100;
final float targetCenterY = DEFAULT_CENTER_Y + 100;
@@ -144,7 +150,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
mInstrumentation.runOnMainSync(() -> {
Mockito.reset(mSpyController);
mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
- targetCenterX, targetCenterY, mAnimationEndCallback2);
+ targetCenterX, targetCenterY, mAnimationCallback2);
mCurrentScale.set(mController.getScale());
mCurrentCenterX.set(mController.getCenterX());
mCurrentCenterY.set(mController.getCenterY());
@@ -158,33 +164,35 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
- verify(mAnimationEndCallback, never()).run();
- verify(mAnimationEndCallback2).run();
+ verify(mAnimationCallback).onResult(false);
+ verify(mAnimationCallback2).onResult(true);
}
@Test
- public void enableWindowMagnificationWithSameSpec_enabling_NoAnimationAndInvokeCallback() {
+ public void enableWindowMagnificationWithSameSpec_enabling_NoAnimationAndInvokeCallback()
+ throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
- mAnimationEndCallback);
+ mAnimationCallback);
mInstrumentation.runOnMainSync(() -> {
Mockito.reset(mSpyController);
mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN,
- Float.NaN, Float.NaN, mAnimationEndCallback2);
+ Float.NaN, Float.NaN, mAnimationCallback2);
});
SystemClock.sleep(mWaitingAnimationPeriod);
verify(mSpyController, never()).enableWindowMagnification(anyFloat(), anyFloat(),
anyFloat());
- verify(mAnimationEndCallback, never()).run();
- verify(mAnimationEndCallback2).run();
+ verify(mAnimationCallback).onResult(false);
+ verify(mAnimationCallback2).onResult(true);
}
@Test
- public void enableWindowMagnification_disabling_expectedValuesAndInvokeCallback() {
+ public void enableWindowMagnification_disabling_expectedValuesAndInvokeCallback()
+ throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
- mAnimationEndCallback);
+ mAnimationCallback);
final float targetScale = DEFAULT_SCALE + 1.0f;
final float targetCenterX = DEFAULT_CENTER_X + 100;
final float targetCenterY = DEFAULT_CENTER_Y + 100;
@@ -193,13 +201,14 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
() -> {
Mockito.reset(mSpyController);
mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
- targetCenterX, targetCenterY, mAnimationEndCallback2);
+ targetCenterX, targetCenterY, mAnimationCallback2);
mCurrentScale.set(mController.getScale());
mCurrentCenterX.set(mController.getCenterX());
mCurrentCenterY.set(mController.getCenterY());
});
// Current spec shouldn't match given spec.
- verify(mAnimationEndCallback2, never()).run();
+ verify(mAnimationCallback2, never()).onResult(anyBoolean());
+ verify(mAnimationCallback).onResult(false);
SystemClock.sleep(mWaitingAnimationPeriod);
verify(mSpyController, atLeast(2)).enableWindowMagnification(
@@ -213,34 +222,35 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
assertTrue(mCenterYCaptor.getAllValues().get(0) > mCurrentCenterY.get());
assertEquals(targetCenterY, mCenterYCaptor.getValue(), 0f);
verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
- verify(mAnimationEndCallback, never()).run();
- verify(mAnimationEndCallback2).run();
+ verify(mAnimationCallback2).onResult(true);
}
@Test
- public void enableWindowMagnificationWithSameSpec_disabling_NoAnimationAndInvokeCallback() {
+ public void enableWindowMagnificationWithSameSpec_disabling_NoAnimationAndInvokeCallback()
+ throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
- mAnimationEndCallback);
+ mAnimationCallback);
mInstrumentation.runOnMainSync(() -> {
Mockito.reset(mSpyController);
mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN,
- Float.NaN, Float.NaN, mAnimationEndCallback2);
+ Float.NaN, Float.NaN, mAnimationCallback2);
});
SystemClock.sleep(mWaitingAnimationPeriod);
verify(mSpyController, never()).enableWindowMagnification(anyFloat(), anyFloat(),
anyFloat());
verify(mSpyController, never()).deleteWindowMagnification();
- verify(mAnimationEndCallback, never()).run();
- verify(mAnimationEndCallback2).run();
+ verify(mAnimationCallback).onResult(false);
+ verify(mAnimationCallback2).onResult(true);
}
@Test
- public void enableWindowMagnification_enabled_expectedValuesAndInvokeCallback() {
+ public void enableWindowMagnification_enabled_expectedValuesAndInvokeCallback()
+ throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
- mAnimationEndCallback);
+ mAnimationCallback);
final float targetScale = DEFAULT_SCALE + 1.0f;
final float targetCenterX = DEFAULT_CENTER_X + 100;
final float targetCenterY = DEFAULT_CENTER_Y + 100;
@@ -248,7 +258,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
mInstrumentation.runOnMainSync(() -> {
Mockito.reset(mSpyController);
mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
- targetCenterX, targetCenterY, mAnimationEndCallback2);
+ targetCenterX, targetCenterY, mAnimationCallback2);
mCurrentScale.set(mController.getScale());
mCurrentCenterX.set(mController.getCenterX());
mCurrentCenterY.set(mController.getCenterY());
@@ -262,19 +272,20 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
- verify(mAnimationEndCallback, never()).run();
- verify(mAnimationEndCallback2).run();
+ verify(mAnimationCallback).onResult(false);
+ verify(mAnimationCallback2).onResult(true);
}
@Test
- public void enableWindowMagnificationWithSameScale_enabled_doNothingButInvokeCallback() {
+ public void enableWindowMagnificationWithSameScale_enabled_doNothingButInvokeCallback()
+ throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
- enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationEndCallback);
+ enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback);
verify(mSpyController, never()).enableWindowMagnification(anyFloat(), anyFloat(),
anyFloat());
- verify(mAnimationEndCallback).run();
+ verify(mAnimationCallback).onResult(true);
}
@Test
@@ -289,10 +300,11 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
}
@Test
- public void deleteWindowMagnification_enabled_expectedValuesAndInvokeCallback() {
+ public void deleteWindowMagnification_enabled_expectedValuesAndInvokeCallback()
+ throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
- deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationEndCallback);
+ deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback);
verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture());
@@ -301,27 +313,29 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
verifyStartValue(mCenterXCaptor, Float.NaN);
verifyStartValue(mCenterYCaptor, Float.NaN);
verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
- verify(mAnimationEndCallback).run();
+ verify(mAnimationCallback).onResult(true);
}
@Test
- public void deleteWindowMagnification_disabled_doNothingAndInvokeCallback() {
- deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationEndCallback);
+ public void deleteWindowMagnification_disabled_doNothingAndInvokeCallback()
+ throws RemoteException {
+ deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback);
Mockito.verifyNoMoreInteractions(mSpyController);
- verify(mAnimationEndCallback).run();
+ verify(mAnimationCallback).onResult(true);
}
@Test
- public void deleteWindowMagnification_enabling_expectedValuesAndInvokeCallback() {
+ public void deleteWindowMagnification_enabling_expectedValuesAndInvokeCallback()
+ throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
- mAnimationEndCallback);
+ mAnimationCallback);
mInstrumentation.runOnMainSync(
() -> {
Mockito.reset(mSpyController);
mWindowMagnificationAnimationController.deleteWindowMagnification(
- mAnimationEndCallback2);
+ mAnimationCallback2);
mCurrentScale.set(mController.getScale());
mCurrentCenterX.set(mController.getCenterX());
mCurrentCenterY.set(mController.getCenterY());
@@ -339,25 +353,25 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
verifyStartValue(mCenterXCaptor, Float.NaN);
verifyStartValue(mCenterYCaptor, Float.NaN);
verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
- verify(mAnimationEndCallback, never()).run();
- verify(mAnimationEndCallback2).run();
+ verify(mAnimationCallback).onResult(false);
+ verify(mAnimationCallback2).onResult(true);
}
@Test
- public void deleteWindowMagnification_disabling_checkStartAndValues() {
+ public void deleteWindowMagnification_disabling_checkStartAndValues() throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
- mAnimationEndCallback);
+ mAnimationCallback);
- deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationEndCallback2);
+ deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback2);
verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture());
verify(mSpyController).deleteWindowMagnification();
assertEquals(1.0f, mScaleCaptor.getValue(), 0f);
verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
- verify(mAnimationEndCallback, never()).run();
- verify(mAnimationEndCallback2).run();
+ verify(mAnimationCallback).onResult(false);
+ verify(mAnimationCallback2).onResult(true);
}
@Test
@@ -386,22 +400,22 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
}
private void enableWindowMagnificationAndWaitAnimating(long duration,
- @Nullable Runnable endCallback) {
+ @Nullable IRemoteMagnificationAnimationCallback callback) {
mInstrumentation.runOnMainSync(
() -> {
Mockito.reset(mSpyController);
mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
- DEFAULT_CENTER_X, DEFAULT_CENTER_Y, endCallback);
+ DEFAULT_CENTER_X, DEFAULT_CENTER_Y, callback);
});
SystemClock.sleep(duration);
}
private void deleteWindowMagnificationAndWaitAnimating(long duration,
- @Nullable Runnable endCallback) {
+ @Nullable IRemoteMagnificationAnimationCallback callback) {
mInstrumentation.runOnMainSync(
() -> {
resetMockObjects();
- mWindowMagnificationAnimationController.deleteWindowMagnification(endCallback);
+ mWindowMagnificationAnimationController.deleteWindowMagnification(callback);
});
SystemClock.sleep(duration);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index c41f4007aa59..80e9703e0e62 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -120,6 +120,7 @@ import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.accessibility.magnification.FullScreenMagnificationController;
import com.android.server.accessibility.magnification.MagnificationGestureHandler;
+import com.android.server.accessibility.magnification.MagnificationTransitionController;
import com.android.server.accessibility.magnification.WindowMagnificationManager;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -258,6 +259,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private Point mTempPoint = new Point();
private boolean mIsAccessibilityButtonShown;
+ private MagnificationTransitionController mMagnificationTransitionController;
private AccessibilityUserState getCurrentUserStateLocked() {
return getUserStateLocked(mCurrentUserId);
@@ -302,6 +304,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mA11yWindowManager = a11yWindowManager;
mA11yDisplayListener = a11yDisplayListener;
mWindowMagnificationMgr = windowMagnificationMgr;
+ mMagnificationTransitionController = new MagnificationTransitionController(this, mLock);
init();
}
@@ -321,6 +324,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
mWindowManagerService, this, mSecurityPolicy, this);
mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
+ mMagnificationTransitionController = new MagnificationTransitionController(this, mLock);
init();
}
@@ -1550,9 +1554,26 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (fallBackMagnificationModeSettingsLocked(userState)) {
return;
}
- mMainHandler.sendMessage(obtainMessage(
- AccessibilityManagerService::notifyRefreshMagnificationModeToInputFilter,
- this));
+ mMagnificationTransitionController.transitionMagnificationModeLocked(
+ Display.DEFAULT_DISPLAY, userState.getMagnificationModeLocked(),
+ this::onMagnificationTransitionEndedLocked);
+ }
+
+ /**
+ * Called when the magnification mode transition is completed.
+ */
+ void onMagnificationTransitionEndedLocked(boolean success) {
+ final AccessibilityUserState userState = getCurrentUserStateLocked();
+ final int previousMode = userState.getMagnificationModeLocked()
+ ^ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
+ if (!success && previousMode != 0) {
+ userState.setMagnificationModeLocked(previousMode);
+ persistMagnificationModeSettingLocked(previousMode);
+ } else {
+ mMainHandler.sendMessage(obtainMessage(
+ AccessibilityManagerService::notifyRefreshMagnificationModeToInputFilter,
+ this));
+ }
}
private void notifyRefreshMagnificationModeToInputFilter() {
@@ -2962,7 +2983,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
getWindowMagnificationMgr().setConnection(connection);
}
- WindowMagnificationManager getWindowMagnificationMgr() {
+ /**
+ * Getter of {@link WindowMagnificationManager}.
+ *
+ * @return WindowMagnificationManager
+ */
+ public WindowMagnificationManager getWindowMagnificationMgr() {
synchronized (mLock) {
if (mWindowMagnificationMgr == null) {
mWindowMagnificationMgr = new WindowMagnificationManager(mContext, mCurrentUserId);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationTransitionController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationTransitionController.java
new file mode 100644
index 000000000000..af4b34f9613a
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationTransitionController.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility.magnification;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.provider.Settings;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.accessibility.MagnificationAnimationCallback;
+
+import com.android.server.accessibility.AccessibilityManagerService;
+
+/**
+ * Handles magnification mode transition.
+ */
+public class MagnificationTransitionController {
+
+ private static final boolean DEBUG = false;
+ private static final String TAG = "MagnificationController";
+ private final AccessibilityManagerService mAms;
+ private final PointF mTempPoint = new PointF();
+ private final Object mLock;
+ private final SparseArray<DisableMagnificationCallback>
+ mMagnificationEndRunnableSparseArray = new SparseArray();
+
+
+ /**
+ * A callback to inform the magnification transition result.
+ */
+ public interface TransitionCallBack {
+ /**
+ * Invoked when the transition ends.
+ * @param success {@code true} if the transition success.
+ */
+ void onResult(boolean success);
+ }
+
+ public MagnificationTransitionController(AccessibilityManagerService ams, Object lock) {
+ mAms = ams;
+ mLock = lock;
+ }
+
+ /**
+ * Transitions to the target Magnification mode with current center of the magnification mode
+ * if it is available.
+ *
+ * @param displayId The logical display
+ * @param targetMode The target magnification mode
+ * @param transitionCallBack The callback invoked when the transition is finished.
+ */
+ public void transitionMagnificationModeLocked(int displayId, int targetMode,
+ @NonNull TransitionCallBack transitionCallBack) {
+ final PointF magnificationCenter = getCurrentMagnificationBoundsCenterLocked(displayId,
+ targetMode);
+ final DisableMagnificationCallback animationCallback =
+ getDisableMagnificationEndRunnableLocked(displayId);
+ if (magnificationCenter == null && animationCallback == null) {
+ transitionCallBack.onResult(true);
+ return;
+ }
+
+ if (animationCallback != null) {
+ if (animationCallback.mCurrentMode == targetMode) {
+ animationCallback.restoreToCurrentMagnificationMode();
+ return;
+ }
+ Slog.w(TAG, "request during transition, abandon current:"
+ + animationCallback.mTargetMode);
+ animationCallback.setExpiredAndRemoveFromListLocked();
+ }
+
+ if (magnificationCenter == null) {
+ Slog.w(TAG, "Invalid center, ignore it");
+ transitionCallBack.onResult(true);
+ return;
+ }
+ final FullScreenMagnificationController screenMagnificationController =
+ getFullScreenMagnificationController();
+ final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationManager();
+ final float scale = windowMagnificationMgr.getPersistedScale();
+ final DisableMagnificationCallback animationEndCallback =
+ new DisableMagnificationCallback(transitionCallBack, displayId, targetMode,
+ scale, magnificationCenter);
+ if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
+ screenMagnificationController.reset(displayId, animationEndCallback);
+ } else {
+ windowMagnificationMgr.disableWindowMagnification(displayId, false,
+ animationEndCallback);
+ }
+ setDisableMagnificationCallbackLocked(displayId, animationEndCallback);
+ }
+
+ private DisableMagnificationCallback getDisableMagnificationEndRunnableLocked(
+ int displayId) {
+ return mMagnificationEndRunnableSparseArray.get(displayId);
+ }
+
+ private void setDisableMagnificationCallbackLocked(int displayId,
+ @Nullable DisableMagnificationCallback callback) {
+ mMagnificationEndRunnableSparseArray.put(displayId, callback);
+ if (DEBUG) {
+ Slog.d(TAG, "setDisableMagnificationCallbackLocked displayId = " + displayId
+ + ", callback = " + callback);
+ }
+ }
+
+ private FullScreenMagnificationController getFullScreenMagnificationController() {
+ return mAms.getFullScreenMagnificationController();
+ }
+
+ private WindowMagnificationManager getWindowMagnificationManager() {
+ return mAms.getWindowMagnificationMgr();
+ }
+
+ private @Nullable
+ PointF getCurrentMagnificationBoundsCenterLocked(int displayId, int targetMode) {
+ if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
+ final WindowMagnificationManager magnificationManager = getWindowMagnificationManager();
+ if (!magnificationManager.isWindowMagnifierEnabled(displayId)) {
+ return null;
+ }
+ mTempPoint.set(magnificationManager.getCenterX(displayId),
+ magnificationManager.getCenterY(displayId));
+ } else {
+ final FullScreenMagnificationController screenMagnificationController =
+ getFullScreenMagnificationController();
+ if (!screenMagnificationController.isMagnifying(displayId)) {
+ return null;
+ }
+ mTempPoint.set(screenMagnificationController.getCenterX(displayId),
+ screenMagnificationController.getCenterY(displayId));
+ }
+ return mTempPoint;
+ }
+
+ private final class DisableMagnificationCallback implements
+ MagnificationAnimationCallback {
+ private final TransitionCallBack mTransitionCallBack;
+ private boolean mExpired = false;
+ private final int mDisplayId;
+ private final int mTargetMode;
+ private final int mCurrentMode;
+ private final float mCurrentScale;
+ private final PointF mCurrentCenter = new PointF();
+
+ DisableMagnificationCallback(TransitionCallBack transitionCallBack,
+ int displayId, int targetMode, float scale, PointF currentCenter) {
+ mTransitionCallBack = transitionCallBack;
+ mDisplayId = displayId;
+ mTargetMode = targetMode;
+ mCurrentMode = mTargetMode ^ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
+ mCurrentScale = scale;
+ mCurrentCenter.set(currentCenter);
+ }
+
+ @Override
+ public void onResult(boolean success) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onResult success = " + success);
+ }
+ if (mExpired) {
+ return;
+ }
+ setExpiredAndRemoveFromListLocked();
+ if (success) {
+ adjustCurrentCenterIfNeededLocked();
+ applyMagnificationModeLocked(mTargetMode);
+ }
+ mTransitionCallBack.onResult(success);
+ }
+ }
+
+ private void adjustCurrentCenterIfNeededLocked() {
+ if (mTargetMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
+ return;
+ }
+ final Region outRegion = new Region();
+ getFullScreenMagnificationController().getMagnificationRegion(mDisplayId, outRegion);
+ if (outRegion.contains((int) mCurrentCenter.x, (int) mCurrentCenter.y)) {
+ return;
+ }
+ final Rect bounds = outRegion.getBounds();
+ mCurrentCenter.set(bounds.exactCenterX(), bounds.exactCenterY());
+ }
+
+ void restoreToCurrentMagnificationMode() {
+ synchronized (mLock) {
+ if (mExpired) {
+ return;
+ }
+ setExpiredAndRemoveFromListLocked();
+ applyMagnificationModeLocked(mCurrentMode);
+ mTransitionCallBack.onResult(true);
+ }
+ }
+
+ void setExpiredAndRemoveFromListLocked() {
+ mExpired = true;
+ setDisableMagnificationCallbackLocked(mDisplayId, null);
+ }
+
+ private void applyMagnificationModeLocked(int mode) {
+ if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
+ getFullScreenMagnificationController().setScaleAndCenter(mDisplayId,
+ mCurrentScale, mCurrentCenter.x,
+ mCurrentCenter.y, true,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ } else {
+ getWindowMagnificationManager().enableWindowMagnification(mDisplayId,
+ mCurrentScale, mCurrentCenter.x,
+ mCurrentCenter.y);
+ }
+ }
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
index a401bcd3eabd..993027d1ca3c 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
@@ -20,11 +20,12 @@ import static android.os.IBinder.DeathRecipient;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.os.RemoteCallback;
import android.os.RemoteException;
import android.util.Slog;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.accessibility.IWindowMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
+import android.view.accessibility.MagnificationAnimationCallback;
/**
* A wrapper of {@link IWindowMagnificationConnection}.
@@ -50,9 +51,10 @@ class WindowMagnificationConnectionWrapper {
}
boolean enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
- @Nullable RemoteCallback endCallback) {
+ @Nullable MagnificationAnimationCallback callback) {
try {
- mConnection.enableWindowMagnification(displayId, scale, centerX, centerY, endCallback);
+ mConnection.enableWindowMagnification(displayId, scale, centerX, centerY,
+ transformToRemoteCallback(callback));
} catch (RemoteException e) {
if (DBG) {
Slog.e(TAG, "Error calling enableWindowMagnification()", e);
@@ -74,9 +76,10 @@ class WindowMagnificationConnectionWrapper {
return true;
}
- boolean disableWindowMagnification(int displayId, @Nullable RemoteCallback endCallback) {
+ boolean disableWindowMagnification(int displayId,
+ @Nullable MagnificationAnimationCallback callback) {
try {
- mConnection.disableWindowMagnification(displayId, endCallback);
+ mConnection.disableWindowMagnification(displayId, transformToRemoteCallback(callback));
} catch (RemoteException e) {
if (DBG) {
Slog.e(TAG, "Error calling disableWindowMagnification()", e);
@@ -134,4 +137,27 @@ class WindowMagnificationConnectionWrapper {
return true;
}
+ private static @Nullable
+ IRemoteMagnificationAnimationCallback transformToRemoteCallback(
+ MagnificationAnimationCallback callback) {
+ if (callback == null) {
+ return null;
+ }
+ return new RemoteAnimationCallback(callback);
+ }
+
+ private static class RemoteAnimationCallback extends
+ IRemoteMagnificationAnimationCallback.Stub {
+
+ private final MagnificationAnimationCallback mCallback;
+
+ RemoteAnimationCallback(@NonNull MagnificationAnimationCallback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void onResult(boolean success) throws RemoteException {
+ mCallback.onResult(success);
+ }
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index 9cbd78bf4482..c8e485f503ec 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -24,7 +24,6 @@ import android.content.IntentFilter;
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
-import android.os.RemoteCallback;
import android.os.RemoteException;
import android.provider.Settings;
import android.util.MathUtils;
@@ -33,6 +32,7 @@ import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.accessibility.IWindowMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
+import android.view.accessibility.MagnificationAnimationCallback;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -91,7 +91,7 @@ public class WindowMagnificationManager implements
*/
public void setConnection(@Nullable IWindowMagnificationConnection connection) {
synchronized (mLock) {
- //Reset connectionWrapper.
+ // Reset connectionWrapper.
if (mConnectionWrapper != null) {
mConnectionWrapper.setConnectionCallback(null);
if (mConnectionCallback != null) {
@@ -241,11 +241,10 @@ public class WindowMagnificationManager implements
* or {@link Float#NaN} to leave unchanged.
* @param centerY The screen-relative Y coordinate around which to center,
* or {@link Float#NaN} to leave unchanged.
- * @param endCallback Called when the animation is ended without any interruption or the
- * window magnifier is disabled already.
+ * @param animationCallback Called when the animation result is valid.
*/
void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
- @Nullable Runnable endCallback) {
+ @Nullable MagnificationAnimationCallback animationCallback) {
synchronized (mLock) {
if (mConnectionWrapper == null) {
return;
@@ -254,7 +253,8 @@ public class WindowMagnificationManager implements
if (magnifier == null) {
magnifier = createWindowMagnifier(displayId);
}
- magnifier.enableWindowMagnificationInternal(scale, centerX, centerY, endCallback);
+ magnifier.enableWindowMagnificationInternal(scale, centerX, centerY,
+ animationCallback);
}
}
@@ -273,16 +273,16 @@ public class WindowMagnificationManager implements
*
* @param displayId The logical display id.
* @param clear {@true} Clears the state of window magnification.
- * @param endCallback Called when the animation is ended without any interruption or the
- * window magnifier is disabled already.
+ * @param animationCallback Called when the animation result is valid.
*/
- void disableWindowMagnification(int displayId, boolean clear, Runnable endCallback) {
+ void disableWindowMagnification(int displayId, boolean clear,
+ MagnificationAnimationCallback animationCallback) {
synchronized (mLock) {
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
if (magnifier == null || mConnectionWrapper == null) {
return;
}
- magnifier.disableWindowMagnificationInternal(endCallback);
+ magnifier.disableWindowMagnificationInternal(animationCallback);
if (clear) {
mWindowMagnifiers.delete(displayId);
}
@@ -312,7 +312,8 @@ public class WindowMagnificationManager implements
* @param displayId The logical display id.
* @return {@code true} if the window magnification is enabled.
*/
- boolean isWindowMagnifierEnabled(int displayId) {
+ @VisibleForTesting
+ public boolean isWindowMagnifierEnabled(int displayId) {
synchronized (mLock) {
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
if (magnifier == null) {
@@ -408,6 +409,38 @@ public class WindowMagnificationManager implements
}
/**
+ * Returns the screen-relative X coordinate of the center of the magnified bounds.
+ *
+ * @param displayId The logical display id
+ * @return the X coordinate. {@link Float#NaN} if the window magnification is not enabled.
+ */
+ float getCenterX(int displayId) {
+ synchronized (mLock) {
+ WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
+ if (magnifier == null) {
+ return Float.NaN;
+ }
+ return magnifier.getCenterX();
+ }
+ }
+
+ /**
+ * Returns the screen-relative Y coordinate of the center of the magnified bounds.
+ *
+ * @param displayId The logical display id
+ * @return the Y coordinate. {@link Float#NaN} if the window magnification is not enabled.
+ */
+ float getCenterY(int displayId) {
+ synchronized (mLock) {
+ WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
+ if (magnifier == null) {
+ return Float.NaN;
+ }
+ return magnifier.getCenterY();
+ }
+ }
+
+ /**
* Creates the windowMagnifier based on the specified display and stores it.
*
* @param displayId logical display id.
@@ -446,11 +479,13 @@ public class WindowMagnificationManager implements
@Override
public void onSourceBoundsChanged(int displayId, Rect sourceBounds) {
- WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
- if (magnifier == null) {
- magnifier = createWindowMagnifier(displayId);
+ synchronized (mLock) {
+ WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
+ if (magnifier == null) {
+ magnifier = createWindowMagnifier(displayId);
+ }
+ magnifier.onSourceBoundsChanged(sourceBounds);
}
- magnifier.onSourceBoundsChanged(sourceBounds);
}
@Override
@@ -479,9 +514,9 @@ public class WindowMagnificationManager implements
private boolean mEnabled;
private final WindowMagnificationManager mWindowMagnificationManager;
- //Records the bounds of window magnification.
+ // Records the bounds of window magnification.
private final Rect mBounds = new Rect();
- //The magnified bounds on the screen.
+ // The magnified bounds on the screen.
private final Rect mSourceBounds = new Rect();
WindowMagnifier(int displayId, WindowMagnificationManager windowMagnificationManager) {
@@ -491,22 +526,23 @@ public class WindowMagnificationManager implements
@GuardedBy("mLock")
void enableWindowMagnificationInternal(float scale, float centerX, float centerY,
- @Nullable Runnable endCallback) {
+ @Nullable MagnificationAnimationCallback animationCallback) {
if (mEnabled) {
return;
}
final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
if (mWindowMagnificationManager.enableWindowMagnificationInternal(mDisplayId, normScale,
- centerX, centerY, endCallback)) {
+ centerX, centerY, animationCallback)) {
mScale = normScale;
mEnabled = true;
}
}
@GuardedBy("mLock")
- void disableWindowMagnificationInternal(@Nullable Runnable endCallback) {
+ void disableWindowMagnificationInternal(
+ @Nullable MagnificationAnimationCallback animationResultCallback) {
if (mEnabled && mWindowMagnificationManager.disableWindowMagnificationInternal(
- mDisplayId, endCallback)) {
+ mDisplayId, animationResultCallback)) {
mEnabled = false;
}
}
@@ -562,26 +598,36 @@ public class WindowMagnificationManager implements
mEnabled = false;
}
+ @GuardedBy("mLock")
public void onSourceBoundsChanged(Rect sourceBounds) {
mSourceBounds.set(sourceBounds);
}
+
+ @GuardedBy("mLock")
+ float getCenterX() {
+ return mEnabled ? mSourceBounds.exactCenterX() : Float.NaN;
+ }
+
+ @GuardedBy("mLock")
+ float getCenterY() {
+ return mEnabled ? mSourceBounds.exactCenterY() : Float.NaN;
+ }
}
private boolean enableWindowMagnificationInternal(int displayId, float scale, float centerX,
- float centerY, Runnable endCallback) {
+ float centerY, MagnificationAnimationCallback animationCallback) {
return mConnectionWrapper != null && mConnectionWrapper.enableWindowMagnification(
- displayId, scale, centerX, centerY,
- endCallback != null ? new RemoteCallback(bundle -> endCallback.run()) : null);
+ displayId, scale, centerX, centerY, animationCallback);
}
private boolean setScaleInternal(int displayId, float scale) {
return mConnectionWrapper != null && mConnectionWrapper.setScale(displayId, scale);
}
- private boolean disableWindowMagnificationInternal(int displayId, Runnable endCallback) {
+ private boolean disableWindowMagnificationInternal(int displayId,
+ MagnificationAnimationCallback animationCallback) {
return mConnectionWrapper != null && mConnectionWrapper.disableWindowMagnification(
- displayId,
- endCallback != null ? new RemoteCallback(bundle -> endCallback.run()) : null);
+ displayId, animationCallback);
}
private boolean moveWindowMagnifierInternal(int displayId, float offsetX, float offsetY) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 23a37c116c6d..b7355ce92c28 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -230,4 +230,19 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
verify(mMockWindowMagnificationMgr, never()).showMagnificationButton(anyInt(),
anyInt());
}
+
+ @SmallTest
+ public void testOnMagnificationTransitionFailed_capabilitiesIsAll_fallBackToPreviousMode() {
+ final AccessibilityUserState userState = mA11yms.mUserStates.get(
+ mA11yms.getCurrentUserIdLocked());
+ userState.setMagnificationCapabilitiesLocked(
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
+ userState.setMagnificationModeLocked(
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+
+ mA11yms.onMagnificationTransitionEndedLocked(false);
+
+ assertEquals(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW,
+ userState.getMagnificationModeLocked());
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationTransitionControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationTransitionControllerTest.java
new file mode 100644
index 000000000000..cd8e39cfd2e7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationTransitionControllerTest.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility.magnification;
+
+import static com.android.server.accessibility.AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+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.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+import android.view.Display;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
+import android.view.accessibility.MagnificationAnimationCallback;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.accessibility.AccessibilityManagerService;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for MagnificationController.
+ */
+@RunWith(AndroidJUnit4.class)
+public class MagnificationTransitionControllerTest {
+
+ private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
+ private static final Region MAGNIFICATION_REGION = new Region(0, 0, 500, 600);
+ private static final float MAGNIFIED_CENTER_X = 100;
+ private static final float MAGNIFIED_CENTER_Y = 200;
+ private static final float DEFAULT_SCALE = 3f;
+ private static final int CURRENT_USER_ID = UserHandle.USER_CURRENT;
+ private static final int MODE_WINDOW = Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+ private static final int MODE_FULLSCREEN =
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+
+ @Mock private AccessibilityManagerService mService;
+ @Mock private MagnificationTransitionController.TransitionCallBack mTransitionCallBack;
+ @Mock private Context mContext;
+ @Mock private FullScreenMagnificationController mScreenMagnificationController;
+ @Captor private ArgumentCaptor<MagnificationAnimationCallback> mCallbackArgumentCaptor;
+
+ private MockWindowMagnificationConnection mMockConnection;
+ private WindowMagnificationManager mWindowMagnificationManager;
+ private MockContentResolver mMockResolver;
+ private MagnificationTransitionController mMagnificationTransitionController;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ FakeSettingsProvider.clearSettingsProvider();
+ mMockResolver = new MockContentResolver();
+ mMockResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ when(mContext.getContentResolver()).thenReturn(mMockResolver);
+ Settings.Secure.putFloatForUser(mMockResolver,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, DEFAULT_SCALE,
+ CURRENT_USER_ID);
+ mWindowMagnificationManager = new WindowMagnificationManager(mContext, CURRENT_USER_ID);
+ when(mService.getFullScreenMagnificationController()).thenReturn(
+ mScreenMagnificationController);
+ when(mService.getWindowMagnificationMgr()).thenReturn(mWindowMagnificationManager);
+ mMockConnection = new MockWindowMagnificationConnection(true);
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mMagnificationTransitionController = new MagnificationTransitionController(mService,
+ new Object());
+ }
+
+ @After
+ public void tearDown() {
+ FakeSettingsProvider.clearSettingsProvider();
+ }
+
+ @Test
+ public void transitionToWindowMode_notMagnifying_doNothing() throws RemoteException {
+ setMagnificationModeSettings(MODE_FULLSCREEN);
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_WINDOW,
+ mTransitionCallBack);
+
+ verify(mTransitionCallBack).onResult(true);
+ verify(mScreenMagnificationController, never()).reset(anyInt(),
+ any(MagnificationAnimationCallback.class));
+ verify(mMockConnection.getConnection(), never()).enableWindowMagnification(anyInt(),
+ anyFloat(), anyFloat(), anyFloat(),
+ nullable(IRemoteMagnificationAnimationCallback.class));
+ }
+
+ @Test
+ public void transitionToWindowMode_fullScreenMagnifying_disableFullScreenAndEnableWindow()
+ throws RemoteException {
+ setMagnificationEnabled(MODE_FULLSCREEN);
+
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_WINDOW,
+ mTransitionCallBack);
+
+ verify(mScreenMagnificationController).reset(eq(TEST_DISPLAY),
+ mCallbackArgumentCaptor.capture());
+ mCallbackArgumentCaptor.getValue().onResult(true);
+ mMockConnection.invokeCallbacks();
+ verify(mTransitionCallBack).onResult(true);
+ assertEquals(MAGNIFIED_CENTER_X, mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_Y, mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 0);
+ }
+
+ @Test
+ public void transitionToWindowMode_disablingWindowMode_enablingWindowWithFormerCenter()
+ throws RemoteException {
+ setMagnificationEnabled(MODE_WINDOW);
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_FULLSCREEN,
+ mTransitionCallBack);
+
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_WINDOW,
+ mTransitionCallBack);
+
+ mMockConnection.invokeCallbacks();
+ verify(mTransitionCallBack).onResult(true);
+ assertEquals(MAGNIFIED_CENTER_X, mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_Y, mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 0);
+ }
+
+ @Test
+ public void transitionToFullScreenMode_windowMagnifying_disableWindowAndEnableFullScreen()
+ throws RemoteException {
+ setMagnificationEnabled(MODE_WINDOW);
+
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_FULLSCREEN,
+ mTransitionCallBack);
+ mMockConnection.invokeCallbacks();
+
+ assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY,
+ DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
+ true, MAGNIFICATION_GESTURE_HANDLER_ID);
+ verify(mTransitionCallBack).onResult(true);
+ }
+
+ @Test
+ public void transitionToFullScreen_centerNotInTheBounds_magnifyTheCenterOfMagnificationBounds()
+ throws RemoteException {
+ final Rect magnificationBounds = MAGNIFICATION_REGION.getBounds();
+ final PointF magnifiedCenter = new PointF(magnificationBounds.right + 100,
+ magnificationBounds.bottom + 100);
+ setMagnificationEnabled(MODE_WINDOW, magnifiedCenter.x, magnifiedCenter.y);
+
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_FULLSCREEN,
+ mTransitionCallBack);
+ mMockConnection.invokeCallbacks();
+
+ assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY, DEFAULT_SCALE,
+ magnificationBounds.exactCenterX(), magnificationBounds.exactCenterY(), true,
+ MAGNIFICATION_GESTURE_HANDLER_ID);
+ verify(mTransitionCallBack).onResult(true);
+ }
+
+ @Test
+ public void transitionToFullScreenMode_disablingFullScreen_enableFullScreenWithFormerCenter()
+ throws RemoteException {
+ setMagnificationEnabled(MODE_FULLSCREEN);
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_WINDOW,
+ mTransitionCallBack);
+
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_FULLSCREEN,
+ mTransitionCallBack);
+
+ verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY,
+ DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
+ true, MAGNIFICATION_GESTURE_HANDLER_ID);
+ verify(mTransitionCallBack).onResult(true);
+ }
+
+
+ @Test
+ public void interruptDuringTransitionToFullScreenMode_windowMagnifying_notifyTransitionFailed()
+ throws RemoteException {
+ setMagnificationEnabled(MODE_WINDOW);
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_FULLSCREEN,
+ mTransitionCallBack);
+
+ // Enable window magnification while animating.
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, DEFAULT_SCALE,
+ Float.NaN, Float.NaN, null);
+ mMockConnection.invokeCallbacks();
+
+ assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ verify(mScreenMagnificationController, never()).setScaleAndCenter(TEST_DISPLAY,
+ DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
+ true, MAGNIFICATION_GESTURE_HANDLER_ID);
+ verify(mTransitionCallBack).onResult(false);
+ }
+
+ private void setMagnificationEnabled(int mode) throws RemoteException {
+ setMagnificationEnabled(mode, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
+ }
+
+ private void setMagnificationEnabled(int mode, float centerX, float centerY)
+ throws RemoteException {
+ setMagnificationModeSettings(mode);
+ Mockito.reset(mScreenMagnificationController);
+ doAnswer(invocation -> {
+ final Region outRegion = invocation.getArgument(1);
+ outRegion.set(MAGNIFICATION_REGION);
+ return null;
+ }).when(mScreenMagnificationController).getMagnificationRegion(anyInt(), any(Region.class));
+
+ final boolean windowMagnifying = mWindowMagnificationManager.isWindowMagnifierEnabled(
+ TEST_DISPLAY);
+ if (windowMagnifying) {
+ mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
+ mMockConnection.invokeCallbacks();
+ }
+ if (mode == MODE_FULLSCREEN) {
+ when(mScreenMagnificationController.isMagnifying(TEST_DISPLAY)).thenReturn(true);
+ when(mScreenMagnificationController.getPersistedScale()).thenReturn(DEFAULT_SCALE);
+ when(mScreenMagnificationController.getScale(TEST_DISPLAY)).thenReturn(DEFAULT_SCALE);
+ when(mScreenMagnificationController.getCenterX(TEST_DISPLAY)).thenReturn(
+ centerX);
+ when(mScreenMagnificationController.getCenterY(TEST_DISPLAY)).thenReturn(
+ centerY);
+ } else {
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, DEFAULT_SCALE,
+ centerX, centerY, null);
+ mMockConnection.invokeCallbacks();
+ }
+ }
+
+ private void setMagnificationModeSettings(int mode) {
+ Settings.Secure.putIntForUser(mMockResolver,
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, mode, CURRENT_USER_ID);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java
index f896d75ecb3b..2a5350454f67 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java
@@ -25,12 +25,13 @@ import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.annotation.Nullable;
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
-import android.os.RemoteCallback;
import android.os.RemoteException;
import android.view.Display;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.accessibility.IWindowMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
@@ -38,16 +39,28 @@ import android.view.accessibility.IWindowMagnificationConnectionCallback;
* Mocks the basic logic of window magnification in System UI. We assume the screen size is
* unlimited, so source bounds is always on the center of the mirror window bounds.
*/
-class MockWindowMagnificationConnection {
+class MockWindowMagnificationConnection {
public static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
private final IWindowMagnificationConnection mConnection;
private final Binder mBinder;
+ private final boolean mSuspendCallback;
+ private boolean mHasPendingCallback = false;
+ private boolean mWindowMagnificationEnabled = false;
private IBinder.DeathRecipient mDeathRecipient;
private IWindowMagnificationConnectionCallback mIMirrorWindowCallback;
+
private Rect mMirrorWindowFrame = new Rect(0, 0, 500, 500);
+ private float mScale = 2.0f;
+ private Rect mSourceBounds = new Rect();
+ private IRemoteMagnificationAnimationCallback mAnimationCallback;
MockWindowMagnificationConnection() throws RemoteException {
+ this(false);
+ }
+
+ MockWindowMagnificationConnection(boolean suspendCallback) throws RemoteException {
+ mSuspendCallback = suspendCallback;
mConnection = mock(IWindowMagnificationConnection.class);
mBinder = mock(Binder.class);
when(mConnection.asBinder()).thenReturn(mBinder);
@@ -66,33 +79,55 @@ class MockWindowMagnificationConnection {
}
private void stubConnection() throws RemoteException {
+ stubEnableWindowMagnification();
+ stubDisableWindowMagnification();
+ }
+
+ private void stubEnableWindowMagnification() throws RemoteException {
doAnswer((invocation) -> {
final int displayId = invocation.getArgument(0);
if (displayId != TEST_DISPLAY) {
throw new IllegalArgumentException("only support default display :" + displayId);
}
+ mWindowMagnificationEnabled = true;
+ final float scale = invocation.getArgument(1);
+ mScale = Float.isNaN(scale) ? mScale : scale;
computeMirrorWindowFrame(invocation.getArgument(2), invocation.getArgument(3));
- final RemoteCallback callback = invocation.getArgument(4);
- if (callback != null) {
- callback.sendResult(null);
+ setAnimationCallback(invocation.getArgument(4));
+ computeSourceBounds();
+ mHasPendingCallback = true;
+ if (!mSuspendCallback) {
+ invokeCallbacksInternal(true);
}
- mIMirrorWindowCallback.onWindowMagnifierBoundsChanged(TEST_DISPLAY,
- mMirrorWindowFrame);
return null;
- }).when(mConnection).enableWindowMagnification(anyInt(),
- anyFloat(), anyFloat(), anyFloat(), nullable(RemoteCallback.class));
+ }).when(mConnection).enableWindowMagnification(anyInt(), anyFloat(), anyFloat(), anyFloat(),
+ nullable(IRemoteMagnificationAnimationCallback.class));
+ }
+ private void stubDisableWindowMagnification() throws RemoteException {
doAnswer((invocation) -> {
final int displayId = invocation.getArgument(0);
if (displayId != TEST_DISPLAY) {
throw new IllegalArgumentException("only support default display :" + displayId);
}
- final RemoteCallback callback = invocation.getArgument(1);
- if (callback != null) {
- callback.sendResult(null);
+ setAnimationCallback(invocation.getArgument(1));
+ mHasPendingCallback = true;
+ if (!mSuspendCallback) {
+ invokeCallbacksInternal(true);
}
return null;
- }).when(mConnection).disableWindowMagnification(anyInt(), nullable(RemoteCallback.class));
+ }).when(mConnection).disableWindowMagnification(anyInt(),
+ nullable(IRemoteMagnificationAnimationCallback.class));
+ }
+
+ private void computeSourceBounds() {
+ final int halfWidth = mMirrorWindowFrame.width() / 2;
+ final int halfHeight = mMirrorWindowFrame.height() / 2;
+ final int left = mMirrorWindowFrame.left + (halfWidth - (int) (halfWidth / mScale));
+ final int right = mMirrorWindowFrame.right - (halfWidth - (int) (halfWidth / mScale));
+ final int top = mMirrorWindowFrame.top + (halfHeight - (int) (halfHeight / mScale));
+ final int bottom = mMirrorWindowFrame.bottom - (halfHeight - (int) (halfHeight / mScale));
+ mSourceBounds.set(left, top, right, bottom);
}
private void computeMirrorWindowFrame(float centerX, float centerY) {
@@ -103,6 +138,13 @@ class MockWindowMagnificationConnection {
mMirrorWindowFrame.offset((int) offsetX, (int) offsetY);
}
+ private void sendAnimationEndCallbackIfNeeded(boolean success) throws RemoteException {
+ if (mAnimationCallback != null) {
+ mAnimationCallback.onResult(success);
+ mAnimationCallback = null;
+ }
+ }
+
IWindowMagnificationConnection getConnection() {
return mConnection;
}
@@ -119,8 +161,38 @@ class MockWindowMagnificationConnection {
return mIMirrorWindowCallback;
}
- public Rect getMirrorWindowFrame() {
+ Rect getMirrorWindowFrame() {
return new Rect(mMirrorWindowFrame);
}
+
+ void invokeCallbacks() throws RemoteException {
+ if (!mSuspendCallback) {
+ throw new IllegalStateException("Invoke callbacks automatically");
+ }
+ invokeCallbacksInternal(true);
+ }
+
+ private void invokeCallbacksInternal(boolean success) throws RemoteException {
+ if (!mHasPendingCallback) {
+ throw new IllegalStateException("There is no any pending callbacks");
+ }
+ if (mWindowMagnificationEnabled && mIMirrorWindowCallback != null) {
+ mIMirrorWindowCallback.onWindowMagnifierBoundsChanged(TEST_DISPLAY,
+ mMirrorWindowFrame);
+ mIMirrorWindowCallback.onSourceBoundsChanged(TEST_DISPLAY,
+ mSourceBounds);
+ }
+ sendAnimationEndCallbackIfNeeded(success);
+ mHasPendingCallback = false;
+ }
+
+ private void setAnimationCallback(
+ @Nullable IRemoteMagnificationAnimationCallback animationCallback)
+ throws RemoteException {
+ if (mAnimationCallback != null) {
+ invokeCallbacksInternal(false);
+ }
+ mAnimationCallback = animationCallback;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
index 9ef65d9cce09..c88bc3b2e15b 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
@@ -17,14 +17,17 @@
package com.android.server.accessibility.magnification;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
-import android.os.RemoteCallback;
import android.os.RemoteException;
import android.provider.Settings;
import android.view.Display;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.accessibility.IWindowMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
+import android.view.accessibility.MagnificationAnimationCallback;
import org.junit.Before;
import org.junit.Test;
@@ -40,26 +43,29 @@ public class WindowMagnificationConnectionWrapperTest {
private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
- @Mock
private IWindowMagnificationConnection mConnection;
@Mock
private IWindowMagnificationConnectionCallback mCallback;
@Mock
- private RemoteCallback.OnResultListener mOnResultListener;
- private RemoteCallback mRemoteCallback;
+ private MagnificationAnimationCallback mAnimationCallback;
+
+ private MockWindowMagnificationConnection mMockWindowMagnificationConnection;
private WindowMagnificationConnectionWrapper mConnectionWrapper;
@Before
- public void setUp() {
+ public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
+ mMockWindowMagnificationConnection = new MockWindowMagnificationConnection();
+ mConnection = mMockWindowMagnificationConnection.getConnection();
mConnectionWrapper = new WindowMagnificationConnectionWrapper(mConnection);
- mRemoteCallback = new RemoteCallback(mOnResultListener);
}
@Test
public void enableWindowMagnification() throws RemoteException {
- mConnectionWrapper.enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f, mRemoteCallback);
- verify(mConnection).enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f, mRemoteCallback);
+ mConnectionWrapper.enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f,
+ mAnimationCallback);
+
+ verify(mAnimationCallback).onResult(true);
}
@Test
@@ -70,8 +76,11 @@ public class WindowMagnificationConnectionWrapperTest {
@Test
public void disableWindowMagnification() throws RemoteException {
- mConnectionWrapper.disableWindowMagnification(TEST_DISPLAY, mRemoteCallback);
- verify(mConnection).disableWindowMagnification(TEST_DISPLAY, mRemoteCallback);
+ mConnectionWrapper.disableWindowMagnification(TEST_DISPLAY, mAnimationCallback);
+
+ verify(mConnection).disableWindowMagnification(eq(TEST_DISPLAY),
+ any(IRemoteMagnificationAnimationCallback.class));
+ verify(mAnimationCallback).onResult(true);
}
@Test
@@ -99,5 +108,4 @@ public class WindowMagnificationConnectionWrapperTest {
mConnectionWrapper.setConnectionCallback(mCallback);
verify(mConnection).setConnectionCallback(mCallback);
}
-
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index dcb1262ad2de..89b0a03a25bb 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -47,7 +47,9 @@ import android.test.mock.MockContentResolver;
import android.view.Display;
import android.view.InputDevice;
import android.view.MotionEvent;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
+import android.view.accessibility.MagnificationAnimationCallback;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.LocalServices;
@@ -73,7 +75,7 @@ public class WindowMagnificationManagerTest {
@Mock
private StatusBarManagerInternal mMockStatusBarManagerInternal;
@Mock
- private Runnable mEndCallback;
+ private MagnificationAnimationCallback mAnimationCallback;
private MockContentResolver mResolver;
private WindowMagnificationManager mWindowMagnificationManager;
@@ -177,9 +179,11 @@ public class WindowMagnificationManagerTest {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f,
- mEndCallback);
+ mAnimationCallback);
- verify(mEndCallback).run();
+ verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f),
+ eq(200f), eq(300f), any(IRemoteMagnificationAnimationCallback.class));
+ verify(mAnimationCallback).onResult(true);
}
@Test
@@ -194,13 +198,17 @@ public class WindowMagnificationManagerTest {
}
@Test
- public void disableWithCallback_hasConnectionAndEnabled_disableWindowMagnification() {
+ public void disableWithCallback_hasConnectionAndEnabled_disableWindowMagnification()
+ throws RemoteException {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false, mEndCallback);
+ mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false,
+ mAnimationCallback);
- verify(mEndCallback).run();
+ verify(mMockConnection.getConnection()).disableWindowMagnification(eq(TEST_DISPLAY),
+ any(IRemoteMagnificationAnimationCallback.class));
+ verify(mAnimationCallback).onResult(true);
}
@Test
@@ -352,6 +360,15 @@ public class WindowMagnificationManagerTest {
assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
}
+ @Test
+ public void centerGetter_enabledOnTestDisplay_expectedValues() {
+ mWindowMagnificationManager.requestConnection(true);
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f);
+
+ assertEquals(mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 100f);
+ assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f);
+ }
+
private MotionEvent generatePointersDownEvent(PointF[] pointersLocation) {
final int len = pointersLocation.length;