Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b0fc6180 authored by Nick Chameyev's avatar Nick Chameyev Committed by Android (Google) Code Review
Browse files

Merge "Take over animating of unfold Shell transitions" into main

parents 9e0426df 4fe9c2c3
Loading
Loading
Loading
Loading
+6 −0
Original line number Original line Diff line number Diff line
@@ -961,6 +961,12 @@
      "group": "WM_DEBUG_RECENTS_ANIMATIONS",
      "group": "WM_DEBUG_RECENTS_ANIMATIONS",
      "at": "com\/android\/server\/wm\/RecentsAnimationController.java"
      "at": "com\/android\/server\/wm\/RecentsAnimationController.java"
    },
    },
    "-1204565480": {
      "message": "Adding display switch to existing collecting transition",
      "level": "DEBUG",
      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
      "at": "com\/android\/server\/wm\/PhysicalDisplaySwitchTransitionLauncher.java"
    },
    "-1198579104": {
    "-1198579104": {
      "message": "Pushing next activity %s out to target's task %s",
      "message": "Pushing next activity %s out to target's task %s",
      "level": "VERBOSE",
      "level": "VERBOSE",
+39 −1
Original line number Original line Diff line number Diff line
@@ -20,6 +20,7 @@ import static android.view.WindowManager.TRANSIT_CHANGE;


import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;


import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.app.ActivityManager;
import android.os.IBinder;
import android.os.IBinder;
import android.view.SurfaceControl;
import android.view.SurfaceControl;
@@ -74,9 +75,9 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
            Executor executor,
            Executor executor,
            Transitions transitions) {
            Transitions transitions) {
        mUnfoldProgressProvider = unfoldProgressProvider;
        mUnfoldProgressProvider = unfoldProgressProvider;
        mTransitions = transitions;
        mTransactionPool = transactionPool;
        mTransactionPool = transactionPool;
        mExecutor = executor;
        mExecutor = executor;
        mTransitions = transitions;


        mAnimators.add(splitUnfoldTaskAnimator);
        mAnimators.add(splitUnfoldTaskAnimator);
        mAnimators.add(fullscreenUnfoldAnimator);
        mAnimators.add(fullscreenUnfoldAnimator);
@@ -104,6 +105,16 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction,
            @NonNull TransitionFinishCallback finishCallback) {
            @NonNull TransitionFinishCallback finishCallback) {
        if (hasUnfold(info) && transition != mTransition) {
            // Take over transition that has unfold, we might receive it if no other handler
            // accepted request in handleRequest, e.g. for rotation + unfold or
            // TRANSIT_NONE + unfold transitions
            mTransition = transition;

            ProtoLog.v(WM_SHELL_TRANSITIONS, "UnfoldTransitionHandler: "
                    + "take over startAnimation");
        }

        if (transition != mTransition) return false;
        if (transition != mTransition) return false;


        for (int i = 0; i < mAnimators.size(); i++) {
        for (int i = 0; i < mAnimators.size(); i++) {
@@ -203,6 +214,33 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
                && request.getDisplayChange().isPhysicalDisplayChanged());
                && request.getDisplayChange().isPhysicalDisplayChanged());
    }
    }


    /** Whether `transitionInfo` contains an unfold action. */
    public boolean hasUnfold(@NonNull TransitionInfo transitionInfo) {
        // Unfold animation won't play when animations are disabled
        if (!ValueAnimator.areAnimatorsEnabled()) return false;

        for (int i = 0; i < transitionInfo.getChanges().size(); i++) {
            final TransitionInfo.Change change = transitionInfo.getChanges().get(i);
            if ((change.getFlags() & TransitionInfo.FLAG_IS_DISPLAY) != 0) {
                if (change.getEndAbsBounds() == null || change.getStartAbsBounds() == null) {
                    continue;
                }

                // Handle only unfolding, currently we don't have an animation when folding
                final int afterArea =
                        change.getEndAbsBounds().width() * change.getEndAbsBounds().height();
                final int beforeArea = change.getStartAbsBounds().width()
                        * change.getStartAbsBounds().height();

                if (afterArea > beforeArea) {
                    return true;
                }
            }
        }

        return false;
    }

    @Nullable
    @Nullable
    @Override
    @Override
    public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
    public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+95 −0
Original line number Original line Diff line number Diff line
@@ -17,9 +17,12 @@
package com.android.wm.shell.unfold;
package com.android.wm.shell.unfold;


import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_NONE;


import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertThat;


import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mock;
@@ -27,6 +30,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verify;


import android.app.ActivityManager;
import android.app.ActivityManager;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Binder;
import android.os.IBinder;
import android.os.IBinder;
import android.view.Display;
import android.view.Display;
@@ -35,6 +39,9 @@ import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
import android.window.WindowContainerTransaction;


