Split window decor lifecycle to a TransitionObserver

There are several transitions involving window decorations that are
handled by Launcher, but we still need to tie window decorations to the
lifecycle of such transitions to release window decorations at the right
time. Thus we move this part of logic into a new class.

Bug: 241975249
Test: atest FreeformTaskTransitionObserverTest
Change-Id: Iff14be6f1fcb0b8ee7f634d5d41567348fff5c06
This commit is contained in:
Garfield Tan
2022-08-15 13:57:00 -07:00
parent 0626b32460
commit 5959b4cd96
5 changed files with 559 additions and 142 deletions

View File

@@ -55,6 +55,7 @@ import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.freeform.FreeformComponents;
import com.android.wm.shell.freeform.FreeformTaskListener;
import com.android.wm.shell.freeform.FreeformTaskTransitionHandler;
import com.android.wm.shell.freeform.FreeformTaskTransitionObserver;
import com.android.wm.shell.fullscreen.FullscreenTaskListener;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.Pip;
@@ -207,8 +208,10 @@ public abstract class WMShellModule {
@DynamicOverride
static FreeformComponents provideFreeformComponents(
FreeformTaskListener<?> taskListener,
FreeformTaskTransitionHandler transitionHandler) {
return new FreeformComponents(taskListener, Optional.of(transitionHandler));
FreeformTaskTransitionHandler transitionHandler,
FreeformTaskTransitionObserver transitionObserver) {
return new FreeformComponents(
taskListener, Optional.of(transitionHandler), Optional.of(transitionObserver));
}
@WMSingleton
@@ -230,19 +233,22 @@ public abstract class WMShellModule {
@WMSingleton
@Provides
static FreeformTaskTransitionHandler provideFreeformTaskTransitionHandler(
ShellInit shellInit,
Transitions transitions,
WindowDecorViewModel<?> windowDecorViewModel) {
return new FreeformTaskTransitionHandler(shellInit, transitions, windowDecorViewModel);
}
@WMSingleton
@Provides
static FreeformTaskTransitionObserver provideFreeformTaskTransitionObserver(
Context context,
ShellInit shellInit,
Transitions transitions,
WindowDecorViewModel<?> windowDecorViewModel,
FullscreenTaskListener<?> fullscreenTaskListener,
FreeformTaskListener<?> freeformTaskListener) {
// TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
// override for this controller from the base module
ShellInit init = FreeformComponents.isFreeformEnabled(context)
? shellInit
: null;
return new FreeformTaskTransitionHandler(init, transitions,
windowDecorViewModel, fullscreenTaskListener, freeformTaskListener);
return new FreeformTaskTransitionObserver(
context, shellInit, transitions, fullscreenTaskListener, freeformTaskListener);
}
//

View File

@@ -33,16 +33,19 @@ import java.util.Optional;
*/
public class FreeformComponents {
public final ShellTaskOrganizer.TaskListener mTaskListener;
public final Optional<Transitions.TransitionHandler> mTaskTransitionHandler;
public final Optional<Transitions.TransitionHandler> mTransitionHandler;
public final Optional<Transitions.TransitionObserver> mTransitionObserver;
/**
* Creates an instance with the given components.
*/
public FreeformComponents(
ShellTaskOrganizer.TaskListener taskListener,
Optional<Transitions.TransitionHandler> taskTransitionHandler) {
Optional<Transitions.TransitionHandler> transitionHandler,
Optional<Transitions.TransitionObserver> transitionObserver) {
mTaskListener = taskListener;
mTaskTransitionHandler = taskTransitionHandler;
mTransitionHandler = transitionHandler;
mTransitionObserver = transitionObserver;
}
/**

View File

@@ -22,7 +22,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
import android.os.IBinder;
import android.util.Log;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.TransitionInfo;
@@ -32,7 +31,6 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.wm.shell.fullscreen.FullscreenTaskListener;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
@@ -41,17 +39,13 @@ import java.util.ArrayList;
import java.util.List;
/**
* The {@link Transitions.TransitionHandler} that handles freeform task launches, closes,
* maximizing and restoring transitions. It also reports transitions so that window decorations can
* be a part of transitions.
* The {@link Transitions.TransitionHandler} that handles freeform task maximizing and restoring
* transitions.
*/
public class FreeformTaskTransitionHandler
implements Transitions.TransitionHandler, FreeformTaskTransitionStarter {
private static final String TAG = "FreeformTH";
private final Transitions mTransitions;
private final FreeformTaskListener<?> mFreeformTaskListener;
private final FullscreenTaskListener<?> mFullscreenTaskListener;
private final WindowDecorViewModel<?> mWindowDecorViewModel;
private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();
@@ -59,21 +53,16 @@ public class FreeformTaskTransitionHandler
public FreeformTaskTransitionHandler(
ShellInit shellInit,
Transitions transitions,
WindowDecorViewModel<?> windowDecorViewModel,
FullscreenTaskListener<?> fullscreenTaskListener,
FreeformTaskListener<?> freeformTaskListener) {
WindowDecorViewModel<?> windowDecorViewModel) {
mTransitions = transitions;
mFullscreenTaskListener = fullscreenTaskListener;
mFreeformTaskListener = freeformTaskListener;
mWindowDecorViewModel = windowDecorViewModel;
if (shellInit != null && Transitions.ENABLE_SHELL_TRANSITIONS) {
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
shellInit.addInitCallback(this::onInit, this);
}
}
private void onInit() {
mWindowDecorViewModel.setFreeformTaskTransitionStarter(this);
mTransitions.addHandler(this);
}
@Override
@@ -101,13 +90,13 @@ public class FreeformTaskTransitionHandler
mPendingTransitionTokens.add(mTransitions.startTransition(type, wct, this));
}
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startT,
@NonNull SurfaceControl.Transaction finishT,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
boolean transitionHandled = false;
final ArrayList<AutoCloseable> windowDecorsInCloseTransitions = new ArrayList<>();
for (TransitionInfo.Change change : info.getChanges()) {
if ((change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0) {
continue;
@@ -119,22 +108,13 @@ public class FreeformTaskTransitionHandler
}
switch (change.getMode()) {
case WindowManager.TRANSIT_OPEN:
transitionHandled |= startOpenTransition(change, startT, finishT);
break;
case WindowManager.TRANSIT_CLOSE:
transitionHandled |= startCloseTransition(
change, windowDecorsInCloseTransitions, startT, finishT);
break;
case WindowManager.TRANSIT_CHANGE:
transitionHandled |= startChangeTransition(
transition, info.getType(), change, startT, finishT);
transition, info.getType(), change);
break;
case WindowManager.TRANSIT_TO_BACK:
transitionHandled |= startMinimizeTransition(transition);
break;
case WindowManager.TRANSIT_TO_FRONT:
break;
}
}
@@ -146,139 +126,43 @@ public class FreeformTaskTransitionHandler
startT.apply();
mTransitions.getMainExecutor().execute(
() -> finishTransition(windowDecorsInCloseTransitions, finishCallback));
return true;
}
private boolean startOpenTransition(
TransitionInfo.Change change,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
switch (change.getTaskInfo().getWindowingMode()){
case WINDOWING_MODE_FREEFORM:
mFreeformTaskListener.createWindowDecoration(change, startT, finishT);
break;
case WINDOWING_MODE_FULLSCREEN:
mFullscreenTaskListener.createWindowDecoration(change, startT, finishT);
break;
default:
return false;
}
// Intercepted transition to manage the window decorations. Let other handlers animate.
return false;
}
private boolean startCloseTransition(
TransitionInfo.Change change,
ArrayList<AutoCloseable> windowDecors,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
final AutoCloseable windowDecor;
switch (change.getTaskInfo().getWindowingMode()) {
case WINDOWING_MODE_FREEFORM:
windowDecor = mFreeformTaskListener.giveWindowDecoration(change.getTaskInfo(),
startT, finishT);
break;
case WINDOWING_MODE_FULLSCREEN:
windowDecor = mFullscreenTaskListener.giveWindowDecoration(change.getTaskInfo(),
startT, finishT);
break;
default:
windowDecor = null;
}
if (windowDecor != null) {
windowDecors.add(windowDecor);
}
// Intercepted transition to manage the window decorations. Let other handlers animate.
return false;
}
private boolean startMinimizeTransition(IBinder transition) {
if (!mPendingTransitionTokens.contains(transition)) {
return false;
}
() -> finishCallback.onTransitionFinished(null, null));
return true;
}
private boolean startChangeTransition(
IBinder transition,
int type,
TransitionInfo.Change change,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
AutoCloseable windowDecor = null;
TransitionInfo.Change change) {
if (!mPendingTransitionTokens.contains(transition)) {
return false;
}
boolean handled = false;
boolean adopted = false;
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
if (type == Transitions.TRANSIT_MAXIMIZE
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
// TODO: Add maximize animations
handled = true;
windowDecor = mFreeformTaskListener.giveWindowDecoration(
change.getTaskInfo(), startT, finishT);
adopted = mFullscreenTaskListener.adoptWindowDecoration(change,
startT, finishT, windowDecor);
}
if (type == Transitions.TRANSIT_RESTORE_FROM_MAXIMIZE
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
// TODO: Add restore animations
handled = true;
windowDecor = mFullscreenTaskListener.giveWindowDecoration(
change.getTaskInfo(), startT, finishT);
adopted = mFreeformTaskListener.adoptWindowDecoration(change,
startT, finishT, windowDecor);
}
if (!adopted) {
releaseWindowDecor(windowDecor);
}
return handled;
}
private void finishTransition(
ArrayList<AutoCloseable> windowDecorsInCloseTransitions,
Transitions.TransitionFinishCallback finishCallback) {
for (AutoCloseable windowDecor : windowDecorsInCloseTransitions) {
releaseWindowDecor(windowDecor);
}
mFreeformTaskListener.onTaskTransitionFinished();
mFullscreenTaskListener.onTaskTransitionFinished();
finishCallback.onTransitionFinished(null, null);
}
private void releaseWindowDecor(AutoCloseable windowDecor) {
if (windowDecor == null) {
return;
}
try {
windowDecor.close();
} catch (Exception e) {
Log.e(TAG, "Failed to release window decoration.", e);
}
private boolean startMinimizeTransition(IBinder transition) {
return mPendingTransitionTokens.contains(transition);
}
@Nullable
@Override
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@NonNull TransitionRequestInfo request) {
final ActivityManager.RunningTaskInfo taskInfo = request.getTriggerTask();
if (taskInfo == null || taskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
return null;
}
switch (request.getType()) {
case WindowManager.TRANSIT_OPEN:
case WindowManager.TRANSIT_CLOSE:
case WindowManager.TRANSIT_TO_FRONT:
case WindowManager.TRANSIT_TO_BACK:
return new WindowContainerTransaction();
default:
return null;
}
return null;
}
}

