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

Commit dd4fc8f5 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Add FreeformTaskTransitionHandler" into tm-qpr-dev am: 3f2c081a

parents 0eedf8b1 3f2c081a
Loading
Loading
Loading
Loading
+10 −10
Original line number Diff line number Diff line
@@ -29,8 +29,6 @@ import com.android.internal.logging.UiEventLogger;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.RootDisplayAreaOrganizer;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.TaskViewFactory;
import com.android.wm.shell.TaskViewFactoryController;
@@ -59,7 +57,7 @@ import com.android.wm.shell.compatui.CompatUIController;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
import com.android.wm.shell.displayareahelper.DisplayAreaHelperController;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.freeform.FreeformTaskListener;
import com.android.wm.shell.freeform.FreeformComponents;
import com.android.wm.shell.fullscreen.FullscreenTaskListener;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer;
@@ -78,7 +76,9 @@ import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.startingsurface.StartingWindowController;
import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
import com.android.wm.shell.startingsurface.phone.PhoneStartingWindowTypeAlgorithm;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.sysui.ShellInterface;
import com.android.wm.shell.transition.ShellTransitions;
import com.android.wm.shell.transition.Transitions;
@@ -339,15 +339,15 @@ public abstract class WMShellBaseModule {
    // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
    @BindsOptionalOf
    @DynamicOverride
    abstract FreeformTaskListener<?> optionalFreeformTaskListener();
    abstract FreeformComponents optionalFreeformComponents();

    @WMSingleton
    @Provides
    static Optional<FreeformTaskListener<?>> provideFreeformTaskListener(
            @DynamicOverride Optional<FreeformTaskListener<?>> freeformTaskListener,
    static Optional<FreeformComponents> provideFreeformTaskListener(
            @DynamicOverride Optional<FreeformComponents> freeformComponents,
            Context context) {
        if (FreeformTaskListener.isFreeformEnabled(context)) {
            return freeformTaskListener;
        if (FreeformComponents.isFreeformEnabled(context)) {
            return freeformComponents;
        }
        return Optional.empty();
    }
@@ -636,7 +636,7 @@ public abstract class WMShellBaseModule {
            FullscreenTaskListener fullscreenTaskListener,
            Optional<UnfoldAnimationController> unfoldAnimationController,
            Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
            Optional<FreeformTaskListener<?>> freeformTaskListener,
            Optional<FreeformComponents> freeformComponents,
            Optional<RecentTasksController> recentTasksOptional,
            Optional<ActivityEmbeddingController> activityEmbeddingOptional,
            Transitions transitions,
@@ -655,7 +655,7 @@ public abstract class WMShellBaseModule {
                fullscreenTaskListener,
                unfoldAnimationController,
                unfoldTransitionHandler,
                freeformTaskListener,
                freeformComponents,
                recentTasksOptional,
                activityEmbeddingOptional,
                transitions,
+21 −2
Original line number Diff line number Diff line
@@ -49,7 +49,9 @@ import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
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.onehanded.OneHandedController;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
@@ -194,10 +196,27 @@ public abstract class WMShellModule {
    @WMSingleton
    @Provides
    @DynamicOverride
    static FreeformComponents provideFreeformComponents(
            FreeformTaskListener<?> taskListener,
            FreeformTaskTransitionHandler transitionHandler) {
        return new FreeformComponents(taskListener, Optional.of(transitionHandler));
    }

    @WMSingleton
    @Provides
    static FreeformTaskListener<?> provideFreeformTaskListener(
            WindowDecorViewModel<?> windowDecorViewModel) {
        return new FreeformTaskListener<>(windowDecorViewModel);
    }

    @WMSingleton
    @Provides
    static FreeformTaskTransitionHandler provideTaskTransitionHandler(
            Transitions transitions,
            WindowDecorViewModel<?> windowDecorViewModel,
            SyncTransactionQueue syncQueue) {
        return new FreeformTaskListener<>(windowDecorViewModel, syncQueue);
            FreeformTaskListener<?> freeformTaskListener) {
        return new FreeformTaskTransitionHandler(transitions, windowDecorViewModel,
                freeformTaskListener);
    }

    //
+56 −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.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;

import android.content.Context;
import android.provider.Settings;

import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.transition.Transitions;

import java.util.Optional;

/**
 * Class that holds freeform related classes. It serves as the single injection point of
 * all freeform classes to avoid leaking implementation details to the base Dagger module.
 */
public class FreeformComponents {
    public final ShellTaskOrganizer.TaskListener mTaskListener;
    public final Optional<Transitions.TransitionHandler> mTaskTransitionHandler;

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

    /**
     * Returns if this device supports freeform.
     */
    public static boolean isFreeformEnabled(Context context) {
        return context.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT)
                || Settings.Global.getInt(context.getContentResolver(),
                DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
    }
}
+136 −42
Original line number Diff line number Diff line
@@ -16,20 +16,18 @@

package com.android.wm.shell.freeform;

import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;

import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.provider.Settings;
import android.util.Slog;
import android.util.Log;
import android.util.SparseArray;
import android.view.SurfaceControl;
import android.window.TransitionInfo;

import androidx.annotation.Nullable;

import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;

import java.io.PrintWriter;
@@ -45,9 +43,9 @@ public class FreeformTaskListener<T extends AutoCloseable>
    private static final String TAG = "FreeformTaskListener";

    private final WindowDecorViewModel<T> mWindowDecorationViewModel;
    private final SyncTransactionQueue mSyncQueue;

    private final SparseArray<State<T>> mTasks = new SparseArray<>();
    private final SparseArray<T> mWindowDecorOfVanishedTasks = new SparseArray<>();

    private static class State<T extends AutoCloseable> {
        RunningTaskInfo mTaskInfo;
@@ -55,58 +53,79 @@ public class FreeformTaskListener<T extends AutoCloseable>
        T mWindowDecoration;
    }

    public FreeformTaskListener(
            WindowDecorViewModel<T> windowDecorationViewModel,
            SyncTransactionQueue syncQueue) {
    public FreeformTaskListener(WindowDecorViewModel<T> windowDecorationViewModel) {
        mWindowDecorationViewModel = windowDecorationViewModel;
        mSyncQueue = syncQueue;
    }

    @Override
    public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
        if (mTasks.get(taskInfo.taskId) != null) {
            throw new RuntimeException("Task appeared more than once: #" + taskInfo.taskId);
        }
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Appeared: #%d",
                taskInfo.taskId);
        final State<T> state = new State<>();
        final State<T> state = createOrUpdateTaskState(taskInfo, leash);
        if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
            SurfaceControl.Transaction t = new SurfaceControl.Transaction();
            state.mWindowDecoration =
                    mWindowDecorationViewModel.createWindowDecoration(taskInfo, leash, t, t);
            t.apply();
        }
    }

    private State<T> createOrUpdateTaskState(RunningTaskInfo taskInfo, SurfaceControl leash) {
        State<T> state = mTasks.get(taskInfo.taskId);
        if (state != null) {
            updateTaskInfo(taskInfo);
            return state;
        }

        state = new State<>();
        state.mTaskInfo = taskInfo;
        state.mLeash = leash;
        state.mWindowDecoration =
                mWindowDecorationViewModel.createWindowDecoration(taskInfo, leash);
        mTasks.put(taskInfo.taskId, state);

        return state;
    }

    @Override
    public void onTaskVanished(RunningTaskInfo taskInfo) {
        State<T> state = mTasks.get(taskInfo.taskId);
        final State<T> state = mTasks.get(taskInfo.taskId);
        if (state == null) {
            Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId);
            // This is possible if the transition happens before this method.
            return;
        }
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Vanished: #%d",
                taskInfo.taskId);
        mTasks.remove(taskInfo.taskId);

        try {
            state.mWindowDecoration.close();
        } catch (Exception e) {
            Slog.e(TAG, "Failed to release window decoration.", e);
        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
            // Save window decorations of closing tasks so that we can hand them over to the
            // transition system if this method happens before the transition. In case where the
            // transition didn't happen, it'd be cleared when the next transition finished.
            if (state.mWindowDecoration != null) {
                mWindowDecorOfVanishedTasks.put(taskInfo.taskId, state.mWindowDecoration);
            }
            return;
        }
        releaseWindowDecor(state.mWindowDecoration);
    }

    @Override
    public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
        State<T> state = mTasks.get(taskInfo.taskId);
        if (state == null) {
            throw new RuntimeException(
                    "Task info changed before appearing: #" + taskInfo.taskId);
        }
        final State<T> state = updateTaskInfo(taskInfo);
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Info Changed: #%d",
                taskInfo.taskId);
        state.mTaskInfo = taskInfo;
        if (state.mWindowDecoration != null) {
            mWindowDecorationViewModel.onTaskInfoChanged(state.mTaskInfo, state.mWindowDecoration);
        }
    }

    private State<T> updateTaskInfo(RunningTaskInfo taskInfo) {
        final State<T> state = mTasks.get(taskInfo.taskId);
        if (state == null) {
            throw new RuntimeException("Task info changed before appearing: #" + taskInfo.taskId);
        }
        state.mTaskInfo = taskInfo;
        return state;
    }

    @Override
    public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
