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

Commit 001d8f47 authored by Fabián Kozynski's avatar Fabián Kozynski
Browse files

Fix QS in split shade

This CL fixes the following for split shade:
* Tiles not responding when in Split shade: pass the correct split shade
  state (from ShadeInteractor).
* Pages peeking: also split shade correct state
* Move to a second page, close and reopen, it should go back to first
  page: when closing, make sure to set the state to CLOSED, so it will
  correctly revert to the first page. Because of the internals of the
  ViewPager, this must be done when the view is attached.
* Leaving scene while in QS mode: fixed with previous fix.
* Animation for entering/exiting QS in split shade. Add a fade that
  matches the header, as well as squishiness.

Test: manual
Test: atest QSSceneAdapterTest QSSceneAdapterImplTest
Bug: 329662922
Flag: ACONFIG com.android.systemui.scene_container DEVELOPMENT
Change-Id: I3a11d9749e68a5ae1c1002bb9728dc555e259e2a
parent d4db8588
Loading
Loading
Loading
Loading
+25 −5
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@@ -36,7 +37,8 @@ import com.android.compose.modifiers.thenIf
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Companion.Collapsing
import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Expanding
import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Unsquishing
import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.UnsquishingQQS
import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.UnsquishingQS
import com.android.systemui.scene.shared.model.Scenes

