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

Commit 2de74827 authored by Vinit Nayak's avatar Vinit Nayak Committed by Winson Chung
Browse files

Fix a few split rotation issues when the IME showing



- Fix an issue with the split bounds being wrong upon rotation. The target
  offset is relative to the actual task bounds and not the last y offset,
  so multiple changing IME insets changes in a row when rotating would
  set the window bounds of each task to be wrong, which affects the
  positionInParent of the task (relative to the single split root), which
  then results in the DefaultTransitionHandler positioning the task
  surfaces at the wrong position when the display rotation transition is
  actually played
- Fix an issue with the divider position being wrong upon rotation. The
  divider position is based on the IME state, but during rotation we can
  get numerous insets changes before the display rotation animation is
  played, so we should not reset the IME state.  In addition, due to races
  between the app/WM and the Shell, there may be no insets changes after
  the display transition is played, so we need to manually update the
  surface position after recreating the divider surface
- Restore handle view visibility & interactive state after recreating the
  divider view upon rotation

Flag: None
Test: Repeatedly rotate the device with the IME showing
Test: atest WMShellUnitTests
Bug: 299581029
Change-Id: Id43c388a27ae3be117857688f1ffd73a86bdea30
Signed-off-by: default avatarWinson Chung <winsonc@google.com>
parent 24a9de23
Loading
Loading
Loading
Loading
+17 −9
Original line number Diff line number Diff line
@@ -83,6 +83,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
    private int mStartPos;
    private GestureDetector mDoubleTapDetector;
    private boolean mInteractive;
    private boolean mHideHandle;
    private boolean mSetTouchRegion = true;
    private int mLastDraggingPosition;
    private int mHandleRegionWidth;
