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

Commit ab2edb20 authored by Riddle Hsu's avatar Riddle Hsu Committed by Automerger Merge Worker
Browse files

Merge "Handle animating exit windows with shell transition" into udc-dev am: 203679cd

parents 22b113e6 203679cd
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -220,7 +220,7 @@ class TaskSnapshotController extends AbsAppSnapshotController<Task, TaskSnapshot
        }
        final WindowState imeWindow = task.getDisplayContent().mInputMethodWindow;
        ScreenCapture.ScreenshotHardwareBuffer imeBuffer = null;
        if (imeWindow != null && imeWindow.isWinVisibleLw()) {
        if (imeWindow != null && imeWindow.isVisible()) {
            final Rect bounds = imeWindow.getParentFrame();
            bounds.offsetTo(0, 0);
            imeBuffer = ScreenCapture.captureLayersExcluding(imeWindow.getSurfaceControl(),
+15 −0
Original line number Diff line number Diff line
@@ -108,6 +108,12 @@ class TransitionController {
     */
    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();

    private final IBinder.DeathRecipient mTransitionPlayerDeath;
@@ -664,6 +670,15 @@ class TransitionController {
        mPlayingTransitions.remove(record);
        updateRunningRemoteAnimation(record, false /* isPlaying */);
        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();
        // Run state-validation checks when no transitions are active anymore.
        if (!inTransition()) {
+20 −32
Original line number Diff line number Diff line
@@ -2416,7 +2416,7 @@ public class WindowManagerService extends IWindowManager.Stub
                    if (wallpaperMayMove) {
                        displayContent.mWallpaperController.adjustWallpaperWindows();
                    }
                    focusMayChange = tryStartExitingAnimation(win, winAnimator, focusMayChange);
                    tryStartExitingAnimation(win, winAnimator);
                }
            }

@@ -2601,8 +2601,7 @@ public class WindowManagerService extends IWindowManager.Stub
        }
    }

    private boolean tryStartExitingAnimation(WindowState win, WindowStateAnimator winAnimator,
            boolean focusMayChange) {
    private void tryStartExitingAnimation(WindowState win, WindowStateAnimator winAnimator) {
        // Try starting an animation; if there isn't one, we
        // can destroy the surface right away.
        int transit = WindowManagerPolicy.TRANSIT_EXIT;
@@ -2610,39 +2609,30 @@ public class WindowManagerService extends IWindowManager.Stub
            transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
        }

        if (win.isWinVisibleLw() && win.mDisplayContent.okToAnimate()) {
        if (win.isVisible() && win.isDisplayed() && win.mDisplayContent.okToAnimate()) {
            String reason = null;
            if (winAnimator.applyAnimationLocked(transit, false)) {
                // This is a WMCore-driven window animation.
                reason = "applyAnimation";
                focusMayChange = true;
                win.mAnimatingExit = true;
            } else if (
                    // This is already animating via a WMCore-driven window animation
                    win.isSelfAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION)
                    // Or already animating as part of a legacy app-transition
                    || win.isAnimating(PARENTS | TRANSITION,
                            ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)
                    // Or already animating as part of a shell-transition.
                    || (win.inTransition()
                            // Filter out non-app windows since transitions don't animate those
                            // (but may still "wait" on them for readiness)
                            && (win.mActivityRecord != null || win.mIsWallpaper))) {
                // TODO(b/247005789): set mAnimatingExit somewhere in shell-transitions setup.
                reason = "animating";
                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;
            } else if (win.isSelfAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION)) {
                // This is already animating via a WMCore-driven window animation.
                reason = "selfAnimating";
            } else {
                if (win.mTransitionController.isShellTransitionsEnabled()) {
                    // Already animating as part of a shell-transition. Currently this only handles
                    // activity window because other types should be WMCore-driven.
                    if ((win.mActivityRecord != null && win.mActivityRecord.inTransition())) {
                        win.mTransitionController.mAnimatingExitWindows.add(win);
                        reason = "inTransition";
                    }
                } else if (win.isAnimating(PARENTS | TRANSITION,
                        ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)) {
                    // Already animating as part of a legacy app-transition.
                    reason = "inLegacyTransition";
                }
            }
            if (reason != null) {
                win.mAnimatingExit = true;
                ProtoLog.d(WM_DEBUG_ANIM,
                        "Set animatingExit: reason=startExitingAnimation/%s win=%s", reason, win);
            }
@@ -2658,8 +2648,6 @@ public class WindowManagerService extends IWindowManager.Stub
        if (mAccessibilityController.hasCallbacks()) {
            mAccessibilityController.onWindowTransition(win, transit);
        }

        return focusMayChange;
    }

    private int createSurfaceControl(SurfaceControl outSurfaceControl, int result,
+3 −13
Original line number Diff line number Diff line
@@ -1921,16 +1921,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
        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,
     * 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
                wasVisible = isWinVisibleLw();
                wasVisible = isVisible();

                if (keepVisibleDeadWindow) {
                    ProtoLog.v(WM_DEBUG_ADD_REMOVE,
@@ -2556,7 +2546,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
                        // look weird if its orientation is changed.
                        && !inRelaunchingActivity();

                if (wasVisible) {
                if (wasVisible && isDisplayed()) {
                    final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE;

                    // Try starting an animation.
@@ -3111,7 +3101,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
     * interacts with it.
     */
    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.
            return false;
        }
+21 −10
Original line number 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.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -91,7 +91,6 @@ import com.android.compatibility.common.util.AdoptShellPermissionsRule;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;

/**
@@ -103,9 +102,6 @@ import org.junit.runner.RunWith;
@RunWith(WindowTestRunner.class)
public class WindowManagerServiceTests extends WindowTestsBase {

    @Rule
    public ExpectedException mExpectedException = ExpectedException.none();

    @Rule
    public AdoptShellPermissionsRule mAdoptShellPermissionsRule = new AdoptShellPermissionsRule(
            InstrumentationRegistry.getInstrumentation().getUiAutomation(),
@@ -198,16 +194,20 @@ public class WindowManagerServiceTests extends WindowTestsBase {
    public void testRelayoutExitingWindow() {
        final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "appWin");
        final WindowSurfaceController surfaceController = mock(WindowSurfaceController.class);
        doReturn(true).when(surfaceController).hasSurface();
        spyOn(win);
        doReturn(true).when(win).isAnimationRunningSelfOrParent();
        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.mHasSurface = true;
        win.mActivityRecord.mAppStopped = true;
        win.mActivityRecord.setVisibleRequested(false);
        win.mActivityRecord.setVisible(false);
        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 h = 200;
        final ClientWindowFrames outFrames = new ClientWindowFrames();
@@ -216,6 +216,17 @@ public class WindowManagerServiceTests extends WindowTestsBase {
        final InsetsState outInsetsState = new InsetsState();
        final InsetsSourceControl.Array outControls = new InsetsSourceControl.Array();
        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,
                outFrames, outConfig, outSurfaceControl, outInsetsState, outControls, outBundle);
        // Because the window is already invisible, it doesn't need to apply exiting animation