object QuickSettings {
@@ -49,6 +51,8 @@ object QuickSettings {
    object Elements {
        val Content =
            ElementKey("QuickSettingsContent", scenePicker = MovableElementScenePicker(SCENES))
        val QuickQuickSettings = ElementKey("QuickQuickSettings")
        val SplitShadeQuickSettings = ElementKey("SplitShadeQuickSettings")
        val FooterActions = ElementKey("QuickSettingsFooterActions")
    }

@@ -78,12 +82,16 @@ private fun SceneScope.stateForQuickSettingsContent(
        is TransitionState.Transition ->
            with(transitionState) {
                when {
                    isSplitShade -> QSSceneAdapter.State.QS
                    fromScene == Scenes.Shade && toScene == Scenes.QuickSettings ->
                    isSplitShade -> UnsquishingQS(squishiness)
                    fromScene == Scenes.Shade && toScene == Scenes.QuickSettings -> {
                        Expanding(progress)
                    fromScene == Scenes.QuickSettings && toScene == Scenes.Shade ->
                    }
                    fromScene == Scenes.QuickSettings && toScene == Scenes.Shade -> {
                        Collapsing(progress)
                    fromScene == Scenes.Shade || toScene == Scenes.Shade -> Unsquishing(squishiness)
                    }
                    fromScene == Scenes.Shade || toScene == Scenes.Shade -> {
                        UnsquishingQQS(squishiness)
                    }
                    fromScene == Scenes.QuickSettings || toScene == Scenes.QuickSettings -> {
                        QSSceneAdapter.State.QS
                    }
@@ -119,6 +127,18 @@ fun SceneScope.QuickSettings(
    squishiness: Float = QuickSettings.SharedValues.SquishinessValues.Default,
) {
    val contentState = stateForQuickSettingsContent(isSplitShade, squishiness)
    val transitionState = layoutState.transitionState
    val isClosing =
        transitionState is TransitionState.Transition &&
            transitionState.progress >= 0.9f && // almost done closing
            !(layoutState.isTransitioning(to = Scenes.Shade) ||
                layoutState.isTransitioning(to = Scenes.QuickSettings))

    if (isClosing) {
        DisposableEffect(Unit) {
            onDispose { qsSceneAdapter.setState(QSSceneAdapter.State.CLOSED) }
        }
    }

    MovableElement(
        key = QuickSettings.Elements.Content,
+12 −5
Original line number Diff line number Diff line
@@ -13,11 +13,18 @@ fun TransitionBuilder.goneToShadeTransition(
) {
    spec = tween(durationMillis = DefaultDuration.times(durationScale).inWholeMilliseconds.toInt())

    fractionRange(start = .58f) { fade(ShadeHeader.Elements.Clock) }
    fractionRange(start = .58f) { fade(ShadeHeader.Elements.CollapsedContentStart) }
    fractionRange(start = .58f) { fade(ShadeHeader.Elements.CollapsedContentEnd) }
    fractionRange(start = .58f) { fade(ShadeHeader.Elements.PrivacyChip) }
    translate(QuickSettings.Elements.Content, y = -ShadeHeader.Dimensions.CollapsedHeight * .66f)
    fractionRange(start = .58f) {
        fade(ShadeHeader.Elements.Clock)
        fade(ShadeHeader.Elements.CollapsedContentStart)
        fade(ShadeHeader.Elements.CollapsedContentEnd)
        fade(ShadeHeader.Elements.PrivacyChip)
        fade(QuickSettings.Elements.SplitShadeQuickSettings)
        fade(QuickSettings.Elements.FooterActions)
    }
    translate(
        QuickSettings.Elements.QuickQuickSettings,
        y = -ShadeHeader.Dimensions.CollapsedHeight * .66f
    )
    translate(Notifications.Elements.NotificationScrim, Edge.Top, false)
}

+24 −15
Original line number Diff line number Diff line
@@ -222,6 +222,7 @@ private fun SceneScope.SingleShade(
                                        horizontal = Shade.Dimensions.HorizontalPadding
                                    )
                            )
                            Box(Modifier.element(QuickSettings.Elements.QuickQuickSettings)) {
                                QuickSettings(
                                    viewModel.qsSceneAdapter,
                                    {
@@ -231,6 +232,7 @@ private fun SceneScope.SingleShade(
                                    isSplitShade = false,
                                    squishiness = tileSquishiness,
                                )
                            }

                            MediaIfVisible(
                                viewModel = viewModel,
@@ -280,6 +282,8 @@ private fun SceneScope.SplitShade(
    val lifecycleOwner = LocalLifecycleOwner.current
    val footerActionsViewModel =
        remember(lifecycleOwner, viewModel) { viewModel.getFooterActionsViewModel(lifecycleOwner) }
    val tileSquishiness by
        animateSceneFloatAsState(value = 1f, key = QuickSettings.SharedValues.TilesSquishiness)

    val navBarBottomHeight = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()
    val density = LocalDensity.current
@@ -324,13 +328,18 @@ private fun SceneScope.SplitShade(
                                .clipScrollableContainer(Orientation.Horizontal)
                                .padding(bottom = navBarBottomHeight)
                        }
                ) {
                    Box(
                        modifier = Modifier.element(QuickSettings.Elements.SplitShadeQuickSettings)
                    ) {
                        QuickSettings(
                            qsSceneAdapter = viewModel.qsSceneAdapter,
                            heightProvider = { viewModel.qsSceneAdapter.qsHeight },
                            isSplitShade = true,
                            modifier = Modifier.fillMaxWidth(),
                            squishiness = tileSquishiness,
                        )
                    }

                    MediaIfVisible(
                        viewModel = viewModel,
+36 −37
Original line number Diff line number Diff line
@@ -27,9 +27,17 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dump.DumpManager
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.QSImpl
import com.android.systemui.qs.dagger.QSComponent
import com.android.systemui.qs.dagger.QSSceneComponent
import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
@@ -41,8 +49,6 @@ import com.google.common.truth.Truth.assertThat
import java.util.Locale
import javax.inject.Provider
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -57,8 +63,9 @@ import org.mockito.Mockito.verify
@OptIn(ExperimentalCoroutinesApi::class)
class QSSceneAdapterImplTest : SysuiTestCase() {

    private val testDispatcher = StandardTestDispatcher()
    private val testScope = TestScope(testDispatcher)
    private val kosmos = Kosmos().apply { testCase = this@QSSceneAdapterImplTest }
    private val testDispatcher = kosmos.testDispatcher
    private val testScope = kosmos.testScope

    private val qsImplProvider =
        object : Provider<QSImpl> {
@@ -107,10 +114,15 @@ class QSSceneAdapterImplTest : SysuiTestCase() {
            }
        }

    private val shadeInteractor = kosmos.shadeInteractor
    private val dumpManager = mock<DumpManager>()

    private val underTest =
        QSSceneAdapterImpl(
            qsSceneComponentFactory,
            qsImplProvider,
            shadeInteractor,
            dumpManager,
            testDispatcher,
            testScope.backgroundScope,
            configurationInteractor,
@@ -158,12 +170,6 @@ class QSSceneAdapterImplTest : SysuiTestCase() {
                    )
                verify(this).setListening(false)
                verify(this).setExpanded(false)
                verify(this)
                    .setTransitionToFullShadeProgress(
                        /* isTransitioningToFullShade= */ false,
                        /* qsTransitionFraction= */ 1f,
                        /* qsSquishinessFraction = */ 1f,
                    )
            }
        }

@@ -187,13 +193,7 @@ class QSSceneAdapterImplTest : SysuiTestCase() {
                        /* squishinessFraction= */ 1f,
                    )
                verify(this).setListening(true)
                verify(this).setExpanded(true)
                verify(this)
                    .setTransitionToFullShadeProgress(
                        /* isTransitioningToFullShade= */ false,
                        /* qsTransitionFraction= */ 1f,
                        /* qsSquishinessFraction = */ 1f,
                    )
                verify(this).setExpanded(false)
            }
        }

@@ -218,12 +218,6 @@ class QSSceneAdapterImplTest : SysuiTestCase() {
                    )
                verify(this).setListening(true)
                verify(this).setExpanded(true)
                verify(this)
                    .setTransitionToFullShadeProgress(
                        /* isTransitioningToFullShade= */ false,
                        /* qsTransitionFraction= */ 1f,
                        /* qsSquishinessFraction = */ 1f,
                    )
            }
        }

@@ -249,12 +243,6 @@ class QSSceneAdapterImplTest : SysuiTestCase() {
                    )
                verify(this).setListening(true)
                verify(this).setExpanded(true)
                verify(this)
                    .setTransitionToFullShadeProgress(
                        /* isTransitioningToFullShade= */ false,
                        /* qsTransitionFraction= */ 1f,
                        /* qsSquishinessFraction = */ 1f,
                    )
            }
        }

@@ -268,7 +256,7 @@ class QSSceneAdapterImplTest : SysuiTestCase() {
            runCurrent()
            clearInvocations(qsImpl!!)

            underTest.setState(QSSceneAdapter.State.Unsquishing(squishiness))
            underTest.setState(QSSceneAdapter.State.UnsquishingQQS(squishiness))
            with(qsImpl!!) {
                verify(this).setQsVisible(true)
                verify(this)
@@ -279,13 +267,7 @@ class QSSceneAdapterImplTest : SysuiTestCase() {
                        /* squishinessFraction= */ squishiness,
                    )
                verify(this).setListening(true)
                verify(this).setExpanded(true)
                verify(this)
                    .setTransitionToFullShadeProgress(
                        /* isTransitioningToFullShade= */ false,
                        /* qsTransitionFraction= */ 1f,
                        /* qsSquishinessFraction = */ squishiness,
                    )
                verify(this).setExpanded(false)
            }
        }

@@ -497,4 +479,21 @@ class QSSceneAdapterImplTest : SysuiTestCase() {

            verify(qsImpl!!).applyBottomNavBarToCustomizerPadding(navBarHeight)
        }

    @Test
    fun dispatchSplitShade() =
        testScope.runTest {
            val shadeRepository = kosmos.fakeShadeRepository
            shadeRepository.setShadeMode(ShadeMode.Single)
            val qsImpl by collectLastValue(underTest.qsImpl)

            underTest.inflate(context)
            runCurrent()

            verify(qsImpl!!).setInSplitShade(false)

            shadeRepository.setShadeMode(ShadeMode.Split)
            runCurrent()
            verify(qsImpl!!).setInSplitShade(true)
        }
}
+9 −2
Original line number Diff line number Diff line
@@ -49,9 +49,16 @@ class QSSceneAdapterTest : SysuiTestCase() {
    }

    @Test
    fun unsquishing_expansionSameAsQQS() {
    fun unsquishingQQS_expansionSameAsQQS() {
        val squishiness = 0.6f
        assertThat(QSSceneAdapter.State.Unsquishing(squishiness).expansion)
        assertThat(QSSceneAdapter.State.UnsquishingQQS(squishiness).expansion)
            .isEqualTo(QSSceneAdapter.State.QQS.expansion)
    }

    @Test
    fun unsquishingQS_expansionSameAsQS() {
        val squishiness = 0.6f
        assertThat(QSSceneAdapter.State.UnsquishingQS(squishiness).expansion)
            .isEqualTo(QSSceneAdapter.State.QS.expansion)
    }
}
Loading