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

Commit 3eda67c8 authored by Jorge Gil's avatar Jorge Gil
Browse files

Add top-padding to App Header when in full immersive

Adds padding equal to the status bar insets so that when the status bar
visibility changes (from the status bar window hidding in immersive and
being transiently shown on user clicks), the App Header is always shown
below the status bar.

Flag: com.android.window.flags.enable_fully_immersive_in_desktop
Bug: 369444147
Test: atest WMShellUnitTests
Test: manual - verify App Header is always under status bar in full
immersive mode

Change-Id: I91ea7c68a7d0faeaf5fb7b41e9b075bd52951ddd
parent cb28f1a1
Loading
Loading
Loading
Loading
+18 −4
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -62,10 +63,12 @@ import android.os.UserHandle;
import android.util.Size;
import android.util.Slog;
import android.view.Choreographer;
import android.view.InsetsState;
import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.window.TaskSnapshot;
@@ -444,8 +447,11 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
            mHandleMenu.relayout(startT, mResult.mCaptionX);
        }

        final boolean inFullImmersive = mDesktopRepository
                .isTaskInFullImmersiveState(taskInfo.taskId);
        updateRelayoutParams(mRelayoutParams, mContext, taskInfo, applyStartTransactionOnDraw,
                shouldSetTaskPositionAndCrop);
                shouldSetTaskPositionAndCrop, inFullImmersive, mDisplayController.getInsetsState(
                        taskInfo.displayId));

        final WindowDecorLinearLayout oldRootView = mResult.mRootView;
        final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
@@ -492,8 +498,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
            ));
        } else {
            mWindowDecorViewHolder.bindData(new AppHeaderViewHolder.HeaderData(
                    mTaskInfo, TaskInfoKt.getRequestingImmersive(mTaskInfo),
                    mDesktopRepository.isTaskInFullImmersiveState(mTaskInfo.taskId)
                    mTaskInfo, TaskInfoKt.getRequestingImmersive(mTaskInfo), inFullImmersive
            ));
        }
        Trace.endSection();
@@ -749,7 +754,9 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
            Context context,
            ActivityManager.RunningTaskInfo taskInfo,
            boolean applyStartTransactionOnDraw,
            boolean shouldSetTaskPositionAndCrop) {
            boolean shouldSetTaskPositionAndCrop,
            boolean inFullImmersiveMode,
            @NonNull InsetsState displayInsetsState) {
        final int captionLayoutId = getDesktopModeWindowDecorLayoutId(taskInfo.getWindowingMode());
        final boolean isAppHeader =
                captionLayoutId == R.layout.desktop_mode_app_header;
@@ -778,6 +785,13 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
                // including non-immersive apps that just don't handle caption insets properly.
                relayoutParams.mInsetSourceFlags |= FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
            }
            if (Flags.enableFullyImmersiveInDesktop() && inFullImmersiveMode) {
                final Insets systemBarInsets = displayInsetsState.calculateInsets(
                        taskInfo.getConfiguration().windowConfiguration.getBounds(),
                        WindowInsets.Type.systemBars() & ~WindowInsets.Type.captionBar(),
                        false /* ignoreVisibility */);
                relayoutParams.mCaptionTopPadding = systemBarInsets.top;
            }
            // Report occluding elements as bounding rects to the insets system so that apps can
            // draw in the empty space in the center:
            //   First, the "app chip" section of the caption bar (+ some extra margins).
+85 −18
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
import static android.view.WindowInsets.Type.captionBar;
import static android.view.WindowInsets.Type.statusBars;
import static android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND;

@@ -125,6 +126,7 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.quality.Strictness;

import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;

@@ -287,7 +289,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {

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

        assertThat(relayoutParams.mShadowRadiusId).isNotEqualTo(Resources.ID_NULL);
    }
@@ -303,7 +307,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                mTestableContext,
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false);
                /* shouldSetTaskPositionAndCrop */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

        assertThat(relayoutParams.mCornerRadius).isGreaterThan(0);
    }
@@ -324,7 +330,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                mTestableContext,
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false);
                /* shouldSetTaskPositionAndCrop */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

        assertThat(relayoutParams.mWindowDecorConfig.densityDpi).isEqualTo(customTaskDensity);
    }
@@ -346,7 +354,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                mTestableContext,
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false);
                /* shouldSetTaskPositionAndCrop */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

        assertThat(relayoutParams.mWindowDecorConfig.densityDpi).isEqualTo(systemDensity);
    }
