Merge "[Flexiglass] Set the top HUN height on the HeadsUpPlaceholder" into main
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationHeadsUpHeight.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationHeadsUpHeight.kt
new file mode 100644
index 0000000..75a565b
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationHeadsUpHeight.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.notifications.ui.composable
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.node.LayoutModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.invalidateMeasurement
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.IntOffset
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
+
+/**
+ * Modify element, which updates the height to the height of current top heads up notification, or
+ * to 0 if there is none.
+ *
+ * @param view Notification stack scroll view
+ */
+fun Modifier.notificationHeadsUpHeight(view: NotificationScrollView) =
+ this then HeadsUpLayoutElement(view)
+
+private data class HeadsUpLayoutElement(
+ val view: NotificationScrollView,
+) : ModifierNodeElement<HeadsUpLayoutNode>() {
+
+ override fun create(): HeadsUpLayoutNode = HeadsUpLayoutNode(view)
+
+ override fun update(node: HeadsUpLayoutNode) {
+ check(view == node.view) { "Trying to reuse the node with a new View." }
+ }
+}
+
+private class HeadsUpLayoutNode(val view: NotificationScrollView) :
+ LayoutModifierNode, Modifier.Node() {
+
+ private val headsUpHeightChangedListener = Runnable { invalidateMeasureIfAttached() }
+
+ override fun onAttach() {
+ super.onAttach()
+ view.addHeadsUpHeightChangedListener(headsUpHeightChangedListener)
+ }
+
+ override fun onDetach() {
+ super.onDetach()
+ view.removeHeadsUpHeightChangedListener(headsUpHeightChangedListener)
+ }
+
+ override fun MeasureScope.measure(
+ measurable: Measurable,
+ constraints: Constraints
+ ): MeasureResult {
+ // TODO(b/339181697) make sure, that the row is already measured.
+ val contentHeight = view.topHeadsUpHeight
+ val placeable =
+ measurable.measure(
+ constraints.copy(minHeight = contentHeight, maxHeight = contentHeight)
+ )
+ return layout(placeable.width, placeable.height) { placeable.place(IntOffset.Zero) }
+ }
+
+ override fun toString(): String {
+ return "HeadsUpLayoutNode(view=$view)"
+ }
+
+ fun invalidateMeasureIfAttached() {
+ if (isAttached) {
+ this.invalidateMeasurement()
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index e6132c6..c26259f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -67,7 +67,6 @@
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.NestedScrollBehavior
import com.android.compose.animation.scene.SceneScope
-import com.android.compose.modifiers.height
import com.android.compose.modifiers.thenIf
import com.android.systemui.common.ui.compose.windowinsets.LocalRawScreenHeight
import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius
@@ -108,18 +107,17 @@
*/
@Composable
fun SceneScope.HeadsUpNotificationSpace(
+ stackScrollView: NotificationScrollView,
viewModel: NotificationsPlaceholderViewModel,
modifier: Modifier = Modifier,
isPeekFromBottom: Boolean = false,
) {
- val headsUpHeight = viewModel.headsUpHeight.collectAsStateWithLifecycle()
-
Element(
Notifications.Elements.HeadsUpNotificationPlaceholder,
modifier =
modifier
- .height { headsUpHeight.value.roundToInt() }
.fillMaxWidth()
+ .notificationHeadsUpHeight(stackScrollView)
.debugBackground(viewModel, DEBUG_HUN_COLOR)
.onGloballyPositioned { coordinates: LayoutCoordinates ->
val boundsInWindow = coordinates.boundsInWindow()
@@ -152,6 +150,7 @@
modifier = Modifier.fillMaxSize(),
)
HeadsUpNotificationSpace(
+ stackScrollView = stackScrollView,
viewModel = viewModel,
modifier = Modifier.align(Alignment.TopCenter),
)
@@ -358,7 +357,7 @@
.onSizeChanged { size -> stackHeight.intValue = size.height },
)
}
- HeadsUpNotificationSpace(viewModel = viewModel)
+ HeadsUpNotificationSpace(stackScrollView = stackScrollView, viewModel = viewModel)
}
}
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 d76b19f..0ee485c 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
@@ -42,7 +42,6 @@
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
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.rememberScrollState
@@ -60,7 +59,6 @@
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.colorResource
-import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.SceneScope
@@ -79,14 +77,13 @@
import com.android.systemui.media.controls.ui.controller.MediaCarouselController
import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.media.dagger.MediaModule
-import com.android.systemui.notifications.ui.composable.NotificationScrollingStack
+import com.android.systemui.notifications.ui.composable.HeadsUpNotificationSpace
import com.android.systemui.qs.footer.ui.compose.FooterActionsWithAnimatedVisibility
import com.android.systemui.qs.ui.viewmodel.QuickSettingsSceneViewModel
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.ComposableScene
-import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.shade.ui.composable.CollapsedShadeHeader
import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
import com.android.systemui.shade.ui.composable.Shade
@@ -99,7 +96,6 @@
import dagger.Lazy
import javax.inject.Inject
import javax.inject.Named
-import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.stateIn
@@ -368,15 +364,11 @@
Modifier.align(Alignment.CenterHorizontally).sysuiResTag("qs_footer_actions"),
)
}
- NotificationScrollingStack(
+ HeadsUpNotificationSpace(
stackScrollView = notificationStackScrollView,
viewModel = notificationsPlaceholderViewModel,
- shadeSession = shadeSession,
- maxScrimTop = { screenHeight },
- shouldPunchHoleBehindScrim = shouldPunchHoleBehindScrim,
- shadeMode = ShadeMode.Single,
- modifier =
- Modifier.fillMaxWidth().offset { IntOffset(x = 0, y = screenHeight.roundToInt()) },
+ modifier = Modifier.align(Alignment.BottomCenter),
+ isPeekFromBottom = true,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index edd2961..99ce454 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -2843,14 +2843,10 @@
mIsSystemChildExpanded = expanded;
}
- public void setLayoutListener(LayoutListener listener) {
+ public void setLayoutListener(@Nullable LayoutListener listener) {
mLayoutListener = listener;
}
- public void removeListener() {
- mLayoutListener = null;
- }
-
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Trace.beginSection(appendTraceStyleTag("ExpNotRow#onLayout"));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index c10c09c..bdfbc4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -242,7 +242,7 @@
public void onLayout() {
mIconsPlaced = false; // Force icons to be re-placed
setMenuLocation();
- mParent.removeListener();
+ mParent.setLayoutListener(null);
}
private void createMenuViews(boolean resetState) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index a9d7cc0..84b7478 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -255,7 +255,8 @@
* The raw amount of the overScroll on the bottom, which is not rubber-banded.
*/
private float mOverScrolledBottomPixels;
- private ListenerSet<Runnable> mStackHeightChangedListeners = new ListenerSet<>();
+ private final ListenerSet<Runnable> mStackHeightChangedListeners = new ListenerSet<>();
+ private final ListenerSet<Runnable> mHeadsUpHeightChangedListeners = new ListenerSet<>();
private NotificationLogger.OnChildLocationsChangedListener mListener;
private OnNotificationLocationsChangedListener mLocationsChangedListener;
private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
@@ -1114,6 +1115,28 @@
mStackHeightChangedListeners.remove(runnable);
}
+ private void notifyHeadsUpHeightChangedForView(View view) {
+ if (mTopHeadsUpRow == view) {
+ notifyHeadsUpHeightChangedListeners();
+ }
+ }
+
+ private void notifyHeadsUpHeightChangedListeners() {
+ for (Runnable listener : mHeadsUpHeightChangedListeners) {
+ listener.run();
+ }
+ }
+
+ @Override
+ public void addHeadsUpHeightChangedListener(@NonNull Runnable runnable) {
+ mHeadsUpHeightChangedListeners.addIfAbsent(runnable);
+ }
+
+ @Override
+ public void removeHeadsUpHeightChangedListener(@NonNull Runnable runnable) {
+ mHeadsUpHeightChangedListeners.remove(runnable);
+ }
+
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (!mSuppressChildrenMeasureAndLayout) {
@@ -2444,6 +2467,11 @@
return mScrollViewFields.getIntrinsicStackHeight();
}
+ @Override
+ public int getTopHeadsUpHeight() {
+ return getTopHeadsUpPinnedHeight();
+ }
+
/**
* Calculate the gap height between two different views
*
@@ -4193,12 +4221,14 @@
requestAnimationOnViewResize(row);
}
requestChildrenUpdate();
+ notifyHeadsUpHeightChangedForView(view);
mAnimateStackYForContentHeightChange = previouslyNeededAnimation;
}
void onChildHeightReset(ExpandableView view) {
updateAnimationState(view);
updateChronometerForChild(view);
+ notifyHeadsUpHeightChangedForView(view);
}
private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
@@ -5573,6 +5603,7 @@
*/
public void setTopHeadsUpRow(@Nullable ExpandableNotificationRow topHeadsUpRow) {
mTopHeadsUpRow = topHeadsUpRow;
+ notifyHeadsUpHeightChangedListeners();
}
public boolean getIsExpanded() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt
index 463c631..f6d9351 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt
@@ -27,9 +27,6 @@
@SysUISingleton
class NotificationViewHeightRepository @Inject constructor() {
- /** The height in px of the current heads up notification. */
- val headsUpHeight = MutableStateFlow(0f)
-
/**
* The amount in px that the notification stack should scroll due to internal expansion. This
* should only happen when a notification expansion hits the bottom of the screen, so it is
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
index e7acbe3..afcf3ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
@@ -65,9 +65,6 @@
}
.distinctUntilChanged()
- /** The height in px of the contents of the HUN. */
- val headsUpHeight: StateFlow<Float> = viewHeightRepository.headsUpHeight.asStateFlow()
-
/** The alpha of the Notification Stack for the brightness mirror */
val alphaForBrightnessMirror: StateFlow<Float> =
placeholderRepository.alphaForBrightnessMirror.asStateFlow()
@@ -110,11 +107,6 @@
placeholderRepository.shadeScrimBounds.value = bounds
}
- /** Sets the height of heads up notification. */
- fun setHeadsUpHeight(height: Float) {
- viewHeightRepository.headsUpHeight.value = height
- }
-
/** Sets whether the notification stack is scrolled to the top. */
fun setScrolledToTop(scrolledToTop: Boolean) {
placeholderRepository.scrolledToTop.value = scrolledToTop
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
index 14b882f..eaaa9a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
@@ -32,6 +32,9 @@
*/
val intrinsicStackHeight: Int
+ /** Height in pixels required to display the top HeadsUp Notification. */
+ val topHeadsUpHeight: Int
+
/**
* Since this is an interface rather than a literal View, this provides cast-like access to the
* underlying view.
@@ -72,9 +75,18 @@
/** Sets whether the view is displayed in doze mode. */
fun setDozing(dozing: Boolean)
- /** Sets a listener to be notified, when the stack height might have changed. */
+ /** Adds a listener to be notified, when the stack height might have changed. */
fun addStackHeightChangedListener(runnable: Runnable)
/** @see addStackHeightChangedListener */
fun removeStackHeightChangedListener(runnable: Runnable)
+
+ /**
+ * Adds a listener to be notified, when the height of the top heads up notification might have
+ * changed.
+ */
+ fun addHeadsUpHeightChangedListener(runnable: Runnable)
+
+ /** @see addHeadsUpHeightChangedListener */
+ fun removeHeadsUpHeightChangedListener(runnable: Runnable)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
index 622d8e7..fd08e89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
@@ -80,7 +80,6 @@
launch { viewModel.maxAlpha.collect { view.setMaxAlpha(it) } }
launch { viewModel.scrolledToTop.collect { view.setScrolledToTop(it) } }
- launch { viewModel.headsUpTop.collect { view.setHeadsUpTop(it) } }
launch { viewModel.expandFraction.collect { view.setExpandFraction(it.coerceIn(0f, 1f)) } }
launch { viewModel.isScrollable.collect { view.setScrollingEnabled(it) } }
launch { viewModel.isDozing.collect { isDozing -> view.setDozing(isDozing) } }
@@ -88,11 +87,9 @@
launchAndDispose {
view.setSyntheticScrollConsumer(viewModel.syntheticScrollConsumer)
view.setCurrentGestureOverscrollConsumer(viewModel.currentGestureOverscrollConsumer)
- view.setHeadsUpHeightConsumer(viewModel.headsUpHeightConsumer)
DisposableHandle {
view.setSyntheticScrollConsumer(null)
view.setCurrentGestureOverscrollConsumer(null)
- view.setHeadsUpHeightConsumer(null)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index c2ce114..a99fbfc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -150,8 +150,6 @@
*/
val currentGestureOverscrollConsumer: (Boolean) -> Unit =
stackAppearanceInteractor::setCurrentGestureOverscroll
- /** Receives the height of the heads up notification. */
- val headsUpHeightConsumer: (Float) -> Unit = stackAppearanceInteractor::setHeadsUpHeight
/** Whether the notification stack is scrollable or not. */
val isScrollable: Flow<Boolean> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
index 97b86e3..ea33be0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
@@ -29,7 +29,6 @@
import com.android.systemui.util.kotlin.FlowDumperImpl
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.StateFlow
/**
* ViewModel used by the Notification placeholders inside the scene container to update the
@@ -74,9 +73,6 @@
val shadeScrimRounding: Flow<ShadeScrimRounding> =
interactor.shadeScrimRounding.dumpWhileCollecting("shadeScrimRounding")
- /** The height in px of the contents of the HUN. */
- val headsUpHeight: StateFlow<Float> = interactor.headsUpHeight.dumpValue("headsUpHeight")
-
/**
* The amount [0-1] that the shade or quick settings has been opened. At 0, the shade is closed;
* at 1, either the shade or quick settings is open.