From c18e755393f6bbf91950092a6c903d161c65a951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabi=C3=A1n=20Kozynski?= <kozynski@google.com> Date: Thu, 6 Jun 2024 15:30:44 -0400 Subject: [PATCH] [Flexiglass] QQS/QS Media in landscape Use the same treatment for both QQS/QS. Leverage the existent linear layouts to put a view that takes half the width and that's where media should go. We create a custom MeasurePolicy to make sure that: * QS is measured with full width * Media is measured with half width and located in the "end" half. We need to do this for both QQS and QS so the animation between the states works properly. Some extra work is needed to make sure that the transition between QQS and QS looks perfect, but that will be undertaken as part of the flexiglass media transitions work. Test: manual Test: atest QSPanelTest Fixes: 335826262 Flag: com.android.systemui.scene_container Change-Id: I89d2c0f6db928c2b5e43d094c1f9feb1b3997063 --- .../qs/ui/composable/QSMediaMeasurePolicy.kt | 61 ++++++++++++++++ .../qs/ui/composable/QuickSettings.kt | 15 +++- .../qs/ui/composable/QuickSettingsScene.kt | 58 +++++++++++---- .../systemui/scene/ui/composable/GoneScene.kt | 4 + .../shade/ui/composable/ShadeScene.kt | 39 ++++++---- .../qs/ui/adapter/QSSceneAdapterImplTest.kt | 19 ++--- .../src/com/android/systemui/qs/QSPanel.java | 43 ++++++++++- .../systemui/qs/QSPanelControllerBase.java | 4 +- .../systemui/qs/ui/adapter/QSSceneAdapter.kt | 4 + .../com/android/systemui/qs/QSPanelTest.kt | 73 +++++++------------ 10 files changed, 225 insertions(+), 95 deletions(-) create mode 100644 packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QSMediaMeasurePolicy.kt 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 000000000000..8fe870393dd1 --- /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 8195df3b01e8..8058dcde8cf8 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 0ee485c496be..1b49b6702275 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 efda4cd3638e..4e334c274657 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 312890e1c783..d51cdd3036f4 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 7388d51a7cf0..b35b7bca4809 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 9c8c17bb1ca0..f3cc35ba31f8 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 3b5cc61057e6..13cedc2d2f3b 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 c7326b08e315..bb36fd5ea979 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 2d282dcd1514..8aaa121640a3 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) -- GitLab