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

Commit e8a1118a authored by An An Yu's avatar An An Yu Committed by Android (Google) Code Review
Browse files

Merge "Polish Enter pip transition from AE split." into main

parents b1ebd95c a9bdce0c
Loading
Loading
Loading
Loading
+32 −16
Original line number Diff line number Diff line
@@ -87,33 +87,28 @@ public class ActivityEmbeddingController implements Transitions.TransitionHandle
        mTransitions.addHandler(this);
    }

    @Override
    public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction,
            @NonNull Transitions.TransitionFinishCallback finishCallback) {
        boolean containsEmbeddingSplit = false;
        boolean containsNonEmbeddedChange = false;
        final List<TransitionInfo.Change> changes = info.getChanges();
        for (int i = changes.size() - 1; i >= 0; i--) {
            final TransitionInfo.Change change = changes.get(i);
            if (!change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
                containsNonEmbeddedChange = true;
            } else if (!change.hasFlags(FLAG_FILLS_TASK)) {
    /** Whether ActivityEmbeddingController should animate this transition. */
    public boolean shouldAnimate(@NonNull TransitionInfo info) {
        boolean containsEmbeddingChange = false;
        for (TransitionInfo.Change change : info.getChanges()) {
            if (!change.hasFlags(FLAG_FILLS_TASK) && change.hasFlags(
                    FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
                // Whether the Task contains any ActivityEmbedding split before or after the
                // transition.
                containsEmbeddingSplit = true;
                containsEmbeddingChange = true;
            }
        }
        if (!containsEmbeddingSplit) {
        if (!containsEmbeddingChange) {
            // Let the system to play the default animation if there is no ActivityEmbedding split
            // window. This allows to play the app customized animation when there is no embedding,
            // such as the device is in a folded state.
            return false;
        }
        if (containsNonEmbeddedChange && !handleNonEmbeddedChanges(changes)) {

        if (containsNonEmbeddedChange(info) && !handleNonEmbeddedChanges(info.getChanges())) {
            return false;
        }

        final TransitionInfo.AnimationOptions options = info.getAnimationOptions();
        if (options != null
                // Scene-transition will be handled by app side.
@@ -123,6 +118,17 @@ public class ActivityEmbeddingController implements Transitions.TransitionHandle
            return false;
        }

        return true;
    }

    @Override
    public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction,
            @NonNull Transitions.TransitionFinishCallback finishCallback) {

        if (!shouldAnimate(info)) return false;

        // Start ActivityEmbedding animation.
        mTransitionCallbacks.put(transition, finishCallback);
        mAnimationRunner.startAnimation(transition, info, startTransaction, finishTransaction);
@@ -136,6 +142,16 @@ public class ActivityEmbeddingController implements Transitions.TransitionHandle
        mAnimationRunner.cancelAnimationFromMerge();
    }

    /** Whether TransitionInfo contains non-ActivityEmbedding embedded window. */
    private boolean containsNonEmbeddedChange(@NonNull TransitionInfo info) {
        for (TransitionInfo.Change change : info.getChanges()) {
            if (!change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
                return true;
            }
        }
        return false;
    }

    private boolean handleNonEmbeddedChanges(List<TransitionInfo.Change> changes) {
        final Rect nonClosingEmbeddedArea = new Rect();
        for (int i = changes.size() - 1; i >= 0; i--) {
+3 −1
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.BubbleData;
import com.android.wm.shell.bubbles.BubbleDataRepository;
@@ -366,11 +367,12 @@ public abstract class WMShellModule {
            KeyguardTransitionHandler keyguardTransitionHandler,
            Optional<DesktopTasksController> desktopTasksController,
            Optional<UnfoldTransitionHandler> unfoldHandler,
            Optional<ActivityEmbeddingController> activityEmbeddingController,
            Transitions transitions) {
        return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional,
                pipTransitionController, recentsTransitionHandler,
                keyguardTransitionHandler, desktopTasksController,
                unfoldHandler);
                unfoldHandler, activityEmbeddingController);
    }

    @WMSingleton
+83 −2
Original line number Diff line number Diff line
@@ -21,7 +21,9 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_PIP;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;

import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
@@ -43,6 +45,7 @@ import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;

import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
import com.android.wm.shell.common.split.SplitScreenUtils;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopTasksController;
@@ -74,6 +77,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
    private final KeyguardTransitionHandler mKeyguardHandler;
    private DesktopTasksController mDesktopTasksController;
    private UnfoldTransitionHandler mUnfoldHandler;
    private ActivityEmbeddingController mActivityEmbeddingController;

    private static class MixedTransition {
        static final int TYPE_ENTER_PIP_FROM_SPLIT = 1;
@@ -93,9 +97,12 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
        /** Recents Transition while in desktop mode. */
        static final int TYPE_RECENTS_DURING_DESKTOP = 6;

        /** Fuld/Unfold transition. */
        /** Fold/Unfold transition. */
        static final int TYPE_UNFOLD = 7;

        /** Enter pip from one of the Activity Embedding windows. */
        static final int TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING = 8;

        /** The default animation for this mixed transition. */
        static final int ANIM_TYPE_DEFAULT = 0;

@@ -150,7 +157,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
            Optional<RecentsTransitionHandler> recentsHandlerOptional,
            KeyguardTransitionHandler keyguardHandler,
            Optional<DesktopTasksController> desktopTasksControllerOptional,
            Optional<UnfoldTransitionHandler> unfoldHandler) {
            Optional<UnfoldTransitionHandler> unfoldHandler,
            Optional<ActivityEmbeddingController> activityEmbeddingController) {
        mPlayer = player;
        mKeyguardHandler = keyguardHandler;
        if (Transitions.ENABLE_SHELL_TRANSITIONS
@@ -170,6 +178,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
                }
                mDesktopTasksController = desktopTasksControllerOptional.orElse(null);
                mUnfoldHandler = unfoldHandler.orElse(null);
                mActivityEmbeddingController = activityEmbeddingController.orElse(null);
            }, this);
        }
    }
@@ -192,6 +201,16 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
            mPipHandler.augmentRequest(transition, request, out);
            mSplitHandler.addEnterOrExitIfNeeded(request, out);
            return out;
        } else if (request.getType() == TRANSIT_PIP
                && (request.getFlags() & FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY) != 0 && (
                mActivityEmbeddingController != null)) {
            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
                    " Got a PiP-enter request from an Activity Embedding split");
            mActiveTransitions.add(new MixedTransition(
                    MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING, transition));
            // Postpone transition splitting to later.
            WindowContainerTransaction out = new WindowContainerTransaction();
            return out;
        } else if (request.getRemoteTransition() != null
                && TransitionUtil.isOpeningType(request.getType())
                && (request.getTriggerTask() == null
@@ -355,6 +374,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
        if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
            return animateEnterPipFromSplit(mixed, info, startTransaction, finishTransaction,
                    finishCallback);
        } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) {
            return animateEnterPipFromActivityEmbedding(mixed, info, startTransaction,
                    finishTransaction, finishCallback);
        } else if (mixed.mType == MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE) {
            return false;
        } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
