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

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

Merge changes I3228c6e2,I51bacced into main

* changes:
  [Flexiglass] Extract QS layout to use slots
  [Flexiglass] Remove unused code in QuickSettingsScene
parents 388f0cd6 4fc4c870
Loading
Loading
Loading
Loading
+61 −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.qs.ui.composable

import androidx.compose.foundation.layout.Arrangement.spacedBy
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier

@Composable
fun QuickSettingsPanelLayout(
    brightness: @Composable () -> Unit,
    tiles: @Composable () -> Unit,
    media: @Composable () -> Unit,
    mediaInRow: Boolean,
    modifier: Modifier = Modifier,
) {
    if (mediaInRow) {
        Column(
            verticalArrangement = spacedBy(QuickSettingsShade.Dimensions.Padding),
            horizontalAlignment = Alignment.CenterHorizontally,
            modifier = modifier,
        ) {
            brightness()
            Row(
                horizontalArrangement = spacedBy(QuickSettingsShade.Dimensions.Padding),
                verticalAlignment = Alignment.CenterVertically,
            ) {
                Box(modifier = Modifier.weight(1f)) { tiles() }
                Box(modifier = Modifier.weight(1f)) { media() }
            }
        }
    } else {
        Column(
            verticalArrangement = spacedBy(QuickSettingsShade.Dimensions.Padding),
            horizontalAlignment = Alignment.CenterHorizontally,
            modifier = modifier,
        ) {
            brightness()
            tiles()
            media()
        }
    }
}
+57 −151
Original line number Diff line number Diff line
@@ -16,17 +16,7 @@

package com.android.systemui.qs.ui.composable

