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

Commit 94cdc7ba authored by Garfield Tan's avatar Garfield Tan Committed by Android (Google) Code Review
Browse files

Merge "Split window decor lifecycle to a TransitionObserver" into tm-qpr-dev

parents 8dfef1a9 5959b4cd
Loading
Loading
Loading
Loading
+16 −10
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.freeform.FreeformComponents;
import com.android.wm.shell.freeform.FreeformTaskListener;
import com.android.wm.shell.freeform.FreeformTaskTransitionHandler;
import com.android.wm.shell.freeform.FreeformTaskTransitionObserver;
import com.android.wm.shell.fullscreen.FullscreenTaskListener;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.Pip;
@@ -207,8 +208,10 @@ public abstract class WMShellModule {
    @DynamicOverride
    static FreeformComponents provideFreeformComponents(
            FreeformTaskListener<?> taskListener,
            FreeformTaskTransitionHandler transitionHandler) {
        return new FreeformComponents(taskListener, Optional.of(transitionHandler));
            FreeformTaskTransitionHandler transitionHandler,
            FreeformTaskTransitionObserver transitionObserver) {
        return new FreeformComponents(
                taskListener, Optional.of(transitionHandler), Optional.of(transitionObserver));
    }

    @WMSingleton
@@ -230,19 +233,22 @@ public abstract class WMShellModule {
    @WMSingleton
    @Provides
    static FreeformTaskTransitionHandler provideFreeformTaskTransitionHandler(
            ShellInit shellInit,
            Transitions transitions,
            WindowDecorViewModel<?> windowDecorViewModel) {
        return new FreeformTaskTransitionHandler(shellInit, transitions, windowDecorViewModel);
    }

    @WMSingleton
    @Provides
    static FreeformTaskTransitionObserver provideFreeformTaskTransitionObserver(
            Context context,
            ShellInit shellInit,
            Transitions transitions,
            WindowDecorViewModel<?> windowDecorViewModel,
            FullscreenTaskListener<?> fullscreenTaskListener,
            FreeformTaskListener<?> freeformTaskListener) {
        // TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
        //                    override for this controller from the base module
        ShellInit init = FreeformComponents.isFreeformEnabled(context)
                ? shellInit
                : null;
        return new FreeformTaskTransitionHandler(init, transitions,
                windowDecorViewModel, fullscreenTaskListener, freeformTaskListener);
        return new FreeformTaskTransitionObserver(
                context, shellInit, transitions, fullscreenTaskListener, freeformTaskListener);
    }

    //
+6 −3
Original line number Diff line number Diff line
@@ -33,16 +33,19 @@ import java.util.Optional;
 */
public class FreeformComponents {
    public final ShellTaskOrganizer.TaskListener mTaskListener;
    public final Optional<Transitions.TransitionHandler> mTaskTransitionHandler;
    public final Optional<Transitions.TransitionHandler> mTransitionHandler;
    public final Optional<Transitions.TransitionObserver> mTransitionObserver;

    /**
     * Creates an instance with the given components.
     */
    public FreeformComponents(
            ShellTaskOrganizer.TaskListener taskListener,
            Optional<Transitions.TransitionHandler> taskTransitionHandler) {
            Optional<Transitions.TransitionHandler> transitionHandler,
            Optional<Transitions.TransitionObserver> transitionObserver) {
        mTaskListener = taskListener;
        mTaskTransitionHandler = taskTransitionHandler;
        mTransitionHandler = transitionHandler;
        mTransitionObserver = transitionObserver;
    }

    /**
+13 −129
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
import android.os.IBinder;
import android.util.Log;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.TransitionInfo;
@@ -32,7 +31,6 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.wm.shell.fullscreen.FullscreenTaskListener;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
@@ -41,17 +39,13 @@ import java.util.ArrayList;
import java.util.List;

/**
 * The {@link Transitions.TransitionHandler} that handles freeform task launches, closes,
 * maximizing and restoring transitions. It also reports transitions so that window decorations can
 * be a part of transitions.
 * The {@link Transitions.TransitionHandler} that handles freeform task maximizing and restoring
 * transitions.
 */
public class FreeformTaskTransitionHandler
        implements Transitions.TransitionHandler, FreeformTaskTransitionStarter {
    private static final String TAG = "FreeformTH";

    private final Transitions mTransitions;
    private final FreeformTaskListener<?> mFreeformTaskListener;
    private final FullscreenTaskListener<?> mFullscreenTaskListener;
    private final WindowDecorViewModel<?> mWindowDecorViewModel;

    private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();
@@ -59,21 +53,16 @@ public class FreeformTaskTransitionHandler
    public FreeformTaskTransitionHandler(
            ShellInit shellInit,
            Transitions transitions,
            WindowDecorViewModel<?> windowDecorViewModel,
            FullscreenTaskListener<?> fullscreenTaskListener,
            FreeformTaskListener<?> freeformTaskListener) {
            WindowDecorViewModel<?> windowDecorViewModel) {
        mTransitions = transitions;
        mFullscreenTaskListener = fullscreenTaskListener;
        mFreeformTaskListener = freeformTaskListener;
        mWindowDecorViewModel = windowDecorViewModel;
        if (shellInit != null && Transitions.ENABLE_SHELL_TRANSITIONS) {
        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
            shellInit.addInitCallback(this::onInit, this);
        }
    }

    private void onInit() {
        mWindowDecorViewModel.setFreeformTaskTransitionStarter(this);
        mTransitions.addHandler(this);
    }

    @Override
@@ -101,13 +90,13 @@ public class FreeformTaskTransitionHandler
        mPendingTransitionTokens.add(mTransitions.startTransition(type, wct, this));
    }


    @Override
    public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction startT,
            @NonNull SurfaceControl.Transaction finishT,
            @NonNull Transitions.TransitionFinishCallback finishCallback) {
        boolean transitionHandled = false;
        final ArrayList<AutoCloseable> windowDecorsInCloseTransitions = new ArrayList<>();
        for (TransitionInfo.Change change : info.getChanges()) {
            if ((change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0) {
                continue;
@@ -119,22 +108,13 @@ public class FreeformTaskTransitionHandler
            }

            switch (change.getMode()) {
                case WindowManager.TRANSIT_OPEN:
                    transitionHandled |= startOpenTransition(change, startT, finishT);
                    break;
                case WindowManager.TRANSIT_CLOSE:
                    transitionHandled |= startCloseTransition(
                            change, windowDecorsInCloseTransitions, startT, finishT);
                    break;
                case WindowManager.TRANSIT_CHANGE:
                    transitionHandled |= startChangeTransition(
                            transition, info.getType(), change, startT, finishT);
                            transition, info.getType(), change);
                    break;
                case WindowManager.TRANSIT_TO_BACK:
                    transitionHandled |= startMinimizeTransition(transition);
                    break;
                case WindowManager.TRANSIT_TO_FRONT:
                    break;
            }
        }

@@ -146,139 +126,43 @@ public class FreeformTaskTransitionHandler

        startT.apply();
        mTransitions.getMainExecutor().execute(
                () -> finishTransition(windowDecorsInCloseTransitions, finishCallback));
        return true;
    }

    private boolean startOpenTransition(
            TransitionInfo.Change change,
            SurfaceControl.Transaction startT,
            SurfaceControl.Transaction finishT) {
        switch (change.getTaskInfo().getWindowingMode()){
            case WINDOWING_MODE_FREEFORM:
                mFreeformTaskListener.createWindowDecoration(change, startT, finishT);
                break;
            case WINDOWING_MODE_FULLSCREEN:
                mFullscreenTaskListener.createWindowDecoration(change, startT, finishT);
                break;
            default:
                return false;
        }

        // Intercepted transition to manage the window decorations. Let other handlers animate.
        return false;
    }

    private boolean startCloseTransition(
            TransitionInfo.Change change,
            ArrayList<AutoCloseable> windowDecors,
            SurfaceControl.Transaction startT,
            SurfaceControl.Transaction finishT) {
        final AutoCloseable windowDecor;
        switch (change.getTaskInfo().getWindowingMode()) {
            case WINDOWING_MODE_FREEFORM:
                windowDecor = mFreeformTaskListener.giveWindowDecoration(change.getTaskInfo(),
                        startT, finishT);
                break;
            case WINDOWING_MODE_FULLSCREEN:
                windowDecor = mFullscreenTaskListener.giveWindowDecoration(change.getTaskInfo(),
                        startT, finishT);
                break;
            default:
                windowDecor = null;
        }
        if (windowDecor != null) {
            windowDecors.add(windowDecor);
        }
        // Intercepted transition to manage the window decorations. Let other handlers animate.
        return false;
    }

    private boolean startMinimizeTransition(IBinder transition) {
        if (!mPendingTransitionTokens.contains(transition)) {
            return false;
        }
                () -> finishCallback.onTransitionFinished(null, null));
        return true;
    }

    private boolean startChangeTransition(
            IBinder transition,
            int type,
            TransitionInfo.Change change,
            SurfaceControl.Transaction startT,
            SurfaceControl.Transaction finishT) {
        AutoCloseable windowDecor = null;

            TransitionInfo.Change change) {
        if (!mPendingTransitionTokens.contains(transition)) {
            return false;
        }

        boolean handled = false;
        boolean adopted = false;
        final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
        if (type == Transitions.TRANSIT_MAXIMIZE
                && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
            // TODO: Add maximize animations
            handled = true;
            windowDecor = mFreeformTaskListener.giveWindowDecoration(
                    change.getTaskInfo(), startT, finishT);
            adopted = mFullscreenTaskListener.adoptWindowDecoration(change,
                    startT, finishT, windowDecor);
        }

        if (type == Transitions.TRANSIT_RESTORE_FROM_MAXIMIZE
                && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
            // TODO: Add restore animations
            handled = true;
            windowDecor = mFullscreenTaskListener.giveWindowDecoration(
                    change.getTaskInfo(), startT, finishT);
            adopted = mFreeformTaskListener.adoptWindowDecoration(change,
                    startT, finishT, windowDecor);
        }

        if (!adopted) {
            releaseWindowDecor(windowDecor);
        }
        return handled;
    }

    private void finishTransition(
            ArrayList<AutoCloseable> windowDecorsInCloseTransitions,
            Transitions.TransitionFinishCallback finishCallback) {
        for (AutoCloseable windowDecor : windowDecorsInCloseTransitions) {
            releaseWindowDecor(windowDecor);
        }
        mFreeformTaskListener.onTaskTransitionFinished();
        mFullscreenTaskListener.onTaskTransitionFinished();
        finishCallback.onTransitionFinished(null, null);
    }

    private void releaseWindowDecor(AutoCloseable windowDecor) {
        if (windowDecor == null) {
            return;
        }
        try {
            windowDecor.close();
        } catch (Exception e) {
            Log.e(TAG, "Failed to release window decoration.", e);
        }
    private boolean startMinimizeTransition(IBinder transition) {
        return mPendingTransitionTokens.contains(transition);
    }

    @Nullable
    @Override
    public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
            @NonNull TransitionRequestInfo request) {
        final ActivityManager.RunningTaskInfo taskInfo = request.getTriggerTask();
        if (taskInfo == null || taskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
        return null;
    }
        switch (request.getType()) {
            case WindowManager.TRANSIT_OPEN:
            case WindowManager.TRANSIT_CLOSE:
            case WindowManager.TRANSIT_TO_FRONT:
            case WindowManager.TRANSIT_TO_BACK:
                return new WindowContainerTransaction();
            default:
                return null;
        }
    }

}
+222 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.freeform;

