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

Commit aa5f0073 authored by mattsziklay's avatar mattsziklay
Browse files

Handle close transitions for freeform tasks.

Dedicates a handler to closing freeform tasks. This prevents unintended
effects of the default handler's animation, such as unrelated tasks
flashing in desktop mode.

Bug: 280829190
Test: Manual
Change-Id: Ibfff5e0be3dd35910ed3adc49d540bc06cffffa4
parent 05ca0c87
Loading
Loading
Loading
Loading
+8 −2
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TabletopModeController;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ShellAnimationThread;
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.desktopmode.DesktopModeController;
@@ -264,8 +265,13 @@ public abstract class WMShellModule {
    static FreeformTaskTransitionHandler provideFreeformTaskTransitionHandler(
            ShellInit shellInit,
            Transitions transitions,
            WindowDecorViewModel windowDecorViewModel) {
        return new FreeformTaskTransitionHandler(shellInit, transitions, windowDecorViewModel);
            Context context,
            WindowDecorViewModel windowDecorViewModel,
            DisplayController displayController,
            @ShellMainThread ShellExecutor mainExecutor,
            @ShellAnimationThread ShellExecutor animExecutor) {
        return new FreeformTaskTransitionHandler(shellInit, transitions, context,
                windowDecorViewModel, displayController, mainExecutor, animExecutor);
    }

    @WMSingleton
+94 −10
Original line number Diff line number Diff line
@@ -19,9 +19,15 @@ package com.android.wm.shell.freeform;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
import android.content.Context;
import android.graphics.Rect;
import android.os.IBinder;
import android.util.ArrayMap;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.TransitionInfo;
@@ -31,6 +37,8 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
@@ -39,23 +47,37 @@ import java.util.ArrayList;
import java.util.List;

/**
 * The {@link Transitions.TransitionHandler} that handles freeform task maximizing and restoring
 * transitions.
 * The {@link Transitions.TransitionHandler} that handles freeform task maximizing, closing, and
 * restoring transitions.
 */
public class FreeformTaskTransitionHandler
        implements Transitions.TransitionHandler, FreeformTaskTransitionStarter {

    private static final int CLOSE_ANIM_DURATION = 400;
    private final Context mContext;
    private final Transitions mTransitions;
    private final WindowDecorViewModel mWindowDecorViewModel;
    private final DisplayController mDisplayController;
    private final ShellExecutor mMainExecutor;
    private final ShellExecutor mAnimExecutor;

    private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();

    private final ArrayMap<IBinder, ArrayList<Animator>> mAnimations = new ArrayMap<>();

    public FreeformTaskTransitionHandler(
            ShellInit shellInit,
            Transitions transitions,
            WindowDecorViewModel windowDecorViewModel) {
            Context context,
            WindowDecorViewModel windowDecorViewModel,
            DisplayController displayController,
            ShellExecutor mainExecutor,
            ShellExecutor animExecutor) {
        mTransitions = transitions;
        mContext = context;
        mWindowDecorViewModel = windowDecorViewModel;
        mDisplayController = displayController;
        mMainExecutor = mainExecutor;
        mAnimExecutor = animExecutor;
        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
            shellInit.addInitCallback(this::onInit, this);
        }
@@ -103,6 +125,14 @@ public class FreeformTaskTransitionHandler
            @NonNull SurfaceControl.Transaction finishT,
            @NonNull Transitions.TransitionFinishCallback finishCallback) {
        boolean transitionHandled = false;
        final ArrayList<Animator> animations = new ArrayList<>();
        final Runnable onAnimFinish = () -> {
            if (!animations.isEmpty()) return;
            mMainExecutor.execute(() -> {
                mAnimations.remove(transition);
                finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
            });
        };
        for (TransitionInfo.Change change : info.getChanges()) {
            if ((change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0) {
                continue;
@@ -121,21 +151,45 @@ public class FreeformTaskTransitionHandler
                case WindowManager.TRANSIT_TO_BACK:
                    transitionHandled |= startMinimizeTransition(transition);
                    break;
                case WindowManager.TRANSIT_CLOSE:
                    if (change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_FREEFORM) {
                        transitionHandled |= startCloseTransition(transition, change,
                                finishT, animations, onAnimFinish);
                    }
                    break;
            }
        }

        mPendingTransitionTokens.remove(transition);

        if (!transitionHandled) {
            return false;
        }

        mAnimations.put(transition, animations);
        // startT must be applied before animations start.
        startT.apply();
        mTransitions.getMainExecutor().execute(
                () -> finishCallback.onTransitionFinished(null, null));
        mAnimExecutor.execute(() -> {
            for (Animator anim : animations) {
                anim.start();
            }
        });
        // Run this here in case no animators are created.
        onAnimFinish.run();
        mPendingTransitionTokens.remove(transition);
        return true;
    }

    @Override
    public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
            @NonNull Transitions.TransitionFinishCallback finishCallback) {
        ArrayList<Animator> animations = mAnimations.get(mergeTarget);
        if (animations == null) return;
        mAnimExecutor.execute(() -> {
            for (Animator anim : animations) {
                anim.end();
            }
        });

    }

    private boolean startChangeTransition(
            IBinder transition,
            int type,
@@ -165,6 +219,36 @@ public class FreeformTaskTransitionHandler
        return mPendingTransitionTokens.contains(transition);
    }

    private boolean startCloseTransition(IBinder transition, TransitionInfo.Change change,
            SurfaceControl.Transaction finishT, ArrayList<Animator> animations,
            Runnable onAnimFinish) {
        if (!mPendingTransitionTokens.contains(transition)) return false;
        int screenHeight = mDisplayController
                .getDisplayLayout(change.getTaskInfo().displayId).height();
        ValueAnimator animator = new ValueAnimator();
        animator.setDuration(CLOSE_ANIM_DURATION)
                .setFloatValues(0f, 1f);
        SurfaceControl.Transaction t = new SurfaceControl.Transaction();
        SurfaceControl sc = change.getLeash();
        finishT.hide(sc);
        Rect startBounds = new Rect(change.getTaskInfo().configuration.windowConfiguration
                .getBounds());
        animator.addUpdateListener(animation -> {
            t.setPosition(sc, startBounds.left,
                    startBounds.top + (animation.getAnimatedFraction() * screenHeight));
            t.apply();
        });
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                animations.remove(animator);
                onAnimFinish.run();
            }
        });
        animations.add(animator);
        return true;
    }

    @Nullable
    @Override
    public WindowContainerTransaction handleRequest(@NonNull IBinder transition,