1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.accessibility;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertNotNull;
21 import static org.junit.Assert.assertTrue;
22 import static org.mockito.ArgumentMatchers.anyBoolean;
23 import static org.mockito.ArgumentMatchers.anyFloat;
24 import static org.mockito.Mockito.atLeast;
25 import static org.mockito.Mockito.never;
26 import static org.mockito.Mockito.spy;
27 import static org.mockito.Mockito.verify;
28 
29 import android.animation.ValueAnimator;
30 import android.annotation.Nullable;
31 import android.app.Instrumentation;
32 import android.content.Context;
33 import android.graphics.Rect;
34 import android.os.Handler;
35 import android.os.RemoteException;
36 import android.os.SystemClock;
37 import android.testing.AndroidTestingRunner;
38 import android.view.SurfaceControl;
39 import android.view.View;
40 import android.view.WindowManager;
41 import android.view.WindowManagerGlobal;
42 import android.view.accessibility.IRemoteMagnificationAnimationCallback;
43 import android.view.animation.AccelerateInterpolator;
44 
45 import androidx.test.InstrumentationRegistry;
46 import androidx.test.filters.LargeTest;
47 
48 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
49 import com.android.systemui.R;
50 import com.android.systemui.SysuiTestCase;
51 import com.android.systemui.model.SysUiState;
52 import com.android.systemui.util.settings.SecureSettings;
53 
54 import org.junit.After;
55 import org.junit.Before;
56 import org.junit.Ignore;
57 import org.junit.Test;
58 import org.junit.runner.RunWith;
59 import org.mockito.Answers;
60 import org.mockito.ArgumentCaptor;
61 import org.mockito.Mock;
62 import org.mockito.Mockito;
63 import org.mockito.MockitoAnnotations;
64 
65 import java.util.concurrent.CountDownLatch;
66 import java.util.concurrent.TimeUnit;
67 import java.util.concurrent.atomic.AtomicReference;
68 
69 @Ignore
70 @LargeTest
71 @RunWith(AndroidTestingRunner.class)
72 public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
73 
74     private static final float DEFAULT_SCALE = 4.0f;
75     private static final float DEFAULT_CENTER_X = 400.0f;
76     private static final float DEFAULT_CENTER_Y = 500.0f;
77     // The duration couldn't too short, otherwise the ValueAnimator won't work in expectation.
78     private static final long ANIMATION_DURATION_MS = 300;
79 
80     private AtomicReference<Float> mCurrentScale = new AtomicReference<>((float) 0);
81     private AtomicReference<Float> mCurrentCenterX = new AtomicReference<>((float) 0);
82     private AtomicReference<Float> mCurrentCenterY = new AtomicReference<>((float) 0);
83     private ArgumentCaptor<Float> mScaleCaptor = ArgumentCaptor.forClass(Float.class);
84     private ArgumentCaptor<Float> mCenterXCaptor = ArgumentCaptor.forClass(Float.class);
85     private ArgumentCaptor<Float> mCenterYCaptor = ArgumentCaptor.forClass(Float.class);
86     private final ArgumentCaptor<Float> mOffsetXCaptor = ArgumentCaptor.forClass(Float.class);
87     private final ArgumentCaptor<Float> mOffsetYCaptor = ArgumentCaptor.forClass(Float.class);
88 
89     @Mock
90     Handler mHandler;
91     @Mock
92     SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
93     @Mock
94     WindowMagnifierCallback mWindowMagnifierCallback;
95     @Mock
96     IRemoteMagnificationAnimationCallback mAnimationCallback;
97     @Mock
98     IRemoteMagnificationAnimationCallback mAnimationCallback2;
99     @Mock(answer = Answers.RETURNS_SELF)
100     SysUiState mSysUiState;
101     @Mock
102     SecureSettings mSecureSettings;
103     private SpyWindowMagnificationController mController;
104     private WindowMagnificationController mSpyController;
105     private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
106     private Instrumentation mInstrumentation;
107     private long mWaitingAnimationPeriod;
108     private long mWaitIntermediateAnimationPeriod;
109 
110     private TestableWindowManager mWindowManager;
111 
112     @Before
setUp()113     public void setUp() throws Exception {
114         MockitoAnnotations.initMocks(this);
115         mInstrumentation = InstrumentationRegistry.getInstrumentation();
116         final WindowManager wm = mContext.getSystemService(WindowManager.class);
117         mWindowManager = spy(new TestableWindowManager(wm));
118         mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
119 
120         mWaitingAnimationPeriod = 2 * ANIMATION_DURATION_MS;
121         mWaitIntermediateAnimationPeriod = ANIMATION_DURATION_MS / 2;
122         mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
123                 mContext, newValueAnimator());
124         mController = new SpyWindowMagnificationController(mContext, mHandler,
125                 mWindowMagnificationAnimationController,
126                 mSfVsyncFrameProvider, null, new SurfaceControl.Transaction(),
127                 mWindowMagnifierCallback, mSysUiState, mSecureSettings);
128         mSpyController = mController.getSpyController();
129     }
130 
131     @After
tearDown()132     public void tearDown() throws Exception {
133         mInstrumentation.runOnMainSync(() -> mController.deleteWindowMagnification());
134     }
135 
136     @Test
enableWindowMagnification_disabled_expectedValuesAndInvokeCallback()137     public void enableWindowMagnification_disabled_expectedValuesAndInvokeCallback()
138             throws RemoteException {
139         enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback);
140 
141         verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
142                 mScaleCaptor.capture(),
143                 mCenterXCaptor.capture(), mCenterYCaptor.capture(),
144                 mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
145         verifyStartValue(mScaleCaptor, 1.0f);
146         verifyStartValue(mCenterXCaptor, DEFAULT_CENTER_X);
147         verifyStartValue(mCenterYCaptor, DEFAULT_CENTER_Y);
148         verifyStartValue(mOffsetXCaptor, 0f);
149         verifyStartValue(mOffsetYCaptor, 0f);
150         verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
151         verify(mAnimationCallback).onResult(true);
152     }
153 
154     @Test
enableWindowMagnificationWithoutCallback_disabled_expectedValues()155     public void enableWindowMagnificationWithoutCallback_disabled_expectedValues() {
156         enableWindowMagnificationWithoutAnimation();
157 
158         verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
159     }
160 
161     @Test
enableWindowMagnificationWithoutCallback_enabled_expectedValues()162     public void enableWindowMagnificationWithoutCallback_enabled_expectedValues() {
163         enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback);
164         final float targetScale = DEFAULT_SCALE + 1.0f;
165         final float targetCenterX = DEFAULT_CENTER_X + 100;
166         final float targetCenterY = DEFAULT_CENTER_Y + 100;
167 
168         mInstrumentation.runOnMainSync(
169                 () -> {
170                     mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
171                             targetCenterX, targetCenterY, null);
172                 });
173 
174         verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
175     }
176 
177     @Test
enableWindowMagnificationWithScaleOne_disabled_NoAnimationAndInvokeCallback()178     public void enableWindowMagnificationWithScaleOne_disabled_NoAnimationAndInvokeCallback()
179             throws RemoteException {
180         mInstrumentation.runOnMainSync(
181                 () -> {
182                     mWindowMagnificationAnimationController.enableWindowMagnification(1,
183                             DEFAULT_CENTER_X, DEFAULT_CENTER_Y, mAnimationCallback);
184                 });
185         SystemClock.sleep(mWaitingAnimationPeriod);
186 
187         verify(mSpyController).enableWindowMagnificationInternal(1, DEFAULT_CENTER_X,
188                 DEFAULT_CENTER_Y, 0f, 0f);
189         verify(mAnimationCallback).onResult(true);
190     }
191 
192     @Test
enableWindowMagnification_enabling_expectedValuesAndInvokeCallback()193     public void enableWindowMagnification_enabling_expectedValuesAndInvokeCallback()
194             throws RemoteException {
195         enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
196                 mAnimationCallback);
197         final float targetScale = DEFAULT_SCALE + 1.0f;
198         final float targetCenterX = DEFAULT_CENTER_X + 100;
199         final float targetCenterY = DEFAULT_CENTER_Y + 100;
200 
201         mInstrumentation.runOnMainSync(() -> {
202             Mockito.reset(mSpyController);
203             mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
204                     targetCenterX, targetCenterY, mAnimationCallback2);
205             mCurrentScale.set(mController.getScale());
206             mCurrentCenterX.set(mController.getCenterX());
207             mCurrentCenterY.set(mController.getCenterY());
208         });
209 
210         SystemClock.sleep(mWaitingAnimationPeriod);
211 
212         verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
213                 mScaleCaptor.capture(),
214                 mCenterXCaptor.capture(), mCenterYCaptor.capture(),
215                 mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
216         verifyStartValue(mScaleCaptor, mCurrentScale.get());
217         verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
218         verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
219         verifyStartValue(mOffsetXCaptor, 0f);
220         verifyStartValue(mOffsetYCaptor, 0f);
221         verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
222         verify(mAnimationCallback).onResult(false);
223         verify(mAnimationCallback2).onResult(true);
224     }
225 
226     @Test
enableWindowMagnificationWithUnchanged_enabling_expectedValuesToDefault()227     public void enableWindowMagnificationWithUnchanged_enabling_expectedValuesToDefault()
228             throws InterruptedException {
229         final CountDownLatch countDownLatch = new CountDownLatch(2);
230         final MockMagnificationAnimationCallback animationCallback =
231                 new MockMagnificationAnimationCallback(countDownLatch);
232 
233         enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
234                 animationCallback);
235         mInstrumentation.runOnMainSync(
236                 () -> {
237                     mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN,
238                             Float.NaN, Float.NaN, animationCallback);
239                 });
240 
241         assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS));
242         // The callback in 2nd enableWindowMagnification will return true
243         assertEquals(1, animationCallback.getSuccessCount());
244         // The callback in 1st enableWindowMagnification will return false
245         assertEquals(1, animationCallback.getFailedCount());
246         verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
247     }
248 
249     @Test
enableWindowMagnificationWithScaleOne_enabled_AnimationAndInvokeCallback()250     public void enableWindowMagnificationWithScaleOne_enabled_AnimationAndInvokeCallback()
251             throws RemoteException {
252         enableWindowMagnificationWithoutAnimation();
253 
254         final float targetScale = 1.0f;
255         final float targetCenterX = DEFAULT_CENTER_X + 100;
256         final float targetCenterY = DEFAULT_CENTER_Y + 100;
257 
258         mInstrumentation.runOnMainSync(() -> {
259             Mockito.reset(mSpyController);
260             mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
261                     targetCenterX, targetCenterY, mAnimationCallback);
262             mCurrentScale.set(mController.getScale());
263             mCurrentCenterX.set(mController.getCenterX());
264             mCurrentCenterY.set(mController.getCenterY());
265         });
266 
267         SystemClock.sleep(mWaitingAnimationPeriod);
268 
269         verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
270                 mScaleCaptor.capture(),
271                 mCenterXCaptor.capture(), mCenterYCaptor.capture(),
272                 mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
273         verifyStartValue(mScaleCaptor, mCurrentScale.get());
274         verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
275         verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
276         verifyStartValue(mOffsetXCaptor, 0f);
277         verifyStartValue(mOffsetYCaptor, 0f);
278 
279         verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
280 
281         verify(mAnimationCallback).onResult(true);
282         assertEquals(WindowMagnificationAnimationController.STATE_ENABLED,
283                 mWindowMagnificationAnimationController.getState());
284     }
285 
286     @Test
enableWindowMagnificationWithScaleLessThanOne_enabled_AnimationAndInvokeCallback()287     public void enableWindowMagnificationWithScaleLessThanOne_enabled_AnimationAndInvokeCallback()
288             throws RemoteException {
289         enableWindowMagnificationWithoutAnimation();
290 
291         final float targetScale = 0.99f;
292         final float targetCenterX = DEFAULT_CENTER_X + 100;
293         final float targetCenterY = DEFAULT_CENTER_Y + 100;
294 
295         mInstrumentation.runOnMainSync(() -> {
296             Mockito.reset(mSpyController);
297             mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
298                     targetCenterX, targetCenterY, mAnimationCallback);
299             mCurrentScale.set(mController.getScale());
300             mCurrentCenterX.set(mController.getCenterX());
301             mCurrentCenterY.set(mController.getCenterY());
302         });
303 
304         SystemClock.sleep(mWaitingAnimationPeriod);
305 
306         verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
307                 mScaleCaptor.capture(),
308                 mCenterXCaptor.capture(), mCenterYCaptor.capture(),
309                 mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
310         verifyStartValue(mScaleCaptor, mCurrentScale.get());
311         verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
312         verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
313         verifyStartValue(mOffsetXCaptor, 0f);
314         verifyStartValue(mOffsetYCaptor, 0f);
315         // It presents the window magnification is disabled.
316         verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
317 
318         verify(mAnimationCallback).onResult(true);
319         assertEquals(WindowMagnificationAnimationController.STATE_DISABLED,
320                 mWindowMagnificationAnimationController.getState());
321     }
322 
323     @Test
324     public void
enableWindowMagnificationWithScaleLessThanOneAndWithoutCallBack_enabled_expectedValues()325             enableWindowMagnificationWithScaleLessThanOneAndWithoutCallBack_enabled_expectedValues()
326             throws RemoteException {
327         enableWindowMagnificationWithoutAnimation();
328 
329         final float targetScale = 0.99f;
330         final float targetCenterX = DEFAULT_CENTER_X + 100;
331         final float targetCenterY = DEFAULT_CENTER_Y + 100;
332 
333         mInstrumentation.runOnMainSync(() -> {
334             Mockito.reset(mSpyController);
335             mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
336                     targetCenterX, targetCenterY, null);
337         });
338 
339         verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
340         assertEquals(WindowMagnificationAnimationController.STATE_DISABLED,
341                 mWindowMagnificationAnimationController.getState());
342     }
343 
344     @Test
345     public void
enableMagnificationWithoutCallback_enabling_expectedValuesAndInvokeFormerCallback()346             enableMagnificationWithoutCallback_enabling_expectedValuesAndInvokeFormerCallback()
347             throws RemoteException {
348         enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
349                 mAnimationCallback);
350         final float targetScale = DEFAULT_SCALE - 1.0f;
351         final float targetCenterX = DEFAULT_CENTER_X + 100;
352         final float targetCenterY = DEFAULT_CENTER_Y + 100;
353 
354         mInstrumentation.runOnMainSync(() -> {
355             Mockito.reset(mSpyController);
356             mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
357                     targetCenterX, targetCenterY, null);
358         });
359         verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
360         verify(mAnimationCallback).onResult(false);
361     }
362 
363     @Test
enableWindowMagnificationWithSameSpec_enabling_NoAnimationAndInvokeCallback()364     public void enableWindowMagnificationWithSameSpec_enabling_NoAnimationAndInvokeCallback()
365             throws RemoteException {
366         enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
367                 mAnimationCallback);
368 
369         mInstrumentation.runOnMainSync(() -> {
370             Mockito.reset(mSpyController);
371             mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN,
372                     Float.NaN, Float.NaN, mAnimationCallback2);
373         });
374         SystemClock.sleep(mWaitingAnimationPeriod);
375 
376         verify(mSpyController, never()).enableWindowMagnificationInternal(anyFloat(), anyFloat(),
377                 anyFloat());
378         verify(mAnimationCallback).onResult(false);
379         verify(mAnimationCallback2).onResult(true);
380     }
381 
382     @Test
enableWindowMagnification_disabling_expectedValuesAndInvokeCallback()383     public void enableWindowMagnification_disabling_expectedValuesAndInvokeCallback()
384             throws RemoteException {
385         enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
386         deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
387                 mAnimationCallback);
388         final float targetScale = DEFAULT_SCALE + 1.0f;
389         final float targetCenterX = DEFAULT_CENTER_X + 100;
390         final float targetCenterY = DEFAULT_CENTER_Y + 100;
391 
392         mInstrumentation.runOnMainSync(
393                 () -> {
394                     Mockito.reset(mSpyController);
395                     mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
396                             targetCenterX, targetCenterY, mAnimationCallback2);
397                     mCurrentScale.set(mController.getScale());
398                     mCurrentCenterX.set(mController.getCenterX());
399                     mCurrentCenterY.set(mController.getCenterY());
400                 });
401         // Current spec shouldn't match given spec.
402         verify(mAnimationCallback2, never()).onResult(anyBoolean());
403         verify(mAnimationCallback).onResult(false);
404         SystemClock.sleep(mWaitingAnimationPeriod);
405 
406         verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
407                 mScaleCaptor.capture(),
408                 mCenterXCaptor.capture(), mCenterYCaptor.capture(),
409                 mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
410         //Animating in reverse, so we only check if the start values are greater than current.
411         assertTrue(mScaleCaptor.getAllValues().get(0) > mCurrentScale.get());
412         assertEquals(targetScale, mScaleCaptor.getValue(), 0f);
413         assertTrue(mCenterXCaptor.getAllValues().get(0) > mCurrentCenterX.get());
414         assertEquals(targetCenterX, mCenterXCaptor.getValue(), 0f);
415         assertTrue(mCenterYCaptor.getAllValues().get(0) > mCurrentCenterY.get());
416         assertEquals(targetCenterY, mCenterYCaptor.getValue(), 0f);
417         verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
418         verify(mAnimationCallback2).onResult(true);
419     }
420 
421     @Test
422     public void
enableMagnificationWithoutCallback_disabling_expectedValuesAndInvokeFormerCallback()423             enableMagnificationWithoutCallback_disabling_expectedValuesAndInvokeFormerCallback()
424             throws RemoteException {
425         enableWindowMagnificationWithoutAnimation();
426         deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
427                 mAnimationCallback);
428         final float targetScale = DEFAULT_SCALE + 1.0f;
429         final float targetCenterX = DEFAULT_CENTER_X + 100;
430         final float targetCenterY = DEFAULT_CENTER_Y + 100;
431 
432         mInstrumentation.runOnMainSync(
433                 () -> {
434                     Mockito.reset(mSpyController);
435                     mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
436                             targetCenterX, targetCenterY, null);
437                 });
438 
439         verify(mAnimationCallback).onResult(false);
440         verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
441     }
442 
443     @Test
enableWindowMagnificationWithSameSpec_disabling_NoAnimationAndInvokeCallback()444     public void enableWindowMagnificationWithSameSpec_disabling_NoAnimationAndInvokeCallback()
445             throws RemoteException {
446         enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
447         deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
448                 mAnimationCallback);
449 
450         mInstrumentation.runOnMainSync(() -> {
451             Mockito.reset(mSpyController);
452             mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN,
453                     Float.NaN, Float.NaN, mAnimationCallback2);
454         });
455         SystemClock.sleep(mWaitingAnimationPeriod);
456 
457         verify(mSpyController, never()).enableWindowMagnificationInternal(anyFloat(), anyFloat(),
458                 anyFloat());
459         verify(mSpyController, never()).deleteWindowMagnification();
460         verify(mAnimationCallback).onResult(false);
461         verify(mAnimationCallback2).onResult(true);
462     }
463 
464     @Test
enableWindowMagnification_enabled_expectedValuesAndInvokeCallback()465     public void enableWindowMagnification_enabled_expectedValuesAndInvokeCallback()
466             throws RemoteException {
467         enableWindowMagnificationWithoutAnimation();
468         final float targetScale = DEFAULT_SCALE + 1.0f;
469         final float targetCenterX = DEFAULT_CENTER_X + 100;
470         final float targetCenterY = DEFAULT_CENTER_Y + 100;
471 
472         mInstrumentation.runOnMainSync(() -> {
473             Mockito.reset(mSpyController);
474             mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
475                     targetCenterX, targetCenterY, mAnimationCallback2);
476             mCurrentScale.set(mController.getScale());
477             mCurrentCenterX.set(mController.getCenterX());
478             mCurrentCenterY.set(mController.getCenterY());
479         });
480 
481         SystemClock.sleep(mWaitingAnimationPeriod);
482 
483         verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
484                 mScaleCaptor.capture(),
485                 mCenterXCaptor.capture(), mCenterYCaptor.capture(),
486                 mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
487         verifyStartValue(mScaleCaptor, mCurrentScale.get());
488         verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
489         verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
490         verifyStartValue(mOffsetXCaptor, 0f);
491         verifyStartValue(mOffsetYCaptor, 0f);
492         verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
493         verify(mAnimationCallback2).onResult(true);
494     }
495 
496     @Test
enableWindowMagnificationWithOffset_expectedValues()497     public void enableWindowMagnificationWithOffset_expectedValues() {
498         final float offsetRatio = -0.1f;
499         final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
500         mInstrumentation.runOnMainSync(() -> {
501             Mockito.reset(mSpyController);
502             mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
503                     windowBounds.exactCenterX(), windowBounds.exactCenterY(),
504                     offsetRatio, offsetRatio, mAnimationCallback);
505         });
506         SystemClock.sleep(mWaitingAnimationPeriod);
507         final View attachedView = mWindowManager.getAttachedView();
508         assertNotNull(attachedView);
509         final Rect mirrorViewBound = new Rect();
510         final View mirrorView = attachedView.findViewById(R.id.surface_view);
511         assertNotNull(mirrorView);
512         mirrorView.getBoundsOnScreen(mirrorViewBound);
513 
514         assertEquals(mirrorViewBound.exactCenterX() - windowBounds.exactCenterX(),
515                 Math.round(offsetRatio * mirrorViewBound.width() / 2), 0.1f);
516         assertEquals(mirrorViewBound.exactCenterY() - windowBounds.exactCenterY(),
517                 Math.round(offsetRatio * mirrorViewBound.height() / 2), 0.1f);
518     }
519 
520     @Test
moveWindowMagnifierToPosition_enabled_expectedValues()521     public void moveWindowMagnifierToPosition_enabled_expectedValues()
522             throws InterruptedException {
523         final CountDownLatch countDownLatch = new CountDownLatch(1);
524         final MockMagnificationAnimationCallback animationCallback =
525                 new MockMagnificationAnimationCallback(countDownLatch);
526         final float targetCenterX = DEFAULT_CENTER_X + 100;
527         final float targetCenterY = DEFAULT_CENTER_Y + 100;
528         enableWindowMagnificationWithoutAnimation();
529 
530         mInstrumentation.runOnMainSync(() -> {
531             mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
532                     targetCenterX, targetCenterY, animationCallback);
533         });
534 
535         assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS));
536         assertEquals(1, animationCallback.getSuccessCount());
537         assertEquals(0, animationCallback.getFailedCount());
538         verifyFinalSpec(DEFAULT_SCALE, targetCenterX, targetCenterY);
539     }
540 
541     @Test
moveWindowMagnifierToPositionMultipleTimes_enabled_expectedValuesToLastOne()542     public void moveWindowMagnifierToPositionMultipleTimes_enabled_expectedValuesToLastOne()
543             throws InterruptedException {
544         final CountDownLatch countDownLatch = new CountDownLatch(4);
545         final MockMagnificationAnimationCallback animationCallback =
546                 new MockMagnificationAnimationCallback(countDownLatch);
547         enableWindowMagnificationWithoutAnimation();
548 
549         mInstrumentation.runOnMainSync(() -> {
550             mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
551                     DEFAULT_CENTER_X + 10, DEFAULT_CENTER_Y + 10, animationCallback);
552             mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
553                     DEFAULT_CENTER_X + 20, DEFAULT_CENTER_Y + 20, animationCallback);
554             mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
555                     DEFAULT_CENTER_X + 30, DEFAULT_CENTER_Y + 30, animationCallback);
556             mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
557                     DEFAULT_CENTER_X + 40, DEFAULT_CENTER_Y + 40, animationCallback);
558         });
559 
560         assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS));
561         // only the last one callback will return true
562         assertEquals(1, animationCallback.getSuccessCount());
563         // the others will return false
564         assertEquals(3, animationCallback.getFailedCount());
565         verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + 40, DEFAULT_CENTER_Y + 40);
566     }
567 
568     @Test
moveWindowMagnifierToPosition_enabling_expectedValuesToLastOne()569     public void moveWindowMagnifierToPosition_enabling_expectedValuesToLastOne()
570             throws InterruptedException {
571         final CountDownLatch countDownLatch = new CountDownLatch(2);
572         final MockMagnificationAnimationCallback animationCallback =
573                 new MockMagnificationAnimationCallback(countDownLatch);
574         final float targetCenterX = DEFAULT_CENTER_X + 100;
575         final float targetCenterY = DEFAULT_CENTER_Y + 100;
576 
577         enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
578                 animationCallback);
579         mInstrumentation.runOnMainSync(
580                 () -> {
581                     mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
582                             targetCenterX, targetCenterY, animationCallback);
583                 });
584 
585         assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS));
586         // The callback in moveWindowMagnifierToPosition will return true
587         assertEquals(1, animationCallback.getSuccessCount());
588         // The callback in enableWindowMagnification will return false
589         assertEquals(1, animationCallback.getFailedCount());
590         verifyFinalSpec(DEFAULT_SCALE, targetCenterX, targetCenterY);
591     }
592 
593     @Test
moveWindowMagnifierToPositionWithCenterUnchanged_enabling_expectedValuesToDefault()594     public void moveWindowMagnifierToPositionWithCenterUnchanged_enabling_expectedValuesToDefault()
595             throws InterruptedException {
596         final CountDownLatch countDownLatch = new CountDownLatch(2);
597         final MockMagnificationAnimationCallback animationCallback =
598                 new MockMagnificationAnimationCallback(countDownLatch);
599 
600         enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
601                 animationCallback);
602         mInstrumentation.runOnMainSync(
603                 () -> {
604                     mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
605                             Float.NaN, Float.NaN, animationCallback);
606                 });
607 
608         assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS));
609         // The callback in moveWindowMagnifierToPosition will return true
610         assertEquals(1, animationCallback.getSuccessCount());
611         // The callback in enableWindowMagnification will return false
612         assertEquals(1, animationCallback.getFailedCount());
613         verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
614     }
615 
616     @Test
enableWindowMagnificationWithSameScale_enabled_doNothingButInvokeCallback()617     public void enableWindowMagnificationWithSameScale_enabled_doNothingButInvokeCallback()
618             throws RemoteException {
619         enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
620 
621         enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback);
622 
623         verify(mSpyController, never()).enableWindowMagnificationInternal(anyFloat(), anyFloat(),
624                 anyFloat());
625         verify(mAnimationCallback).onResult(true);
626     }
627 
628     @Test
deleteWindowMagnification_enabled_expectedValuesAndInvokeCallback()629     public void deleteWindowMagnification_enabled_expectedValuesAndInvokeCallback()
630             throws RemoteException {
631         enableWindowMagnificationWithoutAnimation();
632 
633         deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback);
634 
635         verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
636                 mScaleCaptor.capture(),
637                 mCenterXCaptor.capture(), mCenterYCaptor.capture(),
638                 mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
639         verifyStartValue(mScaleCaptor, DEFAULT_SCALE);
640         verifyStartValue(mCenterXCaptor, Float.NaN);
641         verifyStartValue(mCenterYCaptor, Float.NaN);
642         verifyStartValue(mOffsetXCaptor, 0f);
643         verifyStartValue(mOffsetYCaptor, 0f);
644         verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
645         verify(mAnimationCallback).onResult(true);
646     }
647 
648     @Test
deleteWindowMagnificationWithoutCallback_enabled_expectedValues()649     public void deleteWindowMagnificationWithoutCallback_enabled_expectedValues() {
650         enableWindowMagnificationWithoutAnimation();
651 
652         deleteWindowMagnificationAndWaitAnimating(0, null);
653 
654         verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
655     }
656 
657     @Test
deleteWindowMagnification_disabled_doNothingAndInvokeCallback()658     public void deleteWindowMagnification_disabled_doNothingAndInvokeCallback()
659             throws RemoteException {
660         deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback);
661 
662         Mockito.verifyNoMoreInteractions(mSpyController);
663         verify(mAnimationCallback).onResult(true);
664     }
665 
666     @Test
deleteWindowMagnification_enabling_expectedValuesAndInvokeCallback()667     public void deleteWindowMagnification_enabling_expectedValuesAndInvokeCallback()
668             throws RemoteException {
669         enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
670                 mAnimationCallback);
671 
672         mInstrumentation.runOnMainSync(
673                 () -> {
674                     Mockito.reset(mSpyController);
675                     mWindowMagnificationAnimationController.deleteWindowMagnification(
676                             mAnimationCallback2);
677                     mCurrentScale.set(mController.getScale());
678                     mCurrentCenterX.set(mController.getCenterX());
679                     mCurrentCenterY.set(mController.getCenterY());
680                 });
681         SystemClock.sleep(mWaitingAnimationPeriod);
682         verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
683                 mScaleCaptor.capture(),
684                 mCenterXCaptor.capture(), mCenterYCaptor.capture(),
685                 mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
686 
687         //The animation is in verse, so we only check the start values should no be greater than
688         // the current one.
689         assertTrue(mScaleCaptor.getAllValues().get(0) <= mCurrentScale.get());
690         assertEquals(1.0f, mScaleCaptor.getValue(), 0f);
691         verifyStartValue(mCenterXCaptor, Float.NaN);
692         verifyStartValue(mCenterYCaptor, Float.NaN);
693         verifyStartValue(mOffsetXCaptor, 0f);
694         verifyStartValue(mOffsetYCaptor, 0f);
695         verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
696         verify(mAnimationCallback).onResult(false);
697         verify(mAnimationCallback2).onResult(true);
698     }
699 
700     @Test
deleteWindowMagnificationWithoutCallback_enabling_expectedValuesAndInvokeCallback()701     public void deleteWindowMagnificationWithoutCallback_enabling_expectedValuesAndInvokeCallback()
702             throws RemoteException {
703         enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
704                 mAnimationCallback);
705 
706         mInstrumentation.runOnMainSync(
707                 () -> {
708                     Mockito.reset(mSpyController);
709                     mWindowMagnificationAnimationController.deleteWindowMagnification(null);
710                 });
711 
712         verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
713         verify(mAnimationCallback).onResult(false);
714     }
715 
716     @Test
deleteWindowMagnification_disabling_checkStartAndValues()717     public void deleteWindowMagnification_disabling_checkStartAndValues() throws RemoteException {
718         enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
719         deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
720                 mAnimationCallback);
721 
722         deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback2);
723 
724         verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
725                 mScaleCaptor.capture(),
726                 mCenterXCaptor.capture(), mCenterYCaptor.capture(),
727                 mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
728         assertEquals(1.0f, mScaleCaptor.getValue(), 0f);
729         verifyStartValue(mOffsetXCaptor, 0f);
730         verifyStartValue(mOffsetYCaptor, 0f);
731         verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
732         verify(mAnimationCallback).onResult(false);
733         verify(mAnimationCallback2).onResult(true);
734     }
735 
736     @Test
deleteWindowMagnificationWithoutCallback_disabling_checkStartAndValues()737     public void deleteWindowMagnificationWithoutCallback_disabling_checkStartAndValues()
738             throws RemoteException {
739         enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
740         deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
741                 mAnimationCallback);
742 
743         deleteWindowMagnificationAndWaitAnimating(0, null);
744 
745         verify(mSpyController).deleteWindowMagnification();
746         verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
747         verify(mAnimationCallback).onResult(false);
748     }
749 
750     @Test
moveWindowMagnifier_enabled_vertical_only_expectedValue()751     public void moveWindowMagnifier_enabled_vertical_only_expectedValue() {
752         enableWindowMagnificationWithoutAnimation();
753 
754         // should move vertically since offsetY/offsetX > HORIZONTAL_LOCK_BASE
755         final float offsetX = 50.0f;
756         final float offsetY =
757                 (float) Math.ceil(offsetX * WindowMagnificationController.HORIZONTAL_LOCK_BASE)
758                 + 1.0f;
759         mInstrumentation.runOnMainSync(
760                 () -> mController.moveWindowMagnifier(offsetX, offsetY));
761 
762         verify(mSpyController).moveWindowMagnifier(offsetX, offsetY);
763         verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y + offsetY);
764     }
765 
766     @Test
moveWindowMagnifier_enabled_horinzontal_only_expectedValue()767     public void moveWindowMagnifier_enabled_horinzontal_only_expectedValue() {
768         enableWindowMagnificationWithoutAnimation();
769 
770         // should move vertically since offsetY/offsetX <= HORIZONTAL_LOCK_BASE
771         final float offsetX = 50.0f;
772         final float offsetY =
773                 (float) Math.floor(offsetX * WindowMagnificationController.HORIZONTAL_LOCK_BASE)
774                         - 1.0f;
775         mInstrumentation.runOnMainSync(
776                 () -> mController.moveWindowMagnifier(offsetX, offsetY));
777 
778         verify(mSpyController).moveWindowMagnifier(offsetX, offsetY);
779         verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + offsetX, DEFAULT_CENTER_Y);
780     }
781 
782     @Test
moveWindowMagnifier_enabled_setDiagonalEnabled_expectedValues()783     public void moveWindowMagnifier_enabled_setDiagonalEnabled_expectedValues() {
784         enableWindowMagnificationWithoutAnimation();
785 
786         final float offsetX = 50.0f;
787         final float offsetY =
788                 (float) Math.ceil(offsetX * WindowMagnificationController.HORIZONTAL_LOCK_BASE);
789         // while diagonal scrolling enabled,
790         //  should move with both offsetX and offsetY without regrading offsetY/offsetX
791         mInstrumentation.runOnMainSync(
792                 () -> {
793                     mController.setDiagonalScrolling(true);
794                     mController.moveWindowMagnifier(offsetX, offsetY);
795                 });
796 
797         verify(mSpyController).moveWindowMagnifier(offsetX, offsetY);
798         verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + offsetX, DEFAULT_CENTER_Y + offsetY);
799     }
800 
801     @Test
moveWindowMagnifierToPosition_enabled()802     public void moveWindowMagnifierToPosition_enabled() {
803         final float targetCenterX = DEFAULT_CENTER_X + 100;
804         final float targetCenterY = DEFAULT_CENTER_Y + 100;
805         enableWindowMagnificationWithoutAnimation();
806 
807         mInstrumentation.runOnMainSync(
808                 () -> mController.moveWindowMagnifierToPosition(targetCenterX, targetCenterY,
809                         mAnimationCallback));
810         SystemClock.sleep(mWaitingAnimationPeriod);
811 
812         verifyFinalSpec(DEFAULT_SCALE, targetCenterX, targetCenterY);
813     }
814 
verifyFinalSpec(float expectedScale, float expectedCenterX, float expectedCenterY)815     private void verifyFinalSpec(float expectedScale, float expectedCenterX,
816             float expectedCenterY) {
817         assertEquals(expectedScale, mController.getScale(), 0f);
818         assertEquals(expectedCenterX, mController.getCenterX(), 0f);
819         assertEquals(expectedCenterY, mController.getCenterY(), 0f);
820     }
821 
enableWindowMagnificationWithoutAnimation()822     private void enableWindowMagnificationWithoutAnimation() {
823         mInstrumentation.runOnMainSync(
824                 () -> {
825                     Mockito.reset(mSpyController);
826                     mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
827                             DEFAULT_CENTER_X, DEFAULT_CENTER_Y, null);
828                 });
829     }
830 
enableWindowMagnificationAndWaitAnimating(long duration, @Nullable IRemoteMagnificationAnimationCallback callback)831     private void enableWindowMagnificationAndWaitAnimating(long duration,
832             @Nullable IRemoteMagnificationAnimationCallback callback) {
833         mInstrumentation.runOnMainSync(
834                 () -> {
835                     Mockito.reset(mSpyController);
836                     mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
837                             DEFAULT_CENTER_X, DEFAULT_CENTER_Y, callback);
838                 });
839         SystemClock.sleep(duration);
840     }
841 
deleteWindowMagnificationAndWaitAnimating(long duration, @Nullable IRemoteMagnificationAnimationCallback callback)842     private void deleteWindowMagnificationAndWaitAnimating(long duration,
843             @Nullable IRemoteMagnificationAnimationCallback callback) {
844         mInstrumentation.runOnMainSync(
845                 () -> {
846                     resetMockObjects();
847                     mWindowMagnificationAnimationController.deleteWindowMagnification(callback);
848                 });
849         SystemClock.sleep(duration);
850     }
851 
verifyStartValue(ArgumentCaptor<Float> captor, float startValue)852     private void verifyStartValue(ArgumentCaptor<Float> captor, float startValue) {
853         assertEquals(startValue, captor.getAllValues().get(0), 0f);
854     }
855 
resetMockObjects()856     private void resetMockObjects() {
857         Mockito.reset(mSpyController);
858     }
859 
860     /**
861      * It observes the methods in {@link WindowMagnificationController} since we couldn't spy it
862      * directly.
863      */
864     private static class SpyWindowMagnificationController extends WindowMagnificationController {
865         private WindowMagnificationController mSpyController;
866 
SpyWindowMagnificationController(Context context, Handler handler, WindowMagnificationAnimationController animationController, SfVsyncFrameCallbackProvider sfVsyncFrameProvider, MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction, WindowMagnifierCallback callback, SysUiState sysUiState, SecureSettings secureSettings)867         SpyWindowMagnificationController(Context context, Handler handler,
868                 WindowMagnificationAnimationController animationController,
869                 SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
870                 MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction,
871                 WindowMagnifierCallback callback, SysUiState sysUiState,
872                 SecureSettings secureSettings) {
873             super(
874                     context,
875                     handler,
876                     animationController,
877                     sfVsyncFrameProvider,
878                     mirrorWindowControl,
879                     transaction,
880                     callback,
881                     sysUiState,
882                     WindowManagerGlobal::getWindowSession,
883                     secureSettings);
884             mSpyController = Mockito.mock(WindowMagnificationController.class);
885         }
886 
getSpyController()887         WindowMagnificationController getSpyController() {
888             return mSpyController;
889         }
890 
891         @Override
enableWindowMagnificationInternal(float scale, float centerX, float centerY)892         void enableWindowMagnificationInternal(float scale, float centerX, float centerY) {
893             super.enableWindowMagnificationInternal(scale, centerX, centerY);
894             mSpyController.enableWindowMagnificationInternal(scale, centerX, centerY);
895         }
896 
897         @Override
enableWindowMagnificationInternal(float scale, float centerX, float centerY, float magnificationOffsetFrameRatioX, float magnificationOffsetFrameRatioY)898         void enableWindowMagnificationInternal(float scale, float centerX, float centerY,
899                 float magnificationOffsetFrameRatioX, float magnificationOffsetFrameRatioY) {
900             super.enableWindowMagnificationInternal(scale, centerX, centerY,
901                     magnificationOffsetFrameRatioX, magnificationOffsetFrameRatioY);
902             mSpyController.enableWindowMagnificationInternal(scale, centerX, centerY,
903                     magnificationOffsetFrameRatioX, magnificationOffsetFrameRatioY);
904         }
905 
906         @Override
deleteWindowMagnification()907         void deleteWindowMagnification() {
908             super.deleteWindowMagnification();
909             mSpyController.deleteWindowMagnification();
910         }
911 
912         @Override
moveWindowMagnifier(float offsetX, float offsetY)913         void moveWindowMagnifier(float offsetX, float offsetY) {
914             super.moveWindowMagnifier(offsetX, offsetY);
915             mSpyController.moveWindowMagnifier(offsetX, offsetY);
916         }
917 
918         @Override
moveWindowMagnifierToPosition(float positionX, float positionY, IRemoteMagnificationAnimationCallback callback)919         void moveWindowMagnifierToPosition(float positionX, float positionY,
920                 IRemoteMagnificationAnimationCallback callback) {
921             super.moveWindowMagnifierToPosition(positionX, positionY, callback);
922             mSpyController.moveWindowMagnifierToPosition(positionX, positionY, callback);
923         }
924 
925         @Override
setScale(float scale)926         void setScale(float scale) {
927             super.setScale(scale);
928             mSpyController.setScale(scale);
929         }
930 
931         @Override
updateSysUIStateFlag()932         public void updateSysUIStateFlag() {
933             super.updateSysUIStateFlag();
934             mSpyController.updateSysUIStateFlag();
935         }
936     }
937 
newValueAnimator()938     private static ValueAnimator newValueAnimator() {
939         final ValueAnimator valueAnimator = new ValueAnimator();
940         valueAnimator.setDuration(ANIMATION_DURATION_MS);
941         valueAnimator.setInterpolator(new AccelerateInterpolator(2.5f));
942         valueAnimator.setFloatValues(0.0f, 1.0f);
943         return valueAnimator;
944     }
945 }
946