Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java +60 −53 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.wm.shell.pip2.animation; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.RectEvaluator; import android.animation.ValueAnimator; import android.content.Context; Loading @@ -27,6 +28,7 @@ import android.view.SurfaceControl; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.wm.shell.R; import com.android.wm.shell.pip2.PipSurfaceTransactionHelper; import com.android.wm.shell.shared.animation.Interpolators; Loading @@ -34,8 +36,7 @@ import com.android.wm.shell.shared.animation.Interpolators; /** * Animator that handles bounds animations for exit-via-expanding PIP. */ public class PipExpandAnimator extends ValueAnimator implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener { public class PipExpandAnimator extends ValueAnimator { @NonNull private final SurfaceControl mLeash; private final SurfaceControl.Transaction mStartTransaction; Loading @@ -58,12 +59,61 @@ public class PipExpandAnimator extends ValueAnimator // Bounds updated by the evaluator as animator is running. private final Rect mAnimatedRect = new Rect(); private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; private final RectEvaluator mRectEvaluator; private final RectEvaluator mInsetEvaluator; private final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper; private final Animator.AnimatorListener mAnimatorListener = new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { super.onAnimationStart(animation); if (mAnimationStartCallback != null) { mAnimationStartCallback.run(); } if (mStartTransaction != null) { mStartTransaction.apply(); } } @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); if (mFinishTransaction != null) { // finishTransaction might override some state (eg. corner radii) so we want to // manually set the state to the end of the animation mPipSurfaceTransactionHelper.scaleAndCrop(mFinishTransaction, mLeash, mSourceRectHint, mBaseBounds, mAnimatedRect, getInsets(1f), false /* isInPipDirection */, 1f) .round(mFinishTransaction, mLeash, false /* applyCornerRadius */) .shadow(mFinishTransaction, mLeash, false /* applyCornerRadius */); } if (mAnimationEndCallback != null) { mAnimationEndCallback.run(); } } }; private final ValueAnimator.AnimatorUpdateListener mAnimatorUpdateListener = new AnimatorUpdateListener() { @Override public void onAnimationUpdate(@NonNull ValueAnimator animation) { final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); final float fraction = getAnimatedFraction(); // TODO (b/350801661): implement fixed rotation Rect insets = getInsets(fraction); mPipSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mSourceRectHint, mBaseBounds, mAnimatedRect, insets, false /* isInPipDirection */, fraction) .round(tx, mLeash, false /* applyCornerRadius */) .shadow(tx, mLeash, false /* applyCornerRadius */); tx.apply(); } }; public PipExpandAnimator(Context context, @NonNull SurfaceControl leash, SurfaceControl.Transaction startTransaction, Loading Loading @@ -105,8 +155,8 @@ public class PipExpandAnimator extends ValueAnimator setObjectValues(startBounds, endBounds); setEvaluator(mRectEvaluator); setInterpolator(Interpolators.FAST_OUT_SLOW_IN); addListener(this); addUpdateListener(this); addListener(mAnimatorListener); addUpdateListener(mAnimatorUpdateListener); } public void setAnimationStartCallback(@NonNull Runnable runnable) { Loading @@ -117,58 +167,15 @@ public class PipExpandAnimator extends ValueAnimator mAnimationEndCallback = runnable; } @Override public void onAnimationStart(@NonNull Animator animation) { if (mAnimationStartCallback != null) { mAnimationStartCallback.run(); } if (mStartTransaction != null) { mStartTransaction.apply(); } } @Override public void onAnimationEnd(@NonNull Animator animation) { if (mFinishTransaction != null) { // finishTransaction might override some state (eg. corner radii) so we want to // manually set the state to the end of the animation mPipSurfaceTransactionHelper.scaleAndCrop(mFinishTransaction, mLeash, mSourceRectHint, mBaseBounds, mAnimatedRect, getInsets(1f), false /* isInPipDirection */, 1f) .round(mFinishTransaction, mLeash, false /* applyCornerRadius */) .shadow(mFinishTransaction, mLeash, false /* applyCornerRadius */); } if (mAnimationEndCallback != null) { mAnimationEndCallback.run(); } } @Override public void onAnimationUpdate(@NonNull ValueAnimator animation) { final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); final float fraction = getAnimatedFraction(); // TODO (b/350801661): implement fixed rotation Rect insets = getInsets(fraction); mPipSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mSourceRectHint, mBaseBounds, mAnimatedRect, insets, false /* isInPipDirection */, fraction) .round(tx, mLeash, false /* applyCornerRadius */) .shadow(tx, mLeash, false /* applyCornerRadius */); tx.apply(); } private Rect getInsets(float fraction) { final Rect startInsets = mSourceRectHintInsets; final Rect endInsets = mZeroInsets; return mInsetEvaluator.evaluate(fraction, startInsets, endInsets); } // no-ops @Override public void onAnimationCancel(@NonNull Animator animation) {} @Override public void onAnimationRepeat(@NonNull Animator animation) {} @VisibleForTesting void setSurfaceControlTransactionFactory( @NonNull PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) { mSurfaceControlTransactionFactory = factory; } } libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java 0 → 100644 +185 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.wm.shell.pip2.animation; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; import android.graphics.Matrix; import android.graphics.Rect; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.Surface; import android.view.SurfaceControl; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import com.android.wm.shell.pip2.PipSurfaceTransactionHelper; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; /** * Unit test against {@link PipExpandAnimator}. */ @SmallTest @TestableLooper.RunWithLooper @RunWith(AndroidTestingRunner.class) public class PipExpandAnimatorTest { @Mock private Context mMockContext; @Mock private Resources mMockResources; @Mock private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mMockFactory; @Mock private SurfaceControl.Transaction mMockTransaction; @Mock private SurfaceControl.Transaction mMockStartTransaction; @Mock private SurfaceControl.Transaction mMockFinishTransaction; @Mock private Runnable mMockStartCallback; @Mock private Runnable mMockEndCallback; private PipExpandAnimator mPipExpandAnimator; private Rect mBaseBounds; private Rect mStartBounds; private Rect mEndBounds; private Rect mSourceRectHint; @Surface.Rotation private int mRotation; private SurfaceControl mTestLeash; @Before public void setUp() { MockitoAnnotations.initMocks(this); when(mMockContext.getResources()).thenReturn(mMockResources); when(mMockResources.getInteger(anyInt())).thenReturn(0); when(mMockFactory.getTransaction()).thenReturn(mMockTransaction); // No-op on the mMockTransaction when(mMockTransaction.setAlpha(any(SurfaceControl.class), anyFloat())) .thenReturn(mMockTransaction); when(mMockTransaction.setCrop(any(SurfaceControl.class), any(Rect.class))) .thenReturn(mMockTransaction); when(mMockTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any())) .thenReturn(mMockTransaction); when(mMockTransaction.setCornerRadius(any(SurfaceControl.class), anyFloat())) .thenReturn(mMockTransaction); when(mMockTransaction.setShadowRadius(any(SurfaceControl.class), anyFloat())) .thenReturn(mMockTransaction); // Do the same for mMockFinishTransaction when(mMockFinishTransaction.setAlpha(any(SurfaceControl.class), anyFloat())) .thenReturn(mMockFinishTransaction); when(mMockFinishTransaction.setCrop(any(SurfaceControl.class), any(Rect.class))) .thenReturn(mMockFinishTransaction); when(mMockFinishTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any())) .thenReturn(mMockFinishTransaction); when(mMockFinishTransaction.setCornerRadius(any(SurfaceControl.class), anyFloat())) .thenReturn(mMockFinishTransaction); when(mMockFinishTransaction.setShadowRadius(any(SurfaceControl.class), anyFloat())) .thenReturn(mMockFinishTransaction); mTestLeash = new SurfaceControl.Builder() .setContainerLayer() .setName("PipExpandAnimatorTest") .setCallsite("PipExpandAnimatorTest") .build(); } @Test public void setAnimationStartCallback_expand_callbackStartCallback() { mRotation = Surface.ROTATION_0; mBaseBounds = new Rect(0, 0, 1_000, 2_000); mStartBounds = new Rect(500, 1_000, 1_000, 2_000); mEndBounds = new Rect(mBaseBounds); mPipExpandAnimator = new PipExpandAnimator(mMockContext, mTestLeash, mMockStartTransaction, mMockFinishTransaction, mBaseBounds, mStartBounds, mEndBounds, mSourceRectHint, mRotation); mPipExpandAnimator.setSurfaceControlTransactionFactory(mMockFactory); mPipExpandAnimator.setAnimationStartCallback(mMockStartCallback); mPipExpandAnimator.setAnimationEndCallback(mMockEndCallback); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { mPipExpandAnimator.start(); mPipExpandAnimator.pause(); }); verify(mMockStartCallback).run(); verifyZeroInteractions(mMockEndCallback); } @Test public void setAnimationEndCallback_expand_callbackStartAndEndCallback() { mRotation = Surface.ROTATION_0; mBaseBounds = new Rect(0, 0, 1_000, 2_000); mStartBounds = new Rect(500, 1_000, 1_000, 2_000); mEndBounds = new Rect(mBaseBounds); mPipExpandAnimator = new PipExpandAnimator(mMockContext, mTestLeash, mMockStartTransaction, mMockFinishTransaction, mBaseBounds, mStartBounds, mEndBounds, mSourceRectHint, mRotation); mPipExpandAnimator.setSurfaceControlTransactionFactory(mMockFactory); mPipExpandAnimator.setAnimationStartCallback(mMockStartCallback); mPipExpandAnimator.setAnimationEndCallback(mMockEndCallback); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { mPipExpandAnimator.start(); mPipExpandAnimator.end(); }); verify(mMockStartCallback).run(); verify(mMockEndCallback).run(); } @Test public void onAnimationEnd_expand_leashIsFullscreen() { mRotation = Surface.ROTATION_0; mBaseBounds = new Rect(0, 0, 1_000, 2_000); mStartBounds = new Rect(500, 1_000, 1_000, 2_000); mEndBounds = new Rect(mBaseBounds); mPipExpandAnimator = new PipExpandAnimator(mMockContext, mTestLeash, mMockStartTransaction, mMockFinishTransaction, mBaseBounds, mStartBounds, mEndBounds, mSourceRectHint, mRotation); mPipExpandAnimator.setSurfaceControlTransactionFactory(mMockFactory); mPipExpandAnimator.setAnimationStartCallback(mMockStartCallback); mPipExpandAnimator.setAnimationEndCallback(mMockEndCallback); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { mPipExpandAnimator.start(); clearInvocations(mMockTransaction); mPipExpandAnimator.end(); }); verify(mMockTransaction).setCrop(mTestLeash, mEndBounds); verify(mMockTransaction).setCornerRadius(mTestLeash, 0f); verify(mMockTransaction).setShadowRadius(mTestLeash, 0f); } } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java +60 −53 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.wm.shell.pip2.animation; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.RectEvaluator; import android.animation.ValueAnimator; import android.content.Context; Loading @@ -27,6 +28,7 @@ import android.view.SurfaceControl; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.wm.shell.R; import com.android.wm.shell.pip2.PipSurfaceTransactionHelper; import com.android.wm.shell.shared.animation.Interpolators; Loading @@ -34,8 +36,7 @@ import com.android.wm.shell.shared.animation.Interpolators; /** * Animator that handles bounds animations for exit-via-expanding PIP. */ public class PipExpandAnimator extends ValueAnimator implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener { public class PipExpandAnimator extends ValueAnimator { @NonNull private final SurfaceControl mLeash; private final SurfaceControl.Transaction mStartTransaction; Loading @@ -58,12 +59,61 @@ public class PipExpandAnimator extends ValueAnimator // Bounds updated by the evaluator as animator is running. private final Rect mAnimatedRect = new Rect(); private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; private final RectEvaluator mRectEvaluator; private final RectEvaluator mInsetEvaluator; private final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper; private final Animator.AnimatorListener mAnimatorListener = new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { super.onAnimationStart(animation); if (mAnimationStartCallback != null) { mAnimationStartCallback.run(); } if (mStartTransaction != null) { mStartTransaction.apply(); } } @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); if (mFinishTransaction != null) { // finishTransaction might override some state (eg. corner radii) so we want to // manually set the state to the end of the animation mPipSurfaceTransactionHelper.scaleAndCrop(mFinishTransaction, mLeash, mSourceRectHint, mBaseBounds, mAnimatedRect, getInsets(1f), false /* isInPipDirection */, 1f) .round(mFinishTransaction, mLeash, false /* applyCornerRadius */) .shadow(mFinishTransaction, mLeash, false /* applyCornerRadius */); } if (mAnimationEndCallback != null) { mAnimationEndCallback.run(); } } }; private final ValueAnimator.AnimatorUpdateListener mAnimatorUpdateListener = new AnimatorUpdateListener() { @Override public void onAnimationUpdate(@NonNull ValueAnimator animation) { final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); final float fraction = getAnimatedFraction(); // TODO (b/350801661): implement fixed rotation Rect insets = getInsets(fraction); mPipSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mSourceRectHint, mBaseBounds, mAnimatedRect, insets, false /* isInPipDirection */, fraction) .round(tx, mLeash, false /* applyCornerRadius */) .shadow(tx, mLeash, false /* applyCornerRadius */); tx.apply(); } }; public PipExpandAnimator(Context context, @NonNull SurfaceControl leash, SurfaceControl.Transaction startTransaction, Loading Loading @@ -105,8 +155,8 @@ public class PipExpandAnimator extends ValueAnimator setObjectValues(startBounds, endBounds); setEvaluator(mRectEvaluator); setInterpolator(Interpolators.FAST_OUT_SLOW_IN); addListener(this); addUpdateListener(this); addListener(mAnimatorListener); addUpdateListener(mAnimatorUpdateListener); } public void setAnimationStartCallback(@NonNull Runnable runnable) { Loading @@ -117,58 +167,15 @@ public class PipExpandAnimator extends ValueAnimator mAnimationEndCallback = runnable; } @Override public void onAnimationStart(@NonNull Animator animation) { if (mAnimationStartCallback != null) { mAnimationStartCallback.run(); } if (mStartTransaction != null) { mStartTransaction.apply(); } } @Override public void onAnimationEnd(@NonNull Animator animation) { if (mFinishTransaction != null) { // finishTransaction might override some state (eg. corner radii) so we want to // manually set the state to the end of the animation mPipSurfaceTransactionHelper.scaleAndCrop(mFinishTransaction, mLeash, mSourceRectHint, mBaseBounds, mAnimatedRect, getInsets(1f), false /* isInPipDirection */, 1f) .round(mFinishTransaction, mLeash, false /* applyCornerRadius */) .shadow(mFinishTransaction, mLeash, false /* applyCornerRadius */); } if (mAnimationEndCallback != null) { mAnimationEndCallback.run(); } } @Override public void onAnimationUpdate(@NonNull ValueAnimator animation) { final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); final float fraction = getAnimatedFraction(); // TODO (b/350801661): implement fixed rotation Rect insets = getInsets(fraction); mPipSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mSourceRectHint, mBaseBounds, mAnimatedRect, insets, false /* isInPipDirection */, fraction) .round(tx, mLeash, false /* applyCornerRadius */) .shadow(tx, mLeash, false /* applyCornerRadius */); tx.apply(); } private Rect getInsets(float fraction) { final Rect startInsets = mSourceRectHintInsets; final Rect endInsets = mZeroInsets; return mInsetEvaluator.evaluate(fraction, startInsets, endInsets); } // no-ops @Override public void onAnimationCancel(@NonNull Animator animation) {} @Override public void onAnimationRepeat(@NonNull Animator animation) {} @VisibleForTesting void setSurfaceControlTransactionFactory( @NonNull PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) { mSurfaceControlTransactionFactory = factory; } }
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java 0 → 100644 +185 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.wm.shell.pip2.animation; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; import android.graphics.Matrix; import android.graphics.Rect; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.Surface; import android.view.SurfaceControl; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import com.android.wm.shell.pip2.PipSurfaceTransactionHelper; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; /** * Unit test against {@link PipExpandAnimator}. */ @SmallTest @TestableLooper.RunWithLooper @RunWith(AndroidTestingRunner.class) public class PipExpandAnimatorTest { @Mock private Context mMockContext; @Mock private Resources mMockResources; @Mock private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mMockFactory; @Mock private SurfaceControl.Transaction mMockTransaction; @Mock private SurfaceControl.Transaction mMockStartTransaction; @Mock private SurfaceControl.Transaction mMockFinishTransaction; @Mock private Runnable mMockStartCallback; @Mock private Runnable mMockEndCallback; private PipExpandAnimator mPipExpandAnimator; private Rect mBaseBounds; private Rect mStartBounds; private Rect mEndBounds; private Rect mSourceRectHint; @Surface.Rotation private int mRotation; private SurfaceControl mTestLeash; @Before public void setUp() { MockitoAnnotations.initMocks(this); when(mMockContext.getResources()).thenReturn(mMockResources); when(mMockResources.getInteger(anyInt())).thenReturn(0); when(mMockFactory.getTransaction()).thenReturn(mMockTransaction); // No-op on the mMockTransaction when(mMockTransaction.setAlpha(any(SurfaceControl.class), anyFloat())) .thenReturn(mMockTransaction); when(mMockTransaction.setCrop(any(SurfaceControl.class), any(Rect.class))) .thenReturn(mMockTransaction); when(mMockTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any())) .thenReturn(mMockTransaction); when(mMockTransaction.setCornerRadius(any(SurfaceControl.class), anyFloat())) .thenReturn(mMockTransaction); when(mMockTransaction.setShadowRadius(any(SurfaceControl.class), anyFloat())) .thenReturn(mMockTransaction); // Do the same for mMockFinishTransaction when(mMockFinishTransaction.setAlpha(any(SurfaceControl.class), anyFloat())) .thenReturn(mMockFinishTransaction); when(mMockFinishTransaction.setCrop(any(SurfaceControl.class), any(Rect.class))) .thenReturn(mMockFinishTransaction); when(mMockFinishTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any())) .thenReturn(mMockFinishTransaction); when(mMockFinishTransaction.setCornerRadius(any(SurfaceControl.class), anyFloat())) .thenReturn(mMockFinishTransaction); when(mMockFinishTransaction.setShadowRadius(any(SurfaceControl.class), anyFloat())) .thenReturn(mMockFinishTransaction); mTestLeash = new SurfaceControl.Builder() .setContainerLayer() .setName("PipExpandAnimatorTest") .setCallsite("PipExpandAnimatorTest") .build(); } @Test public void setAnimationStartCallback_expand_callbackStartCallback() { mRotation = Surface.ROTATION_0; mBaseBounds = new Rect(0, 0, 1_000, 2_000); mStartBounds = new Rect(500, 1_000, 1_000, 2_000); mEndBounds = new Rect(mBaseBounds); mPipExpandAnimator = new PipExpandAnimator(mMockContext, mTestLeash, mMockStartTransaction, mMockFinishTransaction, mBaseBounds, mStartBounds, mEndBounds, mSourceRectHint, mRotation); mPipExpandAnimator.setSurfaceControlTransactionFactory(mMockFactory); mPipExpandAnimator.setAnimationStartCallback(mMockStartCallback); mPipExpandAnimator.setAnimationEndCallback(mMockEndCallback); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { mPipExpandAnimator.start(); mPipExpandAnimator.pause(); }); verify(mMockStartCallback).run(); verifyZeroInteractions(mMockEndCallback); } @Test public void setAnimationEndCallback_expand_callbackStartAndEndCallback() { mRotation = Surface.ROTATION_0; mBaseBounds = new Rect(0, 0, 1_000, 2_000); mStartBounds = new Rect(500, 1_000, 1_000, 2_000); mEndBounds = new Rect(mBaseBounds); mPipExpandAnimator = new PipExpandAnimator(mMockContext, mTestLeash, mMockStartTransaction, mMockFinishTransaction, mBaseBounds, mStartBounds, mEndBounds, mSourceRectHint, mRotation); mPipExpandAnimator.setSurfaceControlTransactionFactory(mMockFactory); mPipExpandAnimator.setAnimationStartCallback(mMockStartCallback); mPipExpandAnimator.setAnimationEndCallback(mMockEndCallback); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { mPipExpandAnimator.start(); mPipExpandAnimator.end(); }); verify(mMockStartCallback).run(); verify(mMockEndCallback).run(); } @Test public void onAnimationEnd_expand_leashIsFullscreen() { mRotation = Surface.ROTATION_0; mBaseBounds = new Rect(0, 0, 1_000, 2_000); mStartBounds = new Rect(500, 1_000, 1_000, 2_000); mEndBounds = new Rect(mBaseBounds); mPipExpandAnimator = new PipExpandAnimator(mMockContext, mTestLeash, mMockStartTransaction, mMockFinishTransaction, mBaseBounds, mStartBounds, mEndBounds, mSourceRectHint, mRotation); mPipExpandAnimator.setSurfaceControlTransactionFactory(mMockFactory); mPipExpandAnimator.setAnimationStartCallback(mMockStartCallback); mPipExpandAnimator.setAnimationEndCallback(mMockEndCallback); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { mPipExpandAnimator.start(); clearInvocations(mMockTransaction); mPipExpandAnimator.end(); }); verify(mMockTransaction).setCrop(mTestLeash, mEndBounds); verify(mMockTransaction).setCornerRadius(mTestLeash, 0f); verify(mMockTransaction).setShadowRadius(mTestLeash, 0f); } }