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

Commit 838177fa authored by Ebru Kurnaz's avatar Ebru Kurnaz Committed by Android (Google) Code Review
Browse files

Merge "Use wm callbacks for sys decor in navigation bar instead of command queue." into main

parents c40eb275 eaeb23fc
Loading
Loading
Loading
Loading
+70 −0
Original line number Diff line number Diff line
@@ -29,17 +29,21 @@ import android.view.IDisplayWindowListener
import android.view.mockIWindowManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.app.displaylib.DisplayDecorationListener
import com.android.app.displaylib.DisplayRepository.PendingDisplay
import com.android.app.displaylib.DisplaysWithDecorationsRepositoryCompat
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.FlowValue
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.mockCommandQueue
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.kotlinArgumentCaptor
import com.android.systemui.util.mockito.mock
import com.android.window.flags.Flags
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.TestScope
@@ -73,6 +77,7 @@ class DisplayRepositoryTest : SysuiTestCase() {

    private val testHandler = kosmos.fakeHandler
    private val testScope = kosmos.testScope
    private val testDispacher = kosmos.testDispatcher
    private val defaultDisplay =
        display(type = TYPE_INTERNAL, id = DEFAULT_DISPLAY, state = Display.STATE_ON)

@@ -87,6 +92,10 @@ class DisplayRepositoryTest : SysuiTestCase() {
        }
    }

    private val displayRepositoryCompat: DisplaysWithDecorationsRepositoryCompat by lazy {
        kosmos.displaysWithDecorationsRepositoryCompat
    }

    @Before
    fun setup() {
        setDisplays(listOf(defaultDisplay))
@@ -734,6 +743,67 @@ class DisplayRepositoryTest : SysuiTestCase() {
            assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(1)
        }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM)
    fun displaysWithSystemDecorationsCompat_registerListener_notifyAddSystemDecor() =
        testScope.runTest {
            val listener = mock<DisplayDecorationListener>()
            displayRepositoryCompat.registerDisplayDecorationListener(listener, testDispacher)
            captureWmListener()

            wmListener.onDisplayAddSystemDecorations(1)

            verify(listener).onDisplayAddSystemDecorations(1)
        }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM)
    fun displaysWithSystemDecorationsCompat_registerListener_notifyInitialDisplaysWithSysDecor() =
        testScope.runTest {
            setDisplays(0)
            whenever(windowManager.shouldShowSystemDecors(0)).thenReturn(true)
            val listener = mock<DisplayDecorationListener>()

            displayRepositoryCompat.registerDisplayDecorationListener(listener, testDispacher)
            captureWmListener()

            verify(listener).onDisplayAddSystemDecorations(0)
        }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM)
    fun displaysWithSystemDecorationsCompat_registerListener_notifyRemoveSystemDecor() =
        testScope.runTest {
            setDisplays(0)
            whenever(windowManager.shouldShowSystemDecors(0)).thenReturn(true)
            val listener = mock<DisplayDecorationListener>()

            displayRepositoryCompat.registerDisplayDecorationListener(listener, testDispacher)
            captureWmListener()
            wmListener.onDisplayRemoveSystemDecorations(0)

            verify(listener).onDisplayAddSystemDecorations(0)
            verify(listener).onDisplayRemoveSystemDecorations(0)
        }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM)
    fun displaysWithSystemDecorationsCompat_registerListener_notifyAllDisplaysWithSysDecor() =
        testScope.runTest {
            setDisplays(0, 2)
            whenever(windowManager.shouldShowSystemDecors(0)).thenReturn(true)
            whenever(windowManager.shouldShowSystemDecors(2)).thenReturn(true)
            val listener = mock<DisplayDecorationListener>()

            displayRepositoryCompat.registerDisplayDecorationListener(listener, testDispacher)
            captureWmListener()
            wmListener.onDisplayAddSystemDecorations(3)

            verify(listener).onDisplayAddSystemDecorations(0)
            verify(listener).onDisplayAddSystemDecorations(2)
            verify(listener).onDisplayAddSystemDecorations(3)
        }

    @Test
    fun getDisplay_slowMappingToDisplay_returnsRegardless() =
        testScope.runTest {
+9 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.view.IWindowManager
import com.android.app.displaylib.DisplayLibBackground
import com.android.app.displaylib.DisplayLibComponent
import com.android.app.displaylib.DisplaysWithDecorationsRepository
import com.android.app.displaylib.DisplaysWithDecorationsRepositoryCompat
import com.android.app.displaylib.PerDisplayRepository
import com.android.app.displaylib.createDisplayLibComponent
import com.android.systemui.CoreStartable
@@ -159,4 +160,12 @@ object DisplayLibModule {
    ): DisplaysWithDecorationsRepository {
        return displayLibComponent.displaysWithDecorationsRepository
    }

    @Provides
    @SysUISingleton
    fun providesDisplaysWithDecorationsRepositoryCompatFromLib(
        displayLibComponent: DisplayLibComponent
    ): DisplaysWithDecorationsRepositoryCompat {
        return displayLibComponent.displaysWithDecorationsRepositoryCompat
    }
}
+66 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.display.flags

