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

Commit 8fea9c8e authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Notify client to clear intermediate rotated adjustments

For example: launch a landscape activity while device is portrait,
and then launch another portrait activity before previous transition
is done. If the landscape activity is destroyed before sending the
fixed rotation adjustment for restoration, the adjustment is always
retained by client side app. That causes the Display objects
associated with application Resources to always return rotation from
the adjustment.

This change notifies client to clear the rotated adjustments if the
the display won't be rotated by the next top activity before the
transition is done, so the app can get the correct rotation in time.
The transform will be cleared at the end of transition animation so
the closing animation won't jump cut with rotation change. E.g.
launch an activity in different orientation and press home or back
key before the launch animation is finished.

Also simply a bit for the path of clearing fixed rotation state to
avoid sending duplicated adjustments.

Bug: 161519202
Bug: 168263090
Bug: 177390830
Test: DisplayContentTests#testClearIntermediateFixedRotationAdjustments

Change-Id: Ica074604df3e74eabbdef931531abe51855103e5
Merged-In: Ica074604df3e74eabbdef931531abe51855103e5
parent 7e2d8b8b
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -1513,6 +1513,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
        }
        final int rotation = rotationForActivityInDifferentOrientation(r);
        if (rotation == ROTATION_UNDEFINED) {
            // The display rotation won't be changed by current top activity. The client side
            // adjustments of previous rotated activity should be cleared earlier. Otherwise if
            // the current top is in the same process, it may get the rotated state. The transform
            // will be cleared later with transition callback to ensure smooth animation.
            if (hasTopFixedRotationLaunchingApp()) {
                mFixedRotationLaunchingApp.notifyFixedRotationTransform(false /* enabled */);
            }
            return false;
        }
        if (!r.getParent().matchParentBounds()) {
+5 −7
Original line number Diff line number Diff line
@@ -622,11 +622,6 @@ class WindowToken extends WindowContainer<WindowState> {
        state.mIsTransforming = false;
        if (applyDisplayRotation != null) {
            applyDisplayRotation.run();
        } else {
            // The display will not rotate to the rotation of this container, let's cancel them.
            for (int i = state.mAssociatedTokens.size() - 1; i >= 0; i--) {
                state.mAssociatedTokens.get(i).cancelFixedRotationTransform();
            }
        }
        // The state is cleared at the end, because it is used to indicate that other windows can
        // use seamless rotation when applying rotation to display.
@@ -634,11 +629,15 @@ class WindowToken extends WindowContainer<WindowState> {
            final WindowToken token = state.mAssociatedTokens.get(i);
            token.mFixedRotationTransformState = null;
            token.notifyFixedRotationTransform(false /* enabled */);
            if (applyDisplayRotation == null) {
                // Notify cancellation because the display does not change rotation.
                token.cancelFixedRotationTransform();
            }
        }
    }

    /** Notifies application side to enable or disable the rotation adjustment of display info. */
    private void notifyFixedRotationTransform(boolean enabled) {
    void notifyFixedRotationTransform(boolean enabled) {
        FixedRotationAdjustments adjustments = null;
        // A token may contain windows of the same processes or different processes. The list is
        // used to avoid sending the same adjustments to a process multiple times.
@@ -682,7 +681,6 @@ class WindowToken extends WindowContainer<WindowState> {
            // The window may be detached or detaching.
            return;
        }
        notifyFixedRotationTransform(false /* enabled */);
        final int originalRotation = getWindowConfiguration().getRotation();
        onConfigurationChanged(parent.getConfiguration());
        onCancelFixedRotationTransform(originalRotation);
+31 −0
Original line number Diff line number Diff line
@@ -88,6 +88,7 @@ import static org.mockito.Mockito.doCallRealMethod;
import android.annotation.SuppressLint;
import android.app.ActivityTaskManager;
import android.app.WindowConfiguration;
import android.app.servertransaction.FixedRotationAdjustmentsItem;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Region;
@@ -1345,6 +1346,36 @@ public class DisplayContentTests extends WindowTestsBase {
        assertFalse(recentsActivity.hasFixedRotationTransform());
    }

    @Test
    public void testClearIntermediateFixedRotationAdjustments() throws RemoteException {
        final ActivityRecord activity = new ActivityTestsBase.StackBuilder(mWm.mRoot)
                .setDisplay(mDisplayContent).build().getTopMostActivity();
        mDisplayContent.setFixedRotationLaunchingApp(activity,
                (mDisplayContent.getRotation() + 1) % 4);
        // Create a window so FixedRotationAdjustmentsItem can be sent.
        createWindow(null, TYPE_APPLICATION_STARTING, activity, "AppWin");
        final ActivityRecord activity2 = new ActivityTestsBase.StackBuilder(mWm.mRoot)
                .setDisplay(mDisplayContent).build().getTopMostActivity();
        activity2.setVisible(false);
        clearInvocations(mWm.mAtmService.getLifecycleManager());
        // The first activity has applied fixed rotation but the second activity becomes the top
        // before the transition is done and it has the same rotation as display, so the dispatched
        // rotation adjustment of first activity must be cleared.
        mDisplayContent.handleTopActivityLaunchingInDifferentOrientation(activity2,
                false /* checkOpening */);

        final ArgumentCaptor<FixedRotationAdjustmentsItem> adjustmentsCaptor =
                ArgumentCaptor.forClass(FixedRotationAdjustmentsItem.class);
        verify(mWm.mAtmService.getLifecycleManager(), atLeastOnce()).scheduleTransaction(
                eq(activity.app.getThread()), adjustmentsCaptor.capture());
        // The transformation is kept for animation in real case.
        assertTrue(activity.hasFixedRotationTransform());
        final FixedRotationAdjustmentsItem clearAdjustments = FixedRotationAdjustmentsItem.obtain(
                activity.token, null /* fixedRotationAdjustments */);
        // The captor may match other items. The first one must be the item to clear adjustments.
        assertEquals(clearAdjustments, adjustmentsCaptor.getAllValues().get(0));
    }

    @Test
    public void testRemoteRotation() {
        DisplayContent dc = createNewDisplay();