View File

@@ -0,0 +1,222 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.wm.shell.freeform;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import android.app.ActivityManager;
import android.content.Context;
import android.os.IBinder;
import android.util.Log;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.TransitionInfo;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.android.wm.shell.fullscreen.FullscreenTaskListener;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* The {@link Transitions.TransitionHandler} that handles freeform task launches, closes,
* maximizing and restoring transitions. It also reports transitions so that window decorations can
* be a part of transitions.
*/
public class FreeformTaskTransitionObserver implements Transitions.TransitionObserver {
private static final String TAG = "FreeformTO";
private final Transitions mTransitions;
private final FreeformTaskListener<?> mFreeformTaskListener;
private final FullscreenTaskListener<?> mFullscreenTaskListener;
private final Map<IBinder, List<AutoCloseable>> mTransitionToWindowDecors = new HashMap<>();
public FreeformTaskTransitionObserver(
Context context,
ShellInit shellInit,
Transitions transitions,
FullscreenTaskListener<?> fullscreenTaskListener,
FreeformTaskListener<?> freeformTaskListener) {
mTransitions = transitions;
mFreeformTaskListener = freeformTaskListener;
mFullscreenTaskListener = fullscreenTaskListener;
if (Transitions.ENABLE_SHELL_TRANSITIONS && FreeformComponents.isFreeformEnabled(context)) {
shellInit.addInitCallback(this::onInit, this);
}
}
@VisibleForTesting
void onInit() {
mTransitions.registerObserver(this);
}
@Override
public void onTransitionReady(
@NonNull IBinder transition,
@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startT,
@NonNull SurfaceControl.Transaction finishT) {
final ArrayList<AutoCloseable> windowDecors = new ArrayList<>();
for (TransitionInfo.Change change : info.getChanges()) {
if ((change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0) {
continue;
}
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
if (taskInfo == null || taskInfo.taskId == -1) {
continue;
}
switch (change.getMode()) {
case WindowManager.TRANSIT_OPEN:
onOpenTransitionReady(change, startT, finishT);
break;
case WindowManager.TRANSIT_CLOSE: {
onCloseTransitionReady(change, windowDecors, startT, finishT);
break;
}
case WindowManager.TRANSIT_CHANGE:
onChangeTransitionReady(info.getType(), change, startT, finishT);
break;
}
}
if (!windowDecors.isEmpty()) {
mTransitionToWindowDecors.put(transition, windowDecors);
}
}
private void onOpenTransitionReady(
TransitionInfo.Change change,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
switch (change.getTaskInfo().getWindowingMode()){
case WINDOWING_MODE_FREEFORM:
mFreeformTaskListener.createWindowDecoration(change, startT, finishT);
break;
case WINDOWING_MODE_FULLSCREEN:
mFullscreenTaskListener.createWindowDecoration(change, startT, finishT);
break;
}
}
private void onCloseTransitionReady(
TransitionInfo.Change change,
ArrayList<AutoCloseable> windowDecors,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
final AutoCloseable windowDecor;
switch (change.getTaskInfo().getWindowingMode()) {
case WINDOWING_MODE_FREEFORM:
windowDecor = mFreeformTaskListener.giveWindowDecoration(change.getTaskInfo(),
startT, finishT);
break;
case WINDOWING_MODE_FULLSCREEN:
windowDecor = mFullscreenTaskListener.giveWindowDecoration(change.getTaskInfo(),
startT, finishT);
break;
default:
windowDecor = null;
}
if (windowDecor != null) {
windowDecors.add(windowDecor);
}
}
private void onChangeTransitionReady(
int type,
TransitionInfo.Change change,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
AutoCloseable windowDecor = null;
boolean adopted = false;
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
if (type == Transitions.TRANSIT_MAXIMIZE
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
windowDecor = mFreeformTaskListener.giveWindowDecoration(
change.getTaskInfo(), startT, finishT);
adopted = mFullscreenTaskListener.adoptWindowDecoration(
change, startT, finishT, windowDecor);
}
if (type == Transitions.TRANSIT_RESTORE_FROM_MAXIMIZE
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
windowDecor = mFullscreenTaskListener.giveWindowDecoration(
change.getTaskInfo(), startT, finishT);
adopted = mFreeformTaskListener.adoptWindowDecoration(
change, startT, finishT, windowDecor);
}
if (!adopted) {
releaseWindowDecor(windowDecor);
}
}
@Override
public void onTransitionStarting(@NonNull IBinder transition) {}
@Override
public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) {
final List<AutoCloseable> windowDecorsOfMerged = mTransitionToWindowDecors.get(merged);
if (windowDecorsOfMerged == null) {
// We are adding window decorations of the merged transition to them of the playing
// transition so if there is none of them there is nothing to do.
return;
}
mTransitionToWindowDecors.remove(merged);
final List<AutoCloseable> windowDecorsOfPlaying = mTransitionToWindowDecors.get(playing);
if (windowDecorsOfPlaying != null) {
windowDecorsOfPlaying.addAll(windowDecorsOfMerged);
} else {
mTransitionToWindowDecors.put(playing, windowDecorsOfMerged);
}
}
@Override
public void onTransitionFinished(@NonNull IBinder transition, boolean aborted) {
final List<AutoCloseable> windowDecors = mTransitionToWindowDecors.getOrDefault(
transition, Collections.emptyList());
mTransitionToWindowDecors.remove(transition);
for (AutoCloseable windowDecor : windowDecors) {
releaseWindowDecor(windowDecor);
}
mFullscreenTaskListener.onTaskTransitionFinished();
mFreeformTaskListener.onTaskTransitionFinished();
}
private static void releaseWindowDecor(AutoCloseable windowDecor) {
if (windowDecor == null) {
return;
}
try {
windowDecor.close();
} catch (Exception e) {
Log.e(TAG, "Failed to release window decoration.", e);
}
}
}