import com.android.window.flags.FakeFeatureFlagsImpl;
import com.android.window.flags.FeatureFlags;
import com.android.window.flags.Flags;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.sysui.ShellInit;
@@ -45,6 +52,8 @@ import com.android.wm.shell.unfold.animation.SplitTaskUnfoldAnimator;


import org.junit.Before;
import org.junit.Before;
import org.junit.Test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;


import java.util.ArrayList;
import java.util.ArrayList;
import java.util.List;
import java.util.List;
@@ -131,6 +140,55 @@ public class UnfoldTransitionHandlerTest {
        verify(finishCallback, never()).onTransitionFinished(any());
        verify(finishCallback, never()).onTransitionFinished(any());
    }
    }


    @Test
    public void startAnimation_sameTransitionAsHandleRequest_startsAnimation() {
        TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo();
        mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo);
        TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);

        boolean animationStarted = mUnfoldTransitionHandler.startAnimation(
                mTransition,
                mock(TransitionInfo.class),
                mock(SurfaceControl.Transaction.class),
                mock(SurfaceControl.Transaction.class),
                finishCallback
        );

        assertThat(animationStarted).isTrue();
    }

    @Test
    public void startAnimation_differentTransitionFromRequestWithUnfold_startsAnimation() {
        mUnfoldTransitionHandler.handleRequest(new Binder(), createNoneTransitionInfo());
        TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);

        boolean animationStarted = mUnfoldTransitionHandler.startAnimation(
                mTransition,
                createUnfoldTransitionInfo(),
                mock(SurfaceControl.Transaction.class),
                mock(SurfaceControl.Transaction.class),
                finishCallback
        );

        assertThat(animationStarted).isTrue();
    }

    @Test
    public void startAnimation_differentTransitionFromRequestWithoutUnfold_doesNotStart() {
        mUnfoldTransitionHandler.handleRequest(new Binder(), createNoneTransitionInfo());
        TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);

        boolean animationStarted = mUnfoldTransitionHandler.startAnimation(
                mTransition,
                createNonUnfoldTransitionInfo(),
                mock(SurfaceControl.Transaction.class),
                mock(SurfaceControl.Transaction.class),
                finishCallback
        );

        assertThat(animationStarted).isFalse();
    }

    @Test
    @Test
    public void startAnimation_animationFinishes_finishesTheTransition() {
    public void startAnimation_animationFinishes_finishesTheTransition() {
        TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo();
        TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo();
@@ -215,6 +273,12 @@ public class UnfoldTransitionHandlerTest {
                triggerTaskInfo, /* remoteTransition= */ null, displayChange, 0 /* flags */);
                triggerTaskInfo, /* remoteTransition= */ null, displayChange, 0 /* flags */);
    }
    }


    private TransitionRequestInfo createNoneTransitionInfo() {
        return new TransitionRequestInfo(TRANSIT_NONE,
                /* triggerTask= */ null, /* remoteTransition= */ null,
                /* displayChange= */ null,  /* flags= */ 0);
    }

    private static class TestShellUnfoldProgressProvider implements ShellUnfoldProgressProvider,
    private static class TestShellUnfoldProgressProvider implements ShellUnfoldProgressProvider,
            ShellUnfoldProgressProvider.UnfoldListener {
            ShellUnfoldProgressProvider.UnfoldListener {


@@ -277,4 +341,35 @@ public class UnfoldTransitionHandlerTest {
            return false;
            return false;
        }
        }
    }
    }

    static class TestCase {
        private final boolean mShouldHandleMixedUnfold;

        public TestCase(boolean shouldHandleMixedUnfold) {
            mShouldHandleMixedUnfold = shouldHandleMixedUnfold;
        }

        public boolean mixedUnfoldFlagEnabled() {
            return mShouldHandleMixedUnfold;
        }

        @Override
        public String toString() {
            return "shouldHandleMixedUnfold flag = " + mShouldHandleMixedUnfold;
        }
    }

    private TransitionInfo createUnfoldTransitionInfo() {
        TransitionInfo transitionInfo = new TransitionInfo(TRANSIT_CHANGE, /* flags= */ 0);
        TransitionInfo.Change change = new TransitionInfo.Change(null, mock(SurfaceControl.class));
        change.setStartAbsBounds(new Rect(0, 0, 10, 10));
        change.setEndAbsBounds(new Rect(0, 0, 100, 100));
        change.setFlags(TransitionInfo.FLAG_IS_DISPLAY);
        transitionInfo.addChange(change);
        return transitionInfo;
    }

    private TransitionInfo createNonUnfoldTransitionInfo() {
        return new TransitionInfo(TRANSIT_CHANGE, /* flags= */ 0);
    }
}
}
 No newline at end of file
+22 −7
Original line number Original line Diff line number Diff line
@@ -34,6 +34,8 @@ import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
import android.window.WindowContainerTransaction;


import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.wm.DeviceStateController.DeviceState;
import com.android.server.wm.DeviceStateController.DeviceState;


