diff options
3 files changed, 97 insertions, 62 deletions
diff --git a/core/java/android/view/accessibility/MagnificationAnimationCallback.java b/core/java/android/view/accessibility/MagnificationAnimationCallback.java new file mode 100644 index 000000000000..491f7fb32a8c --- /dev/null +++ b/core/java/android/view/accessibility/MagnificationAnimationCallback.java @@ -0,0 +1,31 @@ +/* + * 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 android.view.accessibility; + +/** + * A callback for magnification animation result. + * @hide + */ +public interface MagnificationAnimationCallback { + /** + * 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/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java index b6f2a47dd5e2..c583dcc2b1be 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java @@ -37,6 +37,7 @@ import android.util.SparseArray; import android.view.Display; import android.view.MagnificationSpec; import android.view.View; +import android.view.accessibility.MagnificationAnimationCallback; import android.view.animation.DecelerateInterpolator; import com.android.internal.R; @@ -63,7 +64,7 @@ public class FullScreenMagnificationController { private static final boolean DEBUG = false; private static final String LOG_TAG = "FullScreenMagnificationController"; - private static final Runnable STUB_RUNNABLE = () -> { + private static final MagnificationAnimationCallback STUB_ANIMATION_CALLBACK = success -> { }; public static final float MIN_SCALE = 1.0f; public static final float MAX_SCALE = 8.0f; @@ -304,18 +305,19 @@ public class FullScreenMagnificationController { } } - void sendSpecToAnimation(MagnificationSpec spec, Runnable endCallback) { + void sendSpecToAnimation(MagnificationSpec spec, + MagnificationAnimationCallback animationCallback) { if (DEBUG) { Slog.i(LOG_TAG, - "sendSpecToAnimation(spec = " + spec + ", endCallback = " + endCallback - + ")"); + "sendSpecToAnimation(spec = " + spec + ", animationCallback = " + + animationCallback + ")"); } if (Thread.currentThread().getId() == mMainThreadId) { - mSpecAnimationBridge.updateSentSpecMainThread(spec, endCallback); + mSpecAnimationBridge.updateSentSpecMainThread(spec, animationCallback); } else { final Message m = PooledLambda.obtainMessage( SpecAnimationBridge::updateSentSpecMainThread, - mSpecAnimationBridge, spec, endCallback); + mSpecAnimationBridge, spec, animationCallback); mControllerCtx.getHandler().sendMessage(m); } } @@ -415,11 +417,11 @@ public class FullScreenMagnificationController { @GuardedBy("mLock") boolean reset(boolean animate) { - return reset(transformToStubRunnable(animate)); + return reset(transformToStubCallback(animate)); } @GuardedBy("mLock") - boolean reset(Runnable endCallback) { + boolean reset(MagnificationAnimationCallback animationCallback) { if (!mRegistered) { return false; } @@ -430,7 +432,7 @@ public class FullScreenMagnificationController { onMagnificationChangedLocked(); } mIdOfLastServiceToMagnify = INVALID_ID; - sendSpecToAnimation(spec, endCallback); + sendSpecToAnimation(spec, animationCallback); return changed; } @@ -458,24 +460,23 @@ public class FullScreenMagnificationController { final float centerX = normPivotX + offsetX; final float centerY = normPivotY + offsetY; mIdOfLastServiceToMagnify = id; - return setScaleAndCenter(scale, centerX, centerY, transformToStubRunnable(animate), id); + return setScaleAndCenter(scale, centerX, centerY, transformToStubCallback(animate), id); } @GuardedBy("mLock") boolean setScaleAndCenter(float scale, float centerX, float centerY, - Runnable endCallback, int id) { + MagnificationAnimationCallback animationCallback, int id) { if (!mRegistered) { return false; } if (DEBUG) { Slog.i(LOG_TAG, "setScaleAndCenterLocked(scale = " + scale + ", centerX = " + centerX - + ", centerY = " + centerY + ", endCallback = " + endCallback - + ", id = " + id - + ")"); + + ", centerY = " + centerY + ", endCallback = " + + animationCallback + ", id = " + id + ")"); } final boolean changed = updateMagnificationSpecLocked(scale, centerX, centerY); - sendSpecToAnimation(mCurrentMagnificationSpec, endCallback); + sendSpecToAnimation(mCurrentMagnificationSpec, animationCallback); if (isMagnifying() && (id != INVALID_ID)) { mIdOfLastServiceToMagnify = id; } @@ -875,7 +876,7 @@ public class FullScreenMagnificationController { * the spec did not change */ public boolean reset(int displayId, boolean animate) { - return reset(displayId, animate ? STUB_RUNNABLE : null); + return reset(displayId, animate ? STUB_ANIMATION_CALLBACK : null); } /** @@ -883,18 +884,19 @@ public class FullScreenMagnificationController { * transition. * * @param displayId The logical display id. - * @param endCallback Called when the animation is ended or the spec did not change. + * @param animationCallback Called when the animation result is valid. * {@code null} to transition immediately * @return {@code true} if the magnification spec changed, {@code false} if * the spec did not change */ - public boolean reset(int displayId, Runnable endCallback) { + public boolean reset(int displayId, + MagnificationAnimationCallback animationCallback) { synchronized (mLock) { final DisplayMagnification display = mDisplays.get(displayId); if (display == null) { return false; } - return display.reset(endCallback); + return display.reset(animationCallback); } } @@ -946,7 +948,7 @@ public class FullScreenMagnificationController { return false; } return display.setScaleAndCenter(Float.NaN, centerX, centerY, - animate ? STUB_RUNNABLE : null, id); + animate ? STUB_ANIMATION_CALLBACK : null, id); } } @@ -970,7 +972,7 @@ public class FullScreenMagnificationController { public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY, boolean animate, int id) { return setScaleAndCenter(displayId, scale, centerX, centerY, - transformToStubRunnable(animate), id); + transformToStubCallback(animate), id); } /** @@ -984,20 +986,20 @@ public class FullScreenMagnificationController { * center and scale, or {@link Float#NaN} to leave unchanged * @param centerY the screen-relative Y coordinate around which to * center and scale, or {@link Float#NaN} to leave unchanged - * @param endCallback called when the transition is finished successfully or the spec did not - * change. {@code null} to transition immediately. + * @param animationCallback Called when the animation result is valid. + * {@code null} to transition immediately * @param id the ID of the service requesting the change * @return {@code true} if the magnification spec changed, {@code false} if * the spec did not change */ public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY, - Runnable endCallback, int id) { + MagnificationAnimationCallback animationCallback, int id) { synchronized (mLock) { final DisplayMagnification display = mDisplays.get(displayId); if (display == null) { return false; } - return display.setScaleAndCenter(scale, centerX, centerY, endCallback, id); + return display.setScaleAndCenter(scale, centerX, centerY, animationCallback, id); } } @@ -1230,7 +1232,7 @@ public class FullScreenMagnificationController { private final ValueAnimator mValueAnimator; // Called when the callee wants animating and the sent spec matches the target spec. - private Runnable mEndCallback; + private MagnificationAnimationCallback mAnimationCallback; private final Object mLock; private final int mDisplayId; @@ -1268,33 +1270,35 @@ public class FullScreenMagnificationController { } } - void updateSentSpecMainThread(MagnificationSpec spec, Runnable endCallback) { + void updateSentSpecMainThread(MagnificationSpec spec, + MagnificationAnimationCallback animationCallback) { if (mValueAnimator.isRunning()) { - // Avoid AnimationEnd Callback. - mEndCallback = null; mValueAnimator.cancel(); } - mEndCallback = endCallback; + mAnimationCallback = animationCallback; // If the current and sent specs don't match, update the sent spec. synchronized (mLock) { final boolean changed = !mSentMagnificationSpec.equals(spec); if (changed) { - if (mEndCallback != null) { + if (mAnimationCallback != null) { animateMagnificationSpecLocked(spec); } else { setMagnificationSpecLocked(spec); } } else { - sendEndCallbackMainThread(); + sendEndCallbackMainThread(true); } } } - private void sendEndCallbackMainThread() { - if (mEndCallback != null) { - mEndCallback.run(); - mEndCallback = null; + private void sendEndCallbackMainThread(boolean success) { + if (mAnimationCallback != null) { + if (DEBUG) { + Slog.d(LOG_TAG, "sendEndCallbackMainThread: " + success); + } + mAnimationCallback.onResult(success); + mAnimationCallback = null; } } @@ -1337,17 +1341,16 @@ public class FullScreenMagnificationController { @Override public void onAnimationStart(Animator animation) { - } @Override public void onAnimationEnd(Animator animation) { - sendEndCallbackMainThread(); + sendEndCallbackMainThread(true); } @Override public void onAnimationCancel(Animator animation) { - + sendEndCallbackMainThread(false); } @Override @@ -1481,7 +1484,7 @@ public class FullScreenMagnificationController { } @Nullable - private static Runnable transformToStubRunnable(boolean animate) { - return animate ? STUB_RUNNABLE : null; + private static MagnificationAnimationCallback transformToStubCallback(boolean animate) { + return animate ? STUB_ANIMATION_CALLBACK : null; } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java index 57bfbf33d680..6acd9b6b3803 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java @@ -44,6 +44,7 @@ import android.graphics.Rect; import android.graphics.Region; import android.os.Looper; import android.view.MagnificationSpec; +import android.view.accessibility.MagnificationAnimationCallback; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; @@ -99,12 +100,12 @@ public class FullScreenMagnificationControllerTest { ValueAnimator.AnimatorListener mStateListener; FullScreenMagnificationController mFullScreenMagnificationController; - Runnable mEndCallback; + MagnificationAnimationCallback mAnimationCallback; @Before public void setUp() { Looper looper = InstrumentationRegistry.getContext().getMainLooper(); - mEndCallback = Mockito.mock(Runnable.class); + mAnimationCallback = Mockito.mock(MagnificationAnimationCallback.class); // Pretending ID of the Thread associated with looper as main thread ID in controller when(mMockContext.getMainLooper()).thenReturn(looper); when(mMockControllerCtx.getContext()).thenReturn(mMockContext); @@ -323,7 +324,6 @@ public class FullScreenMagnificationControllerTest { for (int i = 0; i < DISPLAY_COUNT; i++) { setScaleAndCenter_animated_stateChangesAndAnimationHappens(i); resetMockWindowManager(); - Mockito.reset(mEndCallback); } } @@ -336,7 +336,7 @@ public class FullScreenMagnificationControllerTest { MagnificationSpec endSpec = getMagnificationSpec(scale, offsets); assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, scale, - newCenter.x, newCenter.y, mEndCallback, SERVICE_ID_1)); + newCenter.x, newCenter.y, mAnimationCallback, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5); @@ -365,18 +365,17 @@ public class FullScreenMagnificationControllerTest { mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); mStateListener.onAnimationEnd(mMockValueAnimator); verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec))); - verify(mEndCallback).run(); + verify(mAnimationCallback).onResult(true); } @Test public void testSetScaleAndCenterWithAnimation_sameSpec_noAnimationButInvokeEndCallback() { for (int i = 0; i < DISPLAY_COUNT; i++) { - setScaleAndCenter_sameSpec_noAnimationButInvokeEndCallback(i); - Mockito.reset(mEndCallback); + setScaleAndCenter_sameSpec_noAnimationButInvokeCallbacks(i); } } - private void setScaleAndCenter_sameSpec_noAnimationButInvokeEndCallback(int displayId) { + private void setScaleAndCenter_sameSpec_noAnimationButInvokeCallbacks(int displayId) { register(displayId); final PointF center = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; final float targetScale = 2.0f; @@ -385,11 +384,11 @@ public class FullScreenMagnificationControllerTest { mMessageCapturingHandler.sendAllMessages(); assertFalse(mFullScreenMagnificationController.setScaleAndCenter(displayId, - targetScale, center.x, center.y, mEndCallback, SERVICE_ID_1)); + targetScale, center.x, center.y, mAnimationCallback, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); verify(mMockValueAnimator, never()).start(); - verify(mEndCallback).run(); + verify(mAnimationCallback).onResult(true); } @Test @@ -673,38 +672,38 @@ public class FullScreenMagnificationControllerTest { public void testReset_notMagnifying_noStateChangeButInvokeCallback() { for (int i = 0; i < DISPLAY_COUNT; i++) { reset_notMagnifying_noStateChangeButInvokeCallback(i); - Mockito.reset(mEndCallback); } } private void reset_notMagnifying_noStateChangeButInvokeCallback(int displayId) { register(displayId); - assertFalse(mFullScreenMagnificationController.reset(displayId, mEndCallback)); + assertFalse(mFullScreenMagnificationController.reset(displayId, mAnimationCallback)); mMessageCapturingHandler.sendAllMessages(); verify(mMockAms, never()).notifyMagnificationChanged(eq(displayId), any(Region.class), anyFloat(), anyFloat(), anyFloat()); - verify(mEndCallback).run(); + verify(mAnimationCallback).onResult(true); } @Test - public void testReset_Magnifying_resetsMagnificationAndInvokeLastEndCallback() { + public void testReset_Magnifying_resetsMagnificationAndInvokeCallbacks() { for (int i = 0; i < DISPLAY_COUNT; i++) { - reset_Magnifying_resetsMagnificationAndInvokeLastEndCallback(i); + reset_Magnifying_resetsMagnificationAndInvokeCallbacks(i); } } - private void reset_Magnifying_resetsMagnificationAndInvokeLastEndCallback(int displayId) { + private void reset_Magnifying_resetsMagnificationAndInvokeCallbacks(int displayId) { register(displayId); float scale = 2.5f; PointF firstCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, - scale, firstCenter.x, firstCenter.y, mEndCallback, SERVICE_ID_1)); + scale, firstCenter.x, firstCenter.y, mAnimationCallback, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); Mockito.reset(mMockValueAnimator); // Stubs the logic after the animation is started. doAnswer(invocation -> { + mStateListener.onAnimationCancel(mMockValueAnimator); mStateListener.onAnimationEnd(mMockValueAnimator); return null; }).when(mMockValueAnimator).cancel(); @@ -713,13 +712,14 @@ public class FullScreenMagnificationControllerTest { float fraction = 0.33f; when(mMockValueAnimator.getAnimatedFraction()).thenReturn(fraction); mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); - Runnable lastEndCallback = Mockito.mock(Runnable.class); + MagnificationAnimationCallback lastAnimationCallback = Mockito.mock( + MagnificationAnimationCallback.class); - assertTrue(mFullScreenMagnificationController.reset(displayId, lastEndCallback)); + assertTrue(mFullScreenMagnificationController.reset(displayId, lastAnimationCallback)); mMessageCapturingHandler.sendAllMessages(); // Verify expected actions. - verify(mEndCallback, never()).run(); + verify(mAnimationCallback).onResult(false); verify(mMockValueAnimator).start(); verify(mMockValueAnimator).cancel(); @@ -729,7 +729,7 @@ public class FullScreenMagnificationControllerTest { mStateListener.onAnimationEnd(mMockValueAnimator); assertFalse(mFullScreenMagnificationController.isMagnifying(DISPLAY_0)); - verify(lastEndCallback).run(); + verify(lastAnimationCallback).onResult(true); } @Test @@ -1142,6 +1142,7 @@ public class FullScreenMagnificationControllerTest { verify(mMockValueAnimator).addListener(animatorListenerArgumentCaptor.capture()); mStateListener = animatorListenerArgumentCaptor.getValue(); Mockito.reset(mMockValueAnimator); // Ignore other initialization + Mockito.reset(mAnimationCallback); } private void zoomIn2xToMiddle(int displayId) { |