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

Commit 598f56e0 authored by Fabian Kozynski's avatar Fabian Kozynski Committed by Android (Google) Code Review
Browse files

Merge "[Flexiglass] Fix QQS - QS animation" into main

parents 04c8a572 0958e4a7
Loading
Loading
Loading
Loading
+11 −3
Original line number Original line Diff line number Diff line
@@ -49,7 +49,13 @@ import com.android.systemui.shade.ui.composable.Shade
 *
 *
 * Please keep the list sorted alphabetically.
 * Please keep the list sorted alphabetically.
 */
 */
class SceneContainerTransitions : SceneContainerTransitionsBuilder {
class SceneContainerTransitions(
    /**
     * Pass to transitions that animate QS tiles to disable the shared element animation (e.g.
     * QuickSettings to Shade when QuickSettings is on the second page).
     */
    private val animateQsTilesAsShared: () -> Boolean = { true }
) : SceneContainerTransitionsBuilder {
    override fun build(
    override fun build(
        shadeExpansionMotion: VerticalExpandContainerSpec,
        shadeExpansionMotion: VerticalExpandContainerSpec,
        revealHaptics: ContainerRevealHaptics,
        revealHaptics: ContainerRevealHaptics,
@@ -146,7 +152,9 @@ class SceneContainerTransitions : SceneContainerTransitionsBuilder {
                to = Scenes.Shade,
                to = Scenes.Shade,
                cuj = Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE, // NOTYPO
                cuj = Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE, // NOTYPO
            ) {
            ) {
                reversed { shadeToQuickSettingsTransition() }
                reversed {
                    shadeToQuickSettingsTransition(animateQsTilesAsShared = animateQsTilesAsShared)
                }
                sharedElement(
                sharedElement(
                    Notifications.Elements.HeadsUpNotificationPlaceholder,
                    Notifications.Elements.HeadsUpNotificationPlaceholder,
                    enabled = false,
                    enabled = false,
@@ -157,7 +165,7 @@ class SceneContainerTransitions : SceneContainerTransitionsBuilder {
                to = Scenes.QuickSettings,
                to = Scenes.QuickSettings,
                cuj = Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE, // NOTYPO
                cuj = Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE, // NOTYPO
            ) {
            ) {
                shadeToQuickSettingsTransition()
                shadeToQuickSettingsTransition(animateQsTilesAsShared = animateQsTilesAsShared)
            }
            }
            from(
            from(
                Scenes.Shade,
                Scenes.Shade,
+28 −1
Original line number Original line Diff line number Diff line
package com.android.systemui.scene.ui.composable.transitions
package com.android.systemui.scene.ui.composable.transitions


import androidx.compose.animation.core.tween
import androidx.compose.animation.core.tween
import com.android.compose.animation.scene.ContentKey
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.ElementMatcher
import com.android.compose.animation.scene.TransitionBuilder
import com.android.compose.animation.scene.TransitionBuilder
import com.android.compose.animation.scene.UserActionDistance
import com.android.compose.animation.scene.UserActionDistance
import com.android.systemui.notifications.ui.composable.Notifications
import com.android.systemui.notifications.ui.composable.Notifications
import com.android.systemui.qs.shared.ui.ElementKeys
import com.android.systemui.qs.ui.composable.QuickSettings
import com.android.systemui.qs.ui.composable.QuickSettings
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.ui.composable.ShadeHeader
import com.android.systemui.shade.ui.composable.ShadeHeader
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.milliseconds


fun TransitionBuilder.shadeToQuickSettingsTransition(durationScale: Double = 1.0) {
fun TransitionBuilder.shadeToQuickSettingsTransition(
    durationScale: Double = 1.0,
    animateQsTilesAsShared: () -> Boolean = { true },
) {
    spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
    spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
    distance = UserActionDistance { fromContent, _, _ ->
    distance = UserActionDistance { fromContent, _, _ ->
        val distance =
        val distance =
@@ -23,6 +30,19 @@ fun TransitionBuilder.shadeToQuickSettingsTransition(durationScale: Double = 1.0
    translate(Notifications.Elements.NotificationScrim, Edge.Bottom)
    translate(Notifications.Elements.NotificationScrim, Edge.Bottom)
    timestampRange(endMillis = 83) { fade(QuickSettings.Elements.FooterActions) }
    timestampRange(endMillis = 83) { fade(QuickSettings.Elements.FooterActions) }


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

    anchoredTranslate(ElementKeys.QuickSettingsContent, ElementKeys.GridAnchor)

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

    // This will animate between 0f (QQS) and 0.5, fading in the QQS tiles when coming back
    // from non first page QS. The QS content ends fading out at 0.43f, so there's a brief
    // overlap, but because they are really faint, it looks better than complete black without
    // overlap.
    fractionRange(end = 0.5f) { fade(QqsTileElementMatcher) }
    anchoredTranslate(QqsTileElementMatcher, ElementKeys.GridAnchor)

    val translationY = ShadeHeader.Dimensions.CollapsedHeightForTransitions
    val translationY = ShadeHeader.Dimensions.CollapsedHeightForTransitions
    translate(ShadeHeader.Elements.CollapsedContentStart, y = translationY)
    translate(ShadeHeader.Elements.CollapsedContentStart, y = translationY)
    translate(ShadeHeader.Elements.CollapsedContentEnd, y = translationY)
    translate(ShadeHeader.Elements.CollapsedContentEnd, y = translationY)
@@ -44,3 +64,10 @@ fun TransitionBuilder.shadeToQuickSettingsTransition(durationScale: Double = 1.0
}
}


private val DefaultDuration = 500.milliseconds
private val DefaultDuration = 500.milliseconds

private val QqsTileElementMatcher =
    object : ElementMatcher {
        override fun matches(key: ElementKey, content: ContentKey): Boolean {
            return content == Scenes.Shade && ElementKeys.TileElementMatcher.matches(key, content)
        }
    }
+25 −0
Original line number Original line 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.qs.panels.data.repository

import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject

@SysUISingleton
class InFirstPageRepository @Inject constructor() {
    var inFirstPage = true
}
+24 −0
Original line number Original line 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.qs.panels.domain.interactor

import com.android.systemui.qs.panels.data.repository.InFirstPageRepository
import javax.inject.Inject

class InFirstPageInteractor @Inject constructor(inFirstPageRepository: InFirstPageRepository) {
    var inFirstPage by inFirstPageRepository::inFirstPage
}
+33 −0
Original line number Original line 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.qs.panels.ui.viewmodel

import com.android.systemui.qs.panels.domain.interactor.InFirstPageInteractor
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject

class AnimateQsTilesViewModel
@AssistedInject
constructor(private val inFirstPageInteractor: InFirstPageInteractor) {
    val animateQsTiles
        get() = inFirstPageInteractor.inFirstPage

    @AssistedFactory
    interface Factory {
        fun create(): AnimateQsTilesViewModel
    }
}
Loading