@@ -211,11 +212,8 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
    }

    /** Sets up essential dependencies of the divider bar. */
    public void setup(
            SplitLayout layout,
            SplitWindowManager splitWindowManager,
            SurfaceControlViewHost viewHost,
            InsetsState insetsState) {
    public void setup(SplitLayout layout, SplitWindowManager splitWindowManager,
            SurfaceControlViewHost viewHost, InsetsState insetsState) {
        mSplitLayout = layout;
        mSplitWindowManager = splitWindowManager;
        mViewHost = viewHost;
@@ -277,6 +275,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
                R.dimen.docked_stack_divider_lift_elevation);
        mDoubleTapDetector = new GestureDetector(getContext(), new DoubleTapListener());
        mInteractive = true;
        mHideHandle = false;
        setOnTouchListener(this);
        mHandle.setAccessibilityDelegate(mHandleDelegate);
        setWillNotDraw(false);
@@ -469,10 +468,11 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
    void setInteractive(boolean interactive, boolean hideHandle, String from) {
        if (interactive == mInteractive) return;
        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
                "Set divider bar %s from %s", interactive ? "interactive" : "non-interactive",
                from);
                "Set divider bar %s hide handle=%b from %s",
                interactive ? "interactive" : "non-interactive", hideHandle, from);
        mInteractive = interactive;
        if (!mInteractive && hideHandle && mMoving) {
        mHideHandle = hideHandle;
        if (!mInteractive && mHideHandle && mMoving) {
            final int position = mSplitLayout.getDividePosition();
            mSplitLayout.flingDividePosition(
                    mLastDraggingPosition,
@@ -482,7 +482,15 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
            mMoving = false;
        }
        releaseTouching();
        mHandle.setVisibility(!mInteractive && hideHandle ? View.INVISIBLE : View.VISIBLE);
        mHandle.setVisibility(!mInteractive && mHideHandle ? View.INVISIBLE : View.VISIBLE);
    }

    boolean isInteractive() {
        return mInteractive;
    }

    boolean isHandleHidden() {
        return mHideHandle;
    }

    private class DoubleTapListener extends GestureDetector.SimpleOnGestureListener {
+20 −6
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.Nullable;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.animation.Interpolators;
@@ -70,6 +71,7 @@ import com.android.wm.shell.common.InteractionJankMonitorUtils;
import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition;
import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.protolog.ShellProtoLogGroup;

import java.io.PrintWriter;
import java.util.function.Consumer;
@@ -420,7 +422,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
    public void init() {
        if (mInitialized) return;
        mInitialized = true;
        mSplitWindowManager.init(this, mInsetsState);
        mSplitWindowManager.init(this, mInsetsState, false /* isRestoring */);
        mDisplayImeController.addPositionProcessor(mImePositionProcessor);
    }

@@ -442,14 +444,19 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
    }

    /** Releases and re-inflates {@link DividerView} on the root surface. */
    public void update(SurfaceControl.Transaction t) {
    public void update(SurfaceControl.Transaction t, boolean resetImePosition) {
        if (!mInitialized) {
            init();
            return;
        }
        mSplitWindowManager.release(t);
        if (resetImePosition) {
            mImePositionProcessor.reset();
        mSplitWindowManager.init(this, mInsetsState);
        }
        mSplitWindowManager.init(this, mInsetsState, true /* isRestoring */);
        // Update the surface positions again after recreating the divider in case nothing else
        // triggers it
        mSplitLayoutHandler.onLayoutPositionChanging(SplitLayout.this);
    }

    @Override
@@ -868,6 +875,9 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
        pw.println(prefix + TAG + ":");
        pw.println(innerPrefix + "mAllowLeftRightSplitInPortrait=" + mAllowLeftRightSplitInPortrait);
        pw.println(innerPrefix + "mIsLeftRightSplit=" + mIsLeftRightSplit);
        pw.println(innerPrefix + "mFreezeDividerWindow=" + mFreezeDividerWindow);
        pw.println(innerPrefix + "mDimNonImeSide=" + mDimNonImeSide);
        pw.println(innerPrefix + "mDividerPosition=" + mDividerPosition);
        pw.println(innerPrefix + "bounds1=" + mBounds1.toShortString());
        pw.println(innerPrefix + "dividerBounds=" + mDividerBounds.toShortString());
        pw.println(innerPrefix + "bounds2=" + mBounds2.toShortString());
@@ -1151,14 +1161,16 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
            mTargetYOffset = needOffset ? getTargetYOffset() : 0;

            if (mTargetYOffset != mLastYOffset) {
                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
                        "Split IME animation starting, fromY=%d toY=%d",
                        mLastYOffset, mTargetYOffset);
                // Freeze the configuration size with offset to prevent app get a configuration
                // changed or relaunch. This is required to make sure client apps will calculate
                // insets properly after layout shifted.
                if (mTargetYOffset == 0) {
                    mSplitLayoutHandler.setLayoutOffsetTarget(0, 0, SplitLayout.this);
                } else {
                    mSplitLayoutHandler.setLayoutOffsetTarget(0, mTargetYOffset - mLastYOffset,
                            SplitLayout.this);
                    mSplitLayoutHandler.setLayoutOffsetTarget(0, mTargetYOffset, SplitLayout.this);
                }
            }

@@ -1183,6 +1195,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
        public void onImeEndPositioning(int displayId, boolean cancel,
                SurfaceControl.Transaction t) {
            if (displayId != mDisplayId || !mHasImeFocus || cancel) return;
            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
                    "Split IME animation ending, canceled=%b", cancel);
            onProgress(1.0f);
            mSplitLayoutHandler.onLayoutPositionChanging(SplitLayout.this);
        }
