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

Commit 203679cd authored by Riddle Hsu's avatar Riddle Hsu Committed by Android (Google) Code Review
Browse files

Merge "Handle animating exit windows with shell transition" into udc-dev

parents 78d12d31 745db83b
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -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(),
+15 −0
Original line number Original line Diff line number Diff line
@@ -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()) {
+20 −32
Original line number Original line Diff line number Diff line
@@ -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,
+3 −13
Original line number Original line Diff line number Diff line
@@ -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;
        }
        }
+21 −10
Original line number Original line Diff line number Diff line
@@ -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