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

Commit e1fa5a85 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Clean-up change transition when cancelled before anim starts"

parents 520e6616 25b5619a
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -143,7 +143,7 @@ class AppWindowThumbnail implements Animatable {

    void destroy() {
        mSurfaceAnimator.cancelAnimation();
        mSurfaceControl.remove();
        getPendingTransaction().remove(mSurfaceControl);
    }

    /**
+47 −11
Original line number Diff line number Diff line
@@ -1275,7 +1275,19 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
    void onDisplayChanged(DisplayContent dc) {
        DisplayContent prevDc = mDisplayContent;
        super.onDisplayChanged(dc);
        if (prevDc != null && prevDc.mFocusedApp == this) {
        if (prevDc == null) {
            return;
        }
        if (prevDc.mChangingApps.contains(this)) {
            // This gets called *after* the AppWindowToken has been reparented to the new display.
            // That reparenting resulted in this window changing modes (eg. FREEFORM -> FULLSCREEN),
            // so this token is now "frozen" while waiting for the animation to start on prevDc
            // (which will be cancelled since the window is no-longer a child). However, since this
            // is no longer a child of prevDc, this won't be notified of the cancelled animation,
            // so we need to cancel the change transition here.
            clearChangeLeash(getPendingTransaction(), true /* cancel */);
        }
        if (prevDc.mFocusedApp == this) {
            prevDc.setFocusedApp(null);
            final TaskStack stack = dc.getTopStack();
            if (stack != null) {
@@ -1584,7 +1596,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
    }

    private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
        if (!isVisible() || getDisplayContent().mAppTransition.isTransitionSet()) {
        if (mWmService.mDisableTransitionAnimation
                || !isVisible()
                || getDisplayContent().mAppTransition.isTransitionSet()
                || getSurfaceControl() == null) {
            return false;
        }
        // Only do an animation into and out-of freeform mode for now. Other mode
@@ -2561,9 +2576,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
            return;
        } else if (mTransitChangeLeash != null) {
            // unparent mTransitChangeLeash for clean-up
            t.hide(mTransitChangeLeash);
            t.reparent(mTransitChangeLeash, null);
            mTransitChangeLeash = null;
            clearChangeLeash(t, false /* cancel */);
        }

        if (mAnimatingAppWindowTokenRegistry != null) {
@@ -2659,15 +2672,36 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
        return super.isSelfAnimating();
    }

    /**
     * @param cancel {@code true} if clearing the leash due to cancelling instead of transferring
     *                            to another leash.
     */
    private void clearChangeLeash(Transaction t, boolean cancel) {
        if (mTransitChangeLeash == null) {
            return;
        }
        if (cancel) {
            clearThumbnail();
            SurfaceControl sc = getSurfaceControl();
            SurfaceControl parentSc = getParentSurfaceControl();
            // Don't reparent if surface is getting destroyed
            if (parentSc != null && sc != null) {
                t.reparent(sc, getParentSurfaceControl());
            }
        }
        t.hide(mTransitChangeLeash);
        t.reparent(mTransitChangeLeash, null);
        mTransitChangeLeash = null;
        if (cancel) {
            onAnimationLeashDestroyed(t);
        }
    }

    @Override
    void cancelAnimation() {
        cancelAnimationOnly();
        clearThumbnail();
        if (mTransitChangeLeash != null) {
            getPendingTransaction().hide(mTransitChangeLeash);
            getPendingTransaction().reparent(mTransitChangeLeash, null);
            mTransitChangeLeash = null;
        }
        clearChangeLeash(getPendingTransaction(), true /* cancel */);
    }

    /**
@@ -3003,7 +3037,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
    void removeFromPendingTransition() {
        if (isWaitingForTransitionStart() && mDisplayContent != null) {
            mDisplayContent.mOpeningApps.remove(this);
            mDisplayContent.mChangingApps.remove(this);
            if (mDisplayContent.mChangingApps.remove(this)) {
                clearChangeLeash(getPendingTransaction(), true /* cancel */);
            }
            mDisplayContent.mClosingApps.remove(this);
        }
    }
