Merge "Handle animating exit windows with shell transition" into udc-dev
This commit is contained in:
@@ -220,7 +220,7 @@ class TaskSnapshotController extends AbsAppSnapshotController<Task, TaskSnapshot
|
|||||||
}
|
}
|
||||||
final WindowState imeWindow = task.getDisplayContent().mInputMethodWindow;
|
final WindowState imeWindow = task.getDisplayContent().mInputMethodWindow;
|
||||||
ScreenCapture.ScreenshotHardwareBuffer imeBuffer = null;
|
ScreenCapture.ScreenshotHardwareBuffer imeBuffer = null;
|
||||||
if (imeWindow != null && imeWindow.isWinVisibleLw()) {
|
if (imeWindow != null && imeWindow.isVisible()) {
|
||||||
final Rect bounds = imeWindow.getParentFrame();
|
final Rect bounds = imeWindow.getParentFrame();
|
||||||
bounds.offsetTo(0, 0);
|
bounds.offsetTo(0, 0);
|
||||||
imeBuffer = ScreenCapture.captureLayersExcluding(imeWindow.getSurfaceControl(),
|
imeBuffer = ScreenCapture.captureLayersExcluding(imeWindow.getSurfaceControl(),
|
||||||
|
|||||||
@@ -108,6 +108,12 @@ class TransitionController {
|
|||||||
*/
|
*/
|
||||||
private final ArrayList<Transition> mPlayingTransitions = new ArrayList<>();
|
private final ArrayList<Transition> mPlayingTransitions = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The windows that request to be invisible while it is in transition. After the transition
|
||||||
|
* is finished and the windows are no longer animating, their surfaces will be destroyed.
|
||||||
|
*/
|
||||||
|
final ArrayList<WindowState> mAnimatingExitWindows = new ArrayList<>();
|
||||||
|
|
||||||
final Lock mRunningLock = new Lock();
|
final Lock mRunningLock = new Lock();
|
||||||
|
|
||||||
private final IBinder.DeathRecipient mTransitionPlayerDeath;
|
private final IBinder.DeathRecipient mTransitionPlayerDeath;
|
||||||
@@ -664,6 +670,15 @@ class TransitionController {
|
|||||||
mPlayingTransitions.remove(record);
|
mPlayingTransitions.remove(record);
|
||||||
updateRunningRemoteAnimation(record, false /* isPlaying */);
|
updateRunningRemoteAnimation(record, false /* isPlaying */);
|
||||||
record.finishTransition();
|
record.finishTransition();
|
||||||
|
for (int i = mAnimatingExitWindows.size() - 1; i >= 0; i--) {
|
||||||
|
final WindowState w = mAnimatingExitWindows.get(i);
|
||||||
|
if (w.mAnimatingExit && w.mHasSurface && !w.inTransition()) {
|
||||||
|
w.onExitAnimationDone();
|
||||||
|
}
|
||||||
|
if (!w.mAnimatingExit || !w.mHasSurface) {
|
||||||
|
mAnimatingExitWindows.remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
mRunningLock.doNotifyLocked();
|
mRunningLock.doNotifyLocked();
|
||||||
// Run state-validation checks when no transitions are active anymore.
|
// Run state-validation checks when no transitions are active anymore.
|
||||||
if (!inTransition()) {
|
if (!inTransition()) {
|
||||||
|
|||||||
@@ -2426,7 +2426,7 @@ public class WindowManagerService extends IWindowManager.Stub
|
|||||||
if (wallpaperMayMove) {
|
if (wallpaperMayMove) {
|
||||||
displayContent.mWallpaperController.adjustWallpaperWindows();
|
displayContent.mWallpaperController.adjustWallpaperWindows();
|
||||||
}
|
}
|
||||||
focusMayChange = tryStartExitingAnimation(win, winAnimator, focusMayChange);
|
tryStartExitingAnimation(win, winAnimator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2611,8 +2611,7 @@ public class WindowManagerService extends IWindowManager.Stub
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean tryStartExitingAnimation(WindowState win, WindowStateAnimator winAnimator,
|
private void tryStartExitingAnimation(WindowState win, WindowStateAnimator winAnimator) {
|
||||||
boolean focusMayChange) {
|
|
||||||
// Try starting an animation; if there isn't one, we
|
// Try starting an animation; if there isn't one, we
|
||||||
// can destroy the surface right away.
|
// can destroy the surface right away.
|
||||||
int transit = WindowManagerPolicy.TRANSIT_EXIT;
|
int transit = WindowManagerPolicy.TRANSIT_EXIT;
|
||||||
@@ -2620,39 +2619,30 @@ public class WindowManagerService extends IWindowManager.Stub
|
|||||||
transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
|
transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (win.isWinVisibleLw() && win.mDisplayContent.okToAnimate()) {
|
if (win.isVisible() && win.isDisplayed() && win.mDisplayContent.okToAnimate()) {
|
||||||
String reason = null;
|
String reason = null;
|
||||||
if (winAnimator.applyAnimationLocked(transit, false)) {
|
if (winAnimator.applyAnimationLocked(transit, false)) {
|
||||||
// This is a WMCore-driven window animation.
|
// This is a WMCore-driven window animation.
|
||||||
reason = "applyAnimation";
|
reason = "applyAnimation";
|
||||||
focusMayChange = true;
|
} else if (win.isSelfAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION)) {
|
||||||
win.mAnimatingExit = true;
|
// This is already animating via a WMCore-driven window animation.
|
||||||
} else if (
|
reason = "selfAnimating";
|
||||||
// This is already animating via a WMCore-driven window animation
|
} else {
|
||||||
win.isSelfAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION)
|
if (win.mTransitionController.isShellTransitionsEnabled()) {
|
||||||
// Or already animating as part of a legacy app-transition
|
// Already animating as part of a shell-transition. Currently this only handles
|
||||||
|| win.isAnimating(PARENTS | TRANSITION,
|
// activity window because other types should be WMCore-driven.
|
||||||
ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)
|
if ((win.mActivityRecord != null && win.mActivityRecord.inTransition())) {
|
||||||
// Or already animating as part of a shell-transition.
|
win.mTransitionController.mAnimatingExitWindows.add(win);
|
||||||
|| (win.inTransition()
|
reason = "inTransition";
|
||||||
// Filter out non-app windows since transitions don't animate those
|
}
|
||||||
// (but may still "wait" on them for readiness)
|
} else if (win.isAnimating(PARENTS | TRANSITION,
|
||||||
&& (win.mActivityRecord != null || win.mIsWallpaper))) {
|
ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)) {
|
||||||
// TODO(b/247005789): set mAnimatingExit somewhere in shell-transitions setup.
|
// Already animating as part of a legacy app-transition.
|
||||||
reason = "animating";
|
reason = "inLegacyTransition";
|
||||||
win.mAnimatingExit = true;
|
}
|
||||||
} else if (win.mDisplayContent.mWallpaperController.isWallpaperTarget(win)
|
|
||||||
&& win.mAttrs.type != TYPE_NOTIFICATION_SHADE) {
|
|
||||||
reason = "isWallpaperTarget";
|
|
||||||
// If the wallpaper is currently behind this app window, they should be updated
|
|
||||||
// in a transaction to avoid artifacts.
|
|
||||||
// For NotificationShade, sysui is in charge of running window animation and it
|
|
||||||
// updates the client view visibility only after both NotificationShade and the
|
|
||||||
// wallpaper are hidden. So the exit animation is not needed and can destroy its
|
|
||||||
// surface immediately.
|
|
||||||
win.mAnimatingExit = true;
|
|
||||||
}
|
}
|
||||||
if (reason != null) {
|
if (reason != null) {
|
||||||
|
win.mAnimatingExit = true;
|
||||||
ProtoLog.d(WM_DEBUG_ANIM,
|
ProtoLog.d(WM_DEBUG_ANIM,
|
||||||
"Set animatingExit: reason=startExitingAnimation/%s win=%s", reason, win);
|
"Set animatingExit: reason=startExitingAnimation/%s win=%s", reason, win);
|
||||||
}
|
}
|
||||||
@@ -2668,8 +2658,6 @@ public class WindowManagerService extends IWindowManager.Stub
|
|||||||
if (mAccessibilityController.hasCallbacks()) {
|
if (mAccessibilityController.hasCallbacks()) {
|
||||||
mAccessibilityController.onWindowTransition(win, transit);
|
mAccessibilityController.onWindowTransition(win, transit);
|
||||||
}
|
}
|
||||||
|
|
||||||
return focusMayChange;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int createSurfaceControl(SurfaceControl outSurfaceControl, int result,
|
private int createSurfaceControl(SurfaceControl outSurfaceControl, int result,
|
||||||
|
|||||||
@@ -1921,16 +1921,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
|
|||||||
return !isWallpaper || mToken.isVisibleRequested();
|
return !isWallpaper || mToken.isVisibleRequested();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Is this window visible, ignoring its app token? It is not visible if there is no surface,
|
|
||||||
* or we are in the process of running an exit animation that will remove the surface.
|
|
||||||
*/
|
|
||||||
// TODO: Can we consolidate this with #isVisible() or have a more appropriate name for this?
|
|
||||||
boolean isWinVisibleLw() {
|
|
||||||
return (mActivityRecord == null || mActivityRecord.isVisibleRequested()
|
|
||||||
|| mActivityRecord.isAnimating(TRANSITION | PARENTS)) && isVisible();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The same as isVisible(), but follows the current hidden state of the associated app token,
|
* The same as isVisible(), but follows the current hidden state of the associated app token,
|
||||||
* not the pending requested hidden state.
|
* not the pending requested hidden state.
|
||||||
@@ -2530,7 +2520,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we are not currently running the exit animation, we need to see about starting one
|
// If we are not currently running the exit animation, we need to see about starting one
|
||||||
wasVisible = isWinVisibleLw();
|
wasVisible = isVisible();
|
||||||
|
|
||||||
if (keepVisibleDeadWindow) {
|
if (keepVisibleDeadWindow) {
|
||||||
ProtoLog.v(WM_DEBUG_ADD_REMOVE,
|
ProtoLog.v(WM_DEBUG_ADD_REMOVE,
|
||||||
@@ -2556,7 +2546,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
|
|||||||
// look weird if its orientation is changed.
|
// look weird if its orientation is changed.
|
||||||
&& !inRelaunchingActivity();
|
&& !inRelaunchingActivity();
|
||||||
|
|
||||||
if (wasVisible) {
|
if (wasVisible && isDisplayed()) {
|
||||||
final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE;
|
final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE;
|
||||||
|
|
||||||
// Try starting an animation.
|
// Try starting an animation.
|
||||||
@@ -3111,7 +3101,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
|
|||||||
* interacts with it.
|
* interacts with it.
|
||||||
*/
|
*/
|
||||||
private boolean shouldKeepVisibleDeadAppWindow() {
|
private boolean shouldKeepVisibleDeadAppWindow() {
|
||||||
if (!isWinVisibleLw() || mActivityRecord == null || !mActivityRecord.isClientVisible()) {
|
if (!isVisible() || mActivityRecord == null || !mActivityRecord.isClientVisible()) {
|
||||||
// Not a visible app window or the app isn't dead.
|
// Not a visible app window or the app isn't dead.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,12 +49,12 @@ import static com.google.common.truth.Truth.assertThat;
|
|||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.eq;
|
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
@@ -91,7 +91,6 @@ import com.android.compatibility.common.util.AdoptShellPermissionsRule;
|
|||||||
|
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.ExpectedException;
|
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -103,9 +102,6 @@ import org.junit.runner.RunWith;
|
|||||||
@RunWith(WindowTestRunner.class)
|
@RunWith(WindowTestRunner.class)
|
||||||
public class WindowManagerServiceTests extends WindowTestsBase {
|
public class WindowManagerServiceTests extends WindowTestsBase {
|
||||||
|
|
||||||
@Rule
|
|
||||||
public ExpectedException mExpectedException = ExpectedException.none();
|
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public AdoptShellPermissionsRule mAdoptShellPermissionsRule = new AdoptShellPermissionsRule(
|
public AdoptShellPermissionsRule mAdoptShellPermissionsRule = new AdoptShellPermissionsRule(
|
||||||
InstrumentationRegistry.getInstrumentation().getUiAutomation(),
|
InstrumentationRegistry.getInstrumentation().getUiAutomation(),
|
||||||
@@ -198,16 +194,20 @@ public class WindowManagerServiceTests extends WindowTestsBase {
|
|||||||
public void testRelayoutExitingWindow() {
|
public void testRelayoutExitingWindow() {
|
||||||
final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "appWin");
|
final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "appWin");
|
||||||
final WindowSurfaceController surfaceController = mock(WindowSurfaceController.class);
|
final WindowSurfaceController surfaceController = mock(WindowSurfaceController.class);
|
||||||
doReturn(true).when(surfaceController).hasSurface();
|
|
||||||
spyOn(win);
|
|
||||||
doReturn(true).when(win).isAnimationRunningSelfOrParent();
|
|
||||||
win.mWinAnimator.mSurfaceController = surfaceController;
|
win.mWinAnimator.mSurfaceController = surfaceController;
|
||||||
|
win.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
|
||||||
|
doReturn(true).when(surfaceController).hasSurface();
|
||||||
|
spyOn(win.mTransitionController);
|
||||||
|
doReturn(true).when(win.mTransitionController).isShellTransitionsEnabled();
|
||||||
|
doReturn(true).when(win.mTransitionController).inTransition(
|
||||||
|
eq(win.mActivityRecord));
|
||||||
win.mViewVisibility = View.VISIBLE;
|
win.mViewVisibility = View.VISIBLE;
|
||||||
win.mHasSurface = true;
|
win.mHasSurface = true;
|
||||||
win.mActivityRecord.mAppStopped = true;
|
win.mActivityRecord.mAppStopped = true;
|
||||||
win.mActivityRecord.setVisibleRequested(false);
|
|
||||||
win.mActivityRecord.setVisible(false);
|
|
||||||
mWm.mWindowMap.put(win.mClient.asBinder(), win);
|
mWm.mWindowMap.put(win.mClient.asBinder(), win);
|
||||||
|
spyOn(mWm.mWindowPlacerLocked);
|
||||||
|
// Skip unnecessary operations of relayout.
|
||||||
|
doNothing().when(mWm.mWindowPlacerLocked).performSurfacePlacement(anyBoolean());
|
||||||
final int w = 100;
|
final int w = 100;
|
||||||
final int h = 200;
|
final int h = 200;
|
||||||
final ClientWindowFrames outFrames = new ClientWindowFrames();
|
final ClientWindowFrames outFrames = new ClientWindowFrames();
|
||||||
@@ -216,6 +216,17 @@ public class WindowManagerServiceTests extends WindowTestsBase {
|
|||||||
final InsetsState outInsetsState = new InsetsState();
|
final InsetsState outInsetsState = new InsetsState();
|
||||||
final InsetsSourceControl.Array outControls = new InsetsSourceControl.Array();
|
final InsetsSourceControl.Array outControls = new InsetsSourceControl.Array();
|
||||||
final Bundle outBundle = new Bundle();
|
final Bundle outBundle = new Bundle();
|
||||||
|
mWm.relayoutWindow(win.mSession, win.mClient, win.mAttrs, w, h, View.GONE, 0, 0, 0,
|
||||||
|
outFrames, outConfig, outSurfaceControl, outInsetsState, outControls, outBundle);
|
||||||
|
// The window is in transition, so its destruction is deferred.
|
||||||
|
assertTrue(win.mAnimatingExit);
|
||||||
|
assertFalse(win.mDestroying);
|
||||||
|
assertTrue(win.mTransitionController.mAnimatingExitWindows.contains(win));
|
||||||
|
|
||||||
|
win.mAnimatingExit = false;
|
||||||
|
win.mViewVisibility = View.VISIBLE;
|
||||||
|
win.mActivityRecord.setVisibleRequested(false);
|
||||||
|
win.mActivityRecord.setVisible(false);
|
||||||
mWm.relayoutWindow(win.mSession, win.mClient, win.mAttrs, w, h, View.GONE, 0, 0, 0,
|
mWm.relayoutWindow(win.mSession, win.mClient, win.mAttrs, w, h, View.GONE, 0, 0, 0,
|
||||||
outFrames, outConfig, outSurfaceControl, outInsetsState, outControls, outBundle);
|
outFrames, outConfig, outSurfaceControl, outInsetsState, outControls, outBundle);
|
||||||
// Because the window is already invisible, it doesn't need to apply exiting animation
|
// Because the window is already invisible, it doesn't need to apply exiting animation
|
||||||
|
|||||||
Reference in New Issue
Block a user