+11 −1
Original line number Diff line number Diff line
@@ -62,6 +62,10 @@ public final class SplitWindowManager extends WindowlessWindowManager {
    // Used to "pass" a transaction to WWM.remove so that view removal can be synchronized.
    private SurfaceControl.Transaction mSyncTransaction = null;

    // For saving/restoring state
    private boolean mLastDividerInteractive = true;
    private boolean mLastDividerHandleHidden;

    public interface ParentContainerCallbacks {
        void attachToParentSurface(SurfaceControl.Builder b);
        void onLeashReady(SurfaceControl leash);
@@ -107,7 +111,7 @@ public final class SplitWindowManager extends WindowlessWindowManager {
    }

    /** Inflates {@link DividerView} on to the root surface. */
    void init(SplitLayout splitLayout, InsetsState insetsState) {
    void init(SplitLayout splitLayout, InsetsState insetsState, boolean isRestoring) {
        if (mDividerView != null || mViewHost != null) {
            throw new UnsupportedOperationException(
                    "Try to inflate divider view again without release first");
@@ -130,6 +134,10 @@ public final class SplitWindowManager extends WindowlessWindowManager {
        lp.accessibilityTitle = mContext.getResources().getString(R.string.accessibility_divider);
        mViewHost.setView(mDividerView, lp);
        mDividerView.setup(splitLayout, this, mViewHost, insetsState);
        if (isRestoring) {
            mDividerView.setInteractive(mLastDividerInteractive, mLastDividerHandleHidden,
                    "restore_setup");
        }
    }

    /**
@@ -138,6 +146,8 @@ public final class SplitWindowManager extends WindowlessWindowManager {
     */
    void release(@Nullable SurfaceControl.Transaction t) {
        if (mDividerView != null) {
            mLastDividerInteractive = mDividerView.isInteractive();
            mLastDividerHandleHidden = mDividerView.isHandleHidden();
            mDividerView = null;
        }

+9 −6
Original line number Diff line number Diff line
@@ -1666,7 +1666,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
    }

    void finishEnterSplitScreen(SurfaceControl.Transaction finishT) {
        mSplitLayout.update(finishT);
        mSplitLayout.update(finishT, true /* resetImePosition */);
        mMainStage.getSplitDecorManager().inflate(mContext, mMainStage.mRootLeash,
                getMainStageBounds());
        mSideStage.getSplitDecorManager().inflate(mContext, mSideStage.mRootLeash,
@@ -1860,9 +1860,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
                && mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)
                && mMainStage.isActive()) {
            // Clear the divider remote animating flag as the divider will be re-rendered to apply
            // the new rotation config.
            // the new rotation config.  Don't reset the IME state since those updates are not in
            // sync with task info changes.
            mIsDividerRemoteAnimating = false;
            mSplitLayout.update(null /* t */);
            mSplitLayout.update(null /* t */, false /* resetImePosition */);
            onLayoutSizeChanged(mSplitLayout);
        }
    }
@@ -2325,7 +2326,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
     */
    public void updateSurfaces(SurfaceControl.Transaction transaction) {
        updateSurfaceBounds(mSplitLayout, transaction, /* applyResizingOffset */ false);
        mSplitLayout.update(transaction);
        mSplitLayout.update(transaction, true /* resetImePosition */);
    }

    private void onDisplayChange(int displayId, int fromRotation, int toRotation,
@@ -2598,7 +2599,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
                final TransitionInfo.Change change = info.getChanges().get(iC);
                if (change.getMode() == TRANSIT_CHANGE
                        && (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
                    mSplitLayout.update(startTransaction);
                    // Don't reset the IME state since those updates are not in sync with the
                    // display change transition
                    mSplitLayout.update(startTransaction, false /* resetImePosition */);
                }

                if (mMixedHandler.isEnteringPip(change, transitType)) {
@@ -2699,7 +2702,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
                    startTransaction, finishTransaction, finishCallback)) {
                if (mSplitTransitions.isPendingResize(transition)) {
                    // Only need to update in resize because divider exist before transition.
                    mSplitLayout.update(startTransaction);
                    mSplitLayout.update(startTransaction, true /* resetImePosition */);
                    startTransaction.apply();
                }
                return true;
+1 −1
Original line number Diff line number Diff line
@@ -69,7 +69,7 @@ public class DividerViewTest extends ShellTestCase {
        SplitWindowManager splitWindowManager = new SplitWindowManager("TestSplitWindowManager",
                mContext,
                configuration, mCallbacks);
        splitWindowManager.init(mSplitLayout, new InsetsState());
        splitWindowManager.init(mSplitLayout, new InsetsState(), false /* isRestoring */);
        mDividerView = spy((DividerView) splitWindowManager.getDividerView());
    }

Loading