+66 −11
Original line number Diff line number Diff line
@@ -16,24 +16,29 @@

package com.android.server.wm;

import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import android.graphics.Rect;
import android.os.IBinder;
import android.view.Display;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationTarget;

import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;

import org.junit.Before;
import org.junit.Test;

/**
@@ -49,14 +54,20 @@ public class AppChangeTransitionTests extends WindowTestsBase {
    private Task mTask;
    private WindowTestUtils.TestAppWindowToken mToken;

    @Before
    public void setUp() throws Exception {
        mStack = createTaskStackOnDisplay(mDisplayContent);
    public void setUpOnDisplay(DisplayContent dc) {
        mStack = createTaskStackOnDisplay(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, dc);
        mTask = createTaskInStack(mStack, 0 /* userId */);
        mToken = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
        mToken = WindowTestUtils.createTestAppWindowToken(dc);
        mToken.mSkipOnParentChanged = false;

        mTask.addChild(mToken, 0);

        // Set a remote animator with snapshot disabled. Snapshots don't work in wmtests.
        RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
        RemoteAnimationAdapter adapter =
                new RemoteAnimationAdapter(new TestRemoteAnimationRunner(), 10, 1, false);
        definition.addRemoteAnimation(TRANSIT_TASK_CHANGE_WINDOWING_MODE, adapter);
        dc.registerRemoteAnimations(definition);
    }

    class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
@@ -85,14 +96,58 @@ public class AppChangeTransitionTests extends WindowTestsBase {

    @Test
    public void testModeChangeRemoteAnimatorNoSnapshot() {
        RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
        RemoteAnimationAdapter adapter =
                new RemoteAnimationAdapter(new TestRemoteAnimationRunner(), 10, 1, false);
        definition.addRemoteAnimation(TRANSIT_TASK_CHANGE_WINDOWING_MODE, adapter);
        mDisplayContent.registerRemoteAnimations(definition);
        // setup currently defaults to no snapshot.
        setUpOnDisplay(mDisplayContent);

        mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
        assertEquals(1, mDisplayContent.mChangingApps.size());

        // Verify we are in a change transition, but without a snapshot.
        // Though, the test will actually have crashed by now if a snapshot is attempted.
        assertNull(mToken.getThumbnail());
        assertTrue(mToken.isInChangeTransition());

        waitUntilHandlersIdle();
        mToken.removeImmediately();
    }

    @Test
    public void testCancelPendingChangeOnRemove() {
        // setup currently defaults to no snapshot.
        setUpOnDisplay(mDisplayContent);

        mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
        assertEquals(1, mDisplayContent.mChangingApps.size());
        assertTrue(mToken.isInChangeTransition());

        // Removing the app-token from the display should clean-up the
        // the change leash.
        mDisplayContent.removeAppToken(mToken.token);
        assertEquals(0, mDisplayContent.mChangingApps.size());
        assertFalse(mToken.isInChangeTransition());

        waitUntilHandlersIdle();
        mToken.removeImmediately();
    }

    @Test
    public void testNoChangeWhenMoveDisplay() {
        mDisplayContent.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
        final DisplayContent dc1 = createNewDisplay(Display.STATE_ON);
        dc1.setWindowingMode(WINDOWING_MODE_FREEFORM);
        setUpOnDisplay(dc1);

        assertEquals(WINDOWING_MODE_FREEFORM, mTask.getWindowingMode());

        // Reparenting to a display with different windowing mode may trigger
        // a change transition internally, but it should be cleaned-up once
        // the display change is complete.
        mStack.reparent(mDisplayContent.getDisplayId(), new Rect(), true);

        assertEquals(WINDOWING_MODE_FULLSCREEN, mTask.getWindowingMode());

        // Make sure we're not waiting for a change animation (no leash)
        assertFalse(mToken.isInChangeTransition());
        assertNull(mToken.getThumbnail());

        waitUntilHandlersIdle();