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 androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertNotNull;
23 import static org.junit.Assert.assertTrue;
24 import static org.mockito.ArgumentMatchers.any;
25 import static org.mockito.ArgumentMatchers.anyBoolean;
26 import static org.mockito.ArgumentMatchers.anyFloat;
27 import static org.mockito.ArgumentMatchers.eq;
28 import static org.mockito.Mockito.atLeast;
29 import static org.mockito.Mockito.never;
30 import static org.mockito.Mockito.spy;
31 import static org.mockito.Mockito.times;
32 import static org.mockito.Mockito.verify;
33 
34 import android.animation.ValueAnimator;
35 import android.annotation.Nullable;
36 import android.content.Context;
37 import android.graphics.Rect;
38 import android.os.Handler;
39 import android.os.RemoteException;
40 import android.platform.test.annotations.RequiresFlagsDisabled;
41 import android.platform.test.annotations.RequiresFlagsEnabled;
42 import android.platform.test.flag.junit.CheckFlagsRule;
43 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
44 import android.testing.AndroidTestingRunner;
45 import android.view.SurfaceControl;
46 import android.view.SurfaceControlViewHost;
47 import android.view.View;
48 import android.view.WindowManager;
49 import android.view.WindowManagerGlobal;
50 import android.view.accessibility.IRemoteMagnificationAnimationCallback;
51 import android.view.animation.AccelerateInterpolator;
52 import android.window.InputTransferToken;
53 
54 import androidx.test.filters.LargeTest;
55 
56 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
57 import com.android.systemui.Flags;
58 import com.android.systemui.SysuiTestCase;
59 import com.android.systemui.animation.AnimatorTestRule;
60 import com.android.systemui.model.SysUiState;
61 import com.android.systemui.res.R;
62 import com.android.systemui.util.settings.SecureSettings;
63 
64 import org.junit.After;
65 import org.junit.Before;
66 import org.junit.Rule;
67 import org.junit.Test;
68 import org.junit.runner.RunWith;
69 import org.mockito.Answers;
70 import org.mockito.ArgumentCaptor;
71 import org.mockito.Mock;
72 import org.mockito.Mockito;
73 import org.mockito.MockitoAnnotations;
74 
75 import java.util.ArrayList;
76 import java.util.List;
77 import java.util.concurrent.atomic.AtomicReference;
78 import java.util.function.Supplier;
79 
80 @LargeTest
81 @RunWith(AndroidTestingRunner.class)
82 public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
83 
84     @Rule
85     public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(this);
86     @Rule
87     public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
88     private static final float DEFAULT_SCALE = 4.0f;
89     private static final float DEFAULT_CENTER_X = 400.0f;
90     private static final float DEFAULT_CENTER_Y = 500.0f;
91 
92     private AtomicReference<Float> mCurrentScale = new AtomicReference<>((float) 0);
93     private AtomicReference<Float> mCurrentCenterX = new AtomicReference<>((float) 0);
94     private AtomicReference<Float> mCurrentCenterY = new AtomicReference<>((float) 0);
95     private ArgumentCaptor<Float> mScaleCaptor = ArgumentCaptor.forClass(Float.class);
96     private ArgumentCaptor<Float> mCenterXCaptor = ArgumentCaptor.forClass(Float.class);
97     private ArgumentCaptor<Float> mCenterYCaptor = ArgumentCaptor.forClass(Float.class);
98     private final ArgumentCaptor<Float> mOffsetXCaptor = ArgumentCaptor.forClass(Float.class);
99     private final ArgumentCaptor<Float> mOffsetYCaptor = ArgumentCaptor.forClass(Float.class);
100 
101     @Mock
102     Handler mHandler;
103     @Mock
104     SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
105     @Mock
106     WindowMagnifierCallback mWindowMagnifierCallback;
107     @Mock
108     IRemoteMagnificationAnimationCallback mAnimationCallback;
109     @Mock
110     IRemoteMagnificationAnimationCallback mAnimationCallback2;
111     @Mock(answer = Answers.RETURNS_SELF)
112     SysUiState mSysUiState;
113     @Mock
114     SecureSettings mSecureSettings;
115     private SpyWindowMagnificationController mController;
116     private WindowMagnificationController mSpyController;
117     private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
118 
119     private long mWaitAnimationDuration;
120     private long mWaitPartialAnimationDuration;
121 
122     private TestableWindowManager mWindowManager;
123     private ValueAnimator mValueAnimator;
124     // This list contains all SurfaceControlViewHosts created during a given test. If the
125     // magnification window is recreated during a test, the list will contain more than a single
126     // element.
127     private List<SurfaceControlViewHost> mSurfaceControlViewHosts = new ArrayList<>();
128     // The most recently created SurfaceControlViewHost.
129     private SurfaceControlViewHost mSurfaceControlViewHost;
130     private SurfaceControl.Transaction mTransaction;
131 
132     @Before
setUp()133     public void setUp() throws Exception {
134         MockitoAnnotations.initMocks(this);
135         final WindowManager wm = mContext.getSystemService(WindowManager.class);
136         mWindowManager = spy(new TestableWindowManager(wm));
137         mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
138 
139         // Using the animation duration in WindowMagnificationAnimationController for testing.
140         mWaitAnimationDuration = mContext.getResources()
141                 .getInteger(com.android.internal.R.integer.config_longAnimTime);
142         mWaitPartialAnimationDuration = mWaitAnimationDuration / 2;
143 
144         mValueAnimator = newValueAnimator();
145         mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
146                 mContext, mValueAnimator);
147 
148         Supplier<SurfaceControlViewHost> scvhSupplier = () -> {
149             mSurfaceControlViewHost = spy(new SurfaceControlViewHost(
150                     mContext, mContext.getDisplay(), new InputTransferToken(),
151                     "WindowMagnification"));
152             mSurfaceControlViewHosts.add(mSurfaceControlViewHost);
153             return mSurfaceControlViewHost;
154         };
155 
156         mTransaction = spy(new SurfaceControl.Transaction());
157         mController = new SpyWindowMagnificationController(
158                 mContext,
159                 mHandler,
160                 mWindowMagnificationAnimationController,
161                 /* mirrorWindowControl= */ null,
162                 mTransaction,
163                 mWindowMagnifierCallback,
164                 mSysUiState,
165                 mSecureSettings,
166                 scvhSupplier,
167                 mSfVsyncFrameProvider);
168 
169         mSpyController = mController.getSpyController();
170     }
171 
172     @After
tearDown()173     public void tearDown() throws Exception {
174         getInstrumentation().runOnMainSync(() -> {
175             mController.deleteWindowMagnification();
176         });
177     }
178 
179     @Test
enableWindowMagnification_disabled_expectedValuesAndInvokeCallback()180     public void enableWindowMagnification_disabled_expectedValuesAndInvokeCallback()
181             throws RemoteException {
182         enableWindowMagnificationAndWaitAnimating(mWaitAnimationDuration, mAnimationCallback);
183 
184         verify(mSpyController, atLeast(2)).updateWindowMagnificationInternal(
185                 mScaleCaptor.capture(),
186                 mCenterXCaptor.capture(), mCenterYCaptor.capture(),
187                 mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
188         verifyStartValue(mScaleCaptor, 1.0f);
189         verifyStartValue(mCenterXCaptor, DEFAULT_CENTER_X);
190         verifyStartValue(mCenterYCaptor, DEFAULT_CENTER_Y);
191         verifyStartValue(mOffsetXCaptor, 0f);
192         verifyStartValue(mOffsetYCaptor, 0f);
193         verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
194         verify(mAnimationCallback).onResult(true);
195     }
196 
197     @Test
enableWindowMagnificationWithoutCallback_disabled_expectedValues()198     public void enableWindowMagnificationWithoutCallback_disabled_expectedValues() {
199         enableWindowMagnificationWithoutAnimation();
200 
201         verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
202     }
203 
204     @Test
enableWindowMagnificationWithoutCallback_enabled_expectedValues()205     public void enableWindowMagnificationWithoutCallback_enabled_expectedValues() {
206         enableWindowMagnificationAndWaitAnimating(mWaitAnimationDuration, mAnimationCallback);
207         final float targetScale = DEFAULT_SCALE + 1.0f;
208         final float targetCenterX = DEFAULT_CENTER_X + 100;
209         final float targetCenterY = DEFAULT_CENTER_Y + 100;
210 
211         enableWindowMagnificationWithoutAnimation(targetScale, targetCenterX, targetCenterY);
212 
213         verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
214     }
215 
216     @Test
enableWindowMagnificationWithScaleOne_disabled_NoAnimationAndInvokeCallback()217     public void enableWindowMagnificationWithScaleOne_disabled_NoAnimationAndInvokeCallback()
218             throws RemoteException {
219         enableWindowMagnificationAndWaitAnimating(
220                 mWaitAnimationDuration, /* targetScale= */ 1.0f,
221                 DEFAULT_CENTER_X, DEFAULT_CENTER_Y, mAnimationCallback);
222 
223         verify(mSpyController).updateWindowMagnificationInternal(1.0f, DEFAULT_CENTER_X,
224                 DEFAULT_CENTER_Y, 0f, 0f);
225         verify(mAnimationCallback).onResult(true);
226     }
227 
228     @Test
enableWindowMagnification_enabling_expectedValuesAndInvokeCallback()229     public void enableWindowMagnification_enabling_expectedValuesAndInvokeCallback()
230             throws RemoteException {
231         enableWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration,
232                 mAnimationCallback);
233         final float targetScale = DEFAULT_SCALE + 1.0f;
234         final float targetCenterX = DEFAULT_CENTER_X + 100;
235         final float targetCenterY = DEFAULT_CENTER_Y + 100;
236 
237         resetMockObjects();
238         getInstrumentation().runOnMainSync(() -> {
239             mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
240                     targetCenterX, targetCenterY, mAnimationCallback2);
241             mCurrentScale.set(mController.getScale());
242             mCurrentCenterX.set(mController.getCenterX());
243             mCurrentCenterY.set(mController.getCenterY());
244             advanceTimeBy(mWaitAnimationDuration);
245         });
246 
247         verify(mSpyController, atLeast(2)).updateWindowMagnificationInternal(
248                 mScaleCaptor.capture(),
249                 mCenterXCaptor.capture(), mCenterYCaptor.capture(),
250                 mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
251         verifyStartValue(mScaleCaptor, mCurrentScale.get());
252         verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
253         verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
254         verifyStartValue(mOffsetXCaptor, 0f);
255         verifyStartValue(mOffsetYCaptor, 0f);
256         verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
257         verify(mAnimationCallback).onResult(false);
258         verify(mAnimationCallback2).onResult(true);
259     }
260 
261     @Test
enableWindowMagnificationWithUnchanged_enabling_expectedValuesToDefault()262     public void enableWindowMagnificationWithUnchanged_enabling_expectedValuesToDefault()
263             throws RemoteException {
264         enableWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration,
265                 mAnimationCallback);
266 
267         enableWindowMagnificationAndWaitAnimating(mWaitAnimationDuration, Float.NaN,
268                 Float.NaN, Float.NaN, mAnimationCallback2);
269 
270         // The callback in 2nd enableWindowMagnification will return true
271         verify(mAnimationCallback2).onResult(true);
272         // The callback in 1st enableWindowMagnification will return false
273         verify(mAnimationCallback).onResult(false);
274         verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
275     }
276 
277     @RequiresFlagsEnabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER)
278     @Test
279     public void
enableWindowMagnificationScaleOne_enabledAndWindowlessFlagOn_AnimationAndCallbackTrue()280             enableWindowMagnificationScaleOne_enabledAndWindowlessFlagOn_AnimationAndCallbackTrue()
281             throws RemoteException {
282         enableWindowMagnificationWithoutAnimation();
283 
284         // Wait for Rects updated.
285         waitForIdleSync();
286         View mirrorView = mSurfaceControlViewHost.getView();
287         final float targetScale = 1.0f;
288         // Move the magnifier to the top left corner, within the boundary
289         final float targetCenterX = mirrorView.getWidth() / 2.0f;
290         final float targetCenterY = mirrorView.getHeight() / 2.0f;
291 
292         Mockito.reset(mSpyController);
293         getInstrumentation().runOnMainSync(() -> {
294             mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
295                     targetCenterX, targetCenterY, mAnimationCallback);
296             mCurrentScale.set(mController.getScale());
297             mCurrentCenterX.set(mController.getCenterX());
298             mCurrentCenterY.set(mController.getCenterY());
299             advanceTimeBy(mWaitAnimationDuration);
300         });
301 
302         verify(mSpyController, atLeast(2)).updateWindowMagnificationInternal(
303                 mScaleCaptor.capture(),
304                 mCenterXCaptor.capture(), mCenterYCaptor.capture(),
305                 mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
306         verifyStartValue(mScaleCaptor, mCurrentScale.get());
307         verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
308         verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
309         verifyStartValue(mOffsetXCaptor, 0f);
310         verifyStartValue(mOffsetYCaptor, 0f);
311 
312         verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
313 
314         verify(mAnimationCallback).onResult(true);
315         assertEquals(WindowMagnificationAnimationController.STATE_ENABLED,
316                 mWindowMagnificationAnimationController.getState());
317     }
318 
319     @RequiresFlagsDisabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER)
320     @Test
321     public void
enableWindowMagnificationScaleOne_enabledAndWindowlessFlagOff_AnimationAndCallbackTrue()322             enableWindowMagnificationScaleOne_enabledAndWindowlessFlagOff_AnimationAndCallbackTrue()
323             throws RemoteException {
324         enableWindowMagnificationWithoutAnimation();
325 
326         // Wait for Rects updated.
327         waitForIdleSync();
328         View mirrorView = mWindowManager.getAttachedView();
329         final float targetScale = 1.0f;
330         // Move the magnifier to the top left corner, within the boundary
331         final float targetCenterX = mirrorView.getWidth() / 2.0f;
332         final float targetCenterY = mirrorView.getHeight() / 2.0f;
333 
334         Mockito.reset(mSpyController);
335         getInstrumentation().runOnMainSync(() -> {
336             mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
337                     targetCenterX, targetCenterY, mAnimationCallback);
338             mCurrentScale.set(mController.getScale());
339             mCurrentCenterX.set(mController.getCenterX());
340             mCurrentCenterY.set(mController.getCenterY());
341             advanceTimeBy(mWaitAnimationDuration);
342         });
343 
344         verify(mSpyController, atLeast(2)).updateWindowMagnificationInternal(
345                 mScaleCaptor.capture(),
346                 mCenterXCaptor.capture(), mCenterYCaptor.capture(),
347                 mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
348         verifyStartValue(mScaleCaptor, mCurrentScale.get());
349         verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
350         verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
351         verifyStartValue(mOffsetXCaptor, 0f);
352         verifyStartValue(mOffsetYCaptor, 0f);
353 
354         verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
355 
356         verify(mAnimationCallback).onResult(true);
357         assertEquals(WindowMagnificationAnimationController.STATE_ENABLED,
358                 mWindowMagnificationAnimationController.getState());
359     }
360 
361     @Test
enableWindowMagnificationWithScaleLessThanOne_enabled_AnimationAndInvokeCallback()362     public void enableWindowMagnificationWithScaleLessThanOne_enabled_AnimationAndInvokeCallback()
363             throws RemoteException {
364         enableWindowMagnificationWithoutAnimation();
365 
366         final float targetScale = 0.99f;
367         final float targetCenterX = DEFAULT_CENTER_X + 100;
368         final float targetCenterY = DEFAULT_CENTER_Y + 100;
369 
370         Mockito.reset(mSpyController);
371         getInstrumentation().runOnMainSync(() -> {
372             mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
373                     targetCenterX, targetCenterY, mAnimationCallback);
374             mCurrentScale.set(mController.getScale());
375             mCurrentCenterX.set(mController.getCenterX());
376             mCurrentCenterY.set(mController.getCenterY());
377             advanceTimeBy(mWaitAnimationDuration);
378         });
379 
380         verify(mSpyController, atLeast(2)).updateWindowMagnificationInternal(
381                 mScaleCaptor.capture(),
382                 mCenterXCaptor.capture(), mCenterYCaptor.capture(),
383                 mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
384         verifyStartValue(mScaleCaptor, mCurrentScale.get());
385         verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
386         verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
387         verifyStartValue(mOffsetXCaptor, 0f);
388         verifyStartValue(mOffsetYCaptor, 0f);
389         // It presents the window magnification is disabled.
390         verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
391 
392         verify(mAnimationCallback).onResult(true);
393         assertEquals(WindowMagnificationAnimationController.STATE_DISABLED,
394                 mWindowMagnificationAnimationController.getState());
395     }
396 
397     @Test
398     public void
enableWindowMagnificationWithScaleLessThanOneAndWithoutCallBack_enabled_expectedValues()399             enableWindowMagnificationWithScaleLessThanOneAndWithoutCallBack_enabled_expectedValues()
400             throws RemoteException {
401         enableWindowMagnificationWithoutAnimation();
402 
403         final float targetScale = 0.99f;
404         final float targetCenterX = DEFAULT_CENTER_X + 100;
405         final float targetCenterY = DEFAULT_CENTER_Y + 100;
406 
407         Mockito.reset(mSpyController);
408         enableWindowMagnificationWithoutAnimation(targetScale, targetCenterX, targetCenterY);
409 
410         verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
411         assertEquals(WindowMagnificationAnimationController.STATE_DISABLED,
412                 mWindowMagnificationAnimationController.getState());
413     }
414 
415     @Test
416     public void
enableMagnificationWithoutCallback_enabling_expectedValuesAndInvokeFormerCallback()417             enableMagnificationWithoutCallback_enabling_expectedValuesAndInvokeFormerCallback()
418             throws RemoteException {
419         enableWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration,
420                 mAnimationCallback);
421         final float targetScale = DEFAULT_SCALE - 1.0f;
422         final float targetCenterX = DEFAULT_CENTER_X + 100;
423         final float targetCenterY = DEFAULT_CENTER_Y + 100;
424 
425         Mockito.reset(mSpyController);
426         enableWindowMagnificationWithoutAnimation(targetScale, targetCenterX, targetCenterY);
427 
428         verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
429         verify(mAnimationCallback).onResult(false);
430     }
431 
432     @Test
enableWindowMagnificationWithSameSpec_enabling_NoAnimationAndInvokeCallback()433     public void enableWindowMagnificationWithSameSpec_enabling_NoAnimationAndInvokeCallback()
434             throws RemoteException {
435         enableWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration,
436                 mAnimationCallback);
437 
438         Mockito.reset(mSpyController);
439         enableWindowMagnificationAndWaitAnimating(
440                 mWaitAnimationDuration, Float.NaN, Float.NaN, Float.NaN, mAnimationCallback2);
441 
442         verify(mSpyController, never()).updateWindowMagnificationInternal(anyFloat(), anyFloat(),
443                 anyFloat());
444         verify(mAnimationCallback).onResult(false);
445         verify(mAnimationCallback2).onResult(true);
446     }
447 
448     @Test
enableWindowMagnification_disabling_expectedValuesAndInvokeCallback()449     public void enableWindowMagnification_disabling_expectedValuesAndInvokeCallback()
450             throws RemoteException {
451         enableWindowMagnificationWithoutAnimation();
452         deleteWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration,
453                 mAnimationCallback);
454         final float targetScale = DEFAULT_SCALE + 1.0f;
455         final float targetCenterX = DEFAULT_CENTER_X + 100;
456         final float targetCenterY = DEFAULT_CENTER_Y + 100;
457 
458         Mockito.reset(mSpyController);
459         getInstrumentation().runOnMainSync(() -> {
460             mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
461                     targetCenterX, targetCenterY, mAnimationCallback2);
462             mCurrentScale.set(mController.getScale());
463             mCurrentCenterX.set(mController.getCenterX());
464             mCurrentCenterY.set(mController.getCenterY());
465         });
466 
467         // Current spec shouldn't match given spec.
468         verify(mAnimationCallback2, never()).onResult(anyBoolean());
469         verify(mAnimationCallback).onResult(false);
470 
471         getInstrumentation().runOnMainSync(() -> {
472             // ValueAnimator.reverse() could not work correctly with the AnimatorTestRule since it
473             // is using SystemClock in reverse() (b/305731398). Therefore, we call end() on the
474             // animator directly to verify the result of animation is correct instead of querying
475             // the animation frame at a specific timing.
476             mValueAnimator.end();
477         });
478 
479         // Verify the method is called in
480         // {@link ValueAnimator.AnimatorUpdateListener#onAnimationUpdate} once
481         verify(mSpyController).updateWindowMagnificationInternal(
482                 mScaleCaptor.capture(),
483                 mCenterXCaptor.capture(), mCenterYCaptor.capture(),
484                 mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
485 
486         // The animation is playing forwards, so we only check the animated values are greater than
487         // the current one. (Asserting the first captured scale)
488         assertTrue(mScaleCaptor.getAllValues().get(0) > mCurrentScale.get());
489         // The last captured scale equalsto the targetScale when we enable window magnification.
490         assertEquals(targetScale, mScaleCaptor.getValue(), 0f);
491         assertTrue(mCenterXCaptor.getAllValues().get(0) > mCurrentCenterX.get());
492         assertEquals(targetCenterX, mCenterXCaptor.getValue(), 0f);
493         assertTrue(mCenterYCaptor.getAllValues().get(0) > mCurrentCenterY.get());
494         assertEquals(targetCenterY, mCenterYCaptor.getValue(), 0f);
495         verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
496         verify(mAnimationCallback2).onResult(true);
497     }
498 
499     @Test
500     public void
enableMagnificationWithoutCallback_disabling_expectedValuesAndInvokeFormerCallback()501             enableMagnificationWithoutCallback_disabling_expectedValuesAndInvokeFormerCallback()
502             throws RemoteException {
503         enableWindowMagnificationWithoutAnimation();
504         deleteWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration,
505                 mAnimationCallback);
506         final float targetScale = DEFAULT_SCALE + 1.0f;
507         final float targetCenterX = DEFAULT_CENTER_X + 100;
508         final float targetCenterY = DEFAULT_CENTER_Y + 100;
509 
510         Mockito.reset(mSpyController);
511         enableWindowMagnificationWithoutAnimation(targetScale, targetCenterX, targetCenterY);
512 
513         verify(mAnimationCallback).onResult(false);
514         verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
515     }
516 
517     @Test
enableWindowMagnificationWithSameSpec_disabling_NoAnimationAndInvokeCallback()518     public void enableWindowMagnificationWithSameSpec_disabling_NoAnimationAndInvokeCallback()
519             throws RemoteException {
520         enableWindowMagnificationWithoutAnimation();
521         deleteWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration,
522                 mAnimationCallback);
523 
524         Mockito.reset(mSpyController);
525         enableWindowMagnificationAndWaitAnimating(mWaitAnimationDuration, Float.NaN,
526                 Float.NaN, Float.NaN, mAnimationCallback2);
527 
528         verify(mSpyController, never()).updateWindowMagnificationInternal(anyFloat(), anyFloat(),
529                 anyFloat());
530         verify(mSpyController, never()).deleteWindowMagnification();
531         verify(mAnimationCallback).onResult(false);
532         verify(mAnimationCallback2).onResult(true);
533     }
534 
535     @Test
enableWindowMagnification_enabled_expectedValuesAndInvokeCallback()536     public void enableWindowMagnification_enabled_expectedValuesAndInvokeCallback()
537             throws RemoteException {
538         enableWindowMagnificationWithoutAnimation();
539         final float targetScale = DEFAULT_SCALE + 1.0f;
540         final float targetCenterX = DEFAULT_CENTER_X + 100;
541         final float targetCenterY = DEFAULT_CENTER_Y + 100;
542 
543         Mockito.reset(mSpyController);
544         getInstrumentation().runOnMainSync(() -> {
545             mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
546                     targetCenterX, targetCenterY, mAnimationCallback2);
547             mCurrentScale.set(mController.getScale());
548             mCurrentCenterX.set(mController.getCenterX());
549             mCurrentCenterY.set(mController.getCenterY());
550             advanceTimeBy(mWaitAnimationDuration);
551         });
552 
553         verify(mSpyController, atLeast(2)).updateWindowMagnificationInternal(
554                 mScaleCaptor.capture(),
555                 mCenterXCaptor.capture(), mCenterYCaptor.capture(),
556                 mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
557         verifyStartValue(mScaleCaptor, mCurrentScale.get());
558         verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
559         verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
560         verifyStartValue(mOffsetXCaptor, 0f);
561         verifyStartValue(mOffsetYCaptor, 0f);
562         verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
563         verify(mAnimationCallback2).onResult(true);
564     }
565 
566     @RequiresFlagsEnabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER)
567     @Test
enableWindowMagnificationWithOffset_windowlessFlagOn_expectedValues()568     public void enableWindowMagnificationWithOffset_windowlessFlagOn_expectedValues() {
569         final float offsetRatio = -0.1f;
570         final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
571 
572         Mockito.reset(mSpyController);
573         getInstrumentation().runOnMainSync(() -> {
574             mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
575                     windowBounds.exactCenterX(), windowBounds.exactCenterY(),
576                     offsetRatio, offsetRatio, mAnimationCallback);
577             advanceTimeBy(mWaitAnimationDuration);
578         });
579         // Wait for Rects update
580         waitForIdleSync();
581 
582         final int mirrorSurfaceMargin = mContext.getResources().getDimensionPixelSize(
583                 R.dimen.magnification_mirror_surface_margin);
584         final int defaultMagnificationWindowSize =
585                 mController.getMagnificationWindowSizeFromIndex(
586                         WindowMagnificationSettings.MagnificationSize.MEDIUM);
587         final int defaultMagnificationFrameSize =
588                 defaultMagnificationWindowSize - 2 * mirrorSurfaceMargin;
589         final int expectedOffset = (int) (defaultMagnificationFrameSize / 2 * offsetRatio);
590 
591         final float expectedX = (int) (windowBounds.exactCenterX() + expectedOffset
592                 - defaultMagnificationWindowSize / 2);
593         final float expectedY = (int) (windowBounds.exactCenterY() + expectedOffset
594                 - defaultMagnificationWindowSize / 2);
595 
596         // This is called 4 times when (1) first creating WindowlessMirrorWindow (2) SurfaceView is
597         // created and we place the mirrored content as a child of the SurfaceView
598         // (3) the animation starts (4) the animation updates
599         verify(mTransaction, times(4))
600                 .setPosition(any(SurfaceControl.class), eq(expectedX), eq(expectedY));
601     }
602 
603     @RequiresFlagsDisabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER)
604     @Test
enableWindowMagnificationWithOffset_windowlessFlagOff_expectedValues()605     public void enableWindowMagnificationWithOffset_windowlessFlagOff_expectedValues() {
606         final float offsetRatio = -0.1f;
607         final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
608 
609         Mockito.reset(mSpyController);
610         getInstrumentation().runOnMainSync(() -> {
611             mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
612                     windowBounds.exactCenterX(), windowBounds.exactCenterY(),
613                     offsetRatio, offsetRatio, mAnimationCallback);
614             advanceTimeBy(mWaitAnimationDuration);
615         });
616 
617         // Wait for Rects update
618         waitForIdleSync();
619         final View attachedView = mWindowManager.getAttachedView();
620         assertNotNull(attachedView);
621         final Rect mirrorViewBound = new Rect();
622         final View mirrorView = attachedView.findViewById(R.id.surface_view);
623         assertNotNull(mirrorView);
624         mirrorView.getBoundsOnScreen(mirrorViewBound);
625 
626         assertEquals((int) (offsetRatio * mirrorViewBound.width() / 2),
627                 (int) (mirrorViewBound.exactCenterX() - windowBounds.exactCenterX()));
628         assertEquals((int) (offsetRatio * mirrorViewBound.height() / 2),
629                 (int) (mirrorViewBound.exactCenterY() - windowBounds.exactCenterY()));
630     }
631 
632     @Test
moveWindowMagnifierToPosition_enabled_expectedValues()633     public void moveWindowMagnifierToPosition_enabled_expectedValues() throws RemoteException {
634         final float targetCenterX = DEFAULT_CENTER_X + 100;
635         final float targetCenterY = DEFAULT_CENTER_Y + 100;
636         enableWindowMagnificationWithoutAnimation();
637 
638         getInstrumentation().runOnMainSync(() -> {
639             mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
640                     targetCenterX, targetCenterY, mAnimationCallback);
641             advanceTimeBy(mWaitAnimationDuration);
642         });
643 
644         verify(mAnimationCallback).onResult(true);
645         verify(mAnimationCallback, never()).onResult(false);
646         verifyFinalSpec(DEFAULT_SCALE, targetCenterX, targetCenterY);
647     }
648 
649     @Test
moveWindowMagnifierToPositionMultipleTimes_enabled_expectedValuesToLastOne()650     public void moveWindowMagnifierToPositionMultipleTimes_enabled_expectedValuesToLastOne()
651             throws RemoteException {
652         enableWindowMagnificationWithoutAnimation();
653 
654         getInstrumentation().runOnMainSync(() -> {
655             mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
656                     DEFAULT_CENTER_X + 10, DEFAULT_CENTER_Y + 10, mAnimationCallback);
657             mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
658                     DEFAULT_CENTER_X + 20, DEFAULT_CENTER_Y + 20, mAnimationCallback);
659             mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
660                     DEFAULT_CENTER_X + 30, DEFAULT_CENTER_Y + 30, mAnimationCallback);
661             mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
662                     DEFAULT_CENTER_X + 40, DEFAULT_CENTER_Y + 40, mAnimationCallback2);
663             advanceTimeBy(mWaitAnimationDuration);
664         });
665 
666         // only the last one callback will return true
667         verify(mAnimationCallback2).onResult(true);
668         // the others will return false
669         verify(mAnimationCallback, times(3)).onResult(false);
670         verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + 40, DEFAULT_CENTER_Y + 40);
671     }
672 
673     @Test
moveWindowMagnifierToPosition_enabling_expectedValuesToLastOne()674     public void moveWindowMagnifierToPosition_enabling_expectedValuesToLastOne()
675             throws RemoteException {
676         final float targetCenterX = DEFAULT_CENTER_X + 100;
677         final float targetCenterY = DEFAULT_CENTER_Y + 100;
678 
679         enableWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration,
680                 mAnimationCallback);
681 
682         getInstrumentation().runOnMainSync(() -> {
683             mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
684                     targetCenterX, targetCenterY, mAnimationCallback2);
685             advanceTimeBy(mWaitAnimationDuration);
686         });
687 
688         // The callback in moveWindowMagnifierToPosition will return true
689         verify(mAnimationCallback2).onResult(true);
690         // The callback in enableWindowMagnification will return false
691         verify(mAnimationCallback).onResult(false);
692         verifyFinalSpec(DEFAULT_SCALE, targetCenterX, targetCenterY);
693     }
694 
695     @Test
moveWindowMagnifierToPositionWithCenterUnchanged_enabling_expectedValuesToDefault()696     public void moveWindowMagnifierToPositionWithCenterUnchanged_enabling_expectedValuesToDefault()
697             throws RemoteException {
698 
699         enableWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration,
700                 mAnimationCallback);
701 
702         getInstrumentation().runOnMainSync(() -> {
703             mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
704                     Float.NaN, Float.NaN, mAnimationCallback2);
705             advanceTimeBy(mWaitAnimationDuration);
706         });
707 
708         // The callback in moveWindowMagnifierToPosition will return true
709         verify(mAnimationCallback2).onResult(true);
710         // The callback in enableWindowMagnification will return false
711         verify(mAnimationCallback).onResult(false);
712         verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
713     }
714 
715     @Test
enableWindowMagnificationWithSameScale_enabled_doNothingButInvokeCallback()716     public void enableWindowMagnificationWithSameScale_enabled_doNothingButInvokeCallback()
717             throws RemoteException {
718         enableWindowMagnificationWithoutAnimation();
719 
720         enableWindowMagnificationAndWaitAnimating(mWaitAnimationDuration, mAnimationCallback);
721 
722         verify(mSpyController, never()).updateWindowMagnificationInternal(anyFloat(), anyFloat(),
723                 anyFloat());
724         verify(mAnimationCallback).onResult(true);
725     }
726 
727     @Test
deleteWindowMagnification_enabled_expectedValuesAndInvokeCallback()728     public void deleteWindowMagnification_enabled_expectedValuesAndInvokeCallback()
729             throws RemoteException {
730         enableWindowMagnificationWithoutAnimation();
731 
732         resetMockObjects();
733         deleteWindowMagnificationAndWaitAnimating(mWaitAnimationDuration, mAnimationCallback);
734 
735         verify(mSpyController, atLeast(2)).updateWindowMagnificationInternal(
736                 mScaleCaptor.capture(),
737                 mCenterXCaptor.capture(), mCenterYCaptor.capture(),
738                 mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
739         verifyStartValue(mScaleCaptor, DEFAULT_SCALE);
740         verifyStartValue(mCenterXCaptor, Float.NaN);
741         verifyStartValue(mCenterYCaptor, Float.NaN);
742         verifyStartValue(mOffsetXCaptor, 0f);
743         verifyStartValue(mOffsetYCaptor, 0f);
744         verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
745         verify(mAnimationCallback).onResult(true);
746     }
747 
748     @Test
deleteWindowMagnificationWithoutCallback_enabled_expectedValues()749     public void deleteWindowMagnificationWithoutCallback_enabled_expectedValues() {
750         enableWindowMagnificationWithoutAnimation();
751 
752         deleteWindowMagnificationAndWaitAnimating(0, null);
753 
754         verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
755     }
756 
757     @Test
deleteWindowMagnification_disabled_doNothingAndInvokeCallback()758     public void deleteWindowMagnification_disabled_doNothingAndInvokeCallback()
759             throws RemoteException {
760         deleteWindowMagnificationAndWaitAnimating(mWaitAnimationDuration, mAnimationCallback);
761 
762         Mockito.verifyNoMoreInteractions(mSpyController);
763         verify(mAnimationCallback).onResult(true);
764     }
765 
766     @Test
deleteWindowMagnification_enabling_expectedValuesAndInvokeCallback()767     public void deleteWindowMagnification_enabling_expectedValuesAndInvokeCallback()
768             throws RemoteException {
769         enableWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration,
770                 mAnimationCallback);
771 
772         resetMockObjects();
773         getInstrumentation().runOnMainSync(() -> {
774             mWindowMagnificationAnimationController.deleteWindowMagnification(
775                     mAnimationCallback2);
776             mCurrentScale.set(mController.getScale());
777             mCurrentCenterX.set(mController.getCenterX());
778             mCurrentCenterY.set(mController.getCenterY());
779             // ValueAnimator.reverse() could not work correctly with the AnimatorTestRule since it
780             // is using SystemClock in reverse() (b/305731398). Therefore, we call end() on the
781             // animator directly to verify the result of animation is correct instead of querying
782             // the animation frame at a specific timing.
783             mValueAnimator.end();
784         });
785 
786         // wait for animation returns
787         waitForIdleSync();
788 
789         // Verify the method is called in
790         // {@link ValueAnimator.AnimatorUpdateListener#onAnimationUpdate} once
791         verify(mSpyController).updateWindowMagnificationInternal(
792                 mScaleCaptor.capture(),
793                 mCenterXCaptor.capture(), mCenterYCaptor.capture(),
794                 mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
795 
796         // The animation is playing backwards, so we only check the animated values should not be
797         // greater than the current one. (Asserting the first captured scale)
798         assertTrue(mScaleCaptor.getAllValues().get(0) <= mCurrentScale.get());
799         // The last captured scale is 1.0 when we delete window magnification.
800         assertEquals(1.0f, mScaleCaptor.getValue(), 0f);
801         verifyStartValue(mCenterXCaptor, Float.NaN);
802         verifyStartValue(mCenterYCaptor, Float.NaN);
803         verifyStartValue(mOffsetXCaptor, 0f);
804         verifyStartValue(mOffsetYCaptor, 0f);
805         verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
806         verify(mAnimationCallback).onResult(false);
807         verify(mAnimationCallback2).onResult(true);
808     }
809 
810     @Test
deleteWindowMagnificationWithoutCallback_enabling_expectedValuesAndInvokeCallback()811     public void deleteWindowMagnificationWithoutCallback_enabling_expectedValuesAndInvokeCallback()
812             throws RemoteException {
813         enableWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration,
814                 mAnimationCallback);
815 
816         Mockito.reset(mSpyController);
817         deleteWindowMagnificationWithoutAnimation();
818 
819         verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
820         verify(mAnimationCallback).onResult(false);
821     }
822 
823     @Test
deleteWindowMagnification_disabling_checkStartAndValues()824     public void deleteWindowMagnification_disabling_checkStartAndValues() throws RemoteException {
825         enableWindowMagnificationWithoutAnimation();
826         deleteWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration,
827                 mAnimationCallback);
828 
829         resetMockObjects();
830         deleteWindowMagnificationAndWaitAnimating(mWaitAnimationDuration, mAnimationCallback2);
831 
832         // Verify the method is called in
833         // {@link ValueAnimator.AnimatorUpdateListener#onAnimationUpdate} once
834         verify(mSpyController).updateWindowMagnificationInternal(
835                 mScaleCaptor.capture(),
836                 mCenterXCaptor.capture(), mCenterYCaptor.capture(),
837                 mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
838         // The last captured scale is 1.0 when we delete window magnification.
839         assertEquals(1.0f, mScaleCaptor.getValue(), 0f);
840         verifyStartValue(mOffsetXCaptor, 0f);
841         verifyStartValue(mOffsetYCaptor, 0f);
842         verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
843         verify(mAnimationCallback).onResult(false);
844         verify(mAnimationCallback2).onResult(true);
845     }
846 
847     @Test
deleteWindowMagnificationWithoutCallback_disabling_checkStartAndValues()848     public void deleteWindowMagnificationWithoutCallback_disabling_checkStartAndValues()
849             throws RemoteException {
850         enableWindowMagnificationWithoutAnimation();
851         deleteWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration,
852                 mAnimationCallback);
853 
854         // Verifying that WindowMagnificationController#deleteWindowMagnification is never called
855         // in previous steps
856         verify(mSpyController, never()).deleteWindowMagnification();
857 
858         deleteWindowMagnificationWithoutAnimation();
859 
860         verify(mSpyController).deleteWindowMagnification();
861         verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
862         verify(mAnimationCallback).onResult(false);
863     }
864 
865     @Test
moveWindowMagnifierWithYXRatioLargerThanBase_enabled_movedOnlyVertically()866     public void moveWindowMagnifierWithYXRatioLargerThanBase_enabled_movedOnlyVertically() {
867         enableWindowMagnificationWithoutAnimation();
868 
869         // should move vertically since offsetY/offsetX > HORIZONTAL_LOCK_BASE
870         final float offsetX = 50.0f;
871         final float offsetY =
872                 (float) Math.ceil(offsetX * WindowMagnificationController.HORIZONTAL_LOCK_BASE)
873                         + 1.0f;
874         getInstrumentation().runOnMainSync(()-> mController.moveWindowMagnifier(offsetX, offsetY));
875 
876         verify(mSpyController).moveWindowMagnifier(offsetX, offsetY);
877         verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y + offsetY);
878     }
879 
880     @Test
moveWindowMagnifierWithYXRatioLessThanBase_enabled_movedOnlyHorizontally()881     public void moveWindowMagnifierWithYXRatioLessThanBase_enabled_movedOnlyHorizontally() {
882         enableWindowMagnificationWithoutAnimation();
883 
884         // should move vertically since offsetY/offsetX <= HORIZONTAL_LOCK_BASE
885         final float offsetX = 50.0f;
886         final float offsetY =
887                 (float) Math.floor(offsetX * WindowMagnificationController.HORIZONTAL_LOCK_BASE)
888                         - 1.0f;
889         getInstrumentation().runOnMainSync(() ->
890                 mController.moveWindowMagnifier(offsetX, offsetY));
891 
892         verify(mSpyController).moveWindowMagnifier(offsetX, offsetY);
893         verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + offsetX, DEFAULT_CENTER_Y);
894     }
895 
896     @Test
moveWindowMagnifier_magnifierEnabledAndDiagonalEnabled_movedDiagonally()897     public void moveWindowMagnifier_magnifierEnabledAndDiagonalEnabled_movedDiagonally() {
898         enableWindowMagnificationWithoutAnimation();
899 
900         final float offsetX = 50.0f;
901         final float offsetY =
902                 (float) Math.ceil(offsetX * WindowMagnificationController.HORIZONTAL_LOCK_BASE);
903         // while diagonal scrolling enabled,
904         //  should move with both offsetX and offsetY without regrading offsetY/offsetX
905         getInstrumentation().runOnMainSync(() -> {
906             mController.setDiagonalScrolling(true);
907             mController.moveWindowMagnifier(offsetX, offsetY);
908         });
909 
910         verify(mSpyController).moveWindowMagnifier(offsetX, offsetY);
911         verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + offsetX, DEFAULT_CENTER_Y + offsetY);
912     }
913 
914     @Test
moveWindowMagnifierToPosition_enabled()915     public void moveWindowMagnifierToPosition_enabled() {
916         final float targetCenterX = DEFAULT_CENTER_X + 100;
917         final float targetCenterY = DEFAULT_CENTER_Y + 100;
918         enableWindowMagnificationWithoutAnimation();
919 
920         getInstrumentation().runOnMainSync(() -> {
921             mController.moveWindowMagnifierToPosition(targetCenterX, targetCenterY,
922                     mAnimationCallback);
923             advanceTimeBy(mWaitAnimationDuration);
924         });
925 
926         verifyFinalSpec(DEFAULT_SCALE, targetCenterX, targetCenterY);
927     }
928 
advanceTimeBy(long timeDelta)929     private void advanceTimeBy(long timeDelta) {
930         mAnimatorTestRule.advanceTimeBy(timeDelta);
931     }
932 
verifyFinalSpec(float expectedScale, float expectedCenterX, float expectedCenterY)933     private void verifyFinalSpec(float expectedScale, float expectedCenterX,
934             float expectedCenterY) {
935         assertEquals(expectedScale, mController.getScale(), 0f);
936         assertEquals(expectedCenterX, mController.getCenterX(), 0f);
937         assertEquals(expectedCenterY, mController.getCenterY(), 0f);
938     }
939 
enableWindowMagnificationWithoutAnimation()940     private void enableWindowMagnificationWithoutAnimation() {
941         enableWindowMagnificationWithoutAnimation(
942                 DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
943     }
944 
enableWindowMagnificationWithoutAnimation( float targetScale, float targetCenterX, float targetCenterY)945     private void enableWindowMagnificationWithoutAnimation(
946             float targetScale, float targetCenterX, float targetCenterY) {
947         getInstrumentation().runOnMainSync(() -> {
948             mWindowMagnificationAnimationController.enableWindowMagnification(
949                     targetScale, targetCenterX, targetCenterY, null);
950         });
951         // wait for animation returns
952         waitForIdleSync();
953     }
954 
enableWindowMagnificationAndWaitAnimating(long duration, @Nullable IRemoteMagnificationAnimationCallback callback)955     private void enableWindowMagnificationAndWaitAnimating(long duration,
956             @Nullable IRemoteMagnificationAnimationCallback callback) {
957         enableWindowMagnificationAndWaitAnimating(
958                 duration, DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y, callback);
959     }
960 
enableWindowMagnificationAndWaitAnimating( long duration, float targetScale, float targetCenterX, float targetCenterY, @Nullable IRemoteMagnificationAnimationCallback callback)961     private void enableWindowMagnificationAndWaitAnimating(
962             long duration,
963             float targetScale,
964             float targetCenterX,
965             float targetCenterY,
966             @Nullable IRemoteMagnificationAnimationCallback callback) {
967         getInstrumentation().runOnMainSync(() -> {
968             mWindowMagnificationAnimationController.enableWindowMagnification(
969                     targetScale, targetCenterX, targetCenterY, callback);
970             advanceTimeBy(duration);
971         });
972         // wait for animation returns
973         waitForIdleSync();
974     }
975 
deleteWindowMagnificationWithoutAnimation()976     private void deleteWindowMagnificationWithoutAnimation() {
977         getInstrumentation().runOnMainSync(() -> {
978             mWindowMagnificationAnimationController.deleteWindowMagnification(null);
979         });
980         // wait for animation returns
981         waitForIdleSync();
982     }
983 
deleteWindowMagnificationAndWaitAnimating(long duration, @Nullable IRemoteMagnificationAnimationCallback callback)984     private void deleteWindowMagnificationAndWaitAnimating(long duration,
985             @Nullable IRemoteMagnificationAnimationCallback callback) {
986         getInstrumentation().runOnMainSync(() -> {
987             mWindowMagnificationAnimationController.deleteWindowMagnification(callback);
988             advanceTimeBy(duration);
989         });
990         // wait for animation returns
991         waitForIdleSync();
992     }
993 
verifyStartValue(ArgumentCaptor<Float> captor, float startValue)994     private void verifyStartValue(ArgumentCaptor<Float> captor, float startValue) {
995         assertEquals(startValue, captor.getAllValues().get(0), 0f);
996     }
997 
resetMockObjects()998     private void resetMockObjects() {
999         Mockito.reset(mSpyController);
1000     }
1001 
1002     /**
1003      * It observes the methods in {@link WindowMagnificationController} since we couldn't spy it
1004      * directly.
1005      */
1006     private static class SpyWindowMagnificationController extends WindowMagnificationController {
1007         private WindowMagnificationController mSpyController;
1008 
SpyWindowMagnificationController(Context context, Handler handler, WindowMagnificationAnimationController animationController, MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction, WindowMagnifierCallback callback, SysUiState sysUiState, SecureSettings secureSettings, Supplier<SurfaceControlViewHost> scvhSupplier, SfVsyncFrameCallbackProvider sfVsyncFrameProvider)1009         SpyWindowMagnificationController(Context context,
1010                 Handler handler,
1011                 WindowMagnificationAnimationController animationController,
1012                 MirrorWindowControl mirrorWindowControl,
1013                 SurfaceControl.Transaction transaction,
1014                 WindowMagnifierCallback callback,
1015                 SysUiState sysUiState,
1016                 SecureSettings secureSettings,
1017                 Supplier<SurfaceControlViewHost> scvhSupplier,
1018                 SfVsyncFrameCallbackProvider sfVsyncFrameProvider) {
1019             super(
1020                     context,
1021                     handler,
1022                     animationController,
1023                     mirrorWindowControl,
1024                     transaction,
1025                     callback,
1026                     sysUiState,
1027                     secureSettings,
1028                     scvhSupplier,
1029                     sfVsyncFrameProvider,
1030                     WindowManagerGlobal::getWindowSession);
1031             mSpyController = Mockito.mock(WindowMagnificationController.class);
1032         }
1033 
getSpyController()1034         WindowMagnificationController getSpyController() {
1035             return mSpyController;
1036         }
1037 
1038         @Override
updateWindowMagnificationInternal(float scale, float centerX, float centerY)1039         void updateWindowMagnificationInternal(float scale, float centerX, float centerY) {
1040             super.updateWindowMagnificationInternal(scale, centerX, centerY);
1041             mSpyController.updateWindowMagnificationInternal(scale, centerX, centerY);
1042         }
1043 
1044         @Override
updateWindowMagnificationInternal(float scale, float centerX, float centerY, float magnificationOffsetFrameRatioX, float magnificationOffsetFrameRatioY)1045         void updateWindowMagnificationInternal(float scale, float centerX, float centerY,
1046                 float magnificationOffsetFrameRatioX, float magnificationOffsetFrameRatioY) {
1047             super.updateWindowMagnificationInternal(scale, centerX, centerY,
1048                     magnificationOffsetFrameRatioX, magnificationOffsetFrameRatioY);
1049             mSpyController.updateWindowMagnificationInternal(scale, centerX, centerY,
1050                     magnificationOffsetFrameRatioX, magnificationOffsetFrameRatioY);
1051         }
1052 
1053         @Override
deleteWindowMagnification()1054         void deleteWindowMagnification() {
1055             super.deleteWindowMagnification();
1056             mSpyController.deleteWindowMagnification();
1057         }
1058 
1059         @Override
moveWindowMagnifier(float offsetX, float offsetY)1060         void moveWindowMagnifier(float offsetX, float offsetY) {
1061             super.moveWindowMagnifier(offsetX, offsetY);
1062             mSpyController.moveWindowMagnifier(offsetX, offsetY);
1063         }
1064 
1065         @Override
moveWindowMagnifierToPosition(float positionX, float positionY, IRemoteMagnificationAnimationCallback callback)1066         void moveWindowMagnifierToPosition(float positionX, float positionY,
1067                 IRemoteMagnificationAnimationCallback callback) {
1068             super.moveWindowMagnifierToPosition(positionX, positionY, callback);
1069             mSpyController.moveWindowMagnifierToPosition(positionX, positionY, callback);
1070         }
1071 
1072         @Override
setScale(float scale)1073         void setScale(float scale) {
1074             super.setScale(scale);
1075             mSpyController.setScale(scale);
1076         }
1077 
1078         @Override
updateSysUIStateFlag()1079         public void updateSysUIStateFlag() {
1080             super.updateSysUIStateFlag();
1081             mSpyController.updateSysUIStateFlag();
1082         }
1083     }
1084 
newValueAnimator()1085     private ValueAnimator newValueAnimator() {
1086         final ValueAnimator valueAnimator = new ValueAnimator();
1087         valueAnimator.setDuration(mWaitAnimationDuration);
1088         valueAnimator.setInterpolator(new AccelerateInterpolator(2.5f));
1089         valueAnimator.setFloatValues(0.0f, 1.0f);
1090         return valueAnimator;
1091     }
1092 }
1093