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

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

Properly animate second page collapse

When the second page of QS is collapsing into QQS, we don't want the QQS
tiles flying from out of the screen. Instead, fade them in and translate
them up from the anchored position.

Test: manual
Bug: 353254353
Flag: com.android.systemui.qs_ui_refactor_compose_fragment
Change-Id: I55c0edf29ebbeb35ddbced53f2966ac90ecf3171
parent a768d736
Loading
Loading
Loading
Loading
+12 −1
Original line number Diff line number Diff line
@@ -66,6 +66,9 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.compose.animation.scene.ContentKey
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.ElementMatcher
import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneScope
@@ -288,7 +291,7 @@ constructor(
                transitions =
                    transitions {
                        from(QuickQuickSettings, QuickSettings) {
                            quickQuickSettingsToQuickSettings()
                            quickQuickSettingsToQuickSettings(viewModel::inFirstPage::get)
                        }
                    },
            )
@@ -704,6 +707,14 @@ object SceneKeys {
            else -> QuickSettings
        }
    }

    val QqsTileElementMatcher =
        object : ElementMatcher {
            override fun matches(key: ElementKey, content: ContentKey): Boolean {
                return content == SceneKeys.QuickQuickSettings &&
                    ElementKeys.TileElementMatcher.matches(key, content)
            }
        }
}

suspend fun synchronizeQsState(state: MutableSceneTransitionLayoutState, expansion: Flow<Float>) {
+11 −1
Original line number Diff line number Diff line
@@ -17,13 +17,23 @@
package com.android.systemui.qs.composefragment.ui

import com.android.compose.animation.scene.TransitionBuilder
import com.android.systemui.qs.composefragment.SceneKeys
import com.android.systemui.qs.shared.ui.ElementKeys

fun TransitionBuilder.quickQuickSettingsToQuickSettings() {
fun TransitionBuilder.quickQuickSettingsToQuickSettings(inFirstPage: () -> Boolean = { true }) {

    fractionRange(start = 0.5f) { fade(ElementKeys.QuickSettingsContent) }

    fractionRange(start = 0.9f) { fade(ElementKeys.FooterActions) }

    anchoredTranslate(ElementKeys.QuickSettingsContent, ElementKeys.GridAnchor)

    sharedElement(ElementKeys.TileElementMatcher, enabled = inFirstPage())

    // This will animate between 0f (QQS) and 0.6, fading in the QQS tiles when coming back
    // from non first page QS. The QS content ends fading out at 0.5f, so there's a brief
    // overlap, but because they are really faint, it looks better than complete black without
    // overlap.
    fractionRange(end = 0.6f) { fade(SceneKeys.QqsTileElementMatcher) }
    anchoredTranslate(SceneKeys.QqsTileElementMatcher, ElementKeys.GridAnchor)
}
+5 −0
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import com.android.systemui.qs.FooterActionsController
import com.android.systemui.qs.composefragment.viewmodel.QSFragmentComposeViewModel.QSExpansionState
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.qs.panels.domain.interactor.TileSquishinessInteractor
import com.android.systemui.qs.panels.ui.viewmodel.PaginatedGridViewModel
import com.android.systemui.qs.ui.viewmodel.QuickSettingsContainerViewModel
import com.android.systemui.shade.LargeScreenHeaderHelper
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
@@ -71,6 +72,7 @@ constructor(
    private val configurationInteractor: ConfigurationInteractor,
    private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
    private val squishinessInteractor: TileSquishinessInteractor,
    private val paginatedGridViewModel: PaginatedGridViewModel,
    @Assisted private val lifecycleScope: LifecycleCoroutineScope,
) : Dumpable, ExclusiveActivatable() {
    val footerActionsViewModel =
@@ -292,6 +294,9 @@ constructor(
     */
    var collapseExpandAccessibilityAction: Runnable? = null

    val inFirstPage: Boolean
        get() = paginatedGridViewModel.inFirstPage

    override suspend fun onActivated(): Nothing {
        hydrateSquishinessInteractor()
    }
+7 −0
Original line number Diff line number Diff line
@@ -31,8 +31,10 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@@ -76,6 +78,11 @@ constructor(

        val pagerState = rememberPagerState(0) { pages.size }

        // Used to track if this is currently in the first page or not, for animations
        LaunchedEffect(key1 = pagerState) {
            snapshotFlow { pagerState.currentPage == 0 }.collect { viewModel.inFirstPage = it }
        }

        Column {
            HorizontalPager(
                state = pagerState,
+6 −0
Original line number Diff line number Diff line
@@ -43,4 +43,10 @@ constructor(
            SharingStarted.WhileSubscribed(),
            paginatedGridInteractor.defaultRows,
        )

    /*
     * Tracks whether the current HorizontalPager (using this viewmodel) is in the first page.
     * This requires it to be a `@SysUISingleton` to be shared between viewmodels.
     */
    var inFirstPage = true
}
Loading