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

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

Merge "Support independent seamless rotation with shell transition"

parents 381a6345 d8152bdd
Loading
Loading
Loading
Loading
+302 −164

File changed.

Preview size limit exceeded, changes collapsed.

+4 −8
Original line number Diff line number Diff line
@@ -1880,7 +1880,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
        }
        if (mAsyncRotationController == null) {
            mAsyncRotationController = new AsyncRotationController(this);
            mAsyncRotationController.hide();
            mAsyncRotationController.start();
            return true;
        }
        return false;
@@ -1890,7 +1890,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
    void finishAsyncRotationIfPossible() {
        final AsyncRotationController controller = mAsyncRotationController;
        if (controller != null && !mDisplayRotation.hasSeamlessRotatingWindow()) {
            controller.show();
            controller.completeAll();
            mAsyncRotationController = null;
        }
    }
@@ -1898,19 +1898,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
    /** Shows the given window which may be hidden for screen rotation. */
    void finishAsyncRotation(WindowToken windowToken) {
        final AsyncRotationController controller = mAsyncRotationController;
        if (controller != null && controller.show(windowToken)) {
        if (controller != null && controller.completeRotation(windowToken)) {
            mAsyncRotationController = null;
        }
    }

    /** Returns {@code true} if the screen rotation animation needs to wait for the window. */
    boolean shouldSyncRotationChange(WindowState w) {
        if (w.mForceSeamlesslyRotate) {
            // The window should look no different before and after rotation.
            return false;
        }
        final AsyncRotationController controller = mAsyncRotationController;
        return controller == null || !controller.isHandledToken(w.mToken);
        return controller == null || !controller.isAsync(w);
    }

    void notifyInsetsChanged(Consumer<WindowState> dispatchInsetsChanged) {
+15 −12
Original line number Diff line number Diff line
@@ -675,18 +675,7 @@ public class DisplayRotation {
            return false;
        }

        // For the upside down rotation we don't rotate seamlessly as the navigation bar moves
        // position. Note most apps (using orientation:sensor or user as opposed to fullSensor)
        // will not enter the reverse portrait orientation, so actually the orientation won't change
        // at all.
        if (oldRotation == mUpsideDownRotation || newRotation == mUpsideDownRotation) {
            return false;
        }

        // If the navigation bar can't change sides, then it will jump when we change orientations
        // and we don't rotate seamlessly - unless that is allowed, eg. with gesture navigation
        // where the navbar is low-profile enough that this isn't very noticeable.
        if (!mAllowSeamlessRotationDespiteNavBarMoving && !mDisplayPolicy.navigationBarCanMove()) {
        if (!canRotateSeamlessly(oldRotation, newRotation)) {
            return false;
        }

@@ -713,6 +702,20 @@ public class DisplayRotation {
        return true;
    }

    boolean canRotateSeamlessly(int oldRotation, int newRotation) {
        // For the upside down rotation we don't rotate seamlessly as the navigation bar moves
        // position. Note most apps (using orientation:sensor or user as opposed to fullSensor)
        // will not enter the reverse portrait orientation, so actually the orientation won't change
        // at all.
        if (oldRotation == mUpsideDownRotation || newRotation == mUpsideDownRotation) {
            return false;
        }
        // If the navigation bar can't change sides, then it will jump when we change orientations
        // and we don't rotate seamlessly - unless that is allowed, eg. with gesture navigation
        // where the navbar is low-profile enough that this isn't very noticeable.
        return mAllowSeamlessRotationDespiteNavBarMoving || mDisplayPolicy.navigationBarCanMove();
    }

    void markForSeamlessRotation(WindowState w, boolean seamlesslyRotated) {
        if (seamlesslyRotated == w.mSeamlesslyRotated || w.mForceSeamlesslyRotate) {
            return;
+28 −9
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.TRANSIT_CLOSE;
@@ -504,7 +505,12 @@ public class TransitionTests extends WindowTestsBase {
        final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
        final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "navBar");
        final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime");
        final WindowState[] windows = { statusBar, navBar, ime };
        final WindowToken decorToken = new WindowToken.Builder(mWm, mock(IBinder.class),
                TYPE_NAVIGATION_BAR_PANEL).setDisplayContent(mDisplayContent)
                .setRoundedCornerOverlay(true).build();
        final WindowState screenDecor =
                createWindow(null, decorToken.windowType, decorToken, "screenDecor");
        final WindowState[] windows = { statusBar, navBar, ime, screenDecor };
        makeWindowVisible(windows);
        mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
        mDisplayContent.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs);
@@ -523,26 +529,27 @@ public class TransitionTests extends WindowTestsBase {
        player.startTransition();

        assertFalse(statusBar.mToken.inTransition());
        assertFalse(decorToken.inTransition());
        assertTrue(ime.mToken.inTransition());
        assertTrue(task.inTransition());
        assertTrue(asyncRotationController.isTargetToken(decorToken));

        screenDecor.setOrientationChanging(false);
        // Status bar finishes drawing before the start transaction. Its fade-in animation will be
        // executed until the transaction is committed, so it is still in target tokens.
        statusBar.setOrientationChanging(false);
        assertTrue(asyncRotationController.isTargetToken(statusBar.mToken));

        final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class);
        final ArgumentCaptor<SurfaceControl.TransactionCommittedListener> listenerCaptor =
                ArgumentCaptor.forClass(SurfaceControl.TransactionCommittedListener.class);
        player.onTransactionReady(startTransaction);
        final SurfaceControl.TransactionCommittedListener transactionCommittedListener =
                onRotationTransactionReady(player, startTransaction);

        verify(startTransaction).addTransactionCommittedListener(any(), listenerCaptor.capture());
        // The transaction is committed, so fade-in animation for status bar is consumed.
        listenerCaptor.getValue().onTransactionCommitted();
        transactionCommittedListener.onTransactionCommitted();
        assertFalse(asyncRotationController.isTargetToken(statusBar.mToken));

        // Status bar finishes drawing after the start transaction, so its fade-in animation can
        // execute directly.
        // Navigation bar finishes drawing after the start transaction, so its fade-in animation
        // can execute directly.
        navBar.setOrientationChanging(false);
        assertFalse(asyncRotationController.isTargetToken(navBar.mToken));
        assertNull(mDisplayContent.getAsyncRotationController());
@@ -577,7 +584,8 @@ public class TransitionTests extends WindowTestsBase {
        final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class);
        final SurfaceControl leash = statusBar.mToken.getAnimationLeash();
        doReturn(true).when(leash).isValid();
        player.onTransactionReady(startTransaction);
        final SurfaceControl.TransactionCommittedListener transactionCommittedListener =
                onRotationTransactionReady(player, startTransaction);
        // The leash should be unrotated.
        verify(startTransaction).setMatrix(eq(leash), any(), any());

@@ -588,6 +596,8 @@ public class TransitionTests extends WindowTestsBase {
                mock(SurfaceControl.Transaction.class);
        final boolean layoutNeeded = statusBar.finishDrawing(postDrawTransaction);
        assertFalse(layoutNeeded);

        transactionCommittedListener.onTransactionCommitted();
        player.finish();
        // The controller should capture the draw transaction and merge it when preparing to run
        // fade-in animation.
@@ -750,4 +760,13 @@ public class TransitionTests extends WindowTestsBase {
            changes.put(curr, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
        }
    }

    private static SurfaceControl.TransactionCommittedListener onRotationTransactionReady(
            TestTransitionPlayer player, SurfaceControl.Transaction startTransaction) {
        final ArgumentCaptor<SurfaceControl.TransactionCommittedListener> listenerCaptor =
                ArgumentCaptor.forClass(SurfaceControl.TransactionCommittedListener.class);
        player.onTransactionReady(startTransaction);
        verify(startTransaction).addTransactionCommittedListener(any(), listenerCaptor.capture());
        return listenerCaptor.getValue();
    }
}