Merge "Put window decors at one place" into tm-qpr-dev

This commit is contained in:
Garfield Tan
2022-10-06 00:03:33 +00:00
committed by Android (Google) Code Review
9 changed files with 131 additions and 564 deletions

View File

@@ -292,17 +292,17 @@ public abstract class WMShellBaseModule {
// Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride} // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
@BindsOptionalOf @BindsOptionalOf
@DynamicOverride @DynamicOverride
abstract FullscreenTaskListener<?> optionalFullscreenTaskListener(); abstract FullscreenTaskListener optionalFullscreenTaskListener();
@WMSingleton @WMSingleton
@Provides @Provides
static FullscreenTaskListener<?> provideFullscreenTaskListener( static FullscreenTaskListener provideFullscreenTaskListener(
@DynamicOverride Optional<FullscreenTaskListener<?>> fullscreenTaskListener, @DynamicOverride Optional<FullscreenTaskListener> fullscreenTaskListener,
ShellInit shellInit, ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer, ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue, SyncTransactionQueue syncQueue,
Optional<RecentTasksController> recentTasksOptional, Optional<RecentTasksController> recentTasksOptional,
Optional<WindowDecorViewModel<?>> windowDecorViewModelOptional) { Optional<WindowDecorViewModel> windowDecorViewModelOptional) {
if (fullscreenTaskListener.isPresent()) { if (fullscreenTaskListener.isPresent()) {
return fullscreenTaskListener.get(); return fullscreenTaskListener.get();
} else { } else {
@@ -316,7 +316,7 @@ public abstract class WMShellBaseModule {
// //
@BindsOptionalOf @BindsOptionalOf
abstract WindowDecorViewModel<?> optionalWindowDecorViewModel(); abstract WindowDecorViewModel optionalWindowDecorViewModel();
// //
// Unfold transition // Unfold transition
@@ -768,7 +768,7 @@ public abstract class WMShellBaseModule {
Optional<SplitScreenController> splitScreenOptional, Optional<SplitScreenController> splitScreenOptional,
Optional<Pip> pipOptional, Optional<Pip> pipOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional, Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener<?> fullscreenTaskListener, FullscreenTaskListener fullscreenTaskListener,
Optional<UnfoldAnimationController> unfoldAnimationController, Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<UnfoldTransitionHandler> unfoldTransitionHandler, Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
Optional<FreeformComponents> freeformComponents, Optional<FreeformComponents> freeformComponents,

View File

@@ -55,7 +55,6 @@ import com.android.wm.shell.freeform.FreeformComponents;
import com.android.wm.shell.freeform.FreeformTaskListener; import com.android.wm.shell.freeform.FreeformTaskListener;
import com.android.wm.shell.freeform.FreeformTaskTransitionHandler; import com.android.wm.shell.freeform.FreeformTaskTransitionHandler;
import com.android.wm.shell.freeform.FreeformTaskTransitionObserver; import com.android.wm.shell.freeform.FreeformTaskTransitionObserver;
import com.android.wm.shell.fullscreen.FullscreenTaskListener;
import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer; import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer;
import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.Pip;
@@ -183,7 +182,7 @@ public abstract class WMShellModule {
@WMSingleton @WMSingleton
@Provides @Provides
static WindowDecorViewModel<?> provideWindowDecorViewModel( static WindowDecorViewModel provideWindowDecorViewModel(
Context context, Context context,
@ShellMainThread Handler mainHandler, @ShellMainThread Handler mainHandler,
@ShellMainThread Choreographer mainChoreographer, @ShellMainThread Choreographer mainChoreographer,
@@ -209,7 +208,7 @@ public abstract class WMShellModule {
@Provides @Provides
@DynamicOverride @DynamicOverride
static FreeformComponents provideFreeformComponents( static FreeformComponents provideFreeformComponents(
FreeformTaskListener<?> taskListener, FreeformTaskListener taskListener,
FreeformTaskTransitionHandler transitionHandler, FreeformTaskTransitionHandler transitionHandler,
FreeformTaskTransitionObserver transitionObserver) { FreeformTaskTransitionObserver transitionObserver) {
return new FreeformComponents( return new FreeformComponents(
@@ -218,18 +217,18 @@ public abstract class WMShellModule {
@WMSingleton @WMSingleton
@Provides @Provides
static FreeformTaskListener<?> provideFreeformTaskListener( static FreeformTaskListener provideFreeformTaskListener(
Context context, Context context,
ShellInit shellInit, ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer, ShellTaskOrganizer shellTaskOrganizer,
Optional<DesktopModeTaskRepository> desktopModeTaskRepository, Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
WindowDecorViewModel<?> windowDecorViewModel) { WindowDecorViewModel windowDecorViewModel) {
// TODO(b/238217847): Temporarily add this check here until we can remove the dynamic // TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
// override for this controller from the base module // override for this controller from the base module
ShellInit init = FreeformComponents.isFreeformEnabled(context) ShellInit init = FreeformComponents.isFreeformEnabled(context)
? shellInit ? shellInit
: null; : null;
return new FreeformTaskListener<>(init, shellTaskOrganizer, desktopModeTaskRepository, return new FreeformTaskListener(init, shellTaskOrganizer, desktopModeTaskRepository,
windowDecorViewModel); windowDecorViewModel);
} }
@@ -238,7 +237,7 @@ public abstract class WMShellModule {
static FreeformTaskTransitionHandler provideFreeformTaskTransitionHandler( static FreeformTaskTransitionHandler provideFreeformTaskTransitionHandler(
ShellInit shellInit, ShellInit shellInit,
Transitions transitions, Transitions transitions,
WindowDecorViewModel<?> windowDecorViewModel) { WindowDecorViewModel windowDecorViewModel) {
return new FreeformTaskTransitionHandler(shellInit, transitions, windowDecorViewModel); return new FreeformTaskTransitionHandler(shellInit, transitions, windowDecorViewModel);
} }
@@ -248,10 +247,9 @@ public abstract class WMShellModule {
Context context, Context context,
ShellInit shellInit, ShellInit shellInit,
Transitions transitions, Transitions transitions,
FullscreenTaskListener<?> fullscreenTaskListener, WindowDecorViewModel windowDecorViewModel) {
FreeformTaskListener<?> freeformTaskListener) {
return new FreeformTaskTransitionObserver( return new FreeformTaskTransitionObserver(
context, shellInit, transitions, fullscreenTaskListener, freeformTaskListener); context, shellInit, transitions, windowDecorViewModel);
} }
// //

View File

@@ -19,12 +19,8 @@ package com.android.wm.shell.freeform;
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FREEFORM; import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FREEFORM;
import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManager.RunningTaskInfo;
import android.util.Log;
import android.util.SparseArray; import android.util.SparseArray;
import android.view.SurfaceControl; import android.view.SurfaceControl;
import android.window.TransitionInfo;
import androidx.annotation.Nullable;
import com.android.internal.protolog.common.ProtoLog; import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTaskOrganizer;
@@ -41,31 +37,26 @@ import java.util.Optional;
/** /**
* {@link ShellTaskOrganizer.TaskListener} for {@link * {@link ShellTaskOrganizer.TaskListener} for {@link
* ShellTaskOrganizer#TASK_LISTENER_TYPE_FREEFORM}. * ShellTaskOrganizer#TASK_LISTENER_TYPE_FREEFORM}.
*
* @param <T> the type of window decoration instance
*/ */
public class FreeformTaskListener<T extends AutoCloseable> public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener {
implements ShellTaskOrganizer.TaskListener {
private static final String TAG = "FreeformTaskListener"; private static final String TAG = "FreeformTaskListener";
private final ShellTaskOrganizer mShellTaskOrganizer; private final ShellTaskOrganizer mShellTaskOrganizer;
private final Optional<DesktopModeTaskRepository> mDesktopModeTaskRepository; private final Optional<DesktopModeTaskRepository> mDesktopModeTaskRepository;
private final WindowDecorViewModel<T> mWindowDecorationViewModel; private final WindowDecorViewModel mWindowDecorationViewModel;
private final SparseArray<State<T>> mTasks = new SparseArray<>(); private final SparseArray<State> mTasks = new SparseArray<>();
private final SparseArray<T> mWindowDecorOfVanishedTasks = new SparseArray<>();
private static class State<T extends AutoCloseable> { private static class State {
RunningTaskInfo mTaskInfo; RunningTaskInfo mTaskInfo;
SurfaceControl mLeash; SurfaceControl mLeash;
T mWindowDecoration;
} }
public FreeformTaskListener( public FreeformTaskListener(
ShellInit shellInit, ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer, ShellTaskOrganizer shellTaskOrganizer,
Optional<DesktopModeTaskRepository> desktopModeTaskRepository, Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
WindowDecorViewModel<T> windowDecorationViewModel) { WindowDecorViewModel windowDecorationViewModel) {
mShellTaskOrganizer = shellTaskOrganizer; mShellTaskOrganizer = shellTaskOrganizer;
mWindowDecorationViewModel = windowDecorationViewModel; mWindowDecorationViewModel = windowDecorationViewModel;
mDesktopModeTaskRepository = desktopModeTaskRepository; mDesktopModeTaskRepository = desktopModeTaskRepository;
@@ -80,12 +71,17 @@ public class FreeformTaskListener<T extends AutoCloseable>
@Override @Override
public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
if (mTasks.get(taskInfo.taskId) != null) {
throw new IllegalStateException("Task appeared more than once: #" + taskInfo.taskId);
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Appeared: #%d", ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Appeared: #%d",
taskInfo.taskId); taskInfo.taskId);
final State<T> state = createOrUpdateTaskState(taskInfo, leash); final State state = new State();
state.mTaskInfo = taskInfo;
state.mLeash = leash;
mTasks.put(taskInfo.taskId, state);
if (!Transitions.ENABLE_SHELL_TRANSITIONS) { if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
SurfaceControl.Transaction t = new SurfaceControl.Transaction(); SurfaceControl.Transaction t = new SurfaceControl.Transaction();
state.mWindowDecoration =
mWindowDecorationViewModel.createWindowDecoration(taskInfo, leash, t, t); mWindowDecorationViewModel.createWindowDecoration(taskInfo, leash, t, t);
t.apply(); t.apply();
} }
@@ -97,28 +93,8 @@ public class FreeformTaskListener<T extends AutoCloseable>
} }
} }
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;
mTasks.put(taskInfo.taskId, state);
return state;
}
@Override @Override
public void onTaskVanished(RunningTaskInfo taskInfo) { public void onTaskVanished(RunningTaskInfo taskInfo) {
final State<T> state = mTasks.get(taskInfo.taskId);
if (state == null) {
// This is possible if the transition happens before this method.
return;
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Vanished: #%d", ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Vanished: #%d",
taskInfo.taskId); taskInfo.taskId);
mTasks.remove(taskInfo.taskId); mTasks.remove(taskInfo.taskId);
@@ -129,26 +105,18 @@ public class FreeformTaskListener<T extends AutoCloseable>
mDesktopModeTaskRepository.ifPresent(it -> it.removeActiveTask(taskInfo.taskId)); mDesktopModeTaskRepository.ifPresent(it -> it.removeActiveTask(taskInfo.taskId));
} }
if (Transitions.ENABLE_SHELL_TRANSITIONS) { if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
// Save window decorations of closing tasks so that we can hand them over to the mWindowDecorationViewModel.destroyWindowDecoration(taskInfo);
// 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 @Override
public void onTaskInfoChanged(RunningTaskInfo taskInfo) { public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
final State<T> state = updateTaskInfo(taskInfo); final State state = mTasks.get(taskInfo.taskId);
state.mTaskInfo = taskInfo;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Info Changed: #%d", ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Info Changed: #%d",
taskInfo.taskId); taskInfo.taskId);
if (state.mWindowDecoration != null) { mWindowDecorationViewModel.onTaskInfoChanged(state.mTaskInfo);
mWindowDecorationViewModel.onTaskInfoChanged(state.mTaskInfo, state.mWindowDecoration);
}
if (DesktopModeStatus.IS_SUPPORTED) { if (DesktopModeStatus.IS_SUPPORTED) {
if (taskInfo.isVisible) { if (taskInfo.isVisible) {
@@ -159,15 +127,6 @@ public class FreeformTaskListener<T extends AutoCloseable>
} }
} }
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 @Override
public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) { public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
b.setParent(findTaskSurface(taskId)); b.setParent(findTaskSurface(taskId));
@@ -186,103 +145,6 @@ public class FreeformTaskListener<T extends AutoCloseable>
return mTasks.get(taskId).mLeash; 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 creates the window decoration; {@code false} otherwise
*/
boolean createWindowDecoration(
TransitionInfo.Change change,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
final State<T> state = createOrUpdateTaskState(change.getTaskInfo(), change.getLeash());
if (state.mWindowDecoration != null) {
return false;
}
state.mWindowDecoration = mWindowDecorationViewModel.createWindowDecoration(
state.mTaskInfo, state.mLeash, startT, finishT);
return true;
}
/**
* 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;
final State<T> state = mTasks.get(taskInfo.taskId);
if (state != null) {
windowDecor = state.mWindowDecoration;
state.mWindowDecoration = null;
} else {
windowDecor =
mWindowDecorOfVanishedTasks.removeReturnOld(taskInfo.taskId);
}
if (windowDecor == null) {
return 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;
}
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();
}
private void releaseWindowDecor(T windowDecor) {
try {
windowDecor.close();
} catch (Exception e) {
Log.e(TAG, "Failed to release window decoration.", e);
}
}
@Override @Override
public void dump(PrintWriter pw, String prefix) { public void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " "; final String innerPrefix = prefix + " ";

View File

@@ -46,14 +46,14 @@ public class FreeformTaskTransitionHandler
implements Transitions.TransitionHandler, FreeformTaskTransitionStarter { implements Transitions.TransitionHandler, FreeformTaskTransitionStarter {
private final Transitions mTransitions; private final Transitions mTransitions;
private final WindowDecorViewModel<?> mWindowDecorViewModel; private final WindowDecorViewModel mWindowDecorViewModel;
private final List<IBinder> mPendingTransitionTokens = new ArrayList<>(); private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();
public FreeformTaskTransitionHandler( public FreeformTaskTransitionHandler(
ShellInit shellInit, ShellInit shellInit,
Transitions transitions, Transitions transitions,
WindowDecorViewModel<?> windowDecorViewModel) { WindowDecorViewModel windowDecorViewModel) {
mTransitions = transitions; mTransitions = transitions;
mWindowDecorViewModel = windowDecorViewModel; mWindowDecorViewModel = windowDecorViewModel;
if (Transitions.ENABLE_SHELL_TRANSITIONS) { if (Transitions.ENABLE_SHELL_TRANSITIONS) {

View File

@@ -16,13 +16,9 @@
package com.android.wm.shell.freeform; 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.app.ActivityManager;
import android.content.Context; import android.content.Context;
import android.os.IBinder; import android.os.IBinder;
import android.util.Log;
import android.view.SurfaceControl; import android.view.SurfaceControl;
import android.view.WindowManager; import android.view.WindowManager;
import android.window.TransitionInfo; import android.window.TransitionInfo;
@@ -31,9 +27,9 @@ import android.window.WindowContainerToken;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.android.wm.shell.fullscreen.FullscreenTaskListener;
import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@@ -47,23 +43,19 @@ import java.util.Map;
* be a part of transitions. * be a part of transitions.
*/ */
public class FreeformTaskTransitionObserver implements Transitions.TransitionObserver { public class FreeformTaskTransitionObserver implements Transitions.TransitionObserver {
private static final String TAG = "FreeformTO";
private final Transitions mTransitions; private final Transitions mTransitions;
private final FreeformTaskListener<?> mFreeformTaskListener; private final WindowDecorViewModel mWindowDecorViewModel;
private final FullscreenTaskListener<?> mFullscreenTaskListener;
private final Map<IBinder, List<AutoCloseable>> mTransitionToWindowDecors = new HashMap<>(); private final Map<IBinder, List<ActivityManager.RunningTaskInfo>> mTransitionToTaskInfo =
new HashMap<>();
public FreeformTaskTransitionObserver( public FreeformTaskTransitionObserver(
Context context, Context context,
ShellInit shellInit, ShellInit shellInit,
Transitions transitions, Transitions transitions,
FullscreenTaskListener<?> fullscreenTaskListener, WindowDecorViewModel windowDecorViewModel) {
FreeformTaskListener<?> freeformTaskListener) {
mTransitions = transitions; mTransitions = transitions;
mFreeformTaskListener = freeformTaskListener; mWindowDecorViewModel = windowDecorViewModel;
mFullscreenTaskListener = fullscreenTaskListener;
if (Transitions.ENABLE_SHELL_TRANSITIONS && FreeformComponents.isFreeformEnabled(context)) { if (Transitions.ENABLE_SHELL_TRANSITIONS && FreeformComponents.isFreeformEnabled(context)) {
shellInit.addInitCallback(this::onInit, this); shellInit.addInitCallback(this::onInit, this);
} }
@@ -80,7 +72,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
@NonNull TransitionInfo info, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction startT,
@NonNull SurfaceControl.Transaction finishT) { @NonNull SurfaceControl.Transaction finishT) {
final ArrayList<AutoCloseable> windowDecors = new ArrayList<>(); final ArrayList<ActivityManager.RunningTaskInfo> taskInfoList = new ArrayList<>();
final ArrayList<WindowContainerToken> taskParents = new ArrayList<>(); final ArrayList<WindowContainerToken> taskParents = new ArrayList<>();
for (TransitionInfo.Change change : info.getChanges()) { for (TransitionInfo.Change change : info.getChanges()) {
if ((change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0) { if ((change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0) {
@@ -110,92 +102,40 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
onOpenTransitionReady(change, startT, finishT); onOpenTransitionReady(change, startT, finishT);
break; break;
case WindowManager.TRANSIT_CLOSE: { case WindowManager.TRANSIT_CLOSE: {
onCloseTransitionReady(change, windowDecors, startT, finishT); taskInfoList.add(change.getTaskInfo());
onCloseTransitionReady(change, startT, finishT);
break; break;
} }
case WindowManager.TRANSIT_CHANGE: case WindowManager.TRANSIT_CHANGE:
onChangeTransitionReady(info.getType(), change, startT, finishT); onChangeTransitionReady(change, startT, finishT);
break; break;
} }
} }
if (!windowDecors.isEmpty()) { mTransitionToTaskInfo.put(transition, taskInfoList);
mTransitionToWindowDecors.put(transition, windowDecors);
}
} }
private void onOpenTransitionReady( private void onOpenTransitionReady(
TransitionInfo.Change change, TransitionInfo.Change change,
SurfaceControl.Transaction startT, SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) { SurfaceControl.Transaction finishT) {
switch (change.getTaskInfo().getWindowingMode()){ mWindowDecorViewModel.createWindowDecoration(
case WINDOWING_MODE_FREEFORM: change.getTaskInfo(), change.getLeash(), startT, finishT);
mFreeformTaskListener.createWindowDecoration(change, startT, finishT);
break;
case WINDOWING_MODE_FULLSCREEN:
mFullscreenTaskListener.createWindowDecoration(change, startT, finishT);
break;
}
} }
private void onCloseTransitionReady( private void onCloseTransitionReady(
TransitionInfo.Change change, TransitionInfo.Change change,
ArrayList<AutoCloseable> windowDecors,
SurfaceControl.Transaction startT, SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) { SurfaceControl.Transaction finishT) {
final AutoCloseable windowDecor; mWindowDecorViewModel.setupWindowDecorationForTransition(
switch (change.getTaskInfo().getWindowingMode()) { change.getTaskInfo(), startT, finishT);
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( private void onChangeTransitionReady(
int type,
TransitionInfo.Change change, TransitionInfo.Change change,
SurfaceControl.Transaction startT, SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) { SurfaceControl.Transaction finishT) {
AutoCloseable windowDecor = null; mWindowDecorViewModel.setupWindowDecorationForTransition(
boolean adopted = false;
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
windowDecor = mFreeformTaskListener.giveWindowDecoration(
change.getTaskInfo(), startT, finishT); change.getTaskInfo(), startT, finishT);
if (windowDecor != null) {
adopted = mFullscreenTaskListener.adoptWindowDecoration(
change, startT, finishT, windowDecor);
} else {
// will return false if it already has the window decor.
adopted = mFullscreenTaskListener.createWindowDecoration(change, startT, finishT);
}
}
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
windowDecor = mFullscreenTaskListener.giveWindowDecoration(
change.getTaskInfo(), startT, finishT);
if (windowDecor != null) {
adopted = mFreeformTaskListener.adoptWindowDecoration(
change, startT, finishT, windowDecor);
} else {
// will return false if it already has the window decor.
adopted = mFreeformTaskListener.createWindowDecoration(change, startT, finishT);
}
}
if (!adopted) {
releaseWindowDecor(windowDecor);
}
} }
@Override @Override
@@ -203,43 +143,32 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
@Override @Override
public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) { public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) {
final List<AutoCloseable> windowDecorsOfMerged = mTransitionToWindowDecors.get(merged); final List<ActivityManager.RunningTaskInfo> infoOfMerged =
if (windowDecorsOfMerged == null) { mTransitionToTaskInfo.get(merged);
if (infoOfMerged == null) {
// We are adding window decorations of the merged transition to them of the playing // 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. // transition so if there is none of them there is nothing to do.
return; return;
} }
mTransitionToWindowDecors.remove(merged); mTransitionToTaskInfo.remove(merged);
final List<AutoCloseable> windowDecorsOfPlaying = mTransitionToWindowDecors.get(playing); final List<ActivityManager.RunningTaskInfo> infoOfPlaying =
if (windowDecorsOfPlaying != null) { mTransitionToTaskInfo.get(playing);
windowDecorsOfPlaying.addAll(windowDecorsOfMerged); if (infoOfPlaying != null) {
infoOfPlaying.addAll(infoOfMerged);
} else { } else {
mTransitionToWindowDecors.put(playing, windowDecorsOfMerged); mTransitionToTaskInfo.put(playing, infoOfMerged);
} }
} }
@Override @Override
public void onTransitionFinished(@NonNull IBinder transition, boolean aborted) { public void onTransitionFinished(@NonNull IBinder transition, boolean aborted) {
final List<AutoCloseable> windowDecors = mTransitionToWindowDecors.getOrDefault( final List<ActivityManager.RunningTaskInfo> taskInfo =
transition, Collections.emptyList()); mTransitionToTaskInfo.getOrDefault(transition, Collections.emptyList());
mTransitionToWindowDecors.remove(transition); mTransitionToTaskInfo.remove(transition);
for (AutoCloseable windowDecor : windowDecors) { for (int i = 0; i < taskInfo.size(); ++i) {
releaseWindowDecor(windowDecor); mWindowDecorViewModel.destroyWindowDecoration(taskInfo.get(i));
}
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);
} }
} }
} }

View File

@@ -22,13 +22,10 @@ import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManager.RunningTaskInfo;
import android.graphics.Point; import android.graphics.Point;
import android.util.Log;
import android.util.SparseArray; import android.util.SparseArray;
import android.view.SurfaceControl; import android.view.SurfaceControl;
import android.window.TransitionInfo;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.protolog.common.ProtoLog; import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTaskOrganizer;
@@ -46,23 +43,20 @@ import java.util.Optional;
* Organizes tasks presented in {@link android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN}. * Organizes tasks presented in {@link android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN}.
* @param <T> the type of window decoration instance * @param <T> the type of window decoration instance
*/ */
public class FullscreenTaskListener<T extends AutoCloseable> public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
implements ShellTaskOrganizer.TaskListener {
private static final String TAG = "FullscreenTaskListener"; private static final String TAG = "FullscreenTaskListener";
private final ShellTaskOrganizer mShellTaskOrganizer; private final ShellTaskOrganizer mShellTaskOrganizer;
private final SparseArray<State<T>> mTasks = new SparseArray<>(); private final SparseArray<State> mTasks = new SparseArray<>();
private final SparseArray<T> mWindowDecorOfVanishedTasks = new SparseArray<>();
private static class State<T extends AutoCloseable> { private static class State {
RunningTaskInfo mTaskInfo; RunningTaskInfo mTaskInfo;
SurfaceControl mLeash; SurfaceControl mLeash;
T mWindowDecoration;
} }
private final SyncTransactionQueue mSyncQueue; private final SyncTransactionQueue mSyncQueue;
private final Optional<RecentTasksController> mRecentTasksOptional; private final Optional<RecentTasksController> mRecentTasksOptional;
private final Optional<WindowDecorViewModel<T>> mWindowDecorViewModelOptional; private final Optional<WindowDecorViewModel> mWindowDecorViewModelOptional;
/** /**
* This constructor is used by downstream products. * This constructor is used by downstream products.
*/ */
@@ -75,7 +69,7 @@ public class FullscreenTaskListener<T extends AutoCloseable>
ShellTaskOrganizer shellTaskOrganizer, ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue, SyncTransactionQueue syncQueue,
Optional<RecentTasksController> recentTasksOptional, Optional<RecentTasksController> recentTasksOptional,
Optional<WindowDecorViewModel<T>> windowDecorViewModelOptional) { Optional<WindowDecorViewModel> windowDecorViewModelOptional) {
mShellTaskOrganizer = shellTaskOrganizer; mShellTaskOrganizer = shellTaskOrganizer;
mSyncQueue = syncQueue; mSyncQueue = syncQueue;
mRecentTasksOptional = recentTasksOptional; mRecentTasksOptional = recentTasksOptional;
@@ -98,21 +92,21 @@ public class FullscreenTaskListener<T extends AutoCloseable>
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Appeared: #%d", ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Appeared: #%d",
taskInfo.taskId); taskInfo.taskId);
final Point positionInParent = taskInfo.positionInParent; final Point positionInParent = taskInfo.positionInParent;
final State<T> state = new State(); final State state = new State();
state.mLeash = leash; state.mLeash = leash;
state.mTaskInfo = taskInfo; state.mTaskInfo = taskInfo;
mTasks.put(taskInfo.taskId, state); mTasks.put(taskInfo.taskId, state);
if (Transitions.ENABLE_SHELL_TRANSITIONS) return; if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
updateRecentsForVisibleFullscreenTask(taskInfo); updateRecentsForVisibleFullscreenTask(taskInfo);
boolean createdWindowDecor = false;
if (mWindowDecorViewModelOptional.isPresent()) { if (mWindowDecorViewModelOptional.isPresent()) {
SurfaceControl.Transaction t = new SurfaceControl.Transaction(); SurfaceControl.Transaction t = new SurfaceControl.Transaction();
state.mWindowDecoration = createdWindowDecor = mWindowDecorViewModelOptional.get()
mWindowDecorViewModelOptional.get().createWindowDecoration(taskInfo, .createWindowDecoration(taskInfo, leash, t, t);
leash, t, t);
t.apply(); t.apply();
} }
if (state.mWindowDecoration == null) { if (!createdWindowDecor) {
mSyncQueue.runInSync(t -> { mSyncQueue.runInSync(t -> {
// Reset several properties back to fullscreen (PiP, for example, leaves all these // Reset several properties back to fullscreen (PiP, for example, leaves all these
// properties in a bad state). // properties in a bad state).
@@ -127,12 +121,11 @@ public class FullscreenTaskListener<T extends AutoCloseable>
@Override @Override
public void onTaskInfoChanged(RunningTaskInfo taskInfo) { public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
final State<T> state = mTasks.get(taskInfo.taskId); final State state = mTasks.get(taskInfo.taskId);
final Point oldPositionInParent = state.mTaskInfo.positionInParent; final Point oldPositionInParent = state.mTaskInfo.positionInParent;
state.mTaskInfo = taskInfo; state.mTaskInfo = taskInfo;
if (state.mWindowDecoration != null) { if (mWindowDecorViewModelOptional.isPresent()) {
mWindowDecorViewModelOptional.get().onTaskInfoChanged( mWindowDecorViewModelOptional.get().onTaskInfoChanged(state.mTaskInfo);
state.mTaskInfo, state.mWindowDecoration);
} }
if (Transitions.ENABLE_SHELL_TRANSITIONS) return; if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
updateRecentsForVisibleFullscreenTask(taskInfo); updateRecentsForVisibleFullscreenTask(taskInfo);
@@ -147,160 +140,13 @@ public class FullscreenTaskListener<T extends AutoCloseable>
@Override @Override
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { 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;
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Vanished: #%d", ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Vanished: #%d",
taskInfo.taskId); taskInfo.taskId);
mTasks.remove(taskInfo.taskId); mTasks.remove(taskInfo.taskId);
if (Transitions.ENABLE_SHELL_TRANSITIONS) { if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
// Save window decorations of closing tasks so that we can hand them over to the if (mWindowDecorViewModelOptional.isPresent()) {
// transition system if this method happens before the transition. In case where the mWindowDecorViewModelOptional.get().destroyWindowDecoration(taskInfo);
// 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
* @return {@code true} if a decoration was actually created.
*/
public boolean createWindowDecoration(TransitionInfo.Change change,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) {
final State<T> state = createOrUpdateTaskState(change.getTaskInfo(), change.getLeash());
if (!mWindowDecorViewModelOptional.isPresent()) return false;
if (state.mWindowDecoration != null) {
// Already has a decoration.
return false;
}
T newWindowDecor = mWindowDecorViewModelOptional.get().createWindowDecoration(
state.mTaskInfo, state.mLeash, startT, finishT);
if (newWindowDecor != null) {
state.mWindowDecoration = newWindowDecor;
return true;
}
return false;
}
/**
* 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()) {
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 {
T newWindowDecor = mWindowDecorViewModelOptional.get().createWindowDecoration(
state.mTaskInfo, state.mLeash, startT, finishT);
if (newWindowDecor != null) {
state.mWindowDecoration = newWindowDecor;
}
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() && windowDecor != null) {
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) {
if (windowDecor == null) {
return;
}
try {
windowDecor.close();
} catch (Exception e) {
Log.e(TAG, "Failed to release window decoration.", e);
} }
} }
@@ -342,6 +188,4 @@ public class FullscreenTaskListener<T extends AutoCloseable>
public String toString() { public String toString() {
return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_FULLSCREEN); return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_FULLSCREEN);
} }
} }

View File

@@ -26,6 +26,7 @@ import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager; import android.app.ActivityTaskManager;
import android.content.Context; import android.content.Context;
import android.os.Handler; import android.os.Handler;
import android.util.SparseArray;
import android.view.Choreographer; import android.view.Choreographer;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.SurfaceControl; import android.view.SurfaceControl;
@@ -46,7 +47,7 @@ import com.android.wm.shell.transition.Transitions;
* View model for the window decoration with a caption and shadows. Works with * View model for the window decoration with a caption and shadows. Works with
* {@link CaptionWindowDecoration}. * {@link CaptionWindowDecoration}.
*/ */
public class CaptionWindowDecorViewModel implements WindowDecorViewModel<CaptionWindowDecoration> { public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
private final ActivityTaskManager mActivityTaskManager; private final ActivityTaskManager mActivityTaskManager;
private final ShellTaskOrganizer mTaskOrganizer; private final ShellTaskOrganizer mTaskOrganizer;
private final Context mContext; private final Context mContext;
@@ -57,6 +58,8 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel<Caption
private FreeformTaskTransitionStarter mTransitionStarter; private FreeformTaskTransitionStarter mTransitionStarter;
private DesktopModeController mDesktopModeController; private DesktopModeController mDesktopModeController;
private final SparseArray<CaptionWindowDecoration> mWindowDecorByTaskId = new SparseArray<>();
public CaptionWindowDecorViewModel( public CaptionWindowDecorViewModel(
Context context, Context context,
Handler mainHandler, Handler mainHandler,
@@ -81,12 +84,12 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel<Caption
} }
@Override @Override
public CaptionWindowDecoration createWindowDecoration( public boolean createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo, ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface, SurfaceControl taskSurface,
SurfaceControl.Transaction startT, SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) { SurfaceControl.Transaction finishT) {
if (!shouldShowWindowDecor(taskInfo)) return null; if (!shouldShowWindowDecor(taskInfo)) return false;
final CaptionWindowDecoration windowDecoration = new CaptionWindowDecoration( final CaptionWindowDecoration windowDecoration = new CaptionWindowDecoration(
mContext, mContext,
mDisplayController, mDisplayController,
@@ -96,30 +99,24 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel<Caption
mMainHandler, mMainHandler,
mMainChoreographer, mMainChoreographer,
mSyncQueue); mSyncQueue);
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
TaskPositioner taskPositioner = new TaskPositioner(mTaskOrganizer, windowDecoration); TaskPositioner taskPositioner = new TaskPositioner(mTaskOrganizer, windowDecoration);
CaptionTouchEventListener touchEventListener = CaptionTouchEventListener touchEventListener =
new CaptionTouchEventListener(taskInfo, taskPositioner); new CaptionTouchEventListener(taskInfo, taskPositioner);
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener); windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
windowDecoration.setDragResizeCallback(taskPositioner); windowDecoration.setDragResizeCallback(taskPositioner);
setupWindowDecorationForTransition(taskInfo, startT, finishT, windowDecoration); setupWindowDecorationForTransition(taskInfo, startT, finishT);
setupCaptionColor(taskInfo, windowDecoration); setupCaptionColor(taskInfo, windowDecoration);
return windowDecoration; return true;
} }
@Override @Override
public CaptionWindowDecoration adoptWindowDecoration(AutoCloseable windowDecor) { public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
if (!(windowDecor instanceof CaptionWindowDecoration)) return null; final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
final CaptionWindowDecoration captionWindowDecor = (CaptionWindowDecoration) windowDecor; if (decoration == null) return;
if (!shouldShowWindowDecor(captionWindowDecor.mTaskInfo)) {
return null;
}
return captionWindowDecor;
}
@Override
public void onTaskInfoChanged(RunningTaskInfo taskInfo, CaptionWindowDecoration decoration) {
decoration.relayout(taskInfo); decoration.relayout(taskInfo);
setupCaptionColor(taskInfo, decoration); setupCaptionColor(taskInfo, decoration);
} }
@@ -132,11 +129,22 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel<Caption
public void setupWindowDecorationForTransition( public void setupWindowDecorationForTransition(
RunningTaskInfo taskInfo, RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT, SurfaceControl.Transaction finishT) {
CaptionWindowDecoration decoration) { final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
if (decoration == null) return;
decoration.relayout(taskInfo, startT, finishT); decoration.relayout(taskInfo, startT, finishT);
} }
@Override
public void destroyWindowDecoration(RunningTaskInfo taskInfo) {
final CaptionWindowDecoration decoration =
mWindowDecorByTaskId.removeReturnOld(taskInfo.taskId);
if (decoration == null) return;
decoration.close();
}
private class CaptionTouchEventListener implements private class CaptionTouchEventListener implements
View.OnClickListener, View.OnTouchListener { View.OnClickListener, View.OnTouchListener {

View File

@@ -19,8 +19,6 @@ package com.android.wm.shell.windowdecor;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.view.SurfaceControl; import android.view.SurfaceControl;
import androidx.annotation.Nullable;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
/** /**
@@ -28,10 +26,8 @@ import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
* customize {@link WindowDecoration}. Its implementations are responsible to interpret user's * customize {@link WindowDecoration}. Its implementations are responsible to interpret user's
* interactions with UI widgets in window decorations and send corresponding requests to system * interactions with UI widgets in window decorations and send corresponding requests to system
* servers. * servers.
*
* @param <T> The actual decoration type
*/ */
public interface WindowDecorViewModel<T extends AutoCloseable> { public interface WindowDecorViewModel {
/** /**
* Sets the transition starter that starts freeform task transitions. * Sets the transition starter that starts freeform task transitions.
@@ -50,29 +46,19 @@ public interface WindowDecorViewModel<T extends AutoCloseable> {
* @param finishT the finish transaction to restore states after the transition * @param finishT the finish transaction to restore states after the transition
* @return the window decoration object * @return the window decoration object
*/ */
@Nullable T createWindowDecoration( boolean createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo, ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface, SurfaceControl taskSurface,
SurfaceControl.Transaction startT, SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT); SurfaceControl.Transaction finishT);
/**
* Adopts the window decoration if possible.
* May be {@code null} if a window decor is not needed or the given one is incompatible.
*
* @param windowDecor the potential window decoration to adopt
* @return the window decoration if it can be adopted, or {@code null} otherwise.
*/
@Nullable T adoptWindowDecoration(@Nullable AutoCloseable windowDecor);
/** /**
* Notifies a task info update on the given task, with the window decoration created previously * Notifies a task info update on the given task, with the window decoration created previously
* for this task by {@link #createWindowDecoration}. * for this task by {@link #createWindowDecoration}.
* *
* @param taskInfo the new task info of the task * @param taskInfo the new task info of the task
* @param windowDecoration the window decoration created for the task
*/ */
void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo, T windowDecoration); void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo);
/** /**
* Notifies a transition is about to start about the given task to give the window decoration a * Notifies a transition is about to start about the given task to give the window decoration a
@@ -80,11 +66,16 @@ public interface WindowDecorViewModel<T extends AutoCloseable> {
* *
* @param startT the start transaction to be applied before the transition * @param startT the start transaction to be applied before the transition
* @param finishT the finish transaction to restore states after the transition * @param finishT the finish transaction to restore states after the transition
* @param windowDecoration the window decoration created for the task
*/ */
void setupWindowDecorationForTransition( void setupWindowDecorationForTransition(
ActivityManager.RunningTaskInfo taskInfo, ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT, SurfaceControl.Transaction finishT);
T windowDecoration);
/**
* Destroys the window decoration of the give task.
*
* @param taskInfo the info of the task
*/
void destroyWindowDecoration(ActivityManager.RunningTaskInfo taskInfo);
} }

View File

@@ -17,17 +17,10 @@
package com.android.wm.shell.freeform; package com.android.wm.shell.freeform;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_OPEN;
import static com.android.wm.shell.transition.Transitions.TRANSIT_MAXIMIZE;
import static com.android.wm.shell.transition.Transitions.TRANSIT_RESTORE_FROM_MAXIMIZE;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.same; import static org.mockito.Mockito.same;
@@ -44,9 +37,9 @@ import android.window.WindowContainerToken;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import com.android.wm.shell.fullscreen.FullscreenTaskListener;
import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -65,9 +58,7 @@ public class FreeformTaskTransitionObserverTest {
@Mock @Mock
private Transitions mTransitions; private Transitions mTransitions;
@Mock @Mock
private FullscreenTaskListener<?> mFullscreenTaskListener; private WindowDecorViewModel mWindowDecorViewModel;
@Mock
private FreeformTaskListener<?> mFreeformTaskListener;
private FreeformTaskTransitionObserver mTransitionObserver; private FreeformTaskTransitionObserver mTransitionObserver;
@@ -82,7 +73,7 @@ public class FreeformTaskTransitionObserverTest {
doReturn(pm).when(context).getPackageManager(); doReturn(pm).when(context).getPackageManager();
mTransitionObserver = new FreeformTaskTransitionObserver( mTransitionObserver = new FreeformTaskTransitionObserver(
context, mShellInit, mTransitions, mFullscreenTaskListener, mFreeformTaskListener); context, mShellInit, mTransitions, mWindowDecorViewModel);
if (Transitions.ENABLE_SHELL_TRANSITIONS) { if (Transitions.ENABLE_SHELL_TRANSITIONS) {
final ArgumentCaptor<Runnable> initRunnableCaptor = ArgumentCaptor.forClass( final ArgumentCaptor<Runnable> initRunnableCaptor = ArgumentCaptor.forClass(
Runnable.class); Runnable.class);
@@ -112,11 +103,12 @@ public class FreeformTaskTransitionObserverTest {
mTransitionObserver.onTransitionReady(transition, info, startT, finishT); mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
mTransitionObserver.onTransitionStarting(transition); mTransitionObserver.onTransitionStarting(transition);
verify(mFreeformTaskListener).createWindowDecoration(change, startT, finishT); verify(mWindowDecorViewModel).createWindowDecoration(
change.getTaskInfo(), change.getLeash(), startT, finishT);
} }
@Test @Test
public void testObtainsWindowDecorOnCloseTransition_freeform() { public void testPreparesWindowDecorOnCloseTransition_freeform() {
final TransitionInfo.Change change = final TransitionInfo.Change change =
createChange(TRANSIT_CLOSE, 1, WINDOWING_MODE_FREEFORM); createChange(TRANSIT_CLOSE, 1, WINDOWING_MODE_FREEFORM);
final TransitionInfo info = new TransitionInfo(TRANSIT_CLOSE, 0); final TransitionInfo info = new TransitionInfo(TRANSIT_CLOSE, 0);
@@ -128,7 +120,8 @@ public class FreeformTaskTransitionObserverTest {
mTransitionObserver.onTransitionReady(transition, info, startT, finishT); mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
mTransitionObserver.onTransitionStarting(transition); mTransitionObserver.onTransitionStarting(transition);
verify(mFreeformTaskListener).giveWindowDecoration(change.getTaskInfo(), startT, finishT); verify(mWindowDecorViewModel).setupWindowDecorationForTransition(
change.getTaskInfo(), startT, finishT);
} }
@Test @Test
@@ -138,17 +131,13 @@ public class FreeformTaskTransitionObserverTest {
final TransitionInfo info = new TransitionInfo(TRANSIT_CLOSE, 0); final TransitionInfo info = new TransitionInfo(TRANSIT_CLOSE, 0);
info.addChange(change); info.addChange(change);
final AutoCloseable windowDecor = mock(AutoCloseable.class);
doReturn(windowDecor).when(mFreeformTaskListener).giveWindowDecoration(
eq(change.getTaskInfo()), any(), any());
final IBinder transition = mock(IBinder.class); final IBinder transition = mock(IBinder.class);
final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class); final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
mTransitionObserver.onTransitionReady(transition, info, startT, finishT); mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
mTransitionObserver.onTransitionStarting(transition); mTransitionObserver.onTransitionStarting(transition);
verify(windowDecor, never()).close(); verify(mWindowDecorViewModel, never()).destroyWindowDecoration(change.getTaskInfo());
} }
@Test @Test
@@ -159,8 +148,6 @@ public class FreeformTaskTransitionObserverTest {
info.addChange(change); info.addChange(change);
final AutoCloseable windowDecor = mock(AutoCloseable.class); final AutoCloseable windowDecor = mock(AutoCloseable.class);
doReturn(windowDecor).when(mFreeformTaskListener).giveWindowDecoration(
eq(change.getTaskInfo()), any(), any());
final IBinder transition = mock(IBinder.class); final IBinder transition = mock(IBinder.class);
final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
@@ -169,7 +156,7 @@ public class FreeformTaskTransitionObserverTest {
mTransitionObserver.onTransitionStarting(transition); mTransitionObserver.onTransitionStarting(transition);
mTransitionObserver.onTransitionFinished(transition, false); mTransitionObserver.onTransitionFinished(transition, false);
verify(windowDecor).close(); verify(mWindowDecorViewModel).destroyWindowDecoration(change.getTaskInfo());
} }
@Test @Test
@@ -192,10 +179,6 @@ public class FreeformTaskTransitionObserverTest {
final TransitionInfo info2 = new TransitionInfo(TRANSIT_CLOSE, 0); final TransitionInfo info2 = new TransitionInfo(TRANSIT_CLOSE, 0);
info2.addChange(change2); info2.addChange(change2);
final AutoCloseable windowDecor2 = mock(AutoCloseable.class);
doReturn(windowDecor2).when(mFreeformTaskListener).giveWindowDecoration(
eq(change2.getTaskInfo()), any(), any());
final IBinder transition2 = mock(IBinder.class); final IBinder transition2 = mock(IBinder.class);
final SurfaceControl.Transaction startT2 = mock(SurfaceControl.Transaction.class); final SurfaceControl.Transaction startT2 = mock(SurfaceControl.Transaction.class);
final SurfaceControl.Transaction finishT2 = mock(SurfaceControl.Transaction.class); final SurfaceControl.Transaction finishT2 = mock(SurfaceControl.Transaction.class);
@@ -204,7 +187,7 @@ public class FreeformTaskTransitionObserverTest {
mTransitionObserver.onTransitionFinished(transition1, false); mTransitionObserver.onTransitionFinished(transition1, false);
verify(windowDecor2).close(); verify(mWindowDecorViewModel).destroyWindowDecoration(change2.getTaskInfo());
} }
@Test @Test
@@ -215,10 +198,6 @@ public class FreeformTaskTransitionObserverTest {
final TransitionInfo info1 = new TransitionInfo(TRANSIT_CLOSE, 0); final TransitionInfo info1 = new TransitionInfo(TRANSIT_CLOSE, 0);
info1.addChange(change1); info1.addChange(change1);
final AutoCloseable windowDecor1 = mock(AutoCloseable.class);
doReturn(windowDecor1).when(mFreeformTaskListener).giveWindowDecoration(
eq(change1.getTaskInfo()), any(), any());
final IBinder transition1 = mock(IBinder.class); final IBinder transition1 = mock(IBinder.class);
final SurfaceControl.Transaction startT1 = mock(SurfaceControl.Transaction.class); final SurfaceControl.Transaction startT1 = mock(SurfaceControl.Transaction.class);
final SurfaceControl.Transaction finishT1 = mock(SurfaceControl.Transaction.class); final SurfaceControl.Transaction finishT1 = mock(SurfaceControl.Transaction.class);
@@ -231,10 +210,6 @@ public class FreeformTaskTransitionObserverTest {
final TransitionInfo info2 = new TransitionInfo(TRANSIT_CLOSE, 0); final TransitionInfo info2 = new TransitionInfo(TRANSIT_CLOSE, 0);
info2.addChange(change2); info2.addChange(change2);
final AutoCloseable windowDecor2 = mock(AutoCloseable.class);
doReturn(windowDecor2).when(mFreeformTaskListener).giveWindowDecoration(
eq(change2.getTaskInfo()), any(), any());
final IBinder transition2 = mock(IBinder.class); final IBinder transition2 = mock(IBinder.class);
final SurfaceControl.Transaction startT2 = mock(SurfaceControl.Transaction.class); final SurfaceControl.Transaction startT2 = mock(SurfaceControl.Transaction.class);
final SurfaceControl.Transaction finishT2 = mock(SurfaceControl.Transaction.class); final SurfaceControl.Transaction finishT2 = mock(SurfaceControl.Transaction.class);
@@ -243,48 +218,8 @@ public class FreeformTaskTransitionObserverTest {
mTransitionObserver.onTransitionFinished(transition1, false); mTransitionObserver.onTransitionFinished(transition1, false);
verify(windowDecor1).close(); verify(mWindowDecorViewModel).destroyWindowDecoration(change1.getTaskInfo());
verify(windowDecor2).close(); verify(mWindowDecorViewModel).destroyWindowDecoration(change2.getTaskInfo());
}
@Test
public void testTransfersWindowDecorOnMaximize() {
final TransitionInfo.Change change =
createChange(TRANSIT_CHANGE, 1, WINDOWING_MODE_FULLSCREEN);
final TransitionInfo info = new TransitionInfo(TRANSIT_MAXIMIZE, 0);
info.addChange(change);
final AutoCloseable windowDecor = mock(AutoCloseable.class);
doReturn(windowDecor).when(mFreeformTaskListener).giveWindowDecoration(
eq(change.getTaskInfo()), any(), any());
final IBinder transition = mock(IBinder.class);
final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
mTransitionObserver.onTransitionStarting(transition);
verify(mFreeformTaskListener).giveWindowDecoration(change.getTaskInfo(), startT, finishT);
verify(mFullscreenTaskListener).adoptWindowDecoration(
eq(change), same(startT), same(finishT), any());
}
@Test
public void testTransfersWindowDecorOnRestoreFromMaximize() {
final TransitionInfo.Change change =
createChange(TRANSIT_CHANGE, 1, WINDOWING_MODE_FREEFORM);
final TransitionInfo info = new TransitionInfo(TRANSIT_RESTORE_FROM_MAXIMIZE, 0);
info.addChange(change);
final IBinder transition = mock(IBinder.class);
final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
mTransitionObserver.onTransitionStarting(transition);
verify(mFullscreenTaskListener).giveWindowDecoration(change.getTaskInfo(), startT, finishT);
verify(mFreeformTaskListener).adoptWindowDecoration(
eq(change), same(startT), same(finishT), any());
} }
private static TransitionInfo.Change createChange(int mode, int taskId, int windowingMode) { private static TransitionInfo.Change createChange(int mode, int taskId, int windowingMode) {