@@ -364,7 +374,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                mTestableContext,
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false);
                /* shouldSetTaskPositionAndCrop */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

        assertThat(relayoutParams.hasInputFeatureSpy()).isTrue();
    }
@@ -381,7 +393,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                mTestableContext,
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false);
                /* shouldSetTaskPositionAndCrop */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

        assertThat(relayoutParams.hasInputFeatureSpy()).isFalse();
    }
@@ -397,7 +411,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                mTestableContext,
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false);
                /* shouldSetTaskPositionAndCrop */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

        assertThat(relayoutParams.hasInputFeatureSpy()).isFalse();
    }
@@ -413,7 +429,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                mTestableContext,
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false);
                /* shouldSetTaskPositionAndCrop */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

        assertThat(hasNoInputChannelFeature(relayoutParams)).isFalse();
    }
@@ -430,7 +448,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                mTestableContext,
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false);
                /* shouldSetTaskPositionAndCrop */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

        assertThat(hasNoInputChannelFeature(relayoutParams)).isTrue();
    }
@@ -447,7 +467,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                mTestableContext,
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false);
                /* shouldSetTaskPositionAndCrop */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

        assertThat(hasNoInputChannelFeature(relayoutParams)).isTrue();
    }
@@ -465,7 +487,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                mTestableContext,
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false);
                /* shouldSetTaskPositionAndCrop */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

        assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) != 0).isTrue();
    }
@@ -484,7 +508,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                mTestableContext,
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false);
                /* shouldSetTaskPositionAndCrop */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

        assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) == 0).isTrue();
    }
@@ -501,7 +527,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                mTestableContext,
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false);
                /* shouldSetTaskPositionAndCrop */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

        assertThat(
                (relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) != 0)
@@ -520,13 +548,41 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                mTestableContext,
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false);
                /* shouldSetTaskPositionAndCrop */ false,
                /* inFullImmersiveMode */ false,
                new InsetsState());

        assertThat(
                (relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) == 0)
                .isTrue();
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
    public void updateRelayoutParams_header_addsPaddingInFullImmersive() {
        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
        taskInfo.configuration.windowConfiguration.setBounds(new Rect(0, 0, 1000, 2000));
        final InsetsState insetsState = createInsetsState(List.of(
                createInsetsSource(
                        0 /* id */, statusBars(), true /* visible */, new Rect(0, 0, 1000, 50)),
                createInsetsSource(
                        1 /* id */, captionBar(), true /* visible */, new Rect(0, 0, 1000, 100))));
        final RelayoutParams relayoutParams = new RelayoutParams();

        DesktopModeWindowDecoration.updateRelayoutParams(
                relayoutParams,
                mTestableContext,
                taskInfo,
                /* applyStartTransactionOnDraw= */ true,
                /* shouldSetTaskPositionAndCrop */ false,
                /* inFullImmersiveMode */ true,
                insetsState);

        // Takes status bar inset as padding, ignores caption bar inset.
        assertThat(relayoutParams.mCaptionTopPadding).isEqualTo(50);
    }

    @Test
    public void relayout_fullscreenTask_appliesTransactionImmediately() {
        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
@@ -1136,14 +1192,25 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                != 0;
    }

    private InsetsState createInsetsState(@WindowInsets.Type.InsetsType int type, boolean visible) {
        final InsetsState state = new InsetsState();
        final InsetsSource source = new InsetsSource(/* id= */0, type);
    private InsetsSource createInsetsSource(int id, @WindowInsets.Type.InsetsType int type,
            boolean visible, @NonNull Rect frame) {
        final InsetsSource source = new InsetsSource(id, type);
        source.setVisible(visible);
        state.addSource(source);
        source.setFrame(frame);
        return source;
    }

    private InsetsState createInsetsState(@NonNull List<InsetsSource> sources) {
        final InsetsState state = new InsetsState();
        sources.forEach(state::addSource);
        return state;
    }

    private InsetsState createInsetsState(@WindowInsets.Type.InsetsType int type, boolean visible) {
        final InsetsSource source = createInsetsSource(0 /* id */, type, visible, new Rect());
        return createInsetsState(List.of(source));
    }

    private static class TestTouchEventListener extends GestureDetector.SimpleOnGestureListener
            implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener,
            View.OnGenericMotionListener, DragDetector.MotionEventHandler {