import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;

import android.app.ActivityManager;
import android.content.Context;
import android.os.IBinder;
import android.util.Log;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.TransitionInfo;

import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;

import com.android.wm.shell.fullscreen.FullscreenTaskListener;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * The {@link Transitions.TransitionHandler} that handles freeform task launches, closes,
 * maximizing and restoring transitions. It also reports transitions so that window decorations can
 * be a part of transitions.
 */
public class FreeformTaskTransitionObserver implements Transitions.TransitionObserver {
    private static final String TAG = "FreeformTO";

    private final Transitions mTransitions;
    private final FreeformTaskListener<?> mFreeformTaskListener;
    private final FullscreenTaskListener<?> mFullscreenTaskListener;

    private final Map<IBinder, List<AutoCloseable>> mTransitionToWindowDecors = new HashMap<>();

    public FreeformTaskTransitionObserver(
            Context context,
            ShellInit shellInit,
            Transitions transitions,
            FullscreenTaskListener<?> fullscreenTaskListener,
            FreeformTaskListener<?> freeformTaskListener) {
        mTransitions = transitions;
        mFreeformTaskListener = freeformTaskListener;
        mFullscreenTaskListener = fullscreenTaskListener;
        if (Transitions.ENABLE_SHELL_TRANSITIONS && FreeformComponents.isFreeformEnabled(context)) {
            shellInit.addInitCallback(this::onInit, this);
        }
    }

