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

Commit f2c815d6 authored by Jorge Gil's avatar Jorge Gil
Browse files

Make App Header visibility follow status bar in full immersive

When the app is fully immersive, the app header should show and hide in
alignment with the status bar.
This change makes the caption visibility configurable with a
RelayoutParam, and disables the force-show of the caption when in
freeform, and instead checks whether the task is in full immersive mode
first.

Flag: com.android.window.flags.enable_fully_immersive_in_desktop
Bug: 369444183
Bug: 369444147
Bug: 369443876
Test: atest WMShellUnitTests
Test: check App Header follows status bar visibility
Change-Id: I7f39961f550ddf8656738e5f32a839b1c454bcad
parent 3eda67c8
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -194,6 +194,8 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
            ActivityManager.RunningTaskInfo taskInfo,
            boolean applyStartTransactionOnDraw,
            boolean setTaskCropAndPosition,
            boolean isStatusBarVisible,
            boolean isKeyguardVisibleAndOccluded,
            InsetsState displayInsetsState) {
        relayoutParams.reset();
        relayoutParams.mRunningTaskInfo = taskInfo;
@@ -204,6 +206,8 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
                : R.dimen.freeform_decor_shadow_unfocused_thickness;
        relayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
        relayoutParams.mSetTaskPositionAndCrop = setTaskCropAndPosition;
        relayoutParams.mIsCaptionVisible = taskInfo.isFreeform()
                || (isStatusBarVisible && !isKeyguardVisibleAndOccluded);

        if (TaskInfoKt.isTransparentCaptionBarAppearance(taskInfo)) {
            // If the app is requesting to customize the caption bar, allow input to fall
@@ -240,7 +244,8 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
        final WindowContainerTransaction wct = new WindowContainerTransaction();

        updateRelayoutParams(mRelayoutParams, taskInfo, applyStartTransactionOnDraw,
                setTaskCropAndPosition, mDisplayController.getInsetsState(taskInfo.displayId));
                setTaskCropAndPosition, mIsStatusBarVisible, mIsKeyguardVisibleAndOccluded,
                mDisplayController.getInsetsState(taskInfo.displayId));

        relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
        // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
+26 −2
Original line number Diff line number Diff line
@@ -450,8 +450,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
        final boolean inFullImmersive = mDesktopRepository
                .isTaskInFullImmersiveState(taskInfo.taskId);
        updateRelayoutParams(mRelayoutParams, mContext, taskInfo, applyStartTransactionOnDraw,
                shouldSetTaskPositionAndCrop, inFullImmersive, mDisplayController.getInsetsState(
                        taskInfo.displayId));
                shouldSetTaskPositionAndCrop, mIsStatusBarVisible, mIsKeyguardVisibleAndOccluded,
                inFullImmersive, mDisplayController.getInsetsState(taskInfo.displayId));

        final WindowDecorLinearLayout oldRootView = mResult.mRootView;
        final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
