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

Commit 64049c4e authored by Jerry Chang's avatar Jerry Chang
Browse files

Adjust split layout with IME animation in split (4/N)

Update split surface dim value to match IME animation.

Bug: 179262787
Test: atest WMShellUnitTests
Test: manual checks split surface dim properly with IME animation.

Change-Id: I17c03def1a7e19d6bdbfc44fa846da4b1efa3275
parent 5d2533cd
Loading
Loading
Loading
Loading
+6 −30
Original line number Diff line number Diff line
@@ -291,40 +291,16 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou

    @Override
    public void onBoundsChanging(SplitLayout layout) {
        final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
        if (dividerLeash == null) return;
        final Rect dividerBounds = layout.getDividerBounds();
        final Rect bounds1 = layout.getBounds1();
        final Rect bounds2 = layout.getBounds2();
        mSyncQueue.runInSync(t -> t
                .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
                .setPosition(mTaskLeash1, bounds1.left, bounds1.top)
                .setPosition(mTaskLeash2, bounds2.left, bounds2.top)
                // Sets crop to prevent visible region of tasks overlap with each other when
                // re-positioning surfaces while resizing.
                .setWindowCrop(mTaskLeash1, bounds1.width(), bounds1.height())
                .setWindowCrop(mTaskLeash2, bounds2.width(), bounds2.height()));
        mSyncQueue.runInSync(t ->
                layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2));
    }

    @Override
    public void onBoundsChanged(SplitLayout layout) {
        final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
        if (dividerLeash == null) return;
        final Rect dividerBounds = layout.getDividerBounds();
        final Rect bounds1 = layout.getBounds1();
        final Rect bounds2 = layout.getBounds2();
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        wct.setBounds(mTaskInfo1.token, bounds1)
                .setBounds(mTaskInfo2.token, bounds2);
        mController.getTaskOrganizer().applyTransaction(wct);
        mSyncQueue.runInSync(t -> t
                // Resets layer of divider bar to make sure it is always on top.
                .setLayer(dividerLeash, Integer.MAX_VALUE)
                .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
                .setPosition(mTaskLeash1, bounds1.left, bounds1.top)
                .setPosition(mTaskLeash2, bounds2.left, bounds2.top)
                // Resets crop to apply new surface bounds directly.
                .setWindowCrop(mTaskLeash1, null)
                .setWindowCrop(mTaskLeash2, null));
        layout.applyTaskChanges(wct, mTaskInfo1, mTaskInfo2);
        mSyncQueue.queue(wct);
        mSyncQueue.runInSync(t ->
                layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2));
    }
}
+95 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -34,6 +35,7 @@ import android.view.SurfaceControl;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;

import androidx.annotation.Nullable;