View File

@@ -0,0 +1,302 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.wm.shell.freeform;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
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.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.IBinder;
import android.view.SurfaceControl;
import android.window.IWindowContainerToken;
import android.window.TransitionInfo;
import android.window.WindowContainerToken;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.fullscreen.FullscreenTaskListener;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/**
* Tests of {@link FreeformTaskTransitionObserver}
*/
@SmallTest
public class FreeformTaskTransitionObserverTest {
@Mock
private ShellInit mShellInit;
@Mock
private Transitions mTransitions;
@Mock
private FullscreenTaskListener<?> mFullscreenTaskListener;
@Mock
private FreeformTaskListener<?> mFreeformTaskListener;
private FreeformTaskTransitionObserver mTransitionObserver;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
PackageManager pm = mock(PackageManager.class);
doReturn(true).when(pm).hasSystemFeature(
PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT);
final Context context = mock(Context.class);
doReturn(pm).when(context).getPackageManager();
mTransitionObserver = new FreeformTaskTransitionObserver(
context, mShellInit, mTransitions, mFullscreenTaskListener, mFreeformTaskListener);
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
final ArgumentCaptor<Runnable> initRunnableCaptor = ArgumentCaptor.forClass(
Runnable.class);
verify(mShellInit).addInitCallback(initRunnableCaptor.capture(),
same(mTransitionObserver));
initRunnableCaptor.getValue().run();
} else {
mTransitionObserver.onInit();
}
}
@Test
public void testRegistersObserverAtInit() {
verify(mTransitions).registerObserver(same(mTransitionObserver));
}
@Test
public void testCreatesWindowDecorOnOpenTransition_freeform() {
final TransitionInfo.Change change =
createChange(TRANSIT_OPEN, 1, WINDOWING_MODE_FREEFORM);
final TransitionInfo info = new TransitionInfo(TRANSIT_OPEN, 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(mFreeformTaskListener).createWindowDecoration(change, startT, finishT);
}
@Test
public void testObtainsWindowDecorOnCloseTransition_freeform() {
final TransitionInfo.Change change =
createChange(TRANSIT_CLOSE, 1, WINDOWING_MODE_FREEFORM);
final TransitionInfo info = new TransitionInfo(TRANSIT_CLOSE, 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(mFreeformTaskListener).giveWindowDecoration(change.getTaskInfo(), startT, finishT);
}
@Test
public void testDoesntCloseWindowDecorDuringCloseTransition() throws Exception {
final TransitionInfo.Change change =
createChange(TRANSIT_CLOSE, 1, WINDOWING_MODE_FREEFORM);
final TransitionInfo info = new TransitionInfo(TRANSIT_CLOSE, 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(windowDecor, never()).close();
}
@Test
public void testClosesWindowDecorAfterCloseTransition() throws Exception {
final TransitionInfo.Change change =
createChange(TRANSIT_CLOSE, 1, WINDOWING_MODE_FREEFORM);
final TransitionInfo info = new TransitionInfo(TRANSIT_CLOSE, 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);
mTransitionObserver.onTransitionFinished(transition, false);
verify(windowDecor).close();
}
@Test
public void testClosesMergedWindowDecorationAfterTransitionFinishes() throws Exception {
// The playing transition
final TransitionInfo.Change change1 =
createChange(TRANSIT_OPEN, 1, WINDOWING_MODE_FREEFORM);
final TransitionInfo info1 = new TransitionInfo(TRANSIT_OPEN, 0);
info1.addChange(change1);
final IBinder transition1 = mock(IBinder.class);
final SurfaceControl.Transaction startT1 = mock(SurfaceControl.Transaction.class);
final SurfaceControl.Transaction finishT1 = mock(SurfaceControl.Transaction.class);
mTransitionObserver.onTransitionReady(transition1, info1, startT1, finishT1);
mTransitionObserver.onTransitionStarting(transition1);
// The merged transition
final TransitionInfo.Change change2 =
createChange(TRANSIT_CLOSE, 2, WINDOWING_MODE_FREEFORM);
final TransitionInfo info2 = new TransitionInfo(TRANSIT_CLOSE, 0);
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 SurfaceControl.Transaction startT2 = mock(SurfaceControl.Transaction.class);
final SurfaceControl.Transaction finishT2 = mock(SurfaceControl.Transaction.class);
mTransitionObserver.onTransitionReady(transition2, info2, startT2, finishT2);
mTransitionObserver.onTransitionMerged(transition2, transition1);
mTransitionObserver.onTransitionFinished(transition1, false);
verify(windowDecor2).close();
}
@Test
public void testClosesAllWindowDecorsOnTransitionMergeAfterCloseTransitions() throws Exception {
// The playing transition
final TransitionInfo.Change change1 =
createChange(TRANSIT_CLOSE, 1, WINDOWING_MODE_FREEFORM);
final TransitionInfo info1 = new TransitionInfo(TRANSIT_CLOSE, 0);
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 SurfaceControl.Transaction startT1 = mock(SurfaceControl.Transaction.class);
final SurfaceControl.Transaction finishT1 = mock(SurfaceControl.Transaction.class);
mTransitionObserver.onTransitionReady(transition1, info1, startT1, finishT1);
mTransitionObserver.onTransitionStarting(transition1);
// The merged transition
final TransitionInfo.Change change2 =
createChange(TRANSIT_CLOSE, 2, WINDOWING_MODE_FREEFORM);
final TransitionInfo info2 = new TransitionInfo(TRANSIT_CLOSE, 0);
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 SurfaceControl.Transaction startT2 = mock(SurfaceControl.Transaction.class);
final SurfaceControl.Transaction finishT2 = mock(SurfaceControl.Transaction.class);
mTransitionObserver.onTransitionReady(transition2, info2, startT2, finishT2);
mTransitionObserver.onTransitionMerged(transition2, transition1);
mTransitionObserver.onTransitionFinished(transition1, false);
verify(windowDecor1).close();
verify(windowDecor2).close();
}
@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) {
final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
taskInfo.taskId = taskId;
taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
final TransitionInfo.Change change = new TransitionInfo.Change(
new WindowContainerToken(mock(IWindowContainerToken.class)),
mock(SurfaceControl.class));
change.setMode(mode);
change.setTaskInfo(taskInfo);
return change;
}
}