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

Commit 362738e6 authored by Nicolo' Mazzucato's avatar Nicolo' Mazzucato
Browse files

Collapse the shade before moving the window to another display

This makes sure that the shade is collapsed before the shade is moved to
another display.
The same shade window element expanded will be re-expanded after the
shade move is done (either QS or notifications)

The code to do this and wait for the expansion to finish has been
extracted to ShadeExpansionStateInteractor.

As a side effect, this prevents flickers when moving the shade, and
makes the motion much more pleasant.

Bug: 362719719
Test: ShadeDisplaysInteractorTest, ShadeExpandedStateInteractorTest
Flag: com.android.systemui.shade_window_goes_around
Change-Id: Icacb547aabe613c2b4a2a5d9b96613b300daa87d
parent b6c9a433
Loading
Loading
Loading
Loading
+13 −7
Original line number Diff line number Diff line
@@ -22,10 +22,13 @@ import android.view.Display
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.scene.ui.view.mockShadeRootView
import com.android.systemui.shade.data.repository.fakeShadeDisplaysRepository
import com.android.systemui.testKosmos
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -41,6 +44,7 @@ import org.mockito.kotlin.whenever
class ShadeDisplaysInteractorTest : SysuiTestCase() {
    val kosmos = testKosmos().useUnconfinedTestDispatcher()

    private val testScope = kosmos.testScope
    private val shadeRootview = kosmos.mockShadeRootView
    private val positionRepository = kosmos.fakeShadeDisplaysRepository
    private val shadeContext = kosmos.mockedWindowContext
@@ -49,7 +53,7 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() {
    private val configuration = mock<Configuration>()
    private val display = mock<Display>()

    private val underTest = kosmos.shadeDisplaysInteractor
    private val underTest by lazy { kosmos.shadeDisplaysInteractor }

    @Before
    fun setup() {
@@ -84,11 +88,13 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() {
    }

    @Test
    fun start_shadeInWrongPosition_logsStartToLatencyTracker() {
    fun start_shadeInWrongPosition_logsStartToLatencyTracker() =
        testScope.runTest {
            whenever(display.displayId).thenReturn(0)
            positionRepository.setDisplayId(1)

            underTest.start()
            advanceUntilIdle()

            verify(latencyTracker).onShadeDisplayChanging(eq(1))
        }
+77 −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.shade.domain.interactor

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.shade.domain.interactor.ShadeExpandedStateInteractorImpl.NotificationElement
import com.android.systemui.shade.domain.interactor.ShadeExpandedStateInteractorImpl.QSElement
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
@SmallTest
@EnableSceneContainer
class ShadeExpandedStateInteractorTest : SysuiTestCase() {
    private val kosmos = testKosmos().useUnconfinedTestDispatcher()

    private val testScope = kosmos.testScope
    private val shadeTestUtil by lazy { kosmos.shadeTestUtil }

    private val underTest: ShadeExpandedStateInteractor by lazy {
        kosmos.shadeExpandedStateInteractor
    }

    @Test
    fun expandedElement_qsExpanded_returnsQSElement() =
        testScope.runTest {
            shadeTestUtil.setShadeAndQsExpansion(shadeExpansion = 0f, qsExpansion = 1f)
            val currentlyExpandedElement = underTest.currentlyExpandedElement

            val element = currentlyExpandedElement.value

            assertThat(element).isInstanceOf(QSElement::class.java)
        }

    @Test
    fun expandedElement_shadeExpanded_returnsShade() =
        testScope.runTest {
            shadeTestUtil.setShadeAndQsExpansion(shadeExpansion = 1f, qsExpansion = 0f)

            val element = underTest.currentlyExpandedElement.value

            assertThat(element).isInstanceOf(NotificationElement::class.java)
        }

    @Test
    fun expandedElement_noneExpanded_returnsNull() =
        testScope.runTest {
            shadeTestUtil.setShadeAndQsExpansion(shadeExpansion = 0f, qsExpansion = 0f)

            val element = underTest.currentlyExpandedElement.value

            assertThat(element).isNull()
        }
}
+5 −2
Original line number Diff line number Diff line
@@ -37,12 +37,13 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.res.R
import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.shade.data.repository.MutableShadeDisplaysRepository
import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor
import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractorImpl
import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
import com.android.systemui.shade.data.repository.ShadeDisplaysRepositoryImpl
import com.android.systemui.shade.display.ShadeDisplayPolicyModule
import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor
import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractorImpl
import com.android.systemui.shade.domain.interactor.ShadeDisplaysInteractor
import com.android.systemui.shade.domain.interactor.ShadeExpandedStateInteractor
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl
import com.android.systemui.statusbar.phone.ConfigurationForwarder
@@ -276,6 +277,8 @@ object ShadeDisplayAwareModule {
@Module
internal interface OptionalShadeDisplayAwareBindings {
    @BindsOptionalOf fun bindOptionalOfWindowRootView(): WindowRootView

    @BindsOptionalOf fun bindOptionalOShadeExpandedStateInteractor(): ShadeExpandedStateInteractor
}

/**
+8 −0
Original line number Diff line number Diff line
@@ -35,6 +35,8 @@ import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLega
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorSceneContainerImpl
import com.android.systemui.shade.domain.interactor.ShadeBackActionInteractor
import com.android.systemui.shade.domain.interactor.ShadeBackActionInteractorImpl
import com.android.systemui.shade.domain.interactor.ShadeExpandedStateInteractor
import com.android.systemui.shade.domain.interactor.ShadeExpandedStateInteractorImpl
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.domain.interactor.ShadeInteractorImpl
import com.android.systemui.shade.domain.interactor.ShadeInteractorLegacyImpl
@@ -176,4 +178,10 @@ abstract class ShadeModule {
    @Binds
    @SysUISingleton
    abstract fun bindShadeModeInteractor(impl: ShadeModeInteractorImpl): ShadeModeInteractor

    @Binds
    @SysUISingleton
    abstract fun bindShadeExpandedStateInteractor(
        impl: ShadeExpandedStateInteractorImpl
    ): ShadeExpandedStateInteractor
}
+6 −3
Original line number Diff line number Diff line
@@ -27,7 +27,7 @@ import com.android.app.tracing.coroutines.TrackTracer
 * them across various threads' logs.
 */
object ShadeTraceLogger {
    private val t = TrackTracer(trackName = "ShadeTraceLogger", trackGroup = "shade")
    val t = TrackTracer(trackName = "ShadeTraceLogger", trackGroup = "shade")

    @JvmStatic
    fun logOnMovedToDisplay(displayId: Int, config: Configuration) {
@@ -44,8 +44,11 @@ object ShadeTraceLogger {
        t.instant { "moveShadeWindowTo(displayId=$displayId)" }
    }

    @JvmStatic
    fun traceReparenting(r: () -> Unit) {
    suspend fun traceReparenting(r: suspend () -> Unit) {
        t.traceAsync({ "reparenting" }) { r() }
    }

    inline fun traceWaitForExpansion(expansion: Float, r: () -> Unit) {
        t.traceAsync({ "waiting for shade expansion to match $expansion" }) { r() }
    }
}
Loading