@@ -197,6 +199,7 @@ public final class SplitLayout {
        mInitialized = false;
        mSplitWindowManager.release();
        mDisplayImeController.removePositionProcessor(mImePositionProcessor);
        mImePositionProcessor.reset();
    }

    /**
@@ -302,8 +305,35 @@ public final class SplitLayout {
        return bounds.width() > bounds.height();
    }

    /** Apply recorded surface layout to the {@link SurfaceControl.Transaction}. */
    public void applySurfaceChanges(SurfaceControl.Transaction t, SurfaceControl leash1,
            SurfaceControl leash2, SurfaceControl dimLayer1, SurfaceControl dimLayer2) {
        final SurfaceControl dividerLeash = getDividerLeash();
        if (dividerLeash != null) {
            t.setPosition(dividerLeash, mDividerBounds.left, mDividerBounds.top)
                    // Resets layer of divider bar to make sure it is always on top.
                    .setLayer(dividerLeash, Integer.MAX_VALUE);
        }

        t.setPosition(leash1, mBounds1.left, mBounds1.top)
                .setWindowCrop(leash1, mBounds1.width(), mBounds1.height());

        t.setPosition(leash2, mBounds2.left, mBounds2.top)
                .setWindowCrop(leash2, mBounds2.width(), mBounds2.height());

        mImePositionProcessor.applySurfaceDimValues(t, dimLayer1, dimLayer2);
    }

    /** Apply recorded task layout to the {@link WindowContainerTransaction}. */
    public void applyTaskChanges(WindowContainerTransaction wct,
            ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2) {
        wct.setBounds(task1.token, mBounds1)
                .setBounds(task2.token, mBounds2);
    }

    /** Handles layout change event. */
    public interface SplitLayoutHandler {

        /** Calls when dismissing split. */
        void onSnappedToDismiss(boolean snappedToEnd);

@@ -324,9 +354,26 @@ public final class SplitLayout {

    /** Records IME top offset changes and updates SplitLayout correspondingly. */
    private class ImePositionProcessor implements DisplayImeController.ImePositionProcessor {
        /**
         * Maximum size of an adjusted split bounds relative to original stack bounds. Used to
         * restrict IME adjustment so that a min portion of top split remains visible.
         */
        private static final float ADJUSTED_SPLIT_FRACTION_MAX = 0.7f;
        private static final float ADJUSTED_NONFOCUS_DIM = 0.3f;

        private final int mDisplayId;

        private float mDimValue1;
        private float mDimValue2;

        private int mStartImeTop;
        private int mEndImeTop;

        private float mTargetDim1;
        private float mTargetDim2;
        private float mLastDim1;
        private float mLastDim2;

        private ImePositionProcessor(int displayId) {
            mDisplayId = displayId;
        }
@@ -337,6 +384,16 @@ public final class SplitLayout {
            if (displayId != mDisplayId) return 0;
            final int imeTargetPosition = getImeTargetPosition();
            if (!mInitialized || imeTargetPosition == SPLIT_POSITION_UNDEFINED) return 0;
            mStartImeTop = showing ? hiddenTop : shownTop;
            mEndImeTop = showing ? shownTop : hiddenTop;

            // Update target dim values
            mLastDim1 = mDimValue1;
            mTargetDim1 = imeTargetPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT && showing
                    ? ADJUSTED_NONFOCUS_DIM : 0.0f;
            mLastDim2 = mDimValue2;
            mTargetDim2 = imeTargetPosition == SPLIT_POSITION_TOP_OR_LEFT && showing
                    ? ADJUSTED_NONFOCUS_DIM : 0.0f;

            // Make {@link DividerView} non-interactive while IME showing in split mode. Listen to
            // ImePositionProcessor#onImeVisibilityChanged directly in DividerView is not enough
@@ -348,10 +405,48 @@ public final class SplitLayout {
            return 0;
        }

        @Override
        public void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t) {
            if (displayId != mDisplayId) return;
            onProgress(getProgress(imeTop));
            mSplitLayoutHandler.onBoundsChanging(SplitLayout.this);
        }

        @Override
        public void onImeEndPositioning(int displayId, boolean cancel,
                SurfaceControl.Transaction t) {
            if (displayId != mDisplayId || cancel) return;
            onProgress(1.0f);
            mSplitLayoutHandler.onBoundsChanging(SplitLayout.this);
        }

        @SplitPosition
        private int getImeTargetPosition() {
            final WindowContainerToken token = mTaskOrganizer.getImeTarget(mDisplayId);
            return mSplitLayoutHandler.getSplitItemPosition(token);
        }

        private float getProgress(int currImeTop) {
            return ((float) currImeTop - mStartImeTop) / (mEndImeTop - mStartImeTop);
        }

        private void onProgress(float progress) {
            mDimValue1 = getProgressValue(mLastDim1, mTargetDim1, progress);
            mDimValue2 = getProgressValue(mLastDim2, mTargetDim2, progress);
        }

        private float getProgressValue(float start, float end, float progress) {
            return start + (end - start) * progress;
        }

        private void reset() {
            mDimValue1 = mDimValue2 = 0.0f;
        }

        private void applySurfaceDimValues(SurfaceControl.Transaction t, SurfaceControl dimLayer1,
                SurfaceControl dimLayer2) {
            t.setAlpha(dimLayer1, mDimValue1).setVisibility(dimLayer1, mDimValue1 > 0.001f);
            t.setAlpha(dimLayer2, mDimValue2).setVisibility(dimLayer2, mDimValue2 > 0.001f);
        }
    }
}
+20 −41
Original line number Diff line number Diff line
@@ -514,56 +514,35 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        exitSplitScreen(mainStageToTop ? mMainStage : mSideStage);
    }

    @Override
    public void onBoundsChanging(SplitLayout layout) {
        final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
        if (dividerLeash == null) return;
        final Rect mainStageBounds = getMainStageBounds();
        final Rect sideStageBounds = getSideStageBounds();

        mSyncQueue.runInSync(t -> t
                .setPosition(dividerLeash,
                        mSplitLayout.getDividerBounds().left, mSplitLayout.getDividerBounds().top)
                .setPosition(mMainStage.mRootLeash, mainStageBounds.left, mainStageBounds.top)
                .setPosition(mSideStage.mRootLeash, sideStageBounds.left, sideStageBounds.top)
                // Sets crop to prevent visible region of tasks overlap with each other when
                // re-positioning surfaces while resizing.
                .setWindowCrop(mMainStage.mRootLeash,
                        mainStageBounds.width(), mainStageBounds.height())
                .setWindowCrop(mSideStage.mRootLeash,
                        sideStageBounds.width(), sideStageBounds.height()));

    }

    @Override
    public void onDoubleTappedDivider() {
        setSideStagePosition(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
                ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT);
    }

    @Override
    public void onBoundsChanging(SplitLayout layout) {
        final StageTaskListener topLeftStage =
                mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
        final StageTaskListener bottomRightStage =
                mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;

        mSyncQueue.runInSync(t -> layout.applySurfaceChanges(t, topLeftStage.mRootLeash,
                bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer));
    }

    @Override
    public void onBoundsChanged(SplitLayout layout) {
        final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
        if (dividerLeash == null) return;
        final Rect mainStageBounds = getMainStageBounds();
        final Rect sideStageBounds = getSideStageBounds();
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        mMainStage.setBounds(mainStageBounds, wct);
        mSideStage.setBounds(sideStageBounds, wct);
        mTaskOrganizer.applyTransaction(wct);
        final StageTaskListener topLeftStage =
                mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
        final StageTaskListener bottomRightStage =
                mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;

        mSyncQueue.runInSync(t -> t
                // Resets layer of divider bar to make sure it is always on top.
                .setLayer(dividerLeash, Integer.MAX_VALUE)
                .setPosition(dividerLeash,
                        mSplitLayout.getDividerBounds().left, mSplitLayout.getDividerBounds().top)
                .setPosition(mMainStage.mRootLeash,
                        mainStageBounds.left, mainStageBounds.top)
                .setPosition(mSideStage.mRootLeash,
                        sideStageBounds.left, sideStageBounds.top)
                // Resets crop to apply new surface bounds directly.
                .setWindowCrop(mMainStage.mRootLeash, null)
                .setWindowCrop(mSideStage.mRootLeash, null));
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo);
        mSyncQueue.queue(wct);
        mSyncQueue.runInSync(t -> layout.applySurfaceChanges(t, topLeftStage.mRootLeash,
                bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer));
    }

    @Override