Loading libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +15 −6 Original line number Diff line number Diff line Loading @@ -85,6 +85,7 @@ import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.unfold.ShellUnfoldProgressProvider; import com.android.wm.shell.unfold.UnfoldAnimationController; import com.android.wm.shell.unfold.UnfoldTransitionHandler; import com.android.wm.shell.windowdecor.WindowDecorViewModel; import java.util.Optional; Loading Loading @@ -294,24 +295,32 @@ public abstract class WMShellBaseModule { // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride} @BindsOptionalOf @DynamicOverride abstract FullscreenTaskListener optionalFullscreenTaskListener(); abstract FullscreenTaskListener<?> optionalFullscreenTaskListener(); @WMSingleton @Provides static FullscreenTaskListener provideFullscreenTaskListener( @DynamicOverride Optional<FullscreenTaskListener> fullscreenTaskListener, static FullscreenTaskListener<?> provideFullscreenTaskListener( @DynamicOverride Optional<FullscreenTaskListener<?>> fullscreenTaskListener, ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, Optional<RecentTasksController> recentTasksOptional) { Optional<RecentTasksController> recentTasksOptional, Optional<WindowDecorViewModel<?>> windowDecorViewModelOptional) { if (fullscreenTaskListener.isPresent()) { return fullscreenTaskListener.get(); } else { return new FullscreenTaskListener(shellInit, shellTaskOrganizer, syncQueue, recentTasksOptional); recentTasksOptional, windowDecorViewModelOptional); } } // // Window Decoration // @BindsOptionalOf abstract WindowDecorViewModel<?> optionalWindowDecorViewModel(); // // Unfold transition // Loading Loading @@ -680,7 +689,7 @@ public abstract class WMShellBaseModule { Optional<SplitScreenController> splitScreenOptional, Optional<Pip> pipOptional, Optional<PipTouchHandler> pipTouchHandlerOptional, FullscreenTaskListener fullscreenTaskListener, FullscreenTaskListener<?> fullscreenTaskListener, Optional<UnfoldAnimationController> unfoldAnimationController, Optional<UnfoldTransitionHandler> unfoldTransitionHandler, Optional<FreeformComponents> freeformComponents, Loading libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +3 −1 Original line number Diff line number Diff line Loading @@ -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.fullscreen.FullscreenTaskListener; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipAnimationController; Loading Loading @@ -233,6 +234,7 @@ public abstract class WMShellModule { 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 Loading @@ -240,7 +242,7 @@ public abstract class WMShellModule { ? shellInit : null; return new FreeformTaskTransitionHandler(init, transitions, windowDecorViewModel, freeformTaskListener); windowDecorViewModel, fullscreenTaskListener, freeformTaskListener); } // Loading libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java +7 −4 Original line number Diff line number Diff line Loading @@ -187,12 +187,14 @@ public class FreeformTaskListener<T extends AutoCloseable> RunningTaskInfo taskInfo, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) { T windowDecor = mWindowDecorOfVanishedTasks.get(taskInfo.taskId); mWindowDecorOfVanishedTasks.remove(taskInfo.taskId); T windowDecor; final State<T> state = mTasks.get(taskInfo.taskId); if (state != null) { windowDecor = windowDecor == null ? state.mWindowDecoration : windowDecor; windowDecor = state.mWindowDecoration; state.mWindowDecoration = null; } else { windowDecor = mWindowDecorOfVanishedTasks.removeReturnOld(taskInfo.taskId); } mWindowDecorationViewModel.setupWindowDecorationForTransition( taskInfo, startT, finishT, windowDecor); Loading Loading @@ -231,7 +233,8 @@ public class FreeformTaskListener<T extends AutoCloseable> if (mWindowDecorOfVanishedTasks.size() == 0) { return; } Log.w(TAG, "Clearing window decors of vanished tasks. There could be visual defects " ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "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)); Loading libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java +36 −14 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ 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; Loading @@ -50,6 +51,7 @@ public class FreeformTaskTransitionHandler private final Transitions mTransitions; private final FreeformTaskListener<?> mFreeformTaskListener; private final FullscreenTaskListener<?> mFullscreenTaskListener; private final WindowDecorViewModel<?> mWindowDecorViewModel; private final List<IBinder> mPendingTransitionTokens = new ArrayList<>(); Loading @@ -58,8 +60,10 @@ public class FreeformTaskTransitionHandler ShellInit shellInit, Transitions transitions, WindowDecorViewModel<?> windowDecorViewModel, FullscreenTaskListener<?> fullscreenTaskListener, FreeformTaskListener<?> freeformTaskListener) { mTransitions = transitions; mFullscreenTaskListener = fullscreenTaskListener; mFreeformTaskListener = freeformTaskListener; mWindowDecorViewModel = windowDecorViewModel; if (shellInit != null && Transitions.ENABLE_SHELL_TRANSITIONS) { Loading Loading @@ -150,10 +154,16 @@ public class FreeformTaskTransitionHandler TransitionInfo.Change change, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) { if (change.getTaskInfo().getWindowingMode() != WINDOWING_MODE_FREEFORM) { 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; } mFreeformTaskListener.createWindowDecoration(change, startT, finishT); // Intercepted transition to manage the window decorations. Let other handlers animate. return false; Loading @@ -164,15 +174,22 @@ public class FreeformTaskTransitionHandler ArrayList<AutoCloseable> windowDecors, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) { if (change.getTaskInfo().getWindowingMode() != WINDOWING_MODE_FREEFORM) { return false; 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; } final AutoCloseable windowDecor = mFreeformTaskListener.giveWindowDecoration(change.getTaskInfo(), startT, finishT); if (windowDecor != null) { windowDecors.add(windowDecor); } // Intercepted transition to manage the window decorations. Let other handlers animate. return false; } Loading @@ -197,24 +214,29 @@ public class FreeformTaskTransitionHandler } boolean handled = false; boolean adopted = false; final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); if (type == Transitions.TRANSIT_MAXIMIZE && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { handled = true; windowDecor = mFreeformTaskListener.giveWindowDecoration( change.getTaskInfo(), startT, finishT); // TODO(b/235638450): Let fullscreen task listener adopt the window decor. adopted = mFullscreenTaskListener.adoptWindowDecoration(change, startT, finishT, windowDecor); } if (type == Transitions.TRANSIT_RESTORE_FROM_MAXIMIZE && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { handled = true; // TODO(b/235638450): Let fullscreen task listener transfer the window decor. mFreeformTaskListener.adoptWindowDecoration(change, startT, finishT, windowDecor); windowDecor = mFullscreenTaskListener.giveWindowDecoration( change.getTaskInfo(), startT, finishT); adopted = mFreeformTaskListener.adoptWindowDecoration(change, startT, finishT, windowDecor); } if (!adopted) { releaseWindowDecor(windowDecor); } return handled; } Loading @@ -225,7 +247,7 @@ public class FreeformTaskTransitionHandler releaseWindowDecor(windowDecor); } mFreeformTaskListener.onTaskTransitionFinished(); // TODO(b/235638450): Dispatch it to fullscreen task listener. mFullscreenTaskListener.onTaskTransitionFinished(); finishCallback.onTransitionFinished(null, null); } Loading libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java +208 −47 Original line number Diff line number Diff line Loading @@ -16,16 +16,21 @@ package com.android.wm.shell.fullscreen; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN; import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.graphics.Point; import android.util.Slog; import android.util.Log; import android.util.SparseArray; import android.view.SurfaceControl; import android.window.TransitionInfo; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; Loading @@ -34,36 +39,49 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.windowdecor.WindowDecorViewModel; import java.io.PrintWriter; import java.util.Optional; /** * Organizes tasks presented in {@link android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN}. * @param <T> the type of window decoration instance */ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { public class FullscreenTaskListener<T extends AutoCloseable> implements ShellTaskOrganizer.TaskListener { private static final String TAG = "FullscreenTaskListener"; private final ShellTaskOrganizer mShellTaskOrganizer; private final SyncTransactionQueue mSyncQueue; private final Optional<RecentTasksController> mRecentTasksOptional; private final SparseArray<TaskData> mDataByTaskId = new SparseArray<>(); private final SparseArray<State<T>> mTasks = new SparseArray<>(); private final SparseArray<T> mWindowDecorOfVanishedTasks = new SparseArray<>(); private static class State<T extends AutoCloseable> { RunningTaskInfo mTaskInfo; SurfaceControl mLeash; T mWindowDecoration; } private final SyncTransactionQueue mSyncQueue; private final Optional<RecentTasksController> mRecentTasksOptional; private final Optional<WindowDecorViewModel<T>> mWindowDecorViewModelOptional; /** * This constructor is used by downstream products. */ public FullscreenTaskListener(SyncTransactionQueue syncQueue) { this(null /* shellInit */, null /* shellTaskOrganizer */, syncQueue, Optional.empty()); this(null /* shellInit */, null /* shellTaskOrganizer */, syncQueue, Optional.empty(), Optional.empty()); } public FullscreenTaskListener(ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, Optional<RecentTasksController> recentTasksOptional) { Optional<RecentTasksController> recentTasksOptional, Optional<WindowDecorViewModel<T>> windowDecorViewModelOptional) { mShellTaskOrganizer = shellTaskOrganizer; mSyncQueue = syncQueue; mRecentTasksOptional = recentTasksOptional; mWindowDecorViewModelOptional = windowDecorViewModelOptional; // Note: Some derivative FullscreenTaskListener implementations do not use ShellInit if (shellInit != null) { shellInit.addInitCallback(this::onInit, this); Loading @@ -76,15 +94,26 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { @Override public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { if (mDataByTaskId.get(taskInfo.taskId) != null) { if (mTasks.get(taskInfo.taskId) != null) { throw new IllegalStateException("Task appeared more than once: #" + taskInfo.taskId); } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Appeared: #%d", taskInfo.taskId); final Point positionInParent = taskInfo.positionInParent; mDataByTaskId.put(taskInfo.taskId, new TaskData(leash, positionInParent)); final State<T> state = new State(); state.mLeash = leash; state.mTaskInfo = taskInfo; mTasks.put(taskInfo.taskId, state); updateRecentsForVisibleFullscreenTask(taskInfo); if (Transitions.ENABLE_SHELL_TRANSITIONS) return; if (shouldShowWindowDecor(taskInfo) && mWindowDecorViewModelOptional.isPresent()) { SurfaceControl.Transaction t = new SurfaceControl.Transaction(); state.mWindowDecoration = mWindowDecorViewModelOptional.get().createWindowDecoration(taskInfo, leash, t, t); t.apply(); } else { mSyncQueue.runInSync(t -> { // Reset several properties back to fullscreen (PiP, for example, leaves all these // properties in a bad state). Loading @@ -94,37 +123,175 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { t.setMatrix(leash, 1, 0, 0, 1); t.show(leash); }); updateRecentsForVisibleFullscreenTask(taskInfo); } } @Override public void onTaskInfoChanged(RunningTaskInfo taskInfo) { if (Transitions.ENABLE_SHELL_TRANSITIONS) return; final State<T> state = mTasks.get(taskInfo.taskId); final Point oldPositionInParent = state.mTaskInfo.positionInParent; state.mTaskInfo = taskInfo; if (state.mWindowDecoration != null) { mWindowDecorViewModelOptional.get().onTaskInfoChanged( state.mTaskInfo, state.mWindowDecoration); } updateRecentsForVisibleFullscreenTask(taskInfo); if (Transitions.ENABLE_SHELL_TRANSITIONS) return; final TaskData data = mDataByTaskId.get(taskInfo.taskId); final Point positionInParent = taskInfo.positionInParent; if (!positionInParent.equals(data.positionInParent)) { data.positionInParent.set(positionInParent.x, positionInParent.y); final Point positionInParent = state.mTaskInfo.positionInParent; if (!oldPositionInParent.equals(state.mTaskInfo.positionInParent)) { mSyncQueue.runInSync(t -> { t.setPosition(data.surface, positionInParent.x, positionInParent.y); t.setPosition(state.mLeash, positionInParent.x, positionInParent.y); }); } } @Override public void onTaskVanished(RunningTaskInfo taskInfo) { if (mDataByTaskId.get(taskInfo.taskId) == null) { Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId); public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { final State<T> state = mTasks.get(taskInfo.taskId); if (state == null) { // This is possible if the transition happens before this method. return; } mDataByTaskId.remove(taskInfo.taskId); ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Vanished: #%d", taskInfo.taskId); mTasks.remove(taskInfo.taskId); 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); } /** * 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 */ public void createWindowDecoration(TransitionInfo.Change change, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) { final State<T> state = createOrUpdateTaskState(change.getTaskInfo(), change.getLeash()); if (!mWindowDecorViewModelOptional.isPresent() || !shouldShowWindowDecor(state.mTaskInfo)) { return; } state.mWindowDecoration = mWindowDecorViewModelOptional.get().createWindowDecoration( state.mTaskInfo, state.mLeash, startT, finishT); } /** * 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 */ public boolean adoptWindowDecoration( TransitionInfo.Change change, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, @Nullable AutoCloseable windowDecor) { if (!mWindowDecorViewModelOptional.isPresent() || !shouldShowWindowDecor(change.getTaskInfo())) { return false; } final State<T> state = createOrUpdateTaskState(change.getTaskInfo(), change.getLeash()); state.mWindowDecoration = mWindowDecorViewModelOptional.get().adoptWindowDecoration( windowDecor); if (state.mWindowDecoration != null) { mWindowDecorViewModelOptional.get().setupWindowDecorationForTransition( state.mTaskInfo, startT, finishT, state.mWindowDecoration); return true; } else { state.mWindowDecoration = mWindowDecorViewModelOptional.get().createWindowDecoration( state.mTaskInfo, state.mLeash, startT, finishT); return false; } } /** * Clear window decors of vanished tasks. */ public void onTaskTransitionFinished() { if (mWindowDecorOfVanishedTasks.size() == 0) { return; } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "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(); } /** * 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 */ public T giveWindowDecoration( ActivityManager.RunningTaskInfo taskInfo, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) { T windowDecor; final State<T> state = mTasks.get(taskInfo.taskId); if (state != null) { windowDecor = state.mWindowDecoration; state.mWindowDecoration = null; } else { windowDecor = mWindowDecorOfVanishedTasks.removeReturnOld(taskInfo.taskId); } if (mWindowDecorViewModelOptional.isPresent()) { mWindowDecorViewModelOptional.get().setupWindowDecorationForTransition( taskInfo, startT, finishT, windowDecor); } return windowDecor; } private State<T> createOrUpdateTaskState(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { State<T> state = mTasks.get(taskInfo.taskId); if (state != null) { updateTaskInfo(taskInfo); return state; } state = new State<T>(); state.mTaskInfo = taskInfo; state.mLeash = leash; mTasks.put(taskInfo.taskId, state); return state; } private State<T> updateTaskInfo(ActivityManager.RunningTaskInfo taskInfo) { final State<T> state = mTasks.get(taskInfo.taskId); state.mTaskInfo = taskInfo; return state; } private void releaseWindowDecor(T windowDecor) { try { windowDecor.close(); } catch (Exception e) { Log.e(TAG, "Failed to release window decoration.", e); } } private void updateRecentsForVisibleFullscreenTask(RunningTaskInfo taskInfo) { Loading @@ -148,17 +315,17 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { } private SurfaceControl findTaskSurface(int taskId) { if (!mDataByTaskId.contains(taskId)) { if (!mTasks.contains(taskId)) { throw new IllegalArgumentException("There is no surface for taskId=" + taskId); } return mDataByTaskId.get(taskId).surface; return mTasks.get(taskId).mLeash; } @Override public void dump(@NonNull PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + this); pw.println(innerPrefix + mDataByTaskId.size() + " Tasks"); pw.println(innerPrefix + mTasks.size() + " Tasks"); } @Override Loading @@ -166,16 +333,10 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_FULLSCREEN); } /** * Per-task data for each managed task. */ private static class TaskData { public final SurfaceControl surface; public final Point positionInParent; public TaskData(SurfaceControl surface, Point positionInParent) { this.surface = surface; this.positionInParent = positionInParent; } private static boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) { return taskInfo.getConfiguration().windowConfiguration.getDisplayWindowingMode() == WINDOWING_MODE_FREEFORM; } } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +15 −6 Original line number Diff line number Diff line Loading @@ -85,6 +85,7 @@ import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.unfold.ShellUnfoldProgressProvider; import com.android.wm.shell.unfold.UnfoldAnimationController; import com.android.wm.shell.unfold.UnfoldTransitionHandler; import com.android.wm.shell.windowdecor.WindowDecorViewModel; import java.util.Optional; Loading Loading @@ -294,24 +295,32 @@ public abstract class WMShellBaseModule { // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride} @BindsOptionalOf @DynamicOverride abstract FullscreenTaskListener optionalFullscreenTaskListener(); abstract FullscreenTaskListener<?> optionalFullscreenTaskListener(); @WMSingleton @Provides static FullscreenTaskListener provideFullscreenTaskListener( @DynamicOverride Optional<FullscreenTaskListener> fullscreenTaskListener, static FullscreenTaskListener<?> provideFullscreenTaskListener( @DynamicOverride Optional<FullscreenTaskListener<?>> fullscreenTaskListener, ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, Optional<RecentTasksController> recentTasksOptional) { Optional<RecentTasksController> recentTasksOptional, Optional<WindowDecorViewModel<?>> windowDecorViewModelOptional) { if (fullscreenTaskListener.isPresent()) { return fullscreenTaskListener.get(); } else { return new FullscreenTaskListener(shellInit, shellTaskOrganizer, syncQueue, recentTasksOptional); recentTasksOptional, windowDecorViewModelOptional); } } // // Window Decoration // @BindsOptionalOf abstract WindowDecorViewModel<?> optionalWindowDecorViewModel(); // // Unfold transition // Loading Loading @@ -680,7 +689,7 @@ public abstract class WMShellBaseModule { Optional<SplitScreenController> splitScreenOptional, Optional<Pip> pipOptional, Optional<PipTouchHandler> pipTouchHandlerOptional, FullscreenTaskListener fullscreenTaskListener, FullscreenTaskListener<?> fullscreenTaskListener, Optional<UnfoldAnimationController> unfoldAnimationController, Optional<UnfoldTransitionHandler> unfoldTransitionHandler, Optional<FreeformComponents> freeformComponents, Loading
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +3 −1 Original line number Diff line number Diff line Loading @@ -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.fullscreen.FullscreenTaskListener; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipAnimationController; Loading Loading @@ -233,6 +234,7 @@ public abstract class WMShellModule { 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 Loading @@ -240,7 +242,7 @@ public abstract class WMShellModule { ? shellInit : null; return new FreeformTaskTransitionHandler(init, transitions, windowDecorViewModel, freeformTaskListener); windowDecorViewModel, fullscreenTaskListener, freeformTaskListener); } // Loading
libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java +7 −4 Original line number Diff line number Diff line Loading @@ -187,12 +187,14 @@ public class FreeformTaskListener<T extends AutoCloseable> RunningTaskInfo taskInfo, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) { T windowDecor = mWindowDecorOfVanishedTasks.get(taskInfo.taskId); mWindowDecorOfVanishedTasks.remove(taskInfo.taskId); T windowDecor; final State<T> state = mTasks.get(taskInfo.taskId); if (state != null) { windowDecor = windowDecor == null ? state.mWindowDecoration : windowDecor; windowDecor = state.mWindowDecoration; state.mWindowDecoration = null; } else { windowDecor = mWindowDecorOfVanishedTasks.removeReturnOld(taskInfo.taskId); } mWindowDecorationViewModel.setupWindowDecorationForTransition( taskInfo, startT, finishT, windowDecor); Loading Loading @@ -231,7 +233,8 @@ public class FreeformTaskListener<T extends AutoCloseable> if (mWindowDecorOfVanishedTasks.size() == 0) { return; } Log.w(TAG, "Clearing window decors of vanished tasks. There could be visual defects " ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "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)); Loading
libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java +36 −14 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ 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; Loading @@ -50,6 +51,7 @@ public class FreeformTaskTransitionHandler private final Transitions mTransitions; private final FreeformTaskListener<?> mFreeformTaskListener; private final FullscreenTaskListener<?> mFullscreenTaskListener; private final WindowDecorViewModel<?> mWindowDecorViewModel; private final List<IBinder> mPendingTransitionTokens = new ArrayList<>(); Loading @@ -58,8 +60,10 @@ public class FreeformTaskTransitionHandler ShellInit shellInit, Transitions transitions, WindowDecorViewModel<?> windowDecorViewModel, FullscreenTaskListener<?> fullscreenTaskListener, FreeformTaskListener<?> freeformTaskListener) { mTransitions = transitions; mFullscreenTaskListener = fullscreenTaskListener; mFreeformTaskListener = freeformTaskListener; mWindowDecorViewModel = windowDecorViewModel; if (shellInit != null && Transitions.ENABLE_SHELL_TRANSITIONS) { Loading Loading @@ -150,10 +154,16 @@ public class FreeformTaskTransitionHandler TransitionInfo.Change change, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) { if (change.getTaskInfo().getWindowingMode() != WINDOWING_MODE_FREEFORM) { 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; } mFreeformTaskListener.createWindowDecoration(change, startT, finishT); // Intercepted transition to manage the window decorations. Let other handlers animate. return false; Loading @@ -164,15 +174,22 @@ public class FreeformTaskTransitionHandler ArrayList<AutoCloseable> windowDecors, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) { if (change.getTaskInfo().getWindowingMode() != WINDOWING_MODE_FREEFORM) { return false; 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; } final AutoCloseable windowDecor = mFreeformTaskListener.giveWindowDecoration(change.getTaskInfo(), startT, finishT); if (windowDecor != null) { windowDecors.add(windowDecor); } // Intercepted transition to manage the window decorations. Let other handlers animate. return false; } Loading @@ -197,24 +214,29 @@ public class FreeformTaskTransitionHandler } boolean handled = false; boolean adopted = false; final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); if (type == Transitions.TRANSIT_MAXIMIZE && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { handled = true; windowDecor = mFreeformTaskListener.giveWindowDecoration( change.getTaskInfo(), startT, finishT); // TODO(b/235638450): Let fullscreen task listener adopt the window decor. adopted = mFullscreenTaskListener.adoptWindowDecoration(change, startT, finishT, windowDecor); } if (type == Transitions.TRANSIT_RESTORE_FROM_MAXIMIZE && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { handled = true; // TODO(b/235638450): Let fullscreen task listener transfer the window decor. mFreeformTaskListener.adoptWindowDecoration(change, startT, finishT, windowDecor); windowDecor = mFullscreenTaskListener.giveWindowDecoration( change.getTaskInfo(), startT, finishT); adopted = mFreeformTaskListener.adoptWindowDecoration(change, startT, finishT, windowDecor); } if (!adopted) { releaseWindowDecor(windowDecor); } return handled; } Loading @@ -225,7 +247,7 @@ public class FreeformTaskTransitionHandler releaseWindowDecor(windowDecor); } mFreeformTaskListener.onTaskTransitionFinished(); // TODO(b/235638450): Dispatch it to fullscreen task listener. mFullscreenTaskListener.onTaskTransitionFinished(); finishCallback.onTransitionFinished(null, null); } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java +208 −47 Original line number Diff line number Diff line Loading @@ -16,16 +16,21 @@ package com.android.wm.shell.fullscreen; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN; import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.graphics.Point; import android.util.Slog; import android.util.Log; import android.util.SparseArray; import android.view.SurfaceControl; import android.window.TransitionInfo; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; Loading @@ -34,36 +39,49 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.windowdecor.WindowDecorViewModel; import java.io.PrintWriter; import java.util.Optional; /** * Organizes tasks presented in {@link android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN}. * @param <T> the type of window decoration instance */ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { public class FullscreenTaskListener<T extends AutoCloseable> implements ShellTaskOrganizer.TaskListener { private static final String TAG = "FullscreenTaskListener"; private final ShellTaskOrganizer mShellTaskOrganizer; private final SyncTransactionQueue mSyncQueue; private final Optional<RecentTasksController> mRecentTasksOptional; private final SparseArray<TaskData> mDataByTaskId = new SparseArray<>(); private final SparseArray<State<T>> mTasks = new SparseArray<>(); private final SparseArray<T> mWindowDecorOfVanishedTasks = new SparseArray<>(); private static class State<T extends AutoCloseable> { RunningTaskInfo mTaskInfo; SurfaceControl mLeash; T mWindowDecoration; } private final SyncTransactionQueue mSyncQueue; private final Optional<RecentTasksController> mRecentTasksOptional; private final Optional<WindowDecorViewModel<T>> mWindowDecorViewModelOptional; /** * This constructor is used by downstream products. */ public FullscreenTaskListener(SyncTransactionQueue syncQueue) { this(null /* shellInit */, null /* shellTaskOrganizer */, syncQueue, Optional.empty()); this(null /* shellInit */, null /* shellTaskOrganizer */, syncQueue, Optional.empty(), Optional.empty()); } public FullscreenTaskListener(ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, Optional<RecentTasksController> recentTasksOptional) { Optional<RecentTasksController> recentTasksOptional, Optional<WindowDecorViewModel<T>> windowDecorViewModelOptional) { mShellTaskOrganizer = shellTaskOrganizer; mSyncQueue = syncQueue; mRecentTasksOptional = recentTasksOptional; mWindowDecorViewModelOptional = windowDecorViewModelOptional; // Note: Some derivative FullscreenTaskListener implementations do not use ShellInit if (shellInit != null) { shellInit.addInitCallback(this::onInit, this); Loading @@ -76,15 +94,26 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { @Override public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { if (mDataByTaskId.get(taskInfo.taskId) != null) { if (mTasks.get(taskInfo.taskId) != null) { throw new IllegalStateException("Task appeared more than once: #" + taskInfo.taskId); } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Appeared: #%d", taskInfo.taskId); final Point positionInParent = taskInfo.positionInParent; mDataByTaskId.put(taskInfo.taskId, new TaskData(leash, positionInParent)); final State<T> state = new State(); state.mLeash = leash; state.mTaskInfo = taskInfo; mTasks.put(taskInfo.taskId, state); updateRecentsForVisibleFullscreenTask(taskInfo); if (Transitions.ENABLE_SHELL_TRANSITIONS) return; if (shouldShowWindowDecor(taskInfo) && mWindowDecorViewModelOptional.isPresent()) { SurfaceControl.Transaction t = new SurfaceControl.Transaction(); state.mWindowDecoration = mWindowDecorViewModelOptional.get().createWindowDecoration(taskInfo, leash, t, t); t.apply(); } else { mSyncQueue.runInSync(t -> { // Reset several properties back to fullscreen (PiP, for example, leaves all these // properties in a bad state). Loading @@ -94,37 +123,175 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { t.setMatrix(leash, 1, 0, 0, 1); t.show(leash); }); updateRecentsForVisibleFullscreenTask(taskInfo); } } @Override public void onTaskInfoChanged(RunningTaskInfo taskInfo) { if (Transitions.ENABLE_SHELL_TRANSITIONS) return; final State<T> state = mTasks.get(taskInfo.taskId); final Point oldPositionInParent = state.mTaskInfo.positionInParent; state.mTaskInfo = taskInfo; if (state.mWindowDecoration != null) { mWindowDecorViewModelOptional.get().onTaskInfoChanged( state.mTaskInfo, state.mWindowDecoration); } updateRecentsForVisibleFullscreenTask(taskInfo); if (Transitions.ENABLE_SHELL_TRANSITIONS) return; final TaskData data = mDataByTaskId.get(taskInfo.taskId); final Point positionInParent = taskInfo.positionInParent; if (!positionInParent.equals(data.positionInParent)) { data.positionInParent.set(positionInParent.x, positionInParent.y); final Point positionInParent = state.mTaskInfo.positionInParent; if (!oldPositionInParent.equals(state.mTaskInfo.positionInParent)) { mSyncQueue.runInSync(t -> { t.setPosition(data.surface, positionInParent.x, positionInParent.y); t.setPosition(state.mLeash, positionInParent.x, positionInParent.y); }); } } @Override public void onTaskVanished(RunningTaskInfo taskInfo) { if (mDataByTaskId.get(taskInfo.taskId) == null) { Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId); public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { final State<T> state = mTasks.get(taskInfo.taskId); if (state == null) { // This is possible if the transition happens before this method. return; } mDataByTaskId.remove(taskInfo.taskId); ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Vanished: #%d", taskInfo.taskId); mTasks.remove(taskInfo.taskId); 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); } /** * 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 */ public void createWindowDecoration(TransitionInfo.Change change, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) { final State<T> state = createOrUpdateTaskState(change.getTaskInfo(), change.getLeash()); if (!mWindowDecorViewModelOptional.isPresent() || !shouldShowWindowDecor(state.mTaskInfo)) { return; } state.mWindowDecoration = mWindowDecorViewModelOptional.get().createWindowDecoration( state.mTaskInfo, state.mLeash, startT, finishT); } /** * 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 */ public boolean adoptWindowDecoration( TransitionInfo.Change change, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, @Nullable AutoCloseable windowDecor) { if (!mWindowDecorViewModelOptional.isPresent() || !shouldShowWindowDecor(change.getTaskInfo())) { return false; } final State<T> state = createOrUpdateTaskState(change.getTaskInfo(), change.getLeash()); state.mWindowDecoration = mWindowDecorViewModelOptional.get().adoptWindowDecoration( windowDecor); if (state.mWindowDecoration != null) { mWindowDecorViewModelOptional.get().setupWindowDecorationForTransition( state.mTaskInfo, startT, finishT, state.mWindowDecoration); return true; } else { state.mWindowDecoration = mWindowDecorViewModelOptional.get().createWindowDecoration( state.mTaskInfo, state.mLeash, startT, finishT); return false; } } /** * Clear window decors of vanished tasks. */ public void onTaskTransitionFinished() { if (mWindowDecorOfVanishedTasks.size() == 0) { return; } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "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(); } /** * 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 */ public T giveWindowDecoration( ActivityManager.RunningTaskInfo taskInfo, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) { T windowDecor; final State<T> state = mTasks.get(taskInfo.taskId); if (state != null) { windowDecor = state.mWindowDecoration; state.mWindowDecoration = null; } else { windowDecor = mWindowDecorOfVanishedTasks.removeReturnOld(taskInfo.taskId); } if (mWindowDecorViewModelOptional.isPresent()) { mWindowDecorViewModelOptional.get().setupWindowDecorationForTransition( taskInfo, startT, finishT, windowDecor); } return windowDecor; } private State<T> createOrUpdateTaskState(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { State<T> state = mTasks.get(taskInfo.taskId); if (state != null) { updateTaskInfo(taskInfo); return state; } state = new State<T>(); state.mTaskInfo = taskInfo; state.mLeash = leash; mTasks.put(taskInfo.taskId, state); return state; } private State<T> updateTaskInfo(ActivityManager.RunningTaskInfo taskInfo) { final State<T> state = mTasks.get(taskInfo.taskId); state.mTaskInfo = taskInfo; return state; } private void releaseWindowDecor(T windowDecor) { try { windowDecor.close(); } catch (Exception e) { Log.e(TAG, "Failed to release window decoration.", e); } } private void updateRecentsForVisibleFullscreenTask(RunningTaskInfo taskInfo) { Loading @@ -148,17 +315,17 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { } private SurfaceControl findTaskSurface(int taskId) { if (!mDataByTaskId.contains(taskId)) { if (!mTasks.contains(taskId)) { throw new IllegalArgumentException("There is no surface for taskId=" + taskId); } return mDataByTaskId.get(taskId).surface; return mTasks.get(taskId).mLeash; } @Override public void dump(@NonNull PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + this); pw.println(innerPrefix + mDataByTaskId.size() + " Tasks"); pw.println(innerPrefix + mTasks.size() + " Tasks"); } @Override Loading @@ -166,16 +333,10 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_FULLSCREEN); } /** * Per-task data for each managed task. */ private static class TaskData { public final SurfaceControl surface; public final Point positionInParent; public TaskData(SurfaceControl surface, Point positionInParent) { this.surface = surface; this.positionInParent = positionInParent; } private static boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) { return taskInfo.getConfiguration().windowConfiguration.getDisplayWindowingMode() == WINDOWING_MODE_FREEFORM; } }