@@ -755,6 +755,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
            ActivityManager.RunningTaskInfo taskInfo,
            boolean applyStartTransactionOnDraw,
            boolean shouldSetTaskPositionAndCrop,
            boolean isStatusBarVisible,
            boolean isKeyguardVisibleAndOccluded,
            boolean inFullImmersiveMode,
            @NonNull InsetsState displayInsetsState) {
        final int captionLayoutId = getDesktopModeWindowDecorLayoutId(taskInfo.getWindowingMode());
@@ -767,6 +769,28 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
        relayoutParams.mCaptionHeightId = getCaptionHeightIdStatic(taskInfo.getWindowingMode());
        relayoutParams.mCaptionWidthId = getCaptionWidthId(relayoutParams.mLayoutResId);

        final boolean showCaption;
        if (Flags.enableFullyImmersiveInDesktop()) {
            if (inFullImmersiveMode) {
                showCaption = isStatusBarVisible && !isKeyguardVisibleAndOccluded;
            } else {
                showCaption = taskInfo.isFreeform()
                        || (isStatusBarVisible && !isKeyguardVisibleAndOccluded);
            }
        } else {
            // Caption should always be visible in freeform mode. When not in freeform,
            // align with the status bar except when showing over keyguard (where it should not
            // shown).
            //  TODO(b/356405803): Investigate how it's possible for the status bar visibility to
            //   be false while a freeform window is open if the status bar is always
            //   forcibly-shown. It may be that the InsetsState (from which |mIsStatusBarVisible|
            //   is set) still contains an invisible insets source in immersive cases even if the
            //   status bar is shown?
            showCaption = taskInfo.isFreeform()
                    || (isStatusBarVisible && !isKeyguardVisibleAndOccluded);
        }
        relayoutParams.mIsCaptionVisible = showCaption;

        if (isAppHeader) {
            if (TaskInfoKt.isTransparentCaptionBarAppearance(taskInfo)) {
                // If the app is requesting to customize the caption bar, allow input to fall
+8 −13
Original line number Diff line number Diff line
@@ -144,8 +144,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
    TaskDragResizer mTaskDragResizer;
    boolean mIsCaptionVisible;

    private boolean mIsStatusBarVisible;
    private boolean mIsKeyguardVisibleAndOccluded;
    boolean mIsStatusBarVisible;
    boolean mIsKeyguardVisibleAndOccluded;

    /** The most recent set of insets applied to this window decoration. */
    private WindowDecorationInsets mWindowDecorationInsets;
@@ -241,7 +241,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
        }
        rootView = null; // Clear it just in case we use it accidentally

        updateCaptionVisibility(outResult.mRootView);
        updateCaptionVisibility(outResult.mRootView, params);

        final Rect taskBounds = mTaskInfo.getConfiguration().windowConfiguration.getBounds();
        outResult.mWidth = taskBounds.width();
@@ -527,17 +527,10 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
    }

    /**
     * Checks if task has entered/exited immersive mode and requires a change in caption visibility.
     * Update caption visibility state and views.
     */
    private void updateCaptionVisibility(View rootView) {
        // Caption should always be visible in freeform mode. When not in freeform, align with the
        // status bar except when showing over keyguard (where it should not shown).
        //  TODO(b/356405803): Investigate how it's possible for the status bar visibility to be
        //   false while a freeform window is open if the status bar is always forcibly-shown. It
        //   may be that the InsetsState (from which |mIsStatusBarVisible| is set) still contains
        //   an invisible insets source in immersive cases even if the status bar is shown?
        mIsCaptionVisible = mTaskInfo.isFreeform()
                || (mIsStatusBarVisible && !mIsKeyguardVisibleAndOccluded);
    private void updateCaptionVisibility(View rootView, @NonNull RelayoutParams params) {
        mIsCaptionVisible = params.mIsCaptionVisible;
        setCaptionVisibility(rootView, mIsCaptionVisible);
    }

@@ -737,6 +730,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
        int mCornerRadius;

        int mCaptionTopPadding;
        boolean mIsCaptionVisible;

        Configuration mWindowDecorConfig;

@@ -755,6 +749,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
            mCornerRadius = 0;

            mCaptionTopPadding = 0;
            mIsCaptionVisible = false;

            mApplyStartTransactionOnDraw = false;
            mSetTaskPositionAndCrop = false;
+6 −0
Original line number Diff line number Diff line
@@ -47,6 +47,8 @@ class CaptionWindowDecorationTests : ShellTestCase() {
            taskInfo,
            true,
            false,
            true /* isStatusBarVisible */,
            false /* isKeyguardVisibleAndOccluded */,
            InsetsState()
        )

@@ -66,6 +68,8 @@ class CaptionWindowDecorationTests : ShellTestCase() {
            taskInfo,
            true,
            false,
            true /* isStatusBarVisible */,
            false /* isKeyguardVisibleAndOccluded */,
            InsetsState()
        )

@@ -81,6 +85,8 @@ class CaptionWindowDecorationTests : ShellTestCase() {
            taskInfo,
            true,
            false,
            true /* isStatusBarVisible */,
            false /* isKeyguardVisibleAndOccluded */,
            InsetsState()
        )
        Truth.assertThat(relayoutParams.mOccludingCaptionElements.size).isEqualTo(2)
+167 −0
Original line number Diff line number Diff line
@@ -290,6 +290,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
        DesktopModeWindowDecoration.updateRelayoutParams(
                relayoutParams, mContext, taskInfo, /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false,
                /* isStatusBarVisible */ true,
                /* isKeyguardVisibleAndOccluded */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

@@ -308,6 +310,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false,
                /* isStatusBarVisible */ true,
                /* isKeyguardVisibleAndOccluded */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

@@ -331,6 +335,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false,
                /* isStatusBarVisible */ true,
                /* isKeyguardVisibleAndOccluded */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

@@ -355,6 +361,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false,
                /* isStatusBarVisible */ true,
                /* isKeyguardVisibleAndOccluded */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

@@ -375,6 +383,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false,
                /* isStatusBarVisible */ true,
                /* isKeyguardVisibleAndOccluded */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

@@ -394,6 +404,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false,
                /* isStatusBarVisible */ true,
                /* isKeyguardVisibleAndOccluded */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

@@ -412,6 +424,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false,
                /* isStatusBarVisible */ true,
                /* isKeyguardVisibleAndOccluded */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

@@ -430,6 +444,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false,
                /* isStatusBarVisible */ true,
                /* isKeyguardVisibleAndOccluded */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

@@ -449,6 +465,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false,
                /* isStatusBarVisible */ true,
                /* isKeyguardVisibleAndOccluded */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

@@ -468,6 +486,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false,
                /* isStatusBarVisible */ true,
                /* isKeyguardVisibleAndOccluded */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

@@ -488,6 +508,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false,
                /* isStatusBarVisible */ true,
                /* isKeyguardVisibleAndOccluded */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

@@ -509,6 +531,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false,
                /* isStatusBarVisible */ true,
                /* isKeyguardVisibleAndOccluded */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

@@ -528,6 +552,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false,
                /* isStatusBarVisible */ true,
                /* isKeyguardVisibleAndOccluded */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

@@ -549,6 +575,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false,
                /* isStatusBarVisible */ true,
                /* isKeyguardVisibleAndOccluded */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

@@ -576,6 +604,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false,
                /* isStatusBarVisible */ true,
                /* isKeyguardVisibleAndOccluded */ false,
                /* inFullImmersiveMode */ true,
                insetsState);

@@ -583,6 +613,143 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
        assertThat(relayoutParams.mCaptionTopPadding).isEqualTo(50);
    }

    @Test
    @DisableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
    public void updateRelayoutParams_header_statusBarInvisible_captionVisible() {
        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
        final RelayoutParams relayoutParams = new RelayoutParams();

        DesktopModeWindowDecoration.updateRelayoutParams(
                relayoutParams,
                mTestableContext,
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false,
                /* isStatusBarVisible */ false,
                /* isKeyguardVisibleAndOccluded */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

        // Header is always shown because it's assumed the status bar is always visible.
        assertThat(relayoutParams.mIsCaptionVisible).isTrue();
    }

    @Test
    public void updateRelayoutParams_handle_statusBarVisibleAndNotOverKeyguard_captionVisible() {
        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
        final RelayoutParams relayoutParams = new RelayoutParams();

        DesktopModeWindowDecoration.updateRelayoutParams(
                relayoutParams,
                mTestableContext,
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false,
                /* isStatusBarVisible */ true,
                /* isKeyguardVisibleAndOccluded */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

        assertThat(relayoutParams.mIsCaptionVisible).isTrue();
    }

    @Test
    public void updateRelayoutParams_handle_statusBarInvisible_captionNotVisible() {
        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
        final RelayoutParams relayoutParams = new RelayoutParams();

        DesktopModeWindowDecoration.updateRelayoutParams(
                relayoutParams,
                mTestableContext,
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false,
                /* isStatusBarVisible */ false,
                /* isKeyguardVisibleAndOccluded */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

        assertThat(relayoutParams.mIsCaptionVisible).isFalse();
    }

    @Test
    public void updateRelayoutParams_handle_overKeyguard_captionNotVisible() {
        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
        final RelayoutParams relayoutParams = new RelayoutParams();

        DesktopModeWindowDecoration.updateRelayoutParams(
                relayoutParams,
                mTestableContext,
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false,
                /* isStatusBarVisible */ true,
                /* isKeyguardVisibleAndOccluded */ true,
                /* inFullImmersiveMode */ false,
                new InsetsState());

        assertThat(relayoutParams.mIsCaptionVisible).isFalse();
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
    public void updateRelayoutParams_header_fullyImmersive_captionVisFollowsStatusBar() {
        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
        final RelayoutParams relayoutParams = new RelayoutParams();

        DesktopModeWindowDecoration.updateRelayoutParams(
                relayoutParams,
                mTestableContext,
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false,
                /* isStatusBarVisible */ true,
                /* isKeyguardVisibleAndOccluded */ false,
                /* inFullImmersiveMode */ true,
                new InsetsState());

        assertThat(relayoutParams.mIsCaptionVisible).isTrue();

        DesktopModeWindowDecoration.updateRelayoutParams(
                relayoutParams,
                mTestableContext,
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false,
                /* isStatusBarVisible */ false,
                /* isKeyguardVisibleAndOccluded */ false,
                /* inFullImmersiveMode */ true,
                new InsetsState());

        assertThat(relayoutParams.mIsCaptionVisible).isFalse();
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
    public void updateRelayoutParams_header_fullyImmersive_overKeyguard_captionNotVisible() {
        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
        final RelayoutParams relayoutParams = new RelayoutParams();

        DesktopModeWindowDecoration.updateRelayoutParams(
                relayoutParams,
                mTestableContext,
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false,
                /* isStatusBarVisible */ true,
                /* isKeyguardVisibleAndOccluded */ true,
                /* inFullImmersiveMode */ true,
                new InsetsState());

        assertThat(relayoutParams.mIsCaptionVisible).isFalse();
    }

    @Test
    public void relayout_fullscreenTask_appliesTransactionImmediately() {
        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
Loading