import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.clipScrollableContainer
import androidx.compose.foundation.gestures.Orientation
@@ -36,7 +26,6 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.displayCutoutPadding
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
@@ -51,7 +40,6 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
@@ -59,8 +47,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.CompositingStrategy
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLifecycleOwner
@@ -68,7 +54,6 @@ import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
@@ -107,11 +92,9 @@ import com.android.systemui.res.R
import com.android.systemui.scene.session.ui.composable.SaveableSession
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.Scene
import com.android.systemui.settings.brightness.ui.viewModel.BrightnessMirrorViewModel
import com.android.systemui.shade.ui.composable.CollapsedShadeHeader
import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
import com.android.systemui.shade.ui.composable.Shade
import com.android.systemui.shade.ui.composable.ShadeHeader
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
@@ -153,10 +136,6 @@ constructor(
    override fun ContentScope.Content(modifier: Modifier) {
        val viewModel =
            rememberViewModel("QuickSettingsScene-viewModel") { contentViewModelFactory.create() }
        val brightnessMirrorViewModel =
            rememberViewModel("QuickSettingsScene-brightnessMirrorViewModel") {
                viewModel.brightnessMirrorViewModelFactory.create()
            }
        val notificationsPlaceholderViewModel =
            rememberViewModel("QuickSettingsScene-notifPlaceholderViewModel") {
                notificationsPlaceholderViewModelFactory.create()
@@ -165,7 +144,6 @@ constructor(
            notificationStackScrollView = notificationStackScrollView.get(),
            viewModel = viewModel,
            headerViewModel = viewModel.qsContainerViewModel.shadeHeaderViewModel,
            brightnessMirrorViewModel = brightnessMirrorViewModel,
            notificationsPlaceholderViewModel = notificationsPlaceholderViewModel,
            mediaCarouselController = mediaCarouselController,
            mediaHost = mediaHost,
@@ -187,7 +165,6 @@ private fun ContentScope.QuickSettingsScene(
    notificationStackScrollView: NotificationScrollView,
    viewModel: QuickSettingsSceneContentViewModel,
    headerViewModel: ShadeHeaderViewModel,
    brightnessMirrorViewModel: BrightnessMirrorViewModel,
    notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
    mediaCarouselController: MediaCarouselController,
    mediaHost: MediaHost,
@@ -196,7 +173,8 @@ private fun ContentScope.QuickSettingsScene(
    jankMonitor: InteractionJankMonitor,
) {
    val cutoutLocation = LocalDisplayCutout.current().location
    val brightnessMirrorShowing by brightnessMirrorViewModel.isShowing.collectAsStateWithLifecycle()
    val brightnessMirrorShowing =
        viewModel.qsContainerViewModel.brightnessSliderViewModel.showMirror
    val contentAlpha by
        animateFloatAsState(
            targetValue = if (brightnessMirrorShowing) 0f else 1f,
@@ -229,15 +207,8 @@ private fun ContentScope.QuickSettingsScene(
                .thenIf(cutoutLocation != CutoutLocation.CENTER) { Modifier.displayCutoutPadding() }
    ) {
        val density = LocalDensity.current
        val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsStateWithLifecycle()
        val isCustomizerShowing by
            viewModel.qsSceneAdapter.isCustomizerShowing.collectAsStateWithLifecycle()
        val customizingAnimationDuration by
            viewModel.qsSceneAdapter.customizerAnimationDuration.collectAsStateWithLifecycle()
        val screenHeight = with(density) { LocalConfiguration.current.screenHeightDp.dp.toPx() }

        BackHandler(enabled = isCustomizing) { viewModel.qsSceneAdapter.requestCloseCustomizer() }

        val lifecycleOwner = LocalLifecycleOwner.current
        val footerActionsViewModel =
            remember(lifecycleOwner, viewModel) {
@@ -256,34 +227,10 @@ private fun ContentScope.QuickSettingsScene(
                is TransitionState.Transition -> state.fromContent == Scenes.QuickSettings
            }

        LaunchedEffect(isCustomizing, scrollState) {
            if (isCustomizing) {
                scrollState.scrollTo(0)
            }
        }

        // ############# NAV BAR paddings ###############

        val navBarBottomHeight =
            WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()
        val bottomPadding by
            animateDpAsState(
                targetValue = if (isCustomizing) 0.dp else navBarBottomHeight,
                animationSpec = tween(customizingAnimationDuration),
                label = "animateQSSceneBottomPaddingAsState",
            )
        val topPadding by
            animateDpAsState(
                targetValue = if (isCustomizing) ShadeHeader.Dimensions.StatusBarHeight else 0.dp,
                animationSpec = tween(customizingAnimationDuration),
                label = "animateQSSceneTopPaddingAsState",
            )

        LaunchedEffect(navBarBottomHeight, density) {
            with(density) {
                viewModel.qsSceneAdapter.applyBottomNavBarPadding(navBarBottomHeight.roundToPx())
            }
        }

        // ############# Media ###############
        val isMediaVisible = viewModel.isMediaVisible
@@ -304,67 +251,31 @@ private fun ContentScope.QuickSettingsScene(
            modifier =
                Modifier.fillMaxSize()
                    .overscroll(verticalOverscrollEffect)
                    .padding(
                        top = topPadding.coerceAtLeast(0.dp),
                        bottom = bottomPadding.coerceAtLeast(0.dp),
                    ),
                    .padding(bottom = navBarBottomHeight.coerceAtLeast(0.dp)),
        ) {
            Box(modifier = Modifier.fillMaxSize().weight(1f)) {
                val shadeHeaderAndQuickSettingsModifier =
                    if (isCustomizerShowing) {
                        Modifier.fillMaxHeight().align(Alignment.TopCenter)
                    } else {
                Column(
                    modifier =
                        Modifier.verticalScroll(scrollState, enabled = isScrollable)
                            .clipScrollableContainer(Orientation.Horizontal)
                            .fillMaxWidth()
                            .wrapContentHeight(unbounded = true)
                            .align(Alignment.TopCenter)
                    }

                Column(
                    modifier =
                        shadeHeaderAndQuickSettingsModifier.sysuiResTag("expanded_qs_scroll_view")
                            .sysuiResTag("expanded_qs_scroll_view")
                ) {
                    when (LocalWindowSizeClass.current.widthSizeClass) {
                        WindowWidthSizeClass.Compact ->
                            AnimatedVisibility(
                                visible = !isCustomizing,
                                enter =
                                    expandVertically(
                                        animationSpec = tween(customizingAnimationDuration),
                                        expandFrom = Alignment.Top,
                                    ) +
                                        slideInVertically(
                                            animationSpec = tween(customizingAnimationDuration)
                                        ) +
                                        fadeIn(tween(customizingAnimationDuration)),
                                exit =
                                    shrinkVertically(
                                        animationSpec = tween(customizingAnimationDuration),
                                        shrinkTowards = Alignment.Top,
                                    ) +
                                        slideOutVertically(
                                            animationSpec = tween(customizingAnimationDuration)
                                        ) +
                                        fadeOut(tween(customizingAnimationDuration)),
                            ) {
                            ExpandedShadeHeader(
                                viewModel = headerViewModel,
                                modifier = Modifier.padding(horizontal = 16.dp),
                            )
                            }
                        else ->
                            CollapsedShadeHeader(viewModel = headerViewModel, isSplitShade = false)
                    }
                    Spacer(modifier = Modifier.height(16.dp))
                    Column(
                        modifier =
                            Modifier.element(ElementKeys.QuickSettingsContent)
                                .padding(
                                    horizontal =
                                        dimensionResource(id = R.dimen.qs_horizontal_margin)
                                )
                    ) {
                    QuickSettingsPanelLayout(
                        brightness =
                            @Composable {
                                BrightnessSliderContainer(
                                    viewModel.qsContainerViewModel.brightnessSliderViewModel,
                                    containerColors =
@@ -375,56 +286,51 @@ private fun ContentScope.QuickSettingsScene(
                                    modifier =
                                        Modifier.padding(
                                            vertical =
                                        dimensionResource(id = R.dimen.qs_brightness_margin_top)
                                                dimensionResource(
                                                    id = R.dimen.qs_brightness_margin_top
                                                )
                                        ),
                                )
                            },
                        tiles =
                            @Composable {
                                Box {
                                    GridAnchor()
                        // This view has its own horizontal padding
                        val content: @Composable () -> Unit = {
                            TileGrid(
                                viewModel.qsContainerViewModel.tileGridViewModel,
                                modifier =
                                    Modifier.sysuiResTag("quick_settings_panel")
                                        .layoutId(QSMediaMeasurePolicy.LayoutId.QS),
                            )

                                    TileGrid(viewModel.qsContainerViewModel.tileGridViewModel)
                                }
                            },
                        media =
                            @Composable {
                                MediaCarousel(
                                    isVisible = isMediaVisible,
                                    mediaHost = mediaHost,
                                    modifier =
                                        Modifier.fillMaxWidth()
                                        .layoutId(QSMediaMeasurePolicy.LayoutId.Media)
                                            .padding(
                                                horizontal =
                                                dimensionResource(id = R.dimen.qs_horizontal_margin)
                                                    dimensionResource(
                                                        id = R.dimen.qs_horizontal_margin
                                                    )
                                            ),
                                    carouselController = mediaCarouselController,
                                )
                        }
                        val landscapeQsMediaMeasurePolicy = remember {
                            QSMediaMeasurePolicy(
                                { viewModel.qsSceneAdapter.qsHeight },
                                { mediaOffset.roundToPx() },
                            },
                        mediaInRow = mediaInRow,
                        modifier =
                            Modifier.element(ElementKeys.QuickSettingsContent)
                                .padding(
                                    horizontal =
                                        dimensionResource(id = R.dimen.qs_horizontal_margin)
                                )
                        }
                        Column(modifier = Modifier.padding(horizontal = shadeHorizontalPadding)) {
                            if (mediaInRow) {
                                Layout(
                                    content = content,
                                    measurePolicy = landscapeQsMediaMeasurePolicy,
                                .sysuiResTag("quick_settings_panel"),
                    )
                            } else {
                                content()
                            }
                        }
                    }
                }
            }

            FooterActionsWithAnimatedVisibility(
                viewModel = footerActionsViewModel,
                isCustomizing = isCustomizing,
                customizingAnimationDuration = customizingAnimationDuration,
                isCustomizing = false,
                customizingAnimationDuration = 0,
                lifecycleOwner = lifecycleOwner,
                modifier =
                    Modifier.align(Alignment.CenterHorizontally)
+0 −3
Original line number Diff line number Diff line
@@ -37,7 +37,6 @@ import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.startable.sceneContainerStartable
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.settings.brightness.ui.viewmodel.brightnessMirrorViewModelFactory
import com.android.systemui.shade.domain.interactor.disableDualShade
import com.android.systemui.shade.domain.interactor.enableDualShade
import com.android.systemui.shade.domain.interactor.enableSingleShade
@@ -78,9 +77,7 @@ class QuickSettingsSceneContentViewModelTest : SysuiTestCase() {
                }
            underTest =
                QuickSettingsSceneContentViewModel(
                    brightnessMirrorViewModelFactory = brightnessMirrorViewModelFactory,
                    shadeHeaderViewModelFactory = shadeHeaderViewModelFactory,
                    qsSceneAdapter = fakeQsSceneAdapter,
                    qsContainerViewModelFactory = kosmos.quickSettingsContainerViewModelFactory,
                    footerActionsViewModelFactory = footerActionsViewModelFactory,
                    footerActionsController = footerActionsController,
+0 −4
Original line number Diff line number Diff line
@@ -25,12 +25,10 @@ import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
import com.android.systemui.qs.FooterActionsController
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.settings.brightness.ui.viewModel.BrightnessMirrorViewModel
import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
@@ -53,9 +51,7 @@ import kotlinx.coroutines.flow.onEach
class QuickSettingsSceneContentViewModel
@AssistedInject
constructor(
    val brightnessMirrorViewModelFactory: BrightnessMirrorViewModel.Factory,
    val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
    val qsSceneAdapter: QSSceneAdapter,
    qsContainerViewModelFactory: QuickSettingsContainerViewModel.Factory,
    private val footerActionsViewModelFactory: FooterActionsViewModel.Factory,
    private val footerActionsController: FooterActionsController,
+2 −25
Original line number Diff line number Diff line
@@ -16,42 +16,19 @@

package com.android.systemui.qs.ui.viewmodel

import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor
import com.android.systemui.qs.footerActionsController
import com.android.systemui.qs.footerActionsViewModelFactory
import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
import com.android.systemui.settings.brightness.ui.viewmodel.brightnessMirrorViewModelFactory
import com.android.systemui.shade.ui.viewmodel.shadeHeaderViewModelFactory
import com.android.systemui.shade.domain.interactor.shadeModeInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.stub
import com.android.systemui.shade.domain.interactor.shadeModeInteractor
import com.android.systemui.shade.ui.viewmodel.shadeHeaderViewModelFactory

val Kosmos.quickSettingsSceneContentViewModel by Fixture {
    QuickSettingsSceneContentViewModel(
        brightnessMirrorViewModelFactory = brightnessMirrorViewModelFactory,
        shadeHeaderViewModelFactory = shadeHeaderViewModelFactory,
        qsSceneAdapter = FakeQSSceneAdapter({
            // The Quick Settings content installs this view as a child of FrameLayout so its layout
            // params are required.
            val view = mock<View>()
            view.stub {
                on { layoutParams } doReturn FrameLayout.LayoutParams(
                    ViewGroup.MarginLayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.MATCH_PARENT,
                    )
                )
            }
            view
        }),
        qsContainerViewModelFactory = quickSettingsContainerViewModelFactory,
        footerActionsViewModelFactory = footerActionsViewModelFactory,
        footerActionsController = footerActionsController,