@@ -400,6 +422,58 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
        }
    }

    private boolean animateEnterPipFromActivityEmbedding(@NonNull MixedTransition mixed,
            @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction,
            @NonNull Transitions.TransitionFinishCallback finishCallback) {
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for "
                + "entering PIP from an Activity Embedding window");
        // Split into two transitions (wct)
        TransitionInfo.Change pipChange = null;
        final TransitionInfo everythingElse = subCopy(info, TRANSIT_TO_BACK, true /* changes */);
        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
            TransitionInfo.Change change = info.getChanges().get(i);
            if (mPipHandler.isEnteringPip(change, info.getType())) {
                if (pipChange != null) {
                    throw new IllegalStateException("More than 1 pip-entering changes in one"
                            + " transition? " + info);
                }
                pipChange = change;
                // going backwards, so remove-by-index is fine.
                everythingElse.getChanges().remove(i);
            }
        }

        final Transitions.TransitionFinishCallback finishCB = (wct) -> {
            --mixed.mInFlightSubAnimations;
            mixed.joinFinishArgs(wct);
            if (mixed.mInFlightSubAnimations > 0) return;
            mActiveTransitions.remove(mixed);
            finishCallback.onTransitionFinished(mixed.mFinishWCT);
        };

        if (!mActivityEmbeddingController.shouldAnimate(everythingElse)) {
            // Fallback to dispatching to other handlers.
            return false;
        }

        // PIP window should always be on the highest Z order.
        if (pipChange != null) {
            mixed.mInFlightSubAnimations = 2;
            mPipHandler.startEnterAnimation(
                    pipChange, startTransaction.setLayer(pipChange.getLeash(), Integer.MAX_VALUE),
                    finishTransaction,
                    finishCB);
        } else {
            mixed.mInFlightSubAnimations = 1;
        }

        mActivityEmbeddingController.startAnimation(mixed.mTransition, everythingElse,
                startTransaction, finishTransaction, finishCB);
        return true;
    }

    private boolean animateOpenIntentWithRemoteAndPip(@NonNull MixedTransition mixed,
            @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction startTransaction,
@@ -811,6 +885,10 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
                } else {
                    mPipHandler.end();
                }
            } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) {
                mPipHandler.end();
                mActivityEmbeddingController.mergeAnimation(transition, info, t, mergeTarget,
                        finishCallback);
            } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
                mPipHandler.end();
                if (mixed.mLeftoversHandler != null) {
@@ -851,6 +929,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
        if (mixed == null) return;
        if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
            mPipHandler.onTransitionConsumed(transition, aborted, finishT);
        } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) {
            mPipHandler.onTransitionConsumed(transition, aborted, finishT);
            mActivityEmbeddingController.onTransitionConsumed(transition, aborted, finishT);
        } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
            mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
        } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