import android.window.DesktopExperienceFlags
import com.android.systemui.flags.RefactorFlagUtils
import com.android.window.flags.Flags

/** Helper for reading or using the enable wm callback for system decor changes flag state. */
object WmCallbackForSysDecorFlag {
    /** The aconfig flag name */
    const val FLAG_NAME = Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM

    /**
     * When true, this is defined as [DesktopExperienceFlags] to make it possible to enable it
     * together with all the other desktop experience flags from the dev settings.
     *
     * Alternatively, using adb:
     * ```bash
     * adb shell setprop persist.wm.debug.desktop_experience_devopts 1
     * ```
     */
    private const val ENABLED_BY_DESKTOP_EXPERIENCE_DEV_OPTION = false

    val FLAG =
        DesktopExperienceFlags.DesktopExperienceFlag(
            Flags::enableSysDecorsCallbacksViaWm,
            /* shouldOverrideByDevOption= */ ENABLED_BY_DESKTOP_EXPERIENCE_DEV_OPTION,
            Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM,
        )

    /** Is the refactor enabled */
    @JvmStatic
    inline val isEnabled: Boolean
        get() = FLAG.isTrue

    /**
     * Called to ensure code is only run when the flag is enabled. This protects users from the
     * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
     * build to ensure that the refactor author catches issues in testing.
     */
    @JvmStatic
    inline fun isUnexpectedlyInLegacyMode() =
        RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)

    /**
     * Called to ensure code is only run when the flag is disabled. This will throw an exception if
     * the flag is enabled to ensure that the refactor author catches issues in testing.
     */
    @JvmStatic
    inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
}
+42 −10
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler
import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
import static com.android.wm.shell.Flags.enableTaskbarNavbarUnification;
import static com.android.wm.shell.Flags.enableTaskbarOnPhones;
import static com.android.window.flags.Flags.enableSysDecorsCallbacksViaWm;

import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -41,6 +42,8 @@ import android.window.DesktopExperienceFlags;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.app.displaylib.DisplayDecorationListener;
import com.android.app.displaylib.DisplaysWithDecorationsRepositoryCompat;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.RegisterStatusBarResult;
@@ -49,6 +52,7 @@ import com.android.systemui.Dumpable;
import com.android.systemui.LauncherProxyService;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.display.flags.WmCallbackForSysDecorFlag;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.views.NavigationBar;
@@ -67,6 +71,8 @@ import com.android.wm.shell.pip.Pip;

import dalvik.annotation.optimization.NeverCompile;

import kotlinx.coroutines.CoroutineDispatcher;

