| /* |
| * 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.systemui.accessibility; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertNotNull; |
| 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; |
| import static org.mockito.Mockito.spy; |
| import static org.mockito.Mockito.verify; |
| |
| import android.animation.ValueAnimator; |
| import android.annotation.Nullable; |
| import android.app.Instrumentation; |
| import android.content.Context; |
| import android.graphics.Rect; |
| import android.os.Handler; |
| import android.os.RemoteException; |
| import android.os.SystemClock; |
| import android.testing.AndroidTestingRunner; |
| import android.view.SurfaceControl; |
| import android.view.View; |
| import android.view.WindowManager; |
| import android.view.WindowManagerGlobal; |
| import android.view.accessibility.IRemoteMagnificationAnimationCallback; |
| import android.view.animation.AccelerateInterpolator; |
| |
| import androidx.test.InstrumentationRegistry; |
| import androidx.test.filters.LargeTest; |
| |
| import com.android.internal.graphics.SfVsyncFrameCallbackProvider; |
| import com.android.systemui.R; |
| import com.android.systemui.SysuiTestCase; |
| import com.android.systemui.model.SysUiState; |
| import com.android.systemui.util.settings.SecureSettings; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Ignore; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.mockito.Answers; |
| import org.mockito.ArgumentCaptor; |
| import org.mockito.Mock; |
| import org.mockito.Mockito; |
| import org.mockito.MockitoAnnotations; |
| |
| import java.util.concurrent.CountDownLatch; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.atomic.AtomicReference; |
| |
| @Ignore |
| @LargeTest |
| @RunWith(AndroidTestingRunner.class) |
| public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { |
| |
| private static final float DEFAULT_SCALE = 4.0f; |
| private static final float DEFAULT_CENTER_X = 400.0f; |
| private static final float DEFAULT_CENTER_Y = 500.0f; |
| // The duration couldn't too short, otherwise the ValueAnimator won't work in expectation. |
| private static final long ANIMATION_DURATION_MS = 300; |
| |
| private AtomicReference<Float> mCurrentScale = new AtomicReference<>((float) 0); |
| private AtomicReference<Float> mCurrentCenterX = new AtomicReference<>((float) 0); |
| private AtomicReference<Float> mCurrentCenterY = new AtomicReference<>((float) 0); |
| private ArgumentCaptor<Float> mScaleCaptor = ArgumentCaptor.forClass(Float.class); |
| private ArgumentCaptor<Float> mCenterXCaptor = ArgumentCaptor.forClass(Float.class); |
| private ArgumentCaptor<Float> mCenterYCaptor = ArgumentCaptor.forClass(Float.class); |
| private final ArgumentCaptor<Float> mOffsetXCaptor = ArgumentCaptor.forClass(Float.class); |
| private final ArgumentCaptor<Float> mOffsetYCaptor = ArgumentCaptor.forClass(Float.class); |
| |
| @Mock |
| Handler mHandler; |
| @Mock |
| SfVsyncFrameCallbackProvider mSfVsyncFrameProvider; |
| @Mock |
| WindowMagnifierCallback mWindowMagnifierCallback; |
| @Mock |
| IRemoteMagnificationAnimationCallback mAnimationCallback; |
| @Mock |
| IRemoteMagnificationAnimationCallback mAnimationCallback2; |
| @Mock(answer = Answers.RETURNS_SELF) |
| SysUiState mSysUiState; |
| @Mock |
| SecureSettings mSecureSettings; |
| private SpyWindowMagnificationController mController; |
| private WindowMagnificationController mSpyController; |
| private WindowMagnificationAnimationController mWindowMagnificationAnimationController; |
| private Instrumentation mInstrumentation; |
| private long mWaitingAnimationPeriod; |
| private long mWaitIntermediateAnimationPeriod; |
| |
| private TestableWindowManager mWindowManager; |
| |
| @Before |
| public void setUp() throws Exception { |
| MockitoAnnotations.initMocks(this); |
| mInstrumentation = InstrumentationRegistry.getInstrumentation(); |
| final WindowManager wm = mContext.getSystemService(WindowManager.class); |
| mWindowManager = spy(new TestableWindowManager(wm)); |
| mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager); |
| |
| mWaitingAnimationPeriod = 2 * ANIMATION_DURATION_MS; |
| mWaitIntermediateAnimationPeriod = ANIMATION_DURATION_MS / 2; |
| mWindowMagnificationAnimationController = new WindowMagnificationAnimationController( |
| mContext, newValueAnimator()); |
| mController = new SpyWindowMagnificationController(mContext, mHandler, |
| mWindowMagnificationAnimationController, |
| mSfVsyncFrameProvider, null, new SurfaceControl.Transaction(), |
| mWindowMagnifierCallback, mSysUiState, mSecureSettings); |
| mSpyController = mController.getSpyController(); |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| mInstrumentation.runOnMainSync(() -> mController.deleteWindowMagnification()); |
| } |
| |
| @Test |
| public void enableWindowMagnification_disabled_expectedValuesAndInvokeCallback() |
| throws RemoteException { |
| enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback); |
| |
| verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( |
| mScaleCaptor.capture(), |
| mCenterXCaptor.capture(), mCenterYCaptor.capture(), |
| mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); |
| verifyStartValue(mScaleCaptor, 1.0f); |
| verifyStartValue(mCenterXCaptor, DEFAULT_CENTER_X); |
| verifyStartValue(mCenterYCaptor, DEFAULT_CENTER_Y); |
| verifyStartValue(mOffsetXCaptor, 0f); |
| verifyStartValue(mOffsetYCaptor, 0f); |
| verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y); |
| verify(mAnimationCallback).onResult(true); |
| } |
| |
| @Test |
| public void enableWindowMagnificationWithoutCallback_disabled_expectedValues() { |
| enableWindowMagnificationWithoutAnimation(); |
| |
| verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y); |
| } |
| |
| @Test |
| public void enableWindowMagnificationWithoutCallback_enabled_expectedValues() { |
| enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback); |
| final float targetScale = DEFAULT_SCALE + 1.0f; |
| final float targetCenterX = DEFAULT_CENTER_X + 100; |
| final float targetCenterY = DEFAULT_CENTER_Y + 100; |
| |
| mInstrumentation.runOnMainSync( |
| () -> { |
| mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, |
| targetCenterX, targetCenterY, null); |
| }); |
| |
| verifyFinalSpec(targetScale, targetCenterX, targetCenterY); |
| } |
| |
| @Test |
| public void enableWindowMagnificationWithScaleOne_disabled_NoAnimationAndInvokeCallback() |
| throws RemoteException { |
| mInstrumentation.runOnMainSync( |
| () -> { |
| mWindowMagnificationAnimationController.enableWindowMagnification(1, |
| DEFAULT_CENTER_X, DEFAULT_CENTER_Y, mAnimationCallback); |
| }); |
| SystemClock.sleep(mWaitingAnimationPeriod); |
| |
| verify(mSpyController).enableWindowMagnificationInternal(1, DEFAULT_CENTER_X, |
| DEFAULT_CENTER_Y, 0f, 0f); |
| verify(mAnimationCallback).onResult(true); |
| } |
| |
| @Test |
| public void enableWindowMagnification_enabling_expectedValuesAndInvokeCallback() |
| throws RemoteException { |
| enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, |
| mAnimationCallback); |
| final float targetScale = DEFAULT_SCALE + 1.0f; |
| final float targetCenterX = DEFAULT_CENTER_X + 100; |
| final float targetCenterY = DEFAULT_CENTER_Y + 100; |
| |
| mInstrumentation.runOnMainSync(() -> { |
| Mockito.reset(mSpyController); |
| mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, |
| targetCenterX, targetCenterY, mAnimationCallback2); |
| mCurrentScale.set(mController.getScale()); |
| mCurrentCenterX.set(mController.getCenterX()); |
| mCurrentCenterY.set(mController.getCenterY()); |
| }); |
| |
| SystemClock.sleep(mWaitingAnimationPeriod); |
| |
| verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( |
| mScaleCaptor.capture(), |
| mCenterXCaptor.capture(), mCenterYCaptor.capture(), |
| mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); |
| verifyStartValue(mScaleCaptor, mCurrentScale.get()); |
| verifyStartValue(mCenterXCaptor, mCurrentCenterX.get()); |
| verifyStartValue(mCenterYCaptor, mCurrentCenterY.get()); |
| verifyStartValue(mOffsetXCaptor, 0f); |
| verifyStartValue(mOffsetYCaptor, 0f); |
| verifyFinalSpec(targetScale, targetCenterX, targetCenterY); |
| verify(mAnimationCallback).onResult(false); |
| verify(mAnimationCallback2).onResult(true); |
| } |
| |
| @Test |
| public void enableWindowMagnificationWithUnchanged_enabling_expectedValuesToDefault() |
| throws InterruptedException { |
| final CountDownLatch countDownLatch = new CountDownLatch(2); |
| final MockMagnificationAnimationCallback animationCallback = |
| new MockMagnificationAnimationCallback(countDownLatch); |
| |
| enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, |
| animationCallback); |
| mInstrumentation.runOnMainSync( |
| () -> { |
| mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN, |
| Float.NaN, Float.NaN, animationCallback); |
| }); |
| |
| assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS)); |
| // The callback in 2nd enableWindowMagnification will return true |
| assertEquals(1, animationCallback.getSuccessCount()); |
| // The callback in 1st enableWindowMagnification will return false |
| assertEquals(1, animationCallback.getFailedCount()); |
| verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y); |
| } |
| |
| @Test |
| public void enableWindowMagnificationWithScaleOne_enabled_AnimationAndInvokeCallback() |
| throws RemoteException { |
| enableWindowMagnificationWithoutAnimation(); |
| |
| mInstrumentation.runOnMainSync(() -> { |
| Mockito.reset(mSpyController); |
| mWindowMagnificationAnimationController.enableWindowMagnification(1.0f, |
| DEFAULT_CENTER_X + 100, DEFAULT_CENTER_Y + 100, mAnimationCallback); |
| mCurrentScale.set(mController.getScale()); |
| mCurrentCenterX.set(mController.getCenterX()); |
| mCurrentCenterY.set(mController.getCenterY()); |
| }); |
| |
| SystemClock.sleep(mWaitingAnimationPeriod); |
| |
| verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( |
| mScaleCaptor.capture(), |
| mCenterXCaptor.capture(), mCenterYCaptor.capture(), |
| mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); |
| verifyStartValue(mScaleCaptor, mCurrentScale.get()); |
| verifyStartValue(mCenterXCaptor, mCurrentCenterX.get()); |
| verifyStartValue(mCenterYCaptor, mCurrentCenterY.get()); |
| verifyStartValue(mOffsetXCaptor, 0f); |
| verifyStartValue(mOffsetYCaptor, 0f); |
| // It presents the window magnification is disabled. |
| verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); |
| |
| verify(mAnimationCallback).onResult(true); |
| } |
| |
| @Test |
| public void |
| enableMagnificationWithoutCallback_enabling_expectedValuesAndInvokeFormerCallback() |
| throws RemoteException { |
| enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, |
| mAnimationCallback); |
| final float targetScale = DEFAULT_SCALE - 1.0f; |
| final float targetCenterX = DEFAULT_CENTER_X + 100; |
| final float targetCenterY = DEFAULT_CENTER_Y + 100; |
| |
| mInstrumentation.runOnMainSync(() -> { |
| Mockito.reset(mSpyController); |
| mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, |
| targetCenterX, targetCenterY, null); |
| }); |
| verifyFinalSpec(targetScale, targetCenterX, targetCenterY); |
| verify(mAnimationCallback).onResult(false); |
| } |
| |
| @Test |
| public void enableWindowMagnificationWithSameSpec_enabling_NoAnimationAndInvokeCallback() |
| throws RemoteException { |
| enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, |
| mAnimationCallback); |
| |
| mInstrumentation.runOnMainSync(() -> { |
| Mockito.reset(mSpyController); |
| mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN, |
| Float.NaN, Float.NaN, mAnimationCallback2); |
| }); |
| SystemClock.sleep(mWaitingAnimationPeriod); |
| |
| verify(mSpyController, never()).enableWindowMagnificationInternal(anyFloat(), anyFloat(), |
| anyFloat()); |
| verify(mAnimationCallback).onResult(false); |
| verify(mAnimationCallback2).onResult(true); |
| } |
| |
| @Test |
| public void enableWindowMagnification_disabling_expectedValuesAndInvokeCallback() |
| throws RemoteException { |
| enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null); |
| deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, |
| mAnimationCallback); |
| final float targetScale = DEFAULT_SCALE + 1.0f; |
| final float targetCenterX = DEFAULT_CENTER_X + 100; |
| final float targetCenterY = DEFAULT_CENTER_Y + 100; |
| |
| mInstrumentation.runOnMainSync( |
| () -> { |
| Mockito.reset(mSpyController); |
| mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, |
| targetCenterX, targetCenterY, mAnimationCallback2); |
| mCurrentScale.set(mController.getScale()); |
| mCurrentCenterX.set(mController.getCenterX()); |
| mCurrentCenterY.set(mController.getCenterY()); |
| }); |
| // Current spec shouldn't match given spec. |
| verify(mAnimationCallback2, never()).onResult(anyBoolean()); |
| verify(mAnimationCallback).onResult(false); |
| SystemClock.sleep(mWaitingAnimationPeriod); |
| |
| verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( |
| mScaleCaptor.capture(), |
| mCenterXCaptor.capture(), mCenterYCaptor.capture(), |
| mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); |
| //Animating in reverse, so we only check if the start values are greater than current. |
| assertTrue(mScaleCaptor.getAllValues().get(0) > mCurrentScale.get()); |
| assertEquals(targetScale, mScaleCaptor.getValue(), 0f); |
| assertTrue(mCenterXCaptor.getAllValues().get(0) > mCurrentCenterX.get()); |
| assertEquals(targetCenterX, mCenterXCaptor.getValue(), 0f); |
| assertTrue(mCenterYCaptor.getAllValues().get(0) > mCurrentCenterY.get()); |
| assertEquals(targetCenterY, mCenterYCaptor.getValue(), 0f); |
| verifyFinalSpec(targetScale, targetCenterX, targetCenterY); |
| verify(mAnimationCallback2).onResult(true); |
| } |
| |
| @Test |
| public void |
| enableMagnificationWithoutCallback_disabling_expectedValuesAndInvokeFormerCallback() |
| throws RemoteException { |
| enableWindowMagnificationWithoutAnimation(); |
| deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, |
| mAnimationCallback); |
| final float targetScale = DEFAULT_SCALE + 1.0f; |
| final float targetCenterX = DEFAULT_CENTER_X + 100; |
| final float targetCenterY = DEFAULT_CENTER_Y + 100; |
| |
| mInstrumentation.runOnMainSync( |
| () -> { |
| Mockito.reset(mSpyController); |
| mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, |
| targetCenterX, targetCenterY, null); |
| }); |
| |
| verify(mAnimationCallback).onResult(false); |
| verifyFinalSpec(targetScale, targetCenterX, targetCenterY); |
| } |
| |
| @Test |
| public void enableWindowMagnificationWithSameSpec_disabling_NoAnimationAndInvokeCallback() |
| throws RemoteException { |
| enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null); |
| deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, |
| mAnimationCallback); |
| |
| mInstrumentation.runOnMainSync(() -> { |
| Mockito.reset(mSpyController); |
| mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN, |
| Float.NaN, Float.NaN, mAnimationCallback2); |
| }); |
| SystemClock.sleep(mWaitingAnimationPeriod); |
| |
| verify(mSpyController, never()).enableWindowMagnificationInternal(anyFloat(), anyFloat(), |
| anyFloat()); |
| verify(mSpyController, never()).deleteWindowMagnification(); |
| verify(mAnimationCallback).onResult(false); |
| verify(mAnimationCallback2).onResult(true); |
| } |
| |
| @Test |
| public void enableWindowMagnification_enabled_expectedValuesAndInvokeCallback() |
| throws RemoteException { |
| enableWindowMagnificationWithoutAnimation(); |
| final float targetScale = DEFAULT_SCALE + 1.0f; |
| final float targetCenterX = DEFAULT_CENTER_X + 100; |
| final float targetCenterY = DEFAULT_CENTER_Y + 100; |
| |
| mInstrumentation.runOnMainSync(() -> { |
| Mockito.reset(mSpyController); |
| mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, |
| targetCenterX, targetCenterY, mAnimationCallback2); |
| mCurrentScale.set(mController.getScale()); |
| mCurrentCenterX.set(mController.getCenterX()); |
| mCurrentCenterY.set(mController.getCenterY()); |
| }); |
| |
| SystemClock.sleep(mWaitingAnimationPeriod); |
| |
| verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( |
| mScaleCaptor.capture(), |
| mCenterXCaptor.capture(), mCenterYCaptor.capture(), |
| mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); |
| verifyStartValue(mScaleCaptor, mCurrentScale.get()); |
| verifyStartValue(mCenterXCaptor, mCurrentCenterX.get()); |
| verifyStartValue(mCenterYCaptor, mCurrentCenterY.get()); |
| verifyStartValue(mOffsetXCaptor, 0f); |
| verifyStartValue(mOffsetYCaptor, 0f); |
| verifyFinalSpec(targetScale, targetCenterX, targetCenterY); |
| verify(mAnimationCallback2).onResult(true); |
| } |
| |
| @Test |
| public void enableWindowMagnificationWithOffset_expectedValues() { |
| final float offsetRatio = -0.1f; |
| final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds()); |
| mInstrumentation.runOnMainSync(() -> { |
| Mockito.reset(mSpyController); |
| mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE, |
| windowBounds.exactCenterX(), windowBounds.exactCenterY(), |
| offsetRatio, offsetRatio, mAnimationCallback); |
| }); |
| SystemClock.sleep(mWaitingAnimationPeriod); |
| final View attachedView = mWindowManager.getAttachedView(); |
| assertNotNull(attachedView); |
| final Rect mirrorViewBound = new Rect(); |
| final View mirrorView = attachedView.findViewById(R.id.surface_view); |
| assertNotNull(mirrorView); |
| mirrorView.getBoundsOnScreen(mirrorViewBound); |
| |
| assertEquals(mirrorViewBound.exactCenterX() - windowBounds.exactCenterX(), |
| Math.round(offsetRatio * mirrorViewBound.width() / 2), 0.1f); |
| assertEquals(mirrorViewBound.exactCenterY() - windowBounds.exactCenterY(), |
| Math.round(offsetRatio * mirrorViewBound.height() / 2), 0.1f); |
| } |
| |
| @Test |
| public void moveWindowMagnifierToPosition_enabled_expectedValues() |
| throws InterruptedException { |
| final CountDownLatch countDownLatch = new CountDownLatch(1); |
| final MockMagnificationAnimationCallback animationCallback = |
| new MockMagnificationAnimationCallback(countDownLatch); |
| final float targetCenterX = DEFAULT_CENTER_X + 100; |
| final float targetCenterY = DEFAULT_CENTER_Y + 100; |
| enableWindowMagnificationWithoutAnimation(); |
| |
| mInstrumentation.runOnMainSync(() -> { |
| mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( |
| targetCenterX, targetCenterY, animationCallback); |
| }); |
| |
| assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS)); |
| assertEquals(1, animationCallback.getSuccessCount()); |
| assertEquals(0, animationCallback.getFailedCount()); |
| verifyFinalSpec(DEFAULT_SCALE, targetCenterX, targetCenterY); |
| } |
| |
| @Test |
| public void moveWindowMagnifierToPositionMultipleTimes_enabled_expectedValuesToLastOne() |
| throws InterruptedException { |
| final CountDownLatch countDownLatch = new CountDownLatch(4); |
| final MockMagnificationAnimationCallback animationCallback = |
| new MockMagnificationAnimationCallback(countDownLatch); |
| enableWindowMagnificationWithoutAnimation(); |
| |
| mInstrumentation.runOnMainSync(() -> { |
| mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( |
| DEFAULT_CENTER_X + 10, DEFAULT_CENTER_Y + 10, animationCallback); |
| mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( |
| DEFAULT_CENTER_X + 20, DEFAULT_CENTER_Y + 20, animationCallback); |
| mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( |
| DEFAULT_CENTER_X + 30, DEFAULT_CENTER_Y + 30, animationCallback); |
| mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( |
| DEFAULT_CENTER_X + 40, DEFAULT_CENTER_Y + 40, animationCallback); |
| }); |
| |
| assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS)); |
| // only the last one callback will return true |
| assertEquals(1, animationCallback.getSuccessCount()); |
| // the others will return false |
| assertEquals(3, animationCallback.getFailedCount()); |
| verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + 40, DEFAULT_CENTER_Y + 40); |
| } |
| |
| @Test |
| public void moveWindowMagnifierToPosition_enabling_expectedValuesToLastOne() |
| throws InterruptedException { |
| final CountDownLatch countDownLatch = new CountDownLatch(2); |
| final MockMagnificationAnimationCallback animationCallback = |
| new MockMagnificationAnimationCallback(countDownLatch); |
| final float targetCenterX = DEFAULT_CENTER_X + 100; |
| final float targetCenterY = DEFAULT_CENTER_Y + 100; |
| |
| enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, |
| animationCallback); |
| mInstrumentation.runOnMainSync( |
| () -> { |
| mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( |
| targetCenterX, targetCenterY, animationCallback); |
| }); |
| |
| assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS)); |
| // The callback in moveWindowMagnifierToPosition will return true |
| assertEquals(1, animationCallback.getSuccessCount()); |
| // The callback in enableWindowMagnification will return false |
| assertEquals(1, animationCallback.getFailedCount()); |
| verifyFinalSpec(DEFAULT_SCALE, targetCenterX, targetCenterY); |
| } |
| |
| @Test |
| public void moveWindowMagnifierToPositionWithCenterUnchanged_enabling_expectedValuesToDefault() |
| throws InterruptedException { |
| final CountDownLatch countDownLatch = new CountDownLatch(2); |
| final MockMagnificationAnimationCallback animationCallback = |
| new MockMagnificationAnimationCallback(countDownLatch); |
| |
| enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, |
| animationCallback); |
| mInstrumentation.runOnMainSync( |
| () -> { |
| mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( |
| Float.NaN, Float.NaN, animationCallback); |
| }); |
| |
| assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS)); |
| // The callback in moveWindowMagnifierToPosition will return true |
| assertEquals(1, animationCallback.getSuccessCount()); |
| // The callback in enableWindowMagnification will return false |
| assertEquals(1, animationCallback.getFailedCount()); |
| verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y); |
| } |
| |
| @Test |
| public void enableWindowMagnificationWithSameScale_enabled_doNothingButInvokeCallback() |
| throws RemoteException { |
| enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null); |
| |
| enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback); |
| |
| verify(mSpyController, never()).enableWindowMagnificationInternal(anyFloat(), anyFloat(), |
| anyFloat()); |
| verify(mAnimationCallback).onResult(true); |
| } |
| |
| @Test |
| public void deleteWindowMagnification_enabled_expectedValuesAndInvokeCallback() |
| throws RemoteException { |
| enableWindowMagnificationWithoutAnimation(); |
| |
| deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback); |
| |
| verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( |
| mScaleCaptor.capture(), |
| mCenterXCaptor.capture(), mCenterYCaptor.capture(), |
| mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); |
| verifyStartValue(mScaleCaptor, DEFAULT_SCALE); |
| verifyStartValue(mCenterXCaptor, Float.NaN); |
| verifyStartValue(mCenterYCaptor, Float.NaN); |
| verifyStartValue(mOffsetXCaptor, 0f); |
| verifyStartValue(mOffsetYCaptor, 0f); |
| verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); |
| verify(mAnimationCallback).onResult(true); |
| } |
| |
| @Test |
| public void deleteWindowMagnificationWithoutCallback_enabled_expectedValues() { |
| enableWindowMagnificationWithoutAnimation(); |
| |
| deleteWindowMagnificationAndWaitAnimating(0, null); |
| |
| verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); |
| } |
| |
| @Test |
| public void deleteWindowMagnification_disabled_doNothingAndInvokeCallback() |
| throws RemoteException { |
| deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback); |
| |
| Mockito.verifyNoMoreInteractions(mSpyController); |
| verify(mAnimationCallback).onResult(true); |
| } |
| |
| @Test |
| public void deleteWindowMagnification_enabling_expectedValuesAndInvokeCallback() |
| throws RemoteException { |
| enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, |
| mAnimationCallback); |
| |
| mInstrumentation.runOnMainSync( |
| () -> { |
| Mockito.reset(mSpyController); |
| mWindowMagnificationAnimationController.deleteWindowMagnification( |
| mAnimationCallback2); |
| mCurrentScale.set(mController.getScale()); |
| mCurrentCenterX.set(mController.getCenterX()); |
| mCurrentCenterY.set(mController.getCenterY()); |
| }); |
| SystemClock.sleep(mWaitingAnimationPeriod); |
| verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( |
| mScaleCaptor.capture(), |
| mCenterXCaptor.capture(), mCenterYCaptor.capture(), |
| mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); |
| |
| //The animation is in verse, so we only check the start values should no be greater than |
| // the current one. |
| assertTrue(mScaleCaptor.getAllValues().get(0) <= mCurrentScale.get()); |
| assertEquals(1.0f, mScaleCaptor.getValue(), 0f); |
| verifyStartValue(mCenterXCaptor, Float.NaN); |
| verifyStartValue(mCenterYCaptor, Float.NaN); |
| verifyStartValue(mOffsetXCaptor, 0f); |
| verifyStartValue(mOffsetYCaptor, 0f); |
| verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); |
| verify(mAnimationCallback).onResult(false); |
| verify(mAnimationCallback2).onResult(true); |
| } |
| |
| @Test |
| public void deleteWindowMagnificationWithoutCallback_enabling_expectedValuesAndInvokeCallback() |
| throws RemoteException { |
| enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, |
| mAnimationCallback); |
| |
| mInstrumentation.runOnMainSync( |
| () -> { |
| Mockito.reset(mSpyController); |
| mWindowMagnificationAnimationController.deleteWindowMagnification(null); |
| }); |
| |
| verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); |
| verify(mAnimationCallback).onResult(false); |
| } |
| |
| @Test |
| public void deleteWindowMagnification_disabling_checkStartAndValues() throws RemoteException { |
| enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null); |
| deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, |
| mAnimationCallback); |
| |
| deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback2); |
| |
| verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( |
| mScaleCaptor.capture(), |
| mCenterXCaptor.capture(), mCenterYCaptor.capture(), |
| mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); |
| assertEquals(1.0f, mScaleCaptor.getValue(), 0f); |
| verifyStartValue(mOffsetXCaptor, 0f); |
| verifyStartValue(mOffsetYCaptor, 0f); |
| verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); |
| verify(mAnimationCallback).onResult(false); |
| verify(mAnimationCallback2).onResult(true); |
| } |
| |
| @Test |
| public void deleteWindowMagnificationWithoutCallback_disabling_checkStartAndValues() |
| throws RemoteException { |
| enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null); |
| deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, |
| mAnimationCallback); |
| |
| deleteWindowMagnificationAndWaitAnimating(0, null); |
| |
| verify(mSpyController).deleteWindowMagnification(); |
| verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); |
| verify(mAnimationCallback).onResult(false); |
| } |
| |
| @Test |
| public void moveWindowMagnifier_enabled() { |
| enableWindowMagnificationWithoutAnimation(); |
| |
| mInstrumentation.runOnMainSync( |
| () -> mController.moveWindowMagnifier(100f, 200f)); |
| |
| verify(mSpyController).moveWindowMagnifier(100f, 200f); |
| verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + 100f, DEFAULT_CENTER_Y + 100f); |
| } |
| |
| @Test |
| public void moveWindowMagnifierToPosition_enabled() { |
| final float targetCenterX = DEFAULT_CENTER_X + 100; |
| final float targetCenterY = DEFAULT_CENTER_Y + 100; |
| enableWindowMagnificationWithoutAnimation(); |
| |
| mInstrumentation.runOnMainSync( |
| () -> mController.moveWindowMagnifierToPosition(targetCenterX, targetCenterY, |
| mAnimationCallback)); |
| SystemClock.sleep(mWaitingAnimationPeriod); |
| |
| verifyFinalSpec(DEFAULT_SCALE, targetCenterX, targetCenterY); |
| } |
| |
| private void verifyFinalSpec(float expectedScale, float expectedCenterX, |
| float expectedCenterY) { |
| assertEquals(expectedScale, mController.getScale(), 0f); |
| assertEquals(expectedCenterX, mController.getCenterX(), 0f); |
| assertEquals(expectedCenterY, mController.getCenterY(), 0f); |
| } |
| |
| private void enableWindowMagnificationWithoutAnimation() { |
| mInstrumentation.runOnMainSync( |
| () -> { |
| Mockito.reset(mSpyController); |
| mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE, |
| DEFAULT_CENTER_X, DEFAULT_CENTER_Y, null); |
| }); |
| } |
| |
| private void enableWindowMagnificationAndWaitAnimating(long duration, |
| @Nullable IRemoteMagnificationAnimationCallback callback) { |
| mInstrumentation.runOnMainSync( |
| () -> { |
| Mockito.reset(mSpyController); |
| mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE, |
| DEFAULT_CENTER_X, DEFAULT_CENTER_Y, callback); |
| }); |
| SystemClock.sleep(duration); |
| } |
| |
| private void deleteWindowMagnificationAndWaitAnimating(long duration, |
| @Nullable IRemoteMagnificationAnimationCallback callback) { |
| mInstrumentation.runOnMainSync( |
| () -> { |
| resetMockObjects(); |
| mWindowMagnificationAnimationController.deleteWindowMagnification(callback); |
| }); |
| SystemClock.sleep(duration); |
| } |
| |
| private void verifyStartValue(ArgumentCaptor<Float> captor, float startValue) { |
| assertEquals(startValue, captor.getAllValues().get(0), 0f); |
| } |
| |
| private void resetMockObjects() { |
| Mockito.reset(mSpyController); |
| } |
| |
| /** |
| * It observes the methods in {@link WindowMagnificationController} since we couldn't spy it |
| * directly. |
| */ |
| private static class SpyWindowMagnificationController extends WindowMagnificationController { |
| private WindowMagnificationController mSpyController; |
| |
| SpyWindowMagnificationController(Context context, Handler handler, |
| WindowMagnificationAnimationController animationController, |
| SfVsyncFrameCallbackProvider sfVsyncFrameProvider, |
| MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction, |
| WindowMagnifierCallback callback, SysUiState sysUiState, |
| SecureSettings secureSettings) { |
| super( |
| context, |
| handler, |
| animationController, |
| sfVsyncFrameProvider, |
| mirrorWindowControl, |
| transaction, |
| callback, |
| sysUiState, |
| WindowManagerGlobal::getWindowSession, |
| secureSettings); |
| mSpyController = Mockito.mock(WindowMagnificationController.class); |
| } |
| |
| WindowMagnificationController getSpyController() { |
| return mSpyController; |
| } |
| |
| @Override |
| void enableWindowMagnificationInternal(float scale, float centerX, float centerY) { |
| super.enableWindowMagnificationInternal(scale, centerX, centerY); |
| mSpyController.enableWindowMagnificationInternal(scale, centerX, centerY); |
| } |
| |
| @Override |
| void enableWindowMagnificationInternal(float scale, float centerX, float centerY, |
| float magnificationOffsetFrameRatioX, float magnificationOffsetFrameRatioY) { |
| super.enableWindowMagnificationInternal(scale, centerX, centerY, |
| magnificationOffsetFrameRatioX, magnificationOffsetFrameRatioY); |
| mSpyController.enableWindowMagnificationInternal(scale, centerX, centerY, |
| magnificationOffsetFrameRatioX, magnificationOffsetFrameRatioY); |
| } |
| |
| @Override |
| void deleteWindowMagnification() { |
| super.deleteWindowMagnification(); |
| mSpyController.deleteWindowMagnification(); |
| } |
| |
| @Override |
| void moveWindowMagnifier(float offsetX, float offsetY) { |
| super.moveWindowMagnifier(offsetX, offsetX); |
| mSpyController.moveWindowMagnifier(offsetX, offsetY); |
| } |
| |
| @Override |
| void moveWindowMagnifierToPosition(float positionX, float positionY, |
| IRemoteMagnificationAnimationCallback callback) { |
| super.moveWindowMagnifierToPosition(positionX, positionY, callback); |
| mSpyController.moveWindowMagnifierToPosition(positionX, positionY, callback); |
| } |
| |
| @Override |
| void setScale(float scale) { |
| super.setScale(scale); |
| mSpyController.setScale(scale); |
| } |
| |
| @Override |
| public void updateSysUIStateFlag() { |
| super.updateSysUIStateFlag(); |
| mSpyController.updateSysUIStateFlag(); |
| } |
| } |
| |
| private static ValueAnimator newValueAnimator() { |
| final ValueAnimator valueAnimator = new ValueAnimator(); |
| valueAnimator.setDuration(ANIMATION_DURATION_MS); |
| valueAnimator.setInterpolator(new AccelerateInterpolator(2.5f)); |
| valueAnimator.setFloatValues(0.0f, 1.0f); |
| return valueAnimator; |
| } |
| } |