@@ -126,6 +145,93 @@ public class FreeformTaskListener<T extends AutoCloseable>
        return mTasks.get(taskId).mLeash;
    }

    /**
     * Creates a window decoration for a transition.
     *
     * @param change the change of this task transition that needs to have the task layer as the
     *               leash
     * @return {@code true} if it adopts the window decoration; {@code false} otherwise
     */
    void createWindowDecoration(
            TransitionInfo.Change change,
            SurfaceControl.Transaction startT,
            SurfaceControl.Transaction finishT) {
        final State<T> state = createOrUpdateTaskState(change.getTaskInfo(), change.getLeash());
        state.mWindowDecoration = mWindowDecorationViewModel.createWindowDecoration(
                state.mTaskInfo, state.mLeash, startT, finishT);
    }

    /**
     * Gives out the ownership of the task's window decoration. The given task is leaving (of has
     * left) this task listener. This is the transition system asking for the ownership.
     *
     * @param taskInfo the maximizing task
     * @return the window decor of the maximizing task if any
     */
    T giveWindowDecoration(
            RunningTaskInfo taskInfo,
            SurfaceControl.Transaction startT,
            SurfaceControl.Transaction finishT) {
        T windowDecor = mWindowDecorOfVanishedTasks.get(taskInfo.taskId);
        mWindowDecorOfVanishedTasks.remove(taskInfo.taskId);
        final State<T> state = mTasks.get(taskInfo.taskId);
        if (state != null) {
            windowDecor = windowDecor == null ? state.mWindowDecoration : windowDecor;
            state.mWindowDecoration = null;
        }
        mWindowDecorationViewModel.setupWindowDecorationForTransition(
                taskInfo, startT, finishT, windowDecor);
        return windowDecor;
    }

    /**
     * Adopt the incoming window decoration and lets the window decoration prepare for a transition.
     *
     * @param change the change of this task transition that needs to have the task layer as the
     *               leash
     * @param startT the start transaction of this transition
     * @param finishT the finish transaction of this transition
     * @param windowDecor the window decoration to adopt
     * @return {@code true} if it adopts the window decoration; {@code false} otherwise
     */
    boolean adoptWindowDecoration(
            TransitionInfo.Change change,
            SurfaceControl.Transaction startT,
            SurfaceControl.Transaction finishT,
            @Nullable AutoCloseable windowDecor) {
        final State<T> state = createOrUpdateTaskState(change.getTaskInfo(), change.getLeash());
        state.mWindowDecoration = mWindowDecorationViewModel.adoptWindowDecoration(windowDecor);
        if (state.mWindowDecoration != null) {
            mWindowDecorationViewModel.setupWindowDecorationForTransition(
                    state.mTaskInfo, startT, finishT, state.mWindowDecoration);
            return true;
        } else {
            state.mWindowDecoration = mWindowDecorationViewModel.createWindowDecoration(
                    state.mTaskInfo, state.mLeash, startT, finishT);
            return false;
        }
    }

    void onTaskTransitionFinished() {
        if (mWindowDecorOfVanishedTasks.size() == 0) {
            return;
        }
        Log.w(TAG, "Clearing window decors of vanished tasks. There could be visual defects "
                + "if any of them is used later in transitions.");
        for (int i = 0; i < mWindowDecorOfVanishedTasks.size(); ++i) {
            releaseWindowDecor(mWindowDecorOfVanishedTasks.valueAt(i));
        }
        mWindowDecorOfVanishedTasks.clear();
    }

    private void releaseWindowDecor(T windowDecor) {
        try {
            windowDecor.close();
        } catch (Exception e) {
            Log.e(TAG, "Failed to release window decoration.", e);
        }
    }

    @Override
    public void dump(PrintWriter pw, String prefix) {
        final String innerPrefix = prefix + "  ";
@@ -137,16 +243,4 @@ public class FreeformTaskListener<T extends AutoCloseable>
    public String toString() {
        return TAG;
    }

    /**
     * Checks if freeform support is enabled in system.
     *
     * @param context context used to check settings and package manager.
     * @return {@code true} if freeform is enabled, {@code false} if not.
     */
    public static boolean isFreeformEnabled(Context context) {
        return context.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT)
                || Settings.Global.getInt(context.getContentResolver(),
                DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
    }
}
+234 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading