Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java +43 −35 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.ValueAnimator; import android.annotation.IntDef; import android.content.Context; Loading @@ -25,6 +26,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; Loading @@ -34,8 +36,7 @@ import java.lang.annotation.RetentionPolicy; /** * Animator that handles the alpha animation for entering PIP */ public class PipAlphaAnimator extends ValueAnimator implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener { public class PipAlphaAnimator extends ValueAnimator { @IntDef(prefix = {"FADE_"}, value = { FADE_IN, FADE_OUT Loading @@ -47,15 +48,45 @@ public class PipAlphaAnimator extends ValueAnimator implements ValueAnimator.Ani public static final int FADE_IN = 0; public static final int FADE_OUT = 1; private final int mEnterAnimationDuration; private final SurfaceControl mLeash; private final SurfaceControl.Transaction mStartTransaction; 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 (mAnimationEndCallback != null) { mAnimationEndCallback.run(); } } }; private final ValueAnimator.AnimatorUpdateListener mAnimatorUpdateListener = new AnimatorUpdateListener() { @Override public void onAnimationUpdate(@NonNull ValueAnimator animation) { final float alpha = (Float) animation.getAnimatedValue(); mSurfaceControlTransactionFactory.getTransaction() .setAlpha(mLeash, alpha).apply(); } }; // optional callbacks for tracking animation start and end @Nullable private Runnable mAnimationStartCallback; @Nullable private Runnable mAnimationEndCallback; private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory @NonNull private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; public PipAlphaAnimator(Context context, Loading @@ -71,11 +102,11 @@ public class PipAlphaAnimator extends ValueAnimator implements ValueAnimator.Ani } mSurfaceControlTransactionFactory = new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory(); mEnterAnimationDuration = context.getResources() final int enterAnimationDuration = context.getResources() .getInteger(R.integer.config_pipEnterAnimationDuration); setDuration(mEnterAnimationDuration); addListener(this); addUpdateListener(this); setDuration(enterAnimationDuration); addListener(mAnimatorListener); addUpdateListener(mAnimatorUpdateListener); } public void setAnimationStartCallback(@NonNull Runnable runnable) { Loading @@ -86,32 +117,9 @@ public class PipAlphaAnimator extends ValueAnimator implements ValueAnimator.Ani mAnimationEndCallback = runnable; } @Override public void onAnimationStart(@NonNull Animator animation) { if (mAnimationStartCallback != null) { mAnimationStartCallback.run(); } if (mStartTransaction != null) { mStartTransaction.apply(); } } @Override public void onAnimationUpdate(@NonNull ValueAnimator animation) { final float alpha = (Float) animation.getAnimatedValue(); mSurfaceControlTransactionFactory.getTransaction().setAlpha(mLeash, alpha).apply(); @VisibleForTesting void setSurfaceControlTransactionFactory( @NonNull PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) { mSurfaceControlTransactionFactory = factory; } @Override public void onAnimationEnd(@NonNull Animator animation) { if (mAnimationEndCallback != null) { mAnimationEndCallback.run(); } } @Override public void onAnimationCancel(@NonNull Animator animation) {} @Override public void onAnimationRepeat(@NonNull Animator animation) {} } libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java 0 → 100644 +144 −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.testing.AndroidTestingRunner; import android.testing.TestableLooper; 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 PipAlphaAnimator}. */ @SmallTest @TestableLooper.RunWithLooper @RunWith(AndroidTestingRunner.class) public class PipAlphaAnimatorTest { @Mock private Context mMockContext; @Mock private Resources mMockResources; @Mock private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mMockFactory; @Mock private SurfaceControl.Transaction mMockTransaction; @Mock private Runnable mMockStartCallback; @Mock private Runnable mMockEndCallback; private PipAlphaAnimator mPipAlphaAnimator; 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); when(mMockTransaction.setAlpha(any(SurfaceControl.class), anyFloat())) .thenReturn(mMockTransaction); mTestLeash = new SurfaceControl.Builder() .setContainerLayer() .setName("PipAlphaAnimatorTest") .setCallsite("PipAlphaAnimatorTest") .build(); } @Test public void setAnimationStartCallback_fadeInAnimator_callbackStartCallback() { mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockTransaction, PipAlphaAnimator.FADE_IN); mPipAlphaAnimator.setAnimationStartCallback(mMockStartCallback); mPipAlphaAnimator.setAnimationEndCallback(mMockEndCallback); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { mPipAlphaAnimator.start(); mPipAlphaAnimator.pause(); }); verify(mMockStartCallback).run(); verifyZeroInteractions(mMockEndCallback); } @Test public void setAnimationEndCallback_fadeInAnimator_callbackStartAndEndCallback() { mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockTransaction, PipAlphaAnimator.FADE_IN); mPipAlphaAnimator.setAnimationStartCallback(mMockStartCallback); mPipAlphaAnimator.setAnimationEndCallback(mMockEndCallback); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { mPipAlphaAnimator.start(); mPipAlphaAnimator.end(); }); verify(mMockStartCallback).run(); verify(mMockStartCallback).run(); } @Test public void onAnimationEnd_fadeInAnimator_leashVisibleAtEnd() { mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockTransaction, PipAlphaAnimator.FADE_IN); mPipAlphaAnimator.setSurfaceControlTransactionFactory(mMockFactory); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { mPipAlphaAnimator.start(); clearInvocations(mMockTransaction); mPipAlphaAnimator.end(); }); verify(mMockTransaction).setAlpha(mTestLeash, 1.0f); } @Test public void onAnimationEnd_fadeOutAnimator_leashInvisibleAtEnd() { mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockTransaction, PipAlphaAnimator.FADE_OUT); mPipAlphaAnimator.setSurfaceControlTransactionFactory(mMockFactory); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { mPipAlphaAnimator.start(); clearInvocations(mMockTransaction); mPipAlphaAnimator.end(); }); verify(mMockTransaction).setAlpha(mTestLeash, 0f); } } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java +43 −35 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.ValueAnimator; import android.annotation.IntDef; import android.content.Context; Loading @@ -25,6 +26,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; Loading @@ -34,8 +36,7 @@ import java.lang.annotation.RetentionPolicy; /** * Animator that handles the alpha animation for entering PIP */ public class PipAlphaAnimator extends ValueAnimator implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener { public class PipAlphaAnimator extends ValueAnimator { @IntDef(prefix = {"FADE_"}, value = { FADE_IN, FADE_OUT Loading @@ -47,15 +48,45 @@ public class PipAlphaAnimator extends ValueAnimator implements ValueAnimator.Ani public static final int FADE_IN = 0; public static final int FADE_OUT = 1; private final int mEnterAnimationDuration; private final SurfaceControl mLeash; private final SurfaceControl.Transaction mStartTransaction; 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 (mAnimationEndCallback != null) { mAnimationEndCallback.run(); } } }; private final ValueAnimator.AnimatorUpdateListener mAnimatorUpdateListener = new AnimatorUpdateListener() { @Override public void onAnimationUpdate(@NonNull ValueAnimator animation) { final float alpha = (Float) animation.getAnimatedValue(); mSurfaceControlTransactionFactory.getTransaction() .setAlpha(mLeash, alpha).apply(); } }; // optional callbacks for tracking animation start and end @Nullable private Runnable mAnimationStartCallback; @Nullable private Runnable mAnimationEndCallback; private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory @NonNull private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; public PipAlphaAnimator(Context context, Loading @@ -71,11 +102,11 @@ public class PipAlphaAnimator extends ValueAnimator implements ValueAnimator.Ani } mSurfaceControlTransactionFactory = new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory(); mEnterAnimationDuration = context.getResources() final int enterAnimationDuration = context.getResources() .getInteger(R.integer.config_pipEnterAnimationDuration); setDuration(mEnterAnimationDuration); addListener(this); addUpdateListener(this); setDuration(enterAnimationDuration); addListener(mAnimatorListener); addUpdateListener(mAnimatorUpdateListener); } public void setAnimationStartCallback(@NonNull Runnable runnable) { Loading @@ -86,32 +117,9 @@ public class PipAlphaAnimator extends ValueAnimator implements ValueAnimator.Ani mAnimationEndCallback = runnable; } @Override public void onAnimationStart(@NonNull Animator animation) { if (mAnimationStartCallback != null) { mAnimationStartCallback.run(); } if (mStartTransaction != null) { mStartTransaction.apply(); } } @Override public void onAnimationUpdate(@NonNull ValueAnimator animation) { final float alpha = (Float) animation.getAnimatedValue(); mSurfaceControlTransactionFactory.getTransaction().setAlpha(mLeash, alpha).apply(); @VisibleForTesting void setSurfaceControlTransactionFactory( @NonNull PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) { mSurfaceControlTransactionFactory = factory; } @Override public void onAnimationEnd(@NonNull Animator animation) { if (mAnimationEndCallback != null) { mAnimationEndCallback.run(); } } @Override public void onAnimationCancel(@NonNull Animator animation) {} @Override public void onAnimationRepeat(@NonNull Animator animation) {} }
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipAlphaAnimatorTest.java 0 → 100644 +144 −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.testing.AndroidTestingRunner; import android.testing.TestableLooper; 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 PipAlphaAnimator}. */ @SmallTest @TestableLooper.RunWithLooper @RunWith(AndroidTestingRunner.class) public class PipAlphaAnimatorTest { @Mock private Context mMockContext; @Mock private Resources mMockResources; @Mock private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mMockFactory; @Mock private SurfaceControl.Transaction mMockTransaction; @Mock private Runnable mMockStartCallback; @Mock private Runnable mMockEndCallback; private PipAlphaAnimator mPipAlphaAnimator; 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); when(mMockTransaction.setAlpha(any(SurfaceControl.class), anyFloat())) .thenReturn(mMockTransaction); mTestLeash = new SurfaceControl.Builder() .setContainerLayer() .setName("PipAlphaAnimatorTest") .setCallsite("PipAlphaAnimatorTest") .build(); } @Test public void setAnimationStartCallback_fadeInAnimator_callbackStartCallback() { mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockTransaction, PipAlphaAnimator.FADE_IN); mPipAlphaAnimator.setAnimationStartCallback(mMockStartCallback); mPipAlphaAnimator.setAnimationEndCallback(mMockEndCallback); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { mPipAlphaAnimator.start(); mPipAlphaAnimator.pause(); }); verify(mMockStartCallback).run(); verifyZeroInteractions(mMockEndCallback); } @Test public void setAnimationEndCallback_fadeInAnimator_callbackStartAndEndCallback() { mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockTransaction, PipAlphaAnimator.FADE_IN); mPipAlphaAnimator.setAnimationStartCallback(mMockStartCallback); mPipAlphaAnimator.setAnimationEndCallback(mMockEndCallback); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { mPipAlphaAnimator.start(); mPipAlphaAnimator.end(); }); verify(mMockStartCallback).run(); verify(mMockStartCallback).run(); } @Test public void onAnimationEnd_fadeInAnimator_leashVisibleAtEnd() { mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockTransaction, PipAlphaAnimator.FADE_IN); mPipAlphaAnimator.setSurfaceControlTransactionFactory(mMockFactory); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { mPipAlphaAnimator.start(); clearInvocations(mMockTransaction); mPipAlphaAnimator.end(); }); verify(mMockTransaction).setAlpha(mTestLeash, 1.0f); } @Test public void onAnimationEnd_fadeOutAnimator_leashInvisibleAtEnd() { mPipAlphaAnimator = new PipAlphaAnimator(mMockContext, mTestLeash, mMockTransaction, PipAlphaAnimator.FADE_OUT); mPipAlphaAnimator.setSurfaceControlTransactionFactory(mMockFactory); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { mPipAlphaAnimator.start(); clearInvocations(mMockTransaction); mPipAlphaAnimator.end(); }); verify(mMockTransaction).setAlpha(mTestLeash, 0f); } }