    @VisibleForTesting
    void onInit() {
        mTransitions.registerObserver(this);
    }

    @Override
    public void onTransitionReady(
            @NonNull IBinder transition,
            @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction startT,
            @NonNull SurfaceControl.Transaction finishT) {
        final ArrayList<AutoCloseable> windowDecors = new ArrayList<>();
        for (TransitionInfo.Change change : info.getChanges()) {
            if ((change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0) {
                continue;
            }

            final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
            if (taskInfo == null || taskInfo.taskId == -1) {
                continue;
            }

            switch (change.getMode()) {
                case WindowManager.TRANSIT_OPEN:
                    onOpenTransitionReady(change, startT, finishT);
                    break;
                case WindowManager.TRANSIT_CLOSE: {
                    onCloseTransitionReady(change, windowDecors, startT, finishT);
                    break;
                }
                case WindowManager.TRANSIT_CHANGE:
                    onChangeTransitionReady(info.getType(), change, startT, finishT);
                    break;
            }
        }
        if (!windowDecors.isEmpty()) {
            mTransitionToWindowDecors.put(transition, windowDecors);
        }
    }

    private void onOpenTransitionReady(
            TransitionInfo.Change change,
            SurfaceControl.Transaction startT,
            SurfaceControl.Transaction finishT) {
        switch (change.getTaskInfo().getWindowingMode()){
            case WINDOWING_MODE_FREEFORM:
                mFreeformTaskListener.createWindowDecoration(change, startT, finishT);
                break;
            case WINDOWING_MODE_FULLSCREEN:
                mFullscreenTaskListener.createWindowDecoration(change, startT, finishT);
                break;
        }
    }

    private void onCloseTransitionReady(
            TransitionInfo.Change change,
            ArrayList<AutoCloseable> windowDecors,
            SurfaceControl.Transaction startT,
            SurfaceControl.Transaction finishT) {
        final AutoCloseable windowDecor;
        switch (change.getTaskInfo().getWindowingMode()) {
            case WINDOWING_MODE_FREEFORM:
                windowDecor = mFreeformTaskListener.giveWindowDecoration(change.getTaskInfo(),
                        startT, finishT);
                break;
            case WINDOWING_MODE_FULLSCREEN:
                windowDecor = mFullscreenTaskListener.giveWindowDecoration(change.getTaskInfo(),
                        startT, finishT);
                break;
            default:
                windowDecor = null;
        }
        if (windowDecor != null) {
            windowDecors.add(windowDecor);
        }
    }

    private void onChangeTransitionReady(
            int type,
            TransitionInfo.Change change,
            SurfaceControl.Transaction startT,
            SurfaceControl.Transaction finishT) {
        AutoCloseable windowDecor = null;

        boolean adopted = false;
        final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
        if (type == Transitions.TRANSIT_MAXIMIZE
                && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
            windowDecor = mFreeformTaskListener.giveWindowDecoration(
                    change.getTaskInfo(), startT, finishT);
            adopted = mFullscreenTaskListener.adoptWindowDecoration(
                    change, startT, finishT, windowDecor);
        }

        if (type == Transitions.TRANSIT_RESTORE_FROM_MAXIMIZE
                && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
            windowDecor = mFullscreenTaskListener.giveWindowDecoration(
                    change.getTaskInfo(), startT, finishT);
            adopted = mFreeformTaskListener.adoptWindowDecoration(
                    change, startT, finishT, windowDecor);
        }

        if (!adopted) {
            releaseWindowDecor(windowDecor);
        }
    }

    @Override
    public void onTransitionStarting(@NonNull IBinder transition) {}

    @Override
    public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) {
        final List<AutoCloseable> windowDecorsOfMerged = mTransitionToWindowDecors.get(merged);
        if (windowDecorsOfMerged == null) {
            // We are adding window decorations of the merged transition to them of the playing
            // transition so if there is none of them there is nothing to do.
            return;
        }
        mTransitionToWindowDecors.remove(merged);

        final List<AutoCloseable> windowDecorsOfPlaying = mTransitionToWindowDecors.get(playing);
        if (windowDecorsOfPlaying != null) {
            windowDecorsOfPlaying.addAll(windowDecorsOfMerged);
        } else {
            mTransitionToWindowDecors.put(playing, windowDecorsOfMerged);
        }
    }

    @Override
    public void onTransitionFinished(@NonNull IBinder transition, boolean aborted) {
        final List<AutoCloseable> windowDecors = mTransitionToWindowDecors.getOrDefault(
                transition, Collections.emptyList());
        mTransitionToWindowDecors.remove(transition);

        for (AutoCloseable windowDecor : windowDecors) {
            releaseWindowDecor(windowDecor);
        }
        mFullscreenTaskListener.onTaskTransitionFinished();
        mFreeformTaskListener.onTaskTransitionFinished();
    }

    private static void releaseWindowDecor(AutoCloseable windowDecor) {
        if (windowDecor == null) {
            return;
        }
        try {
            windowDecor.close();
        } catch (Exception e) {
            Log.e(TAG, "Failed to release window decoration.", e);
        }
    }
}
+302 −0

File added.

Preview size limit exceeded, changes collapsed.