+6 −0
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@ import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_PIP;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DREAM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
@@ -3686,6 +3687,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
                        getTransitionController(), mWindowManager.mSyncEngine)
                : null;

        if (r.getTaskFragment() != null && r.getTaskFragment().isEmbeddedWithBoundsOverride()
                && transition != null) {
            transition.addFlag(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY);
        }

        final Runnable enterPipRunnable = () -> {
            synchronized (mGlobalLock) {
                if (r.getParent() == null) {
+13 −3
Original line number Diff line number Diff line
@@ -36,7 +36,8 @@ import org.junit.runners.Parameterized
/**
 * Test launching a secondary Activity into Picture-In-Picture mode.
 *
 * Setup: Start from a split A|B. Transition: B enters PIP, observe the window shrink to the bottom
 * Setup: Start from a split A|B.
 * Transition: B enters PIP, observe the window first goes fullscreen then shrink to the bottom
 * right corner on screen.
 *
 * To run this test: `atest FlickerTestsOther:SecondaryActivityEnterPipTest`
@@ -63,7 +64,16 @@ class SecondaryActivityEnterPipTest(flicker: LegacyFlickerTest) :
        }
    }

    /** Main and secondary activity start from a split each taking half of the screen. */
    /**
     * We expect the background layer to be visible during this transition.
     */
    @Presubmit
    @Test
    override fun backgroundLayerNeverVisible(): Unit {}

    /**
     * Main and secondary activity start from a split each taking half of the screen.
     */
    @Presubmit
    @Test
    fun layersStartFromEqualSplit() {
@@ -109,7 +119,7 @@ class SecondaryActivityEnterPipTest(flicker: LegacyFlickerTest) :
                .isVisible(TRANSITION_SNAPSHOT)
                .isInvisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
                .then()
                .isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
                .isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT, isOptional = true)
        }
        flicker.assertLayersEnd {
            visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)