diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QSMediaMeasurePolicy.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QSMediaMeasurePolicy.kt new file mode 100644 index 0000000000000000000000000000000000000000..8fe870393dd19b5490bb5163e3e2e45a43b473db --- /dev/null +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QSMediaMeasurePolicy.kt @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2024 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.ui.layout.Measurable +import androidx.compose.ui.layout.MeasurePolicy +import androidx.compose.ui.layout.MeasureResult +import androidx.compose.ui.layout.MeasureScope +import androidx.compose.ui.layout.layoutId +import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.Density +import androidx.compose.ui.util.fastFirst +import kotlin.math.max + +/* + This layout puts QS taking all horizontal space and media taking the right half of the space. + However, QS (in QSPanel) puts an empty view taking half the horizontal space so that it can be + covered by media. +*/ +class QSMediaMeasurePolicy( + val qsHeight: () -> Int, + val mediaVerticalOffset: Density.() -> Int = { 0 }, +) : MeasurePolicy { + override fun MeasureScope.measure( + measurables: List<Measurable>, + constraints: Constraints + ): MeasureResult { + val qsMeasurable = measurables.fastFirst { it.layoutId == LayoutId.QS } + val mediaMeasurable = measurables.fastFirst { it.layoutId == LayoutId.Media } + + val qsPlaceable = qsMeasurable.measure(constraints) + val mediaPlaceable = + mediaMeasurable.measure(constraints.copy(maxWidth = constraints.maxWidth / 2)) + + val width = qsPlaceable.width + val height = max(qsHeight(), mediaPlaceable.height) + return layout(width, height) { + qsPlaceable.placeRelative(0, 0) + mediaPlaceable.placeRelative(width / 2, mediaVerticalOffset()) + } + } + + enum class LayoutId { + QS, + Media, + } +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt index 8195df3b01e842742a596a907a8c3c166782ac6e..8058dcde8cf808347c7af8182bca616809c84d82 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt @@ -29,6 +29,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.layout.layout import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.scene.ElementKey @@ -62,11 +63,21 @@ object QuickSettings { object SharedValues { val TilesSquishiness = ValueKey("QuickSettingsTileSquishiness") + object SquishinessValues { val Default = 1f val LockscreenSceneStarting = 0f val GoneSceneStarting = 0.3f } + + val MediaLandscapeTopOffset = ValueKey("MediaLandscapeTopOffset") + + object MediaOffset { + val InQQS = 0.dp + // Brightness + padding + val InQS = 92.dp + val Default = 0.dp + } } } @@ -77,8 +88,8 @@ private fun SceneScope.stateForQuickSettingsContent( return when (val transitionState = layoutState.transitionState) { is TransitionState.Idle -> { when (transitionState.currentScene) { - Scenes.Shade -> QSSceneAdapter.State.QQS.takeUnless { isSplitShade } - ?: QSSceneAdapter.State.QS + Scenes.Shade -> + QSSceneAdapter.State.QQS.takeUnless { isSplitShade } ?: QSSceneAdapter.State.QS Scenes.QuickSettings -> QSSceneAdapter.State.QS else -> QSSceneAdapter.State.CLOSED } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt index 0ee485c496bead4e5e53073d9d6f3fa7dea89bd5..1b49b6702275a5e8f31f6e56f72b1ee9201ee918 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt @@ -46,6 +46,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect @@ -56,6 +57,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier 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.LocalDensity import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.res.colorResource @@ -63,6 +66,7 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.TransitionState +import com.android.compose.animation.scene.animateSceneDpAsState import com.android.compose.animation.scene.animateSceneFloatAsState import com.android.compose.modifiers.thenIf import com.android.compose.windowsizeclass.LocalWindowSizeClass @@ -79,6 +83,8 @@ import com.android.systemui.media.controls.ui.view.MediaHost import com.android.systemui.media.dagger.MediaModule import com.android.systemui.notifications.ui.composable.HeadsUpNotificationSpace import com.android.systemui.qs.footer.ui.compose.FooterActionsWithAnimatedVisibility +import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaLandscapeTopOffset +import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaOffset.InQS import com.android.systemui.qs.ui.viewmodel.QuickSettingsSceneViewModel import com.android.systemui.res.R import com.android.systemui.scene.session.ui.composable.SaveableSession @@ -258,6 +264,14 @@ private fun SceneScope.QuickSettingsScene( } } + // ############# Media ############### + val isMediaVisible by viewModel.isMediaVisible.collectAsStateWithLifecycle() + val mediaInRow = + isMediaVisible && + LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Compact + val mediaOffset by + animateSceneDpAsState(value = InQS, key = MediaLandscapeTopOffset, canOverflow = false) + // This is the background for the whole scene, as the elements don't necessarily provide // a background that extends to the edges. Spacer( @@ -337,21 +351,37 @@ private fun SceneScope.QuickSettingsScene( } Spacer(modifier = Modifier.height(16.dp)) // This view has its own horizontal padding - QuickSettings( - viewModel.qsSceneAdapter, - { viewModel.qsSceneAdapter.qsHeight }, - isSplitShade = false, - modifier = Modifier - ) + val content: @Composable () -> Unit = { + QuickSettings( + viewModel.qsSceneAdapter, + { viewModel.qsSceneAdapter.qsHeight }, + isSplitShade = false, + modifier = Modifier.layoutId(QSMediaMeasurePolicy.LayoutId.QS) + ) - val isMediaVisible by viewModel.isMediaVisible.collectAsStateWithLifecycle() - - MediaCarousel( - isVisible = isMediaVisible, - mediaHost = mediaHost, - modifier = Modifier.fillMaxWidth(), - carouselController = mediaCarouselController, - ) + MediaCarousel( + isVisible = isMediaVisible, + mediaHost = mediaHost, + modifier = + Modifier.fillMaxWidth() + .layoutId(QSMediaMeasurePolicy.LayoutId.Media), + carouselController = mediaCarouselController, + ) + } + val landscapeQsMediaMeasurePolicy = remember { + QSMediaMeasurePolicy( + { viewModel.qsSceneAdapter.qsHeight }, + { mediaOffset.roundToPx() }, + ) + } + if (mediaInRow) { + Layout( + content = content, + measurePolicy = landscapeQsMediaMeasurePolicy, + ) + } else { + content() + } } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt index efda4cd3638e8982491ebb91733fcbfda25e8846..4e334c274657fbb9af7276e07fae687fd91804f9 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt @@ -28,11 +28,14 @@ import androidx.compose.ui.unit.IntOffset import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult +import com.android.compose.animation.scene.animateSceneDpAsState import com.android.compose.animation.scene.animateSceneFloatAsState import com.android.internal.policy.SystemBarUtils import com.android.systemui.dagger.SysUISingleton import com.android.systemui.notifications.ui.composable.HeadsUpNotificationSpace import com.android.systemui.qs.ui.composable.QuickSettings +import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaLandscapeTopOffset +import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaOffset.Default import com.android.systemui.res.R import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.ui.viewmodel.GoneSceneViewModel @@ -67,6 +70,7 @@ constructor( value = QuickSettings.SharedValues.SquishinessValues.GoneSceneStarting, key = QuickSettings.SharedValues.TilesSquishiness, ) + animateSceneDpAsState(value = Default, key = MediaLandscapeTopOffset, canOverflow = false) Spacer(modifier.fillMaxSize()) HeadsUpNotificationStack( stackScrollView = notificationStackScrolLView.get(), diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt index 312890e1c78361967b3158f42dc2960165f56c8b..d51cdd3036f40e0c30f62572f4642ddae06760a6 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt @@ -25,7 +25,6 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.clipScrollableContainer import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -35,7 +34,6 @@ 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 import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding @@ -54,6 +52,7 @@ import androidx.compose.ui.Modifier 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.LocalDensity import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.res.colorResource @@ -65,6 +64,7 @@ import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.TransitionState import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult +import com.android.compose.animation.scene.animateSceneDpAsState import com.android.compose.animation.scene.animateSceneFloatAsState import com.android.compose.modifiers.padding import com.android.compose.modifiers.thenIf @@ -84,7 +84,10 @@ import com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL import com.android.systemui.notifications.ui.composable.NotificationScrollingStack import com.android.systemui.qs.footer.ui.compose.FooterActionsWithAnimatedVisibility import com.android.systemui.qs.ui.composable.BrightnessMirror +import com.android.systemui.qs.ui.composable.QSMediaMeasurePolicy import com.android.systemui.qs.ui.composable.QuickSettings +import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaLandscapeTopOffset +import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaOffset.InQQS import com.android.systemui.res.R import com.android.systemui.scene.session.ui.composable.SaveableSession import com.android.systemui.scene.shared.model.Scenes @@ -241,6 +244,8 @@ private fun SceneScope.SingleShade( val mediaInRow = isMediaVisible && LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Compact + val mediaOffset by + animateSceneDpAsState(value = InQQS, key = MediaLandscapeTopOffset, canOverflow = false) Box( modifier = @@ -281,10 +286,10 @@ private fun SceneScope.SingleShade( statusBarIconController = statusBarIconController, ) - val content: @Composable (Modifier) -> Unit = { modifier -> + val content: @Composable () -> Unit = { Box( Modifier.element(QuickSettings.Elements.QuickQuickSettings) - .then(modifier) + .layoutId(QSMediaMeasurePolicy.LayoutId.QS) ) { QuickSettings( viewModel.qsSceneAdapter, @@ -297,21 +302,25 @@ private fun SceneScope.SingleShade( MediaCarousel( isVisible = isMediaVisible, mediaHost = mediaHost, - modifier = Modifier.fillMaxWidth().then(modifier), + modifier = + Modifier.fillMaxWidth() + .layoutId(QSMediaMeasurePolicy.LayoutId.Media), carouselController = mediaCarouselController, ) } - - if (!mediaInRow) { - content(Modifier) + val landscapeQsMediaMeasurePolicy = remember { + QSMediaMeasurePolicy( + { viewModel.qsSceneAdapter.qqsHeight }, + { mediaOffset.roundToPx() }, + ) + } + if (mediaInRow) { + Layout( + content = content, + measurePolicy = landscapeQsMediaMeasurePolicy, + ) } else { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = spacedBy(16.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - content(Modifier.weight(1f)) - } + content() } } }, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt index 7388d51a7cf01c63b2698fafe058bf62cc15d861..b35b7bca4809d9ff2646bc56bbb7ef6e75d5bc4d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt @@ -164,25 +164,16 @@ class QSSceneAdapterImplTest : SysuiTestCase() { with(qsImpl!!) { verify(this).setQsVisible(false) - verify(this, never()) + verify(this) .setQsExpansion( - /* expansion= */ anyFloat(), - /* panelExpansionFraction= */ anyFloat(), - /* proposedTranslation= */ anyFloat(), - /* squishinessFraction= */ anyFloat(), + /* expansion= */ 0f, + /* panelExpansionFraction= */ 1f, + /* proposedTranslation= */ 0f, + /* squishinessFraction= */ 1f, ) verify(this).setListening(false) verify(this).setExpanded(false) } - - underTest.applyLatestExpansionAndSquishiness() - verify(qsImpl!!) - .setQsExpansion( - /* expansion= */ 0f, - /* panelExpansionFraction= */ 1f, - /* proposedTranslation= */ 0f, - /* squishinessFraction= */ 1f, - ) } @Test diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 9c8c17bb1ca09b3b89316ed48ecdaa57620db178..f3cc35ba31f8ebe8dcd781267abcfdc8c7095f1c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -113,6 +113,9 @@ public class QSPanel extends LinearLayout implements Tunable { private boolean mSceneContainerEnabled; + @Nullable + private View mMediaViewPlaceHolderForScene; + public QSPanel(Context context, AttributeSet attrs) { super(context, attrs); mUsingMediaPlayer = useQsMediaPlayer(context); @@ -125,7 +128,6 @@ public class QSPanel extends LinearLayout implements Tunable { setOrientation(VERTICAL); mMovableContentStartIndex = getChildCount(); - } void initialize(QSLogger qsLogger, boolean usingMediaPlayer) { @@ -133,7 +135,7 @@ public class QSPanel extends LinearLayout implements Tunable { mUsingMediaPlayer = usingMediaPlayer; mTileLayout = getOrCreateTileLayout(); - if (mUsingMediaPlayer) { + if (mUsingMediaPlayer || SceneContainerFlag.isEnabled()) { mHorizontalLinearLayout = new RemeasuringLinearLayout(mContext); mHorizontalLinearLayout.setOrientation(LinearLayout.HORIZONTAL); mHorizontalLinearLayout.setVisibility( @@ -151,6 +153,13 @@ public class QSPanel extends LinearLayout implements Tunable { lp.setMarginEnd(marginSize); lp.gravity = Gravity.CENTER_VERTICAL; mHorizontalLinearLayout.addView(mHorizontalContentContainer, lp); + if (SceneContainerFlag.isEnabled()) { + int mediaHeight = mContext.getResources() + .getDimensionPixelSize(R.dimen.qs_media_session_height_expanded); + lp = new LayoutParams(0, mediaHeight, 1); + mMediaViewPlaceHolderForScene = new View(mContext); + mHorizontalLinearLayout.addView(mMediaViewPlaceHolderForScene, lp); + } lp = new LayoutParams(LayoutParams.MATCH_PARENT, 0, 1); addView(mHorizontalLinearLayout, lp); @@ -383,6 +392,13 @@ public class QSPanel extends LinearLayout implements Tunable { if (mTileLayout != null) { mTileLayout.updateResources(); } + + if (mMediaViewPlaceHolderForScene != null) { + ViewGroup.LayoutParams lp = mMediaViewPlaceHolderForScene.getLayoutParams(); + lp.height = mContext.getResources() + .getDimensionPixelSize(R.dimen.qs_media_session_height_expanded); + mMediaViewPlaceHolderForScene.setLayoutParams(lp); + } } protected void updatePadding() { @@ -417,7 +433,8 @@ public class QSPanel extends LinearLayout implements Tunable { } private void updateHorizontalLinearLayoutMargins() { - if (mUsingMediaPlayer && mHorizontalLinearLayout != null && !displayMediaMarginsOnMedia()) { + if ((mUsingMediaPlayer || SceneContainerFlag.isEnabled()) && mHorizontalLinearLayout != null + && !displayMediaMarginsOnMedia()) { LayoutParams lp = (LayoutParams) mHorizontalLinearLayout.getLayoutParams(); lp.bottomMargin = Math.max(mMediaTotalBottomMargin - getPaddingBottom(), 0); mHorizontalLinearLayout.setLayoutParams(lp); @@ -632,6 +649,7 @@ public class QSPanel extends LinearLayout implements Tunable { // using media, the parent should always be this. ViewGroup newParent = horizontal && mUsingMediaPlayer ? mHorizontalContentContainer : this; + if (SceneContainerFlag.isEnabled()) return; switchAllContentToParent(newParent, mTileLayout); reAttachMediaHost(mediaHostView, horizontal); if (needsDynamicRowsAndColumns()) { @@ -647,6 +665,19 @@ public class QSPanel extends LinearLayout implements Tunable { void setColumnRowLayout(boolean withMedia) { mTileLayout.setMinRows(withMedia ? 2 : 1); mTileLayout.setMaxColumns(withMedia ? 2 : 4); + placeTileLayoutForScene(withMedia); + } + + protected void placeTileLayoutForScene(boolean withMedia) { + // The tile layout should be reparented if horizontal and we are using media. If not + // using media, the parent should always be this. + ViewGroup newParent = withMedia ? mHorizontalContentContainer : this; + if (mTileLayout != null && ((View) mTileLayout).getParent() != newParent) { + switchAllContentToParent(newParent, mTileLayout); + } + if (mHorizontalLinearLayout != null) { + mHorizontalLinearLayout.setVisibility(withMedia ? View.VISIBLE : View.GONE); + } } private void updateMargins(ViewGroup mediaHostView) { @@ -699,6 +730,12 @@ public class QSPanel extends LinearLayout implements Tunable { mCanCollapse = canCollapse; } + @Nullable + @VisibleForTesting + View getMediaPlaceholder() { + return mMediaViewPlaceHolderForScene; + } + public interface QSTileLayout { /** */ default void saveInstanceState(Bundle outState) {} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java index 3b5cc61057e6e3d9de5427fb5afef7d7800ded63..13cedc2d2f3bdd0d613e28a0024290860cd9ddb9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java @@ -456,7 +456,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr boolean switchTileLayout(boolean force) { /* Whether or not the panel currently contains a media player. */ boolean horizontal = shouldUseHorizontalLayout(); - if (horizontal != mUsingHorizontalLayout || force) { + if ((!SceneContainerFlag.isEnabled() && horizontal != mUsingHorizontalLayout) || force) { mQSLogger.logSwitchTileLayout(horizontal, mUsingHorizontalLayout, force, mView.getDumpableTag()); mUsingHorizontalLayout = horizontal; @@ -470,7 +470,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr return false; } - void setLayoutForMediaInScene() { + private void setLayoutForMediaInScene() { boolean withMedia = shouldUseHorizontalInScene(); mView.setColumnRowLayout(withMedia); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt index c7326b08e315864d1a4da780f74bb4fb6a1836d9..bb36fd5ea97952dbe9f4d4c200315683085620ec 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt @@ -33,6 +33,7 @@ import com.android.systemui.plugins.qs.QSContainerController import com.android.systemui.qs.QSContainerImpl import com.android.systemui.qs.QSImpl import com.android.systemui.qs.dagger.QSSceneComponent +import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel.state import com.android.systemui.res.R import com.android.systemui.settings.brightness.MirrorController import com.android.systemui.shade.domain.interactor.ShadeInteractor @@ -267,6 +268,7 @@ constructor( override val qqsHeight: Int get() = qsImpl.value?.qqsHeight ?: 0 + override val qsHeight: Int get() = qsImpl.value?.qsHeight ?: 0 @@ -375,8 +377,10 @@ constructor( qs.view.setPadding(0, 0, 0, 0) qs.setContainerController(this@QSSceneAdapterImpl) qs.applyState(state.value) + applyLatestExpansionAndSquishiness() } } + override fun setState(state: QSSceneAdapter.State) { this.state.value = state } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt index 2d282dcd15140ced7090c4f8df45594c459a9130..8aaa121640a38a792053759d52d41cf757b1f4d2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt @@ -14,6 +14,7 @@ package com.android.systemui.qs import android.graphics.Rect +import android.platform.test.flag.junit.FlagsParameterization import android.testing.TestableContext import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper @@ -25,15 +26,16 @@ import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.accessibility.AccessibilityNodeInfo import android.widget.FrameLayout import android.widget.LinearLayout -import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase +import com.android.systemui.flags.DisableSceneContainer +import com.android.systemui.flags.parameterizeSceneContainerFlag import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.qs.QSTileView import com.android.systemui.qs.QSPanelControllerBase.TileRecord import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileViewImpl +import com.android.systemui.res.R import com.google.common.truth.Truth.assertThat import org.junit.After import org.junit.Before @@ -44,11 +46,17 @@ import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +import platform.test.runner.parameterized.ParameterizedAndroidJunit4 +import platform.test.runner.parameterized.Parameters -@RunWith(AndroidJUnit4::class) +@RunWith(ParameterizedAndroidJunit4::class) @RunWithLooper @SmallTest -class QSPanelTest : SysuiTestCase() { +class QSPanelTest(flags: FlagsParameterization) : SysuiTestCase() { + + init { + mSetFlagsRule.setFlagsParameterization(flags) + } @Mock private lateinit var qsLogger: QSLogger @@ -57,9 +65,8 @@ class QSPanelTest : SysuiTestCase() { private lateinit var footer: View - private val themedContext = TestableContext( - ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings) - ) + private val themedContext = + TestableContext(ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings)) @Before @Throws(Exception::class) @@ -106,38 +113,8 @@ class QSPanelTest : SysuiTestCase() { } @Test - fun testTilesFooterVisibleRTLLandscapeMedia() { - qsPanel.layoutDirection = View.LAYOUT_DIRECTION_RTL - // We need at least a tile so the layout has a height - qsPanel.tileLayout?.addTile( - QSPanelControllerBase.TileRecord( - mock(QSTile::class.java), - QSTileViewImpl(themedContext) - ) - ) - - val mediaView = FrameLayout(themedContext) - mediaView.addView(View(themedContext), MATCH_PARENT, 800) - - qsPanel.setUsingHorizontalLayout(/* horizontal */ true, mediaView, /* force */ true) - qsPanel.measure( - /* width */ View.MeasureSpec.makeMeasureSpec(3000, View.MeasureSpec.EXACTLY), - /* height */ View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY) - ) - qsPanel.layout(0, 0, qsPanel.measuredWidth, qsPanel.measuredHeight) - - val tiles = qsPanel.tileLayout as View - // Tiles are effectively to the right of media - assertThat(mediaView isLeftOf tiles) - assertThat(tiles.isVisibleToUser).isTrue() - - assertThat(mediaView isLeftOf footer) - assertThat(footer.isVisibleToUser).isTrue() - } - - @Test + @DisableSceneContainer fun testTilesFooterVisibleLandscapeMedia() { - qsPanel.layoutDirection = View.LAYOUT_DIRECTION_LTR // We need at least a tile so the layout has a height qsPanel.tileLayout?.addTile( QSPanelControllerBase.TileRecord( @@ -158,10 +135,10 @@ class QSPanelTest : SysuiTestCase() { val tiles = qsPanel.tileLayout as View // Tiles are effectively to the left of media - assertThat(tiles isLeftOf mediaView) + assertThat(tiles isLeftOf mediaView).isTrue() assertThat(tiles.isVisibleToUser).isTrue() - assertThat(footer isLeftOf mediaView) + assertThat(footer isLeftOf mediaView).isTrue() assertThat(footer.isVisibleToUser).isTrue() } @@ -169,8 +146,8 @@ class QSPanelTest : SysuiTestCase() { fun testBottomPadding() { val padding = 10 themedContext.orCreateTestableResources.addOverride( - R.dimen.qs_panel_padding_bottom, - padding + R.dimen.qs_panel_padding_bottom, + padding ) qsPanel.updatePadding() assertThat(qsPanel.paddingBottom).isEqualTo(padding) @@ -182,8 +159,8 @@ class QSPanelTest : SysuiTestCase() { val paddingCombined = 100 themedContext.orCreateTestableResources.addOverride(R.dimen.qs_panel_padding_top, padding) themedContext.orCreateTestableResources.addOverride( - R.dimen.qs_panel_padding_top, - paddingCombined + R.dimen.qs_panel_padding_top, + paddingCombined ) qsPanel.updatePadding() @@ -220,7 +197,8 @@ class QSPanelTest : SysuiTestCase() { } @Test - fun initializedWithNoMedia_tileLayoutParentIsAlwaysQsPanel() { + @DisableSceneContainer + fun initializedWithNoMedia_sceneContainerDisabled_tileLayoutParentIsAlwaysQsPanel() { lateinit var panel: QSPanel lateinit var tileLayout: View testableLooper.runWithLooper { @@ -249,6 +227,7 @@ class QSPanelTest : SysuiTestCase() { } @Test + @DisableSceneContainer fun initializeWithNoMedia_mediaNeverAttached() { lateinit var panel: QSPanel testableLooper.runWithLooper { @@ -288,6 +267,10 @@ class QSPanelTest : SysuiTestCase() { assertThat(qsPanel.tileLayout!!.maxColumns).isEqualTo(2) } + companion object { + @Parameters(name = "{0}") @JvmStatic fun getParams() = parameterizeSceneContainerFlag() + } + private infix fun View.isLeftOf(other: View): Boolean { val rect = Rect() getBoundsOnScreen(rect)