public class PhysicalDisplaySwitchTransitionLauncher {
public class PhysicalDisplaySwitchTransitionLauncher {
@@ -117,14 +119,27 @@ public class PhysicalDisplaySwitchTransitionLauncher {
        displayChange.setEndAbsBounds(endAbsBounds);
        displayChange.setEndAbsBounds(endAbsBounds);
        displayChange.setPhysicalDisplayChanged(true);
        displayChange.setPhysicalDisplayChanged(true);


        final Transition t = mTransitionController.requestTransitionIfNeeded(TRANSIT_CHANGE,
        mTransition = null;

        if (mTransitionController.isCollecting()) {
            // Add display container to the currently collecting transition
            mTransitionController.collect(mDisplayContent);
            mTransition = mTransitionController.getCollectingTransition();

            // Make sure that transition is not ready until we finish the remote display change
            mTransition.setReady(mDisplayContent, false);

            ProtoLog.d(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
                    "Adding display switch to existing collecting transition");
        } else {
            mTransition = mTransitionController.requestTransitionIfNeeded(TRANSIT_CHANGE,
                    0 /* flags */,
                    0 /* flags */,
                    mDisplayContent, mDisplayContent, null /* remoteTransition */,
                    mDisplayContent, mDisplayContent, null /* remoteTransition */,
                    displayChange);
                    displayChange);
        }


        if (t != null) {
        if (mTransition != null) {
            mDisplayContent.mAtmService.startPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY);
            mAtmService.startPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY);
            mTransition = t;
        }
        }


        mShouldRequestTransitionOnDisplaySwitch = false;
        mShouldRequestTransitionOnDisplaySwitch = false;
+27 −2
Original line number Original line Diff line number Diff line
@@ -34,6 +34,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.when;


import android.animation.ValueAnimator;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.content.Context;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.Resources;
import android.graphics.Rect;
import android.graphics.Rect;
@@ -61,8 +62,7 @@ import org.mockito.MockitoAnnotations;
 */
 */
@SmallTest
@SmallTest
@Presubmit
@Presubmit
@RunWith(WindowTestRunner.class)
public class PhysicalDisplaySwitchTransitionLauncherTest {
public class PhysicalDisplaySwitchTransitionLauncherTest extends WindowTestsBase {


    @Mock
    @Mock
    DisplayContent mDisplayContent;
    DisplayContent mDisplayContent;
@@ -73,6 +73,8 @@ public class PhysicalDisplaySwitchTransitionLauncherTest extends WindowTestsBase
    @Mock
    @Mock
    ActivityTaskManagerService mActivityTaskManagerService;
    ActivityTaskManagerService mActivityTaskManagerService;
    @Mock
    @Mock
    BLASTSyncEngine mSyncEngine;
    @Mock
    TransitionController mTransitionController;
    TransitionController mTransitionController;


    private PhysicalDisplaySwitchTransitionLauncher mTarget;
    private PhysicalDisplaySwitchTransitionLauncher mTarget;
@@ -216,6 +218,20 @@ public class PhysicalDisplaySwitchTransitionLauncherTest extends WindowTestsBase
        assertTransitionNotRequested();
        assertTransitionNotRequested();
    }
    }


    @Test
    public void testDisplaySwitchAfterUnfolding_otherCollectingTransition_collectsDisplaySwitch() {
        givenCollectingTransition(createTransition(TRANSIT_CHANGE));
        givenAllAnimationsEnabled();
        mTarget.foldStateChanged(FOLDED);

        mTarget.foldStateChanged(OPEN);
        requestDisplaySwitch();

        // Collects to the current transition
        verify(mTransitionController).collect(mDisplayContent);
    }


    @Test
    @Test
    public void testDisplaySwitch_whenNoContentInDisplayContent_noTransition() {
    public void testDisplaySwitch_whenNoContentInDisplayContent_noTransition() {
        givenAllAnimationsEnabled();
        givenAllAnimationsEnabled();
@@ -267,6 +283,15 @@ public class PhysicalDisplaySwitchTransitionLauncherTest extends WindowTestsBase
        when(mTransitionController.isShellTransitionsEnabled()).thenReturn(enabled);
        when(mTransitionController.isShellTransitionsEnabled()).thenReturn(enabled);
    }
    }


    private void givenCollectingTransition(@Nullable Transition transition) {
        when(mTransitionController.isCollecting()).thenReturn(transition != null);
        when(mTransitionController.getCollectingTransition()).thenReturn(transition);
    }

    private Transition createTransition(int type) {
        return new Transition(type, /* flags= */ 0, mTransitionController, mSyncEngine);
    }

    private void givenDisplayContentHasContent(boolean hasContent) {
    private void givenDisplayContentHasContent(boolean hasContent) {
        when(mDisplayContent.getLastHasContent()).thenReturn(hasContent);
        when(mDisplayContent.getLastHasContent()).thenReturn(hasContent);
    }
    }