Loading packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt +62 −56 Original line number Diff line number Diff line Loading @@ -32,11 +32,7 @@ import androidx.activity.OnBackPressedDispatcher import androidx.activity.OnBackPressedDispatcherOwner import androidx.activity.setViewTreeOnBackPressedDispatcherOwner import androidx.annotation.VisibleForTesting import androidx.compose.animation.AnimatedContent import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.togetherWith import androidx.compose.foundation.ScrollState import androidx.compose.foundation.layout.Arrangement.spacedBy import androidx.compose.foundation.layout.Box Loading Loading @@ -120,6 +116,7 @@ import com.android.systemui.qs.composefragment.ui.GridAnchor import com.android.systemui.qs.composefragment.ui.NotificationScrimClipParams import com.android.systemui.qs.composefragment.ui.notificationScrimClip import com.android.systemui.qs.composefragment.ui.quickQuickSettingsToQuickSettings import com.android.systemui.qs.composefragment.ui.toEditMode import com.android.systemui.qs.composefragment.viewmodel.QSFragmentComposeViewModel import com.android.systemui.qs.flags.QSComposeFragment import com.android.systemui.qs.footer.ui.compose.FooterActions Loading @@ -144,6 +141,7 @@ import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch Loading Loading @@ -273,41 +271,12 @@ constructor( // by the composables. .gesturesDisabled(viewModel.showingMirror) ) { val isEditing by viewModel.containerViewModel.editModeViewModel.isEditing .collectAsStateWithLifecycle() val animationSpecEditMode = tween<Float>(EDIT_MODE_TIME_MILLIS) AnimatedContent( targetState = isEditing, transitionSpec = { fadeIn(animationSpecEditMode) togetherWith fadeOut(animationSpecEditMode) }, label = "EditModeAnimatedContent", ) { editing -> if (editing) { val qqsPadding = viewModel.qqsHeaderHeight EditMode( viewModel = viewModel.containerViewModel.editModeViewModel, modifier = Modifier.fillMaxWidth() .padding(top = { qqsPadding }) .padding( horizontal = { QuickSettingsShade.Dimensions.Padding .roundToPx() } ), ) } else { CollapsableQuickSettingsSTL() } } } } } } } /** * STL that contains both QQS (tiles) and QS (brightness, tiles, footer actions), but no Edit Loading @@ -324,12 +293,17 @@ constructor( from(QuickQuickSettings, QuickSettings) { quickQuickSettingsToQuickSettings(viewModel::animateTilesExpansion::get) } to(SceneKeys.EditMode) { spec = tween(durationMillis = EDIT_MODE_TIME_MILLIS) toEditMode() } }, ) LaunchedEffect(Unit) { synchronizeQsState( sceneState, viewModel.containerViewModel.editModeViewModel.isEditing, snapshotFlow { viewModel.expansionState }.map { it.progress }, ) } Loading @@ -342,9 +316,17 @@ constructor( scene(QuickQuickSettings) { LaunchedEffect(Unit) { viewModel.onQQSOpen() } // Cannot pass the element modifier in because the top element has a `testTag` // and this would overwrite it. Box(Modifier.element(QuickQuickSettings.rootElementKey)) { QuickQuickSettingsElement() } } scene(SceneKeys.EditMode) { EditModeElement(Modifier.element(SceneKeys.EditMode.rootElementKey)) } } } override fun setPanelView(notificationPanelView: QS.HeightListener?) { Loading Loading @@ -582,7 +564,7 @@ constructor( } @Composable private fun ContentScope.QuickQuickSettingsElement() { private fun ContentScope.QuickQuickSettingsElement(modifier: Modifier = Modifier) { val qqsPadding = viewModel.qqsHeaderHeight val bottomPadding = viewModel.qqsBottomPadding DisposableEffect(Unit) { Loading @@ -595,7 +577,7 @@ constructor( .squishiness .collectAsStateWithLifecycle() Column(modifier = Modifier.sysuiResTag(ResIdTags.quickQsPanel)) { Column(modifier = modifier.sysuiResTag(ResIdTags.quickQsPanel)) { Box( modifier = Modifier.fillMaxWidth() Loading Loading @@ -666,12 +648,12 @@ constructor( } @Composable private fun ContentScope.QuickSettingsElement() { private fun ContentScope.QuickSettingsElement(modifier: Modifier = Modifier) { val qqsPadding = viewModel.qqsHeaderHeight val qsExtraPadding = dimensionResource(R.dimen.qs_panel_padding_top) Column( modifier = Modifier.collapseExpandSemanticAction( modifier.collapseExpandSemanticAction( stringResource(id = R.string.accessibility_quick_settings_collapse) ) ) { Loading Loading @@ -776,6 +758,18 @@ constructor( } } @Composable private fun EditModeElement(modifier: Modifier = Modifier) { // No need for top padding, the Scaffold inside takes care of the correct insets EditMode( viewModel = viewModel.containerViewModel.editModeViewModel, modifier = modifier .fillMaxWidth() .padding(horizontal = { QuickSettingsShade.Dimensions.Padding.roundToPx() }), ) } private fun Modifier.collapseExpandSemanticAction(label: String): Modifier { return viewModel.collapseExpandAccessibilityAction?.let { semantics { Loading Loading @@ -863,6 +857,7 @@ private val instanceProvider = object SceneKeys { val QuickQuickSettings = SceneKey("QuickQuickSettingsScene") val QuickSettings = SceneKey("QuickSettingsScene") val EditMode = SceneKey("EditModeScene") fun QSFragmentComposeViewModel.QSExpansionState.toIdleSceneKey(): SceneKey { return when { Loading @@ -880,7 +875,11 @@ object SceneKeys { } } suspend fun synchronizeQsState(state: MutableSceneTransitionLayoutState, expansion: Flow<Float>) { private suspend fun synchronizeQsState( state: MutableSceneTransitionLayoutState, editMode: Flow<Boolean>, expansion: Flow<Float>, ) { coroutineScope { val animationScope = this Loading @@ -891,7 +890,13 @@ suspend fun synchronizeQsState(state: MutableSceneTransitionLayoutState, expansi currentTransition = null } expansion.collectLatest { progress -> editMode.combine(expansion, ::Pair).collectLatest { (editMode, progress) -> if (editMode && state.currentScene != SceneKeys.EditMode) { state.setTargetScene(SceneKeys.EditMode, animationScope)?.second?.join() } else if (!editMode && state.currentScene == SceneKeys.EditMode) { state.setTargetScene(SceneKeys.QuickSettings, animationScope)?.second?.join() } if (!editMode) { when (progress) { 0f -> snapTo(QuickQuickSettings) 1f -> snapTo(QuickSettings) Loading @@ -913,6 +918,7 @@ suspend fun synchronizeQsState(state: MutableSceneTransitionLayoutState, expansi } } } } private class ExpansionTransition(currentProgress: Float) : TransitionState.Transition.ChangeScene( Loading packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/ToEditMode.kt 0 → 100644 +28 −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.composefragment.ui import com.android.compose.animation.scene.TransitionBuilder import com.android.systemui.qs.composefragment.SceneKeys fun TransitionBuilder.toEditMode() { fractionRange(start = 0.5f) { fade(SceneKeys.EditMode.rootElementKey) } fractionRange(end = 0.5f) { fade(SceneKeys.QuickQuickSettings.rootElementKey) fade(SceneKeys.QuickSettings.rootElementKey) } } Loading
packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt +62 −56 Original line number Diff line number Diff line Loading @@ -32,11 +32,7 @@ import androidx.activity.OnBackPressedDispatcher import androidx.activity.OnBackPressedDispatcherOwner import androidx.activity.setViewTreeOnBackPressedDispatcherOwner import androidx.annotation.VisibleForTesting import androidx.compose.animation.AnimatedContent import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.togetherWith import androidx.compose.foundation.ScrollState import androidx.compose.foundation.layout.Arrangement.spacedBy import androidx.compose.foundation.layout.Box Loading Loading @@ -120,6 +116,7 @@ import com.android.systemui.qs.composefragment.ui.GridAnchor import com.android.systemui.qs.composefragment.ui.NotificationScrimClipParams import com.android.systemui.qs.composefragment.ui.notificationScrimClip import com.android.systemui.qs.composefragment.ui.quickQuickSettingsToQuickSettings import com.android.systemui.qs.composefragment.ui.toEditMode import com.android.systemui.qs.composefragment.viewmodel.QSFragmentComposeViewModel import com.android.systemui.qs.flags.QSComposeFragment import com.android.systemui.qs.footer.ui.compose.FooterActions Loading @@ -144,6 +141,7 @@ import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch Loading Loading @@ -273,41 +271,12 @@ constructor( // by the composables. .gesturesDisabled(viewModel.showingMirror) ) { val isEditing by viewModel.containerViewModel.editModeViewModel.isEditing .collectAsStateWithLifecycle() val animationSpecEditMode = tween<Float>(EDIT_MODE_TIME_MILLIS) AnimatedContent( targetState = isEditing, transitionSpec = { fadeIn(animationSpecEditMode) togetherWith fadeOut(animationSpecEditMode) }, label = "EditModeAnimatedContent", ) { editing -> if (editing) { val qqsPadding = viewModel.qqsHeaderHeight EditMode( viewModel = viewModel.containerViewModel.editModeViewModel, modifier = Modifier.fillMaxWidth() .padding(top = { qqsPadding }) .padding( horizontal = { QuickSettingsShade.Dimensions.Padding .roundToPx() } ), ) } else { CollapsableQuickSettingsSTL() } } } } } } } /** * STL that contains both QQS (tiles) and QS (brightness, tiles, footer actions), but no Edit Loading @@ -324,12 +293,17 @@ constructor( from(QuickQuickSettings, QuickSettings) { quickQuickSettingsToQuickSettings(viewModel::animateTilesExpansion::get) } to(SceneKeys.EditMode) { spec = tween(durationMillis = EDIT_MODE_TIME_MILLIS) toEditMode() } }, ) LaunchedEffect(Unit) { synchronizeQsState( sceneState, viewModel.containerViewModel.editModeViewModel.isEditing, snapshotFlow { viewModel.expansionState }.map { it.progress }, ) } Loading @@ -342,9 +316,17 @@ constructor( scene(QuickQuickSettings) { LaunchedEffect(Unit) { viewModel.onQQSOpen() } // Cannot pass the element modifier in because the top element has a `testTag` // and this would overwrite it. Box(Modifier.element(QuickQuickSettings.rootElementKey)) { QuickQuickSettingsElement() } } scene(SceneKeys.EditMode) { EditModeElement(Modifier.element(SceneKeys.EditMode.rootElementKey)) } } } override fun setPanelView(notificationPanelView: QS.HeightListener?) { Loading Loading @@ -582,7 +564,7 @@ constructor( } @Composable private fun ContentScope.QuickQuickSettingsElement() { private fun ContentScope.QuickQuickSettingsElement(modifier: Modifier = Modifier) { val qqsPadding = viewModel.qqsHeaderHeight val bottomPadding = viewModel.qqsBottomPadding DisposableEffect(Unit) { Loading @@ -595,7 +577,7 @@ constructor( .squishiness .collectAsStateWithLifecycle() Column(modifier = Modifier.sysuiResTag(ResIdTags.quickQsPanel)) { Column(modifier = modifier.sysuiResTag(ResIdTags.quickQsPanel)) { Box( modifier = Modifier.fillMaxWidth() Loading Loading @@ -666,12 +648,12 @@ constructor( } @Composable private fun ContentScope.QuickSettingsElement() { private fun ContentScope.QuickSettingsElement(modifier: Modifier = Modifier) { val qqsPadding = viewModel.qqsHeaderHeight val qsExtraPadding = dimensionResource(R.dimen.qs_panel_padding_top) Column( modifier = Modifier.collapseExpandSemanticAction( modifier.collapseExpandSemanticAction( stringResource(id = R.string.accessibility_quick_settings_collapse) ) ) { Loading Loading @@ -776,6 +758,18 @@ constructor( } } @Composable private fun EditModeElement(modifier: Modifier = Modifier) { // No need for top padding, the Scaffold inside takes care of the correct insets EditMode( viewModel = viewModel.containerViewModel.editModeViewModel, modifier = modifier .fillMaxWidth() .padding(horizontal = { QuickSettingsShade.Dimensions.Padding.roundToPx() }), ) } private fun Modifier.collapseExpandSemanticAction(label: String): Modifier { return viewModel.collapseExpandAccessibilityAction?.let { semantics { Loading Loading @@ -863,6 +857,7 @@ private val instanceProvider = object SceneKeys { val QuickQuickSettings = SceneKey("QuickQuickSettingsScene") val QuickSettings = SceneKey("QuickSettingsScene") val EditMode = SceneKey("EditModeScene") fun QSFragmentComposeViewModel.QSExpansionState.toIdleSceneKey(): SceneKey { return when { Loading @@ -880,7 +875,11 @@ object SceneKeys { } } suspend fun synchronizeQsState(state: MutableSceneTransitionLayoutState, expansion: Flow<Float>) { private suspend fun synchronizeQsState( state: MutableSceneTransitionLayoutState, editMode: Flow<Boolean>, expansion: Flow<Float>, ) { coroutineScope { val animationScope = this Loading @@ -891,7 +890,13 @@ suspend fun synchronizeQsState(state: MutableSceneTransitionLayoutState, expansi currentTransition = null } expansion.collectLatest { progress -> editMode.combine(expansion, ::Pair).collectLatest { (editMode, progress) -> if (editMode && state.currentScene != SceneKeys.EditMode) { state.setTargetScene(SceneKeys.EditMode, animationScope)?.second?.join() } else if (!editMode && state.currentScene == SceneKeys.EditMode) { state.setTargetScene(SceneKeys.QuickSettings, animationScope)?.second?.join() } if (!editMode) { when (progress) { 0f -> snapTo(QuickQuickSettings) 1f -> snapTo(QuickSettings) Loading @@ -913,6 +918,7 @@ suspend fun synchronizeQsState(state: MutableSceneTransitionLayoutState, expansi } } } } private class ExpansionTransition(currentProgress: Float) : TransitionState.Transition.ChangeScene( Loading
packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/ToEditMode.kt 0 → 100644 +28 −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.composefragment.ui import com.android.compose.animation.scene.TransitionBuilder import com.android.systemui.qs.composefragment.SceneKeys fun TransitionBuilder.toEditMode() { fractionRange(start = 0.5f) { fade(SceneKeys.EditMode.rootElementKey) } fractionRange(end = 0.5f) { fade(SceneKeys.QuickQuickSettings.rootElementKey) fade(SceneKeys.QuickSettings.rootElementKey) } }