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

Commit 23df3a3c authored by Adrian Roos's avatar Adrian Roos
Browse files

Letterboxing: ensure bar contrast

Ensures that any bars that draw over a letterbox draw their own
background such that they always have contrast.

This fixes an issue, where if a light status / navigation bar app was
letterboxed, the bar would still draw dark icons over the now black
letterbox, making the icons unreadable.

To do so, splits the letterbox into a layout and an apply surface
changes phase.

Change-Id: Ia8afa3386d75d9a72434d701b867c3ebc35cc36f
Fixes: 72696928
Test: Open a letterboxed app with a white navigation bar, verify it is visible
Test: ApiDemos > App > Activity > Max Aspect Ratio > 1:1, verify navbar is visible
parent 31ac587b
Loading
Loading
Loading
Loading
+18 −1
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static com.android.server.wm.proto.BarControllerProto.STATE;
import static com.android.server.wm.proto.BarControllerProto.TRANSIENT_STATE;

import android.app.StatusBarManager;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
@@ -68,6 +69,7 @@ public class BarController {
    private boolean mShowTransparent;
    private boolean mSetUnHideFlagWhenNextTransparent;
    private boolean mNoAnimationOnNextShow;
    private final Rect mContentFrame = new Rect();

    private OnBarVisibilityChangedListener mVisibilityChangeListener;

@@ -87,6 +89,15 @@ public class BarController {
        mWin = win;
    }

    /**
     * Sets the frame within which the bar will display its content.
     *
     * This is used to determine if letterboxes interfere with the display of such content.
     */
    public void setContentFrame(Rect frame) {
        mContentFrame.set(frame);
    }

    public void setShowTransparent(boolean transparent) {
        if (transparent != mShowTransparent) {
            mShowTransparent = transparent;
@@ -135,7 +146,8 @@ public class BarController {
                } else {
                    vis &= ~mTranslucentFlag;
                }
                if ((fl & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) {
                if ((fl & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
                        && isTransparentAllowed(win)) {
                    vis |= mTransparentFlag;
                } else {
                    vis &= ~mTransparentFlag;
@@ -148,6 +160,10 @@ public class BarController {
        return vis;
    }

    boolean isTransparentAllowed(WindowState win) {
        return win == null || !win.isLetterboxedOverlappingWith(mContentFrame);
    }

    public boolean setBarShowingLw(final boolean show) {
        if (mWin == null) return false;
        if (show && mTransientBarState == TRANSIENT_BAR_HIDING) {
@@ -328,6 +344,7 @@ public class BarController {
            pw.println(StatusBarManager.windowStateToString(mState));
            pw.print(prefix); pw.print("  "); pw.print("mTransientBar"); pw.print('=');
            pw.println(transientBarStateToString(mTransientBarState));
            pw.print(prefix); pw.print("  mContentFrame="); pw.println(mContentFrame);
        }
    }

+27 −15
Original line number Diff line number Diff line
@@ -4705,6 +4705,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        displayFrames.mStable.top = displayFrames.mUnrestricted.top
                + mStatusBarHeightForRotation[displayFrames.mRotation];

        // Tell the bar controller where the collapsed status bar content is
        mTmpRect.set(mStatusBar.getContentFrameLw());
        mTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
        mTmpRect.top = mStatusBar.getContentFrameLw().top;  // Ignore top display cutout inset
        mTmpRect.bottom = displayFrames.mStable.top;  // Use collapsed status bar size
        mStatusBarController.setContentFrame(mTmpRect);

        boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
        boolean statusBarTranslucent = (sysui
                & (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != 0;
@@ -4838,6 +4845,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                mTmpNavigationFrame, displayFrames.mDisplayCutoutSafe, mTmpNavigationFrame, dcf,
                mTmpNavigationFrame, displayFrames.mDisplayCutoutSafe,
                displayFrames.mDisplayCutout);
        mNavigationBarController.setContentFrame(mNavigationBar.getContentFrameLw());

        if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
        return mNavigationBarController.checkHiddenLw();
    }
@@ -8097,15 +8106,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        return vis;
    }

    private boolean drawsSystemBarBackground(WindowState win) {
        return win == null || (win.getAttrs().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
    }

    private boolean forcesDrawStatusBarBackground(WindowState win) {
        return win == null || (win.getAttrs().privateFlags
                & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0;
    }

    private int updateSystemBarsLw(WindowState win, int oldVis, int vis) {
        final boolean dockedStackVisible =
                mWindowManagerInternal.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
@@ -8129,13 +8129,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                mTopDockedOpaqueWindowState, 0, 0);

        final boolean fullscreenDrawsStatusBarBackground =
                (drawsSystemBarBackground(mTopFullscreenOpaqueWindowState)
                        && (vis & View.STATUS_BAR_TRANSLUCENT) == 0)
                || forcesDrawStatusBarBackground(mTopFullscreenOpaqueWindowState);
                drawsStatusBarBackground(vis, mTopFullscreenOpaqueWindowState);
        final boolean dockedDrawsStatusBarBackground =
                (drawsSystemBarBackground(mTopDockedOpaqueWindowState)
                        && (dockedVis & View.STATUS_BAR_TRANSLUCENT) == 0)
                || forcesDrawStatusBarBackground(mTopDockedOpaqueWindowState);
                drawsStatusBarBackground(dockedVis, mTopDockedOpaqueWindowState);

        // prevent status bar interaction from clearing certain flags
        int type = win.getAttrs().type;
@@ -8238,6 +8234,22 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        return vis;
    }

    private boolean drawsStatusBarBackground(int vis, WindowState win) {
        if (!mStatusBarController.isTransparentAllowed(win)) {
            return false;
        }
        if (win == null) {
            return true;
        }

        final boolean drawsSystemBars =
                (win.getAttrs().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
        final boolean forceDrawsSystemBars =
                (win.getAttrs().privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0;

        return forceDrawsSystemBars || drawsSystemBars && (vis & View.STATUS_BAR_TRANSLUCENT) == 0;
    }

    /**
     * @return the current visibility flags with the nav-bar opacity related flags toggled based
     *         on the nav bar opacity rules chosen by {@link #mNavBarOpacityMode}.
+8 −0
Original line number Diff line number Diff line
@@ -447,6 +447,14 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
            return false;
        }

        /**
         * Returns true if the window has a letterbox and any part of that letterbox overlaps with
         * the given {@code rect}.
         */
        default boolean isLetterboxedOverlappingWith(Rect rect) {
            return false;
        }

        /** @return the current windowing mode of this window. */
        int getWindowingMode();

+24 −16
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.SurfaceControl.HIDDEN;
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
@@ -712,7 +711,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
        if (destroyedSomething) {
            final DisplayContent dc = getDisplayContent();
            dc.assignWindowLayers(true /*setLayoutNeeded*/);
            updateLetterbox(null);
            updateLetterboxSurface(null);
        }
    }

@@ -979,7 +978,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
    void removeChild(WindowState child) {
        super.removeChild(child);
        checkKeyguardFlagsChanged();
        updateLetterbox(child);
        updateLetterboxSurface(child);
    }

    private boolean waitingForReplacement() {
@@ -1470,7 +1469,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
        return isInterestingAndDrawn;
    }

    void updateLetterbox(WindowState winHint) {
    void layoutLetterbox(WindowState winHint) {
        final WindowState w = findMainWindow();
        if (w != winHint && winHint != null && w != null) {
            return;
@@ -1481,19 +1480,20 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
            if (mLetterbox == null) {
                mLetterbox = new Letterbox(() -> makeChildSurface(null));
            }
            mLetterbox.setDimensions(mPendingTransaction, getParent().getBounds(), w.mFrame);
            mLetterbox.layout(getParent().getBounds(), w.mFrame);
        } else if (mLetterbox != null) {
            final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
            // Make sure we have a transaction here, in case we're called outside of a transaction.
            // This does not use mPendingTransaction, because SurfaceAnimator uses a
            // global transaction in onAnimationEnd.
            SurfaceControl.openTransaction();
            try {
                mLetterbox.hide(t);
            } finally {
                SurfaceControl.mergeToGlobalTransaction(t);
                SurfaceControl.closeTransaction();
            mLetterbox.hide();
        }
    }

    void updateLetterboxSurface(WindowState winHint) {
        final WindowState w = findMainWindow();
        if (w != winHint && winHint != null && w != null) {
            return;
        }
        layoutLetterbox(winHint);
        if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {
            mLetterbox.applySurfaceChanges(mPendingTransaction);
        }
    }

@@ -2156,4 +2156,12 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
            return new Rect();
        }
    }

    /**
     * @eturn true if there is a letterbox and any part of that letterbox overlaps with
     * the given {@code rect}.
     */
    boolean isLetterboxOverlappingWith(Rect rect) {
        return mLetterbox != null && mLetterbox.isOverlappingWith(rect);
    }
}
+5 −2
Original line number Diff line number Diff line
@@ -80,7 +80,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_STACK_CRAWLS;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -558,6 +557,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
                    w.updateLastInsetValues();
                }

                if (w.mAppToken != null) {
                    w.mAppToken.layoutLetterbox(w);
                }

                if (DEBUG_LAYOUT) Slog.v(TAG, "  LAYOUT: mFrame=" + w.mFrame
                        + " mContainingFrame=" + w.mContainingFrame
                        + " mDisplayFrame=" + w.mDisplayFrame);
@@ -697,7 +700,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo

        final AppWindowToken atoken = w.mAppToken;
        if (atoken != null) {
            atoken.updateLetterbox(w);
            atoken.updateLetterboxSurface(w);
            final boolean updateAllDrawn = atoken.updateDrawnWindowStates(w);
            if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(atoken)) {
                mTmpUpdateAllDrawn.add(atoken);
Loading