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

Commit 5e6968db authored by Jorim Jaggi's avatar Jorim Jaggi
Browse files

Fix disappearing windows after moving divider to side

Because we only hide the surface when the clip rect got empty
but never showed it again if it got non-empty, app windows
disappeared after moving the docked stack divider to the
edge of the screen. Now we reshow the surfaces if the clip-rect
gets non-empty.

However, this introduces another bug while dismissing the docked
stack: Because we move all windows to the fullscreen stack, we resize
them but until the app transition starts, it can take a while and
during this time the app surface would be visible with the wrong
bounds. To fix this, we notify the windows that we are repositioning
ourselves in our stack. When applying the clip-rect, we detect that
situation and then we set the clip rect to empty if it was just empty
before and we just moved in the stack, to fix this very specific
issue.

I'm really not proud of this solution but at this point we can't
revisit how app transitions are executed in terms of timing and
ordering, so I thought this little hack is the best solution at
this point.

Bug: 26588506
Change-Id: I78305f7f7ef6c3da3c126a58d751117fcee23ca9
parent 8fa4522b
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -153,6 +153,13 @@ public class AppWindowAnimator {
        } else {
            mClearProlongedAnimation = true;
        }

        // Since we are finally starting our animation, we don't need the logic anymore to prevent
        // the app from showing again if we just moved between stacks. See
        // {@link WindowState#notifyMovedInStack}.
        for (int i = mAppToken.allAppWindows.size() - 1; i >= 0; i--) {
            mAppToken.allAppWindows.get(i).resetJustMovedInStack();
        }
    }

    public void setDummyAnimation() {
+8 −0
Original line number Diff line number Diff line
@@ -248,6 +248,14 @@ class Task implements DimLayer.DimLayerUser {
        }
        stack.positionTask(this, position, showForAllUsers());
        resizeLocked(bounds, config, false /* force */);

        for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
            final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
                final WindowState win = windows.get(winNdx);
                win.notifyMovedInStack();
            }
        }
    }

    boolean removeAppToken(AppWindowToken wtoken) {
+38 −0
Original line number Diff line number Diff line
@@ -448,6 +448,11 @@ final class WindowState implements WindowManagerPolicy.WindowState {

    final private Rect mTmpRect = new Rect();

    /**
     * See {@link #notifyMovedInStack}.
     */
    private boolean mJustMovedInStack;

    /**
     * Whether the window was resized by us while it was gone for layout.
     */
@@ -1379,6 +1384,39 @@ final class WindowState implements WindowManagerPolicy.WindowState {
        }
    }

    /**
     * Notifies this window that the corresponding task has just moved in the stack.
     * <p>
     * This is used to fix the following: If we moved in the stack, and if the last clip rect was
     * empty, meaning that our task was completely offscreen, we need to keep it invisible because
     * the actual app transition that updates the visibility is delayed by a few transactions.
     * Instead of messing around with the ordering and timing how transitions and transactions are
     * executed, we introduce this little hack which prevents this window of getting visible again
     * with the wrong bounds until the app transitions has started.
     * <p>
     * This method notifies the window about that we just moved in the stack so we can apply this
     * logic in {@link WindowStateAnimator#updateSurfaceWindowCrop}
     */
    void notifyMovedInStack() {
        mJustMovedInStack = true;
    }

    /**
     * See {@link #notifyMovedInStack}.
     *
     * @return Whether we just got moved in the corresponding stack.
     */
    boolean hasJustMovedInStack() {
        return mJustMovedInStack;
    }

    /**
     * Resets that we just moved in the corresponding stack. See {@link #notifyMovedInStack}.
     */
    void resetJustMovedInStack() {
        mJustMovedInStack = false;
    }

    private final class DeadWindowEventReceiver extends InputEventReceiver {
        DeadWindowEventReceiver(InputChannel inputChannel) {
            super(inputChannel, mService.mH.getLooper());
+5 −0
Original line number Diff line number Diff line
@@ -1191,6 +1191,11 @@ class WindowStateAnimator {

        w.transformFromScreenToSurfaceSpace(clipRect);

        // See {@link WindowState#notifyMovedInStack} for why this is necessary.
        if (w.hasJustMovedInStack() && mLastClipRect.isEmpty() && !clipRect.isEmpty()) {
            clipRect.setEmpty();
        }

        if (!clipRect.equals(mLastClipRect)) {
            mLastClipRect.set(clipRect);
            mSurfaceController.setCropInTransaction(clipRect, recoveringMemory);
+24 −3
Original line number Diff line number Diff line
@@ -62,7 +62,7 @@ class WindowSurfaceController {
    // However, we need to somehow handle the situation where the cropping would completely hide
    // the window. We achieve this by explicitly hiding the surface and not letting it be shown.
    private boolean mHiddenForCrop = false;

    private boolean mHiddenForOtherReasons = false;
    private final String title;

    public WindowSurfaceController(SurfaceSession s,
@@ -95,6 +95,11 @@ class WindowSurfaceController {

    void hideInTransaction(String reason) {
        if (SHOW_TRANSACTIONS) logSurface("HIDE ( " + reason + " )", null);
        mHiddenForOtherReasons = true;
        updateVisibility();
    }

    private void hideSurface() {
        if (mSurfaceControl != null) {
            mSurfaceShown = false;
            try {
@@ -152,9 +157,10 @@ class WindowSurfaceController {
            if (clipRect.width() > 0 && clipRect.height() > 0) {
                mSurfaceControl.setWindowCrop(clipRect);
                mHiddenForCrop = false;
                updateVisibility();
            } else {
                hideInTransaction("setCrop");
                mHiddenForCrop = true;
                updateVisibility();
            }
        } catch (RuntimeException e) {
            Slog.w(TAG, "Error setting crop surface of " + this
@@ -317,11 +323,26 @@ class WindowSurfaceController {
                "SHOW (performLayout)", null);
        if (DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + this
                + " during relayout");
        mHiddenForOtherReasons = false;
        return updateVisibility();
    }

        if (mHiddenForCrop) {
    private boolean updateVisibility() {
        if (mHiddenForCrop || mHiddenForOtherReasons) {
            if (mSurfaceShown) {
                hideSurface();
            }
            return false;
        } else {
            if (!mSurfaceShown) {
                return showSurface();
            } else {
                return true;
            }
        }
    }

    private boolean showSurface() {
        try {
            mSurfaceShown = true;
            mSurfaceControl.show();