import java.io.PrintWriter;
import java.util.Optional;
import java.util.concurrent.Executor;
@@ -132,7 +138,9 @@ public class NavigationBarControllerImpl implements
            Optional<BackAnimation> backAnimation,
            SecureSettings secureSettings,
            DisplayTracker displayTracker,
            DeviceStateManager deviceStateManager) {
            DeviceStateManager deviceStateManager,
            DisplaysWithDecorationsRepositoryCompat displaysWithDecorationsRepositoryCompat,
            @Main CoroutineDispatcher mainCoroutineDispatcher) {
        mContext = context;
        mExecutor = mainExecutor;
        mNavigationBarComponentFactory = navigationBarComponentFactory;
@@ -140,6 +148,10 @@ public class NavigationBarControllerImpl implements
        mDisplayTracker = displayTracker;
        mDisplayManager = mContext.getSystemService(DisplayManager.class);
        commandQueue.addCallback(mCommandQueueCallbacks);
        if (enableSysDecorsCallbacksViaWm()) {
            displaysWithDecorationsRepositoryCompat.registerDisplayDecorationListener(
                    mDisplayDecorationListener, mainCoroutineDispatcher);
        }
        configurationController.addCallback(this);
        mConfigChanges.applyNewConfig(mContext.getResources());
        mNavMode = navigationModeController.addListener(this);
@@ -271,26 +283,23 @@ public class NavigationBarControllerImpl implements
        return mIsLargeScreen || (foldedOrPhone && enableTaskbarNavbarUnification());
    }

    // TODO: b/408503553 - Remove system decor callbacks once the flag is cleaned up.
    private final CommandQueue.Callbacks mCommandQueueCallbacks = new CommandQueue.Callbacks() {
        @Override
        public void onDisplayRemoved(int displayId) {
            onDisplayRemoveSystemDecorations(displayId);
            mDisplayDecorationListener.onDisplayRemoveSystemDecorations(displayId);
        }

        @Override
        public void onDisplayRemoveSystemDecorations(int displayId) {
            removeNavigationBar(displayId);
            mHasNavBar.delete(displayId);
            WmCallbackForSysDecorFlag.assertInLegacyMode();
            mDisplayDecorationListener.onDisplayRemoveSystemDecorations(displayId);
        }

        @Override
        public void onDisplayAddSystemDecorations(int displayId) {
            if (DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue()) {
                mHasNavBar.put(displayId, true);
            }
            Display display = mDisplayManager.getDisplay(displayId);
            mIsLargeScreen = isLargeScreen(mContext);
            createNavigationBar(display, null /* savedState */, null /* result */);
            WmCallbackForSysDecorFlag.assertInLegacyMode();
            mDisplayDecorationListener.onDisplayAddSystemDecorations(displayId);
        }

        @Override
@@ -320,6 +329,29 @@ public class NavigationBarControllerImpl implements
        }
    };

    private final DisplayDecorationListener mDisplayDecorationListener =
            new DisplayDecorationListener() {
                @Override
                public void onDisplayRemoved(int displayId) {
                    onDisplayRemoveSystemDecorations(displayId);
                }

                @Override
                public void onDisplayRemoveSystemDecorations(int displayId) {
                    removeNavigationBar(displayId);
                    mHasNavBar.delete(displayId);
                }

                @Override
                public void onDisplayAddSystemDecorations(int displayId) {
                    if (DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue()) {
                        mHasNavBar.put(displayId, true);
                    }
                    Display display = mDisplayManager.getDisplay(displayId);
                    mIsLargeScreen = isLargeScreen(mContext);
                    createNavigationBar(display, null /* savedState */, null /* result */);
                }};

    /**
     * Recreates the navigation bar for the given display.
     */
+6 −1
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ import android.util.SparseArray;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;

import com.android.app.displaylib.DisplaysWithDecorationsRepositoryCompat;
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.systemui.LauncherProxyService;
import com.android.systemui.SysuiTestCase;
@@ -66,6 +67,8 @@ import com.android.systemui.util.time.FakeSystemClock;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.pip.Pip;

import kotlinx.coroutines.CoroutineDispatcher;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -126,7 +129,9 @@ public class NavigationBarControllerImplTest extends SysuiTestCase {
                        Optional.of(mock(BackAnimation.class)),
                        mock(SecureSettings.class),
                        mDisplayTracker,
                        mDeviceStateManager));
                        mDeviceStateManager,
                        mock(DisplaysWithDecorationsRepositoryCompat.class),
                        mock(CoroutineDispatcher.class)));
        initializeNavigationBars();
        mMockitoSession = mockitoSession().mockStatic(Utilities.class).startMocking();
    }
Loading