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

Commit c03b26df authored by Jerry Chang's avatar Jerry Chang Committed by Android (Google) Code Review
Browse files

Merge "Handle split screen accordingly after recent transition finished" into tm-qpr-dev

parents c0c1f9bd 5cb6bd77
Loading
Loading
Loading
Loading
+17 −6
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@ class SplitScreenTransitions {
    private IBinder mAnimatingTransition = null;
    OneShotRemoteHandler mPendingRemoteHandler = null;
    private OneShotRemoteHandler mActiveRemoteHandler = null;
    private boolean mEnterTransitionMerged;

    private final Transitions.TransitionFinishCallback mRemoteFinishCB = this::onFinish;

@@ -229,6 +230,18 @@ class SplitScreenTransitions {
        }
    }

    void onTransitionMerged(@NonNull IBinder transition) {
        // Once a pending enter transition got merged, make sure to append the reset of finishing
        // operations to the finish transition.
        if (transition == mPendingEnter) {
            mFinishTransaction = mTransactionPool.acquire();
            mStageCoordinator.finishEnterSplitScreen(mFinishTransaction);
            mPendingEnter = null;
            mPendingRemoteHandler = null;
            mEnterTransitionMerged = true;
        }
    }

    void onFinish(WindowContainerTransaction wct, WindowContainerTransactionCallback wctCB) {
        if (!mAnimations.isEmpty()) return;
        if (mAnimatingTransition == mPendingEnter) {
@@ -238,18 +251,16 @@ class SplitScreenTransitions {
            mPendingDismiss = null;
        }
        if (mAnimatingTransition == mPendingRecent) {
            // If the clean-up wct is null when finishing recent transition, it indicates it's
            // returning to home and thus no need to reorder tasks.
            final boolean returnToHome = wct == null;
            if (returnToHome) {
                wct = new WindowContainerTransaction();
            if (!mEnterTransitionMerged) {
                if (wct == null) wct = new WindowContainerTransaction();
                mStageCoordinator.onRecentTransitionFinished(wct, mFinishTransaction);
            }
            mStageCoordinator.onRecentTransitionFinished(returnToHome, wct, mFinishTransaction);
            mPendingRecent = null;
        }
        mPendingRemoteHandler = null;
        mActiveRemoteHandler = null;
        mAnimatingTransition = null;
        mEnterTransitionMerged = false;

        mOnFinish.run();
        if (mFinishTransaction != null) {
+27 −30
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.transitTypeToString;
import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;

import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -369,10 +370,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        sideOptions = sideOptions != null ? sideOptions : new Bundle();
        setSideStagePosition(sidePosition, wct);

        mSplitLayout.setDivideRatio(splitRatio);
        if (mMainStage.isActive()) {
            mMainStage.evictAllChildren(wct);
            mSideStage.evictAllChildren(wct);
        } else {
            // Build a request WCT that will launch both apps such that task 0 is on the main stage
            // while task 1 is on the side stage.
            mMainStage.activate(wct, false /* reparent */);
        }
        mSplitLayout.setDivideRatio(splitRatio);
        updateWindowBounds(mSplitLayout, wct);
        wct.reorder(mRootTaskInfo.token, true);

@@ -1507,16 +1513,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,

    @Override
    public void onTransitionMerged(@NonNull IBinder transition) {
        // Once the pending enter transition got merged, make sure to bring divider bar visible and
        // clear the pending transition from cache to prevent mess-up the following state.
        if (transition == mSplitTransitions.mPendingEnter) {
            final SurfaceControl.Transaction t = mTransactionPool.acquire();
            finishEnterSplitScreen(t);
            mSplitTransitions.mPendingEnter = null;
            mSplitTransitions.mPendingRemoteHandler = null;
            t.apply();
            mTransactionPool.release(t);
        }
        mSplitTransitions.onTransitionMerged(transition);
    }

    @Override
@@ -1748,26 +1745,26 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        return true;
    }

    void onRecentTransitionFinished(boolean returnToHome, WindowContainerTransaction wct,
    void onRecentTransitionFinished(WindowContainerTransaction wct,
            SurfaceControl.Transaction finishT) {
        // Exclude the case that the split screen has been dismissed already.
        if (!mMainStage.isActive()) {
            // The latest split dismissing transition might be a no-op transition and thus won't
            // callback startAnimation, update split visibility here to cover this kind of no-op
            // transition case.
            setSplitsVisible(false);
        // Check if the recent transition is finished by returning to the current split so we can
        // restore the divider bar.
        for (int i = 0; i < wct.getHierarchyOps().size(); ++i) {
            final WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i);
            final IBinder container = op.getContainer();
            if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
                    && (mMainStage.containsContainer(container)
                    || mSideStage.containsContainer(container))) {
                setDividerVisibility(true, finishT);
                return;
            }
        }

        if (returnToHome) {
            // When returning to home from recent apps, the splitting tasks are already hidden, so
            // append the reset of dismissing operations into the clean-up wct.
        // Dismiss the split screen is it's not returning to split.
        prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
        setSplitsVisible(false);
            logExit(EXIT_REASON_RETURN_HOME);
        } else {
            setDividerVisibility(true, finishT);
        }
        setDividerVisibility(false, finishT);
        logExit(EXIT_REASON_UNKNOWN);
    }

    private void addDividerBarToTransition(@NonNull TransitionInfo info,
+25 −33
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
import android.util.SparseArray;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
@@ -47,6 +48,7 @@ import com.android.wm.shell.common.split.SplitDecorManager;
import com.android.wm.shell.splitscreen.SplitScreen.StageType;

import java.io.PrintWriter;
import java.util.function.Predicate;

/**
 * Base class that handle common task org. related for split-screen stages.
@@ -119,63 +121,53 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
    }

    boolean containsToken(WindowContainerToken token) {
        if (token.equals(mRootTaskInfo.token)) {
            return true;
        return contains(t -> t.token.equals(token));
    }

        for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
            if (token.equals(mChildrenTaskInfo.valueAt(i).token)) {
                return true;
            }
        }

        return false;
    boolean containsContainer(IBinder binder) {
        return contains(t -> t.token.asBinder() == binder);
    }

    /**
     * Returns the top visible child task's id.
     */
    int getTopVisibleChildTaskId() {
        for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
            final ActivityManager.RunningTaskInfo info = mChildrenTaskInfo.valueAt(i);
            if (info.isVisible) {
                return info.taskId;
            }
        }
        return INVALID_TASK_ID;
        final ActivityManager.RunningTaskInfo taskInfo = getChildTaskInfo(t -> t.isVisible);
        return taskInfo != null ? taskInfo.taskId : INVALID_TASK_ID;
    }

    /**
     * Returns the top activity uid for the top child task.
     */
    int getTopChildTaskUid() {
        for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
            final ActivityManager.RunningTaskInfo info = mChildrenTaskInfo.valueAt(i);
            if (info.topActivityInfo == null) {
                continue;
            }
            return info.topActivityInfo.applicationInfo.uid;
        }
        return 0;
        final ActivityManager.RunningTaskInfo taskInfo =
                getChildTaskInfo(t -> t.topActivityInfo != null);
        return taskInfo != null ? taskInfo.topActivityInfo.applicationInfo.uid : 0;
    }

    /** @return {@code true} if this listener contains the currently focused task. */
    boolean isFocused() {
        if (mRootTaskInfo == null) {
            return false;
        return contains(t -> t.isFocused);
    }

        if (mRootTaskInfo.isFocused) {
    private boolean contains(Predicate<ActivityManager.RunningTaskInfo> predicate) {
        if (mRootTaskInfo != null && predicate.test(mRootTaskInfo)) {
            return true;
        }

        return getChildTaskInfo(predicate) != null;
    }

    @Nullable
    private ActivityManager.RunningTaskInfo getChildTaskInfo(
            Predicate<ActivityManager.RunningTaskInfo> predicate) {
        for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
            if (mChildrenTaskInfo.valueAt(i).isFocused) {
                return true;
            final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
            if (predicate.test(taskInfo)) {
                return taskInfo;
            }
        }

        return false;
        return null;
    }

    @Override