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

Commit 5d2533cd authored by Jerry Chang's avatar Jerry Chang
Browse files

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

Listen to IME position changes in SplitLayout to make sure it updates
divider view status after re-inflated. Also add functions to get IME
position of split which is needed for integrating IME animation with
new split implementations.

Bug: 179262787
Test: atest WMShellUnitTests
Test: observed divider bar won't change to interactive after rotating
devices with IME shown.

Change-Id: Ic01c23e61ffb666cfc292e56dd6dcf4646be749b
parent 4c425b2a
Loading
Loading
Loading
Loading
+21 −2
Original line number Diff line number Diff line
@@ -20,6 +20,9 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;

import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;

import android.app.ActivityManager;
@@ -47,7 +50,7 @@ import java.io.PrintWriter;
 * {@link #mTaskInfo1} and {@link #mTaskInfo2} in the pair.
 * Also includes all UI for managing the pair like the divider.
 */
class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChangeListener {
class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayoutHandler {
    private static final String TAG = AppPair.class.getSimpleName();

    private ActivityManager.RunningTaskInfo mRootTaskInfo;
@@ -106,7 +109,8 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan
        mSplitLayout = new SplitLayout(TAG + "SplitDivider",
                mDisplayController.getDisplayContext(mRootTaskInfo.displayId),
                mRootTaskInfo.configuration, this /* layoutChangeListener */,
                b -> b.setParent(mRootTaskLeash), mDisplayImeController);
                b -> b.setParent(mRootTaskLeash), mDisplayImeController,
                mController.getTaskOrganizer());

        final WindowContainerToken token1 = task1.token;
        final WindowContainerToken token2 = task2.token;
@@ -216,6 +220,21 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan
        }
    }

    @Override
    public int getSplitItemPosition(WindowContainerToken token) {
        if (token == null) {
            return SPLIT_POSITION_UNDEFINED;
        }

        if (token.equals(mTaskInfo1.getToken())) {
            return SPLIT_POSITION_TOP_OR_LEFT;
        } else if (token.equals(mTaskInfo2.getToken())) {
            return SPLIT_POSITION_BOTTOM_OR_RIGHT;
        }

        return SPLIT_POSITION_UNDEFINED;
    }

    @Override
    public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
        if (taskInfo.taskId == getRootTaskId()) {
+2 −10
Original line number Diff line number Diff line
@@ -36,13 +36,11 @@ import androidx.annotation.Nullable;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.DisplayImeController;

/**
 * Divider for multi window splits.
 */
public class DividerView extends FrameLayout implements View.OnTouchListener,
        DisplayImeController.ImePositionProcessor {
public class DividerView extends FrameLayout implements View.OnTouchListener {
    public static final long TOUCH_ANIMATION_DURATION = 150;
    public static final long TOUCH_RELEASE_ANIMATION_DURATION = 200;

@@ -98,12 +96,6 @@ public class DividerView extends FrameLayout implements View.OnTouchListener,
        setOnTouchListener(this);
    }

    @Override
    public void onImeVisibilityChanged(int displayId, boolean isShowing) {
        if (displayId != getDisplay().getDisplayId()) return;
        setInteractive(!isShowing);
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (mSplitLayout == null || !mInteractive) {
@@ -217,7 +209,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener,
        mViewHost.relayout(lp);
    }

    private void setInteractive(boolean interactive) {
    void setInteractive(boolean interactive) {
        if (interactive == mInteractive) return;
        mInteractive = interactive;
        releaseTouching();
+63 −15
Original line number Diff line number Diff line
@@ -28,14 +28,17 @@ import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
import android.view.SurfaceControl;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.window.WindowContainerToken;

import androidx.annotation.Nullable;

import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.DisplayImeController;

@@ -77,8 +80,11 @@ public final class SplitLayout {
    private final Rect mDividerBounds = new Rect();
    private final Rect mBounds1 = new Rect();
    private final Rect mBounds2 = new Rect();
    private final LayoutChangeListener mLayoutChangeListener;
    private final SplitLayoutHandler mSplitLayoutHandler;
    private final SplitWindowManager mSplitWindowManager;
    private final DisplayImeController mDisplayImeController;
    private final ImePositionProcessor mImePositionProcessor;
    private final ShellTaskOrganizer mTaskOrganizer;

    private Context mContext;
    private DividerSnapAlgorithm mDividerSnapAlgorithm;
@@ -86,18 +92,21 @@ public final class SplitLayout {
    private boolean mInitialized = false;

    public SplitLayout(String windowName, Context context, Configuration configuration,
            LayoutChangeListener layoutChangeListener,
            SplitLayoutHandler splitLayoutHandler,
            SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks,
            DisplayImeController displayImeController) {
            DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer) {
        mContext = context.createConfigurationContext(configuration);
        mLayoutChangeListener = layoutChangeListener;
        mSplitLayoutHandler = splitLayoutHandler;
        mDisplayImeController = displayImeController;
        mSplitWindowManager = new SplitWindowManager(
                windowName, mContext, configuration, parentContainerCallbacks,
                displayImeController);
                windowName, mContext, configuration, parentContainerCallbacks);
        mTaskOrganizer = taskOrganizer;
        mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId());

        mDividerWindowWidth = context.getResources().getDimensionPixelSize(
        final Resources resources = context.getResources();
        mDividerWindowWidth = resources.getDimensionPixelSize(
                com.android.internal.R.dimen.docked_stack_divider_thickness);
        mDividerInsets = context.getResources().getDimensionPixelSize(
        mDividerInsets = resources.getDimensionPixelSize(
                com.android.internal.R.dimen.docked_stack_divider_insets);
        mDividerSize = mDividerWindowWidth - mDividerInsets * 2;

@@ -179,6 +188,7 @@ public final class SplitLayout {
        if (mInitialized) return;
        mInitialized = true;
        mSplitWindowManager.init(this);
        mDisplayImeController.addPositionProcessor(mImePositionProcessor);
    }

    /** Releases the surface holding the current {@link DividerView}. */
@@ -186,6 +196,7 @@ public final class SplitLayout {
        if (!mInitialized) return;
        mInitialized = false;
        mSplitWindowManager.release();
        mDisplayImeController.removePositionProcessor(mImePositionProcessor);
    }

    /**
@@ -194,14 +205,14 @@ public final class SplitLayout {
     */
    void updateDivideBounds(int position) {
        updateBounds(position);
        mLayoutChangeListener.onBoundsChanging(this);
        mSplitWindowManager.setResizingSplits(true);
        mSplitLayoutHandler.onBoundsChanging(this);
    }

    void setDividePosition(int position) {
        mDividePosition = position;
        updateBounds(mDividePosition);
        mLayoutChangeListener.onBoundsChanged(this);
        mSplitLayoutHandler.onBoundsChanged(this);
        mSplitWindowManager.setResizingSplits(false);
    }

@@ -218,11 +229,11 @@ public final class SplitLayout {
    public void snapToTarget(int currentPosition, DividerSnapAlgorithm.SnapTarget snapTarget) {
        switch (snapTarget.flag) {
            case FLAG_DISMISS_START:
                mLayoutChangeListener.onSnappedToDismiss(false /* bottomOrRight */);
                mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */);
                mSplitWindowManager.setResizingSplits(false);
                break;
            case FLAG_DISMISS_END:
                mLayoutChangeListener.onSnappedToDismiss(true /* bottomOrRight */);
                mSplitLayoutHandler.onSnappedToDismiss(true /* bottomOrRight */);
                mSplitWindowManager.setResizingSplits(false);
                break;
            default:
@@ -232,7 +243,7 @@ public final class SplitLayout {
    }

    void onDoubleTappedDivider() {
        mLayoutChangeListener.onDoubleTappedDivider();
        mSplitLayoutHandler.onDoubleTappedDivider();
    }

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

    /** Listens layout change event. */
    public interface LayoutChangeListener {
    /** Handles layout change event. */
    public interface SplitLayoutHandler {
        /** Calls when dismissing split. */
        void onSnappedToDismiss(boolean snappedToEnd);

@@ -305,5 +316,42 @@ public final class SplitLayout {
        /** Calls when user double tapped on the divider bar. */
        default void onDoubleTappedDivider() {
        }

        /** Returns split position of the token. */
        @SplitPosition
        int getSplitItemPosition(WindowContainerToken token);
    }

    /** Records IME top offset changes and updates SplitLayout correspondingly. */
    private class ImePositionProcessor implements DisplayImeController.ImePositionProcessor {

        private final int mDisplayId;

        private ImePositionProcessor(int displayId) {
            mDisplayId = displayId;
        }

        @Override
        public int onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
                boolean showing, boolean isFloating, SurfaceControl.Transaction t) {
            if (displayId != mDisplayId) return 0;
            final int imeTargetPosition = getImeTargetPosition();
            if (!mInitialized || imeTargetPosition == SPLIT_POSITION_UNDEFINED) return 0;

            // Make {@link DividerView} non-interactive while IME showing in split mode. Listen to
            // ImePositionProcessor#onImeVisibilityChanged directly in DividerView is not enough
            // because DividerView won't receive onImeVisibilityChanged callback after it being
            // re-inflated.
            mSplitWindowManager.setInteractive(
                    !showing || imeTargetPosition == SPLIT_POSITION_UNDEFINED);

            return 0;
        }

        @SplitPosition
        private int getImeTargetPosition() {
            final WindowContainerToken token = mTaskOrganizer.getImeTarget(mDisplayId);
            return mSplitLayoutHandler.getSplitItemPosition(token);
        }
    }
}
+6 −7
Original line number Diff line number Diff line
@@ -46,7 +46,6 @@ import android.view.WindowlessWindowManager;
import androidx.annotation.Nullable;

import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayImeController;

/**
 * Holds view hierarchy of a root surface and helps to inflate {@link DividerView} for a split.
@@ -55,7 +54,6 @@ public final class SplitWindowManager extends WindowlessWindowManager {
    private static final String TAG = SplitWindowManager.class.getSimpleName();

    private final String mWindowName;
    private final DisplayImeController mDisplayImeController;
    private final ParentContainerCallbacks mParentContainerCallbacks;
    private Context mContext;
    private SurfaceControlViewHost mViewHost;
@@ -68,13 +66,11 @@ public final class SplitWindowManager extends WindowlessWindowManager {
    }

    public SplitWindowManager(String windowName, Context context, Configuration config,
            ParentContainerCallbacks parentContainerCallbacks,
            DisplayImeController displayImeController) {
            ParentContainerCallbacks parentContainerCallbacks) {
        super(config, null /* rootSurface */, null /* hostInputToken */);
        mContext = context.createConfigurationContext(config);
        mParentContainerCallbacks = parentContainerCallbacks;
        mWindowName = windowName;
        mDisplayImeController = displayImeController;
    }

    @Override
@@ -128,7 +124,6 @@ public final class SplitWindowManager extends WindowlessWindowManager {
        lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
        mViewHost.setView(mDividerView, lp);
        mDividerView.setup(splitLayout, mViewHost);
        mDisplayImeController.addPositionProcessor(mDividerView);
    }

    /**
@@ -137,7 +132,6 @@ public final class SplitWindowManager extends WindowlessWindowManager {
     */
    void release() {
        if (mDividerView != null) {
            mDisplayImeController.removePositionProcessor(mDividerView);
            mDividerView = null;
        }

@@ -152,6 +146,11 @@ public final class SplitWindowManager extends WindowlessWindowManager {
        }
    }

    void setInteractive(boolean interactive) {
        if (mDividerView == null) return;
        mDividerView.setInteractive(interactive);
    }

    void setResizingSplits(boolean resizing) {
        if (resizing == mResizingSplits) return;
        try {
+20 −3
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import static android.view.WindowManager.transitTypeToString;

import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
@@ -53,6 +54,7 @@ import android.window.DisplayAreaInfo;
import android.window.IRemoteTransition;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;

import com.android.internal.R;
@@ -85,7 +87,7 @@ import java.util.List;
 * This rules are mostly implemented in {@link #onStageVisibilityChanged(StageListenerImpl)} and
 * {@link #onStageHasChildrenChanged(StageListenerImpl).}
 */
class StageCoordinator implements SplitLayout.LayoutChangeListener,
class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener, Transitions.TransitionHandler {

    private static final String TAG = StageCoordinator.class.getSimpleName();
@@ -564,6 +566,21 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
                .setWindowCrop(mSideStage.mRootLeash, null));
    }

    @Override
    public int getSplitItemPosition(WindowContainerToken token) {
        if (token == null) {
            return SPLIT_POSITION_UNDEFINED;
        }

        if (token.equals(mMainStage.mRootTaskInfo.getToken())) {
            return getMainStagePosition();
        } else if (token.equals(mSideStage.mRootTaskInfo.getToken())) {
            return getSideStagePosition();
        }

        return SPLIT_POSITION_UNDEFINED;
    }

    @Override
    public void onDisplayAreaAppeared(DisplayAreaInfo displayAreaInfo) {
        mDisplayAreaInfo = displayAreaInfo;
@@ -571,7 +588,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
            mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
                    mDisplayAreaInfo.configuration, this,
                    b -> mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b),
                    mDisplayImeController);
                    mDisplayImeController, mTaskOrganizer);
        }
    }

Loading