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

Commit e30ffaa2 authored by András Kurucz's avatar András Kurucz Committed by Android (Google) Code Review
Browse files

Merge "[Flexiglass] Fix Notification removal animations" into main

parents 61b0874b 3a41c67f
Loading
Loading
Loading
Loading
+93 −0
Original line number Diff line number Diff line
/*
 * 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 be the same as the Notification stack height returned
 * by the legacy Notification stack scroll view in [NotificationScrollView.intrinsicStackHeight].
 *
 * @param view Notification stack scroll view
 * @param padding extra padding in pixels to be added to the received content height.
 */
fun Modifier.notificationStackHeight(view: NotificationScrollView, padding: Int = 0) =
    this then StackLayoutElement(view, padding)

private data class StackLayoutElement(
    val view: NotificationScrollView,
    val padding: Int,
) : ModifierNodeElement<StackLayoutNode>() {

    override fun create(): StackLayoutNode = StackLayoutNode(view, padding)

    override fun update(node: StackLayoutNode) {
        check(view == node.view) { "Trying to reuse the node with a new View." }
        if (node.padding != padding) {
            node.padding = padding
            node.invalidateMeasureIfAttached()
        }
    }
}

private class StackLayoutNode(val view: NotificationScrollView, var padding: Int) :
    LayoutModifierNode, Modifier.Node() {

    private val stackHeightChangedListener = Runnable { invalidateMeasureIfAttached() }

    override fun onAttach() {
        super.onAttach()
        view.addStackHeightChangedListener(stackHeightChangedListener)
    }

    override fun onDetach() {
        super.onDetach()
        view.removeStackHeightChangedListener(stackHeightChangedListener)
    }

    override fun MeasureScope.measure(
        measurable: Measurable,
        constraints: Constraints
    ): MeasureResult {
        val contentHeight = padding + view.intrinsicStackHeight
        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 "StackLayoutNode(view=$view padding:$padding)"
    }

    fun invalidateMeasureIfAttached() {
        if (isAttached) {
            this.invalidateMeasurement()
        }
    }
}
+17 −4
Original line number Diff line number Diff line
@@ -40,6 +40,8 @@ import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
@@ -75,6 +77,7 @@ import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.ui.composable.ShadeHeader
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimRounding
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_MAX_CORNER_RADIUS
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_MAX_SCRIM_ALPHA
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
@@ -158,6 +161,7 @@ fun SceneScope.ConstrainedNotificationStack(
@Composable
fun SceneScope.NotificationScrollingStack(
    shadeSession: SaveableSession,
    stackScrollView: NotificationScrollView,
    viewModel: NotificationsPlaceholderViewModel,
    maxScrimTop: () -> Float,
    shouldPunchHoleBehindScrim: Boolean,
@@ -179,7 +183,12 @@ fun SceneScope.NotificationScrollingStack(
        with(density) { WindowInsets.systemBars.asPaddingValues().calculateBottomPadding().toPx() }
    val screenHeight = LocalRawScreenHeight.current

    val stackHeight = viewModel.stackHeight.collectAsState()
    /**
     * The height in px of the contents of notification stack. Depending on the number of
     * notifications, this can exceed the space available on screen to show notifications, at which
     * point the notification stack should become scrollable.
     */
    val stackHeight = remember { mutableIntStateOf(0) }

    val scrimRounding = viewModel.shadeScrimRounding.collectAsState(ShadeScrimRounding())

@@ -212,7 +221,7 @@ fun SceneScope.NotificationScrollingStack(
    // if contentHeight drops below minimum visible scrim height while scrim is
    // expanded, reset scrim offset.
    LaunchedEffect(stackHeight, scrimOffset) {
        snapshotFlow { stackHeight.value < minVisibleScrimHeight() && scrimOffset.value < 0f }
        snapshotFlow { stackHeight.intValue < minVisibleScrimHeight() && scrimOffset.value < 0f }
            .collect { shouldCollapse -> if (shouldCollapse) scrimOffset.snapTo(0f) }
    }

@@ -324,7 +333,7 @@ fun SceneScope.NotificationScrollingStack(
                                    },
                                    minScrimOffset = minScrimOffset,
                                    maxScrimOffset = 0f,
                                    contentHeight = { stackHeight.value },
                                    contentHeight = { stackHeight.intValue.toFloat() },
                                    minVisibleScrimHeight = minVisibleScrimHeight,
                                    isCurrentGestureOverscroll = {
                                        isCurrentGestureOverscroll.value
@@ -334,7 +343,11 @@ fun SceneScope.NotificationScrollingStack(
                        )
                        .verticalScroll(scrollState)
                        .fillMaxWidth()
                        .height { (stackHeight.value + navBarHeight).roundToInt() },
                        .notificationStackHeight(
                            view = stackScrollView,
                            padding = navBarHeight.toInt()
                        )
                        .onSizeChanged { size -> stackHeight.intValue = size.height },
            )
        }
        HeadsUpNotificationSpace(viewModel = viewModel)
+6 −0
Original line number Diff line number Diff line
@@ -90,10 +90,12 @@ import com.android.systemui.shade.ui.composable.CollapsedShadeHeader
import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
import com.android.systemui.shade.ui.composable.Shade
import com.android.systemui.shade.ui.composable.ShadeHeader
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.phone.ui.StatusBarIconController
import com.android.systemui.statusbar.phone.ui.TintedIconManager
import dagger.Lazy
import javax.inject.Inject
import javax.inject.Named
import kotlin.math.roundToInt
@@ -108,6 +110,7 @@ class QuickSettingsScene
constructor(
    @Application private val applicationScope: CoroutineScope,
    private val shadeSession: SaveableSession,
    private val notificationStackScrollView: Lazy<NotificationScrollView>,
    private val viewModel: QuickSettingsSceneViewModel,
    private val notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
    private val tintedIconManagerFactory: TintedIconManager.Factory,
@@ -130,6 +133,7 @@ constructor(
        modifier: Modifier,
    ) {
        QuickSettingsScene(
            notificationStackScrollView = notificationStackScrollView.get(),
            viewModel = viewModel,
            notificationsPlaceholderViewModel = notificationsPlaceholderViewModel,
            createTintedIconManager = tintedIconManagerFactory::create,
@@ -145,6 +149,7 @@ constructor(

@Composable
private fun SceneScope.QuickSettingsScene(
    notificationStackScrollView: NotificationScrollView,
    viewModel: QuickSettingsSceneViewModel,
    notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
    createTintedIconManager: (ViewGroup, StatusBarLocation) -> TintedIconManager,
@@ -360,6 +365,7 @@ private fun SceneScope.QuickSettingsScene(
            )
        }
        NotificationScrollingStack(
            stackScrollView = notificationStackScrollView,
            viewModel = notificationsPlaceholderViewModel,
            shadeSession = shadeSession,
            maxScrimTop = { screenHeight },
+11 −0
Original line number Diff line number Diff line
@@ -88,9 +88,11 @@ 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.viewmodel.ShadeSceneViewModel
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.phone.ui.StatusBarIconController
import com.android.systemui.statusbar.phone.ui.TintedIconManager
import dagger.Lazy
import javax.inject.Inject
import javax.inject.Named
import kotlin.math.roundToInt
@@ -126,6 +128,7 @@ class ShadeScene
@Inject
constructor(
    private val shadeSession: SaveableSession,
    private val notificationStackScrollView: Lazy<NotificationScrollView>,
    private val viewModel: ShadeSceneViewModel,
    private val tintedIconManagerFactory: TintedIconManager.Factory,
    private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
@@ -144,6 +147,7 @@ constructor(
        modifier: Modifier,
    ) =
        ShadeScene(
            notificationStackScrollView.get(),
            viewModel = viewModel,
            createTintedIconManager = tintedIconManagerFactory::create,
            createBatteryMeterViewController = batteryMeterViewControllerFactory::create,
@@ -163,6 +167,7 @@ constructor(

@Composable
private fun SceneScope.ShadeScene(
    notificationStackScrollView: NotificationScrollView,
    viewModel: ShadeSceneViewModel,
    createTintedIconManager: (ViewGroup, StatusBarLocation) -> TintedIconManager,
    createBatteryMeterViewController: (ViewGroup, StatusBarLocation) -> BatteryMeterViewController,
@@ -176,6 +181,7 @@ private fun SceneScope.ShadeScene(
    when (shadeMode) {
        is ShadeMode.Single ->
            SingleShade(
                notificationStackScrollView = notificationStackScrollView,
                viewModel = viewModel,
                createTintedIconManager = createTintedIconManager,
                createBatteryMeterViewController = createBatteryMeterViewController,
@@ -187,6 +193,7 @@ private fun SceneScope.ShadeScene(
            )
        is ShadeMode.Split ->
            SplitShade(
                notificationStackScrollView = notificationStackScrollView,
                viewModel = viewModel,
                createTintedIconManager = createTintedIconManager,
                createBatteryMeterViewController = createBatteryMeterViewController,
@@ -202,6 +209,7 @@ private fun SceneScope.ShadeScene(

@Composable
private fun SceneScope.SingleShade(
    notificationStackScrollView: NotificationScrollView,
    viewModel: ShadeSceneViewModel,
    createTintedIconManager: (ViewGroup, StatusBarLocation) -> TintedIconManager,
    createBatteryMeterViewController: (ViewGroup, StatusBarLocation) -> BatteryMeterViewController,
@@ -287,6 +295,7 @@ private fun SceneScope.SingleShade(
                    {
                        NotificationScrollingStack(
                            shadeSession = shadeSession,
                            stackScrollView = notificationStackScrollView,
                            viewModel = viewModel.notifications,
                            maxScrimTop = { maxNotifScrimTop.value },
                            shouldPunchHoleBehindScrim = shouldPunchHoleBehindScrim,
@@ -313,6 +322,7 @@ private fun SceneScope.SingleShade(

@Composable
private fun SceneScope.SplitShade(
    notificationStackScrollView: NotificationScrollView,
    viewModel: ShadeSceneViewModel,
    createTintedIconManager: (ViewGroup, StatusBarLocation) -> TintedIconManager,
    createBatteryMeterViewController: (ViewGroup, StatusBarLocation) -> BatteryMeterViewController,
@@ -478,6 +488,7 @@ private fun SceneScope.SplitShade(

                NotificationScrollingStack(
                    shadeSession = shadeSession,
                    stackScrollView = notificationStackScrollView,
                    viewModel = viewModel.notifications,
                    maxScrimTop = { 0f },
                    shouldPunchHoleBehindScrim = false,
+37 −9
Original line number Diff line number Diff line
@@ -128,6 +128,7 @@ import com.android.systemui.statusbar.policy.SplitShadeStateController;
import com.android.systemui.util.Assert;
import com.android.systemui.util.ColorUtilKt;
import com.android.systemui.util.DumpUtilsKt;
import com.android.systemui.util.ListenerSet;

import com.google.errorprone.annotations.CompileTimeConstant;

@@ -254,6 +255,7 @@ public class NotificationStackScrollLayout
     * The raw amount of the overScroll on the bottom, which is not rubber-banded.
     */
    private float mOverScrolledBottomPixels;
    private ListenerSet<Runnable> mStackHeightChangedListeners = new ListenerSet<>();
    private NotificationLogger.OnChildLocationsChangedListener mListener;
    private OnNotificationLocationsChangedListener mLocationsChangedListener;
    private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
@@ -316,7 +318,7 @@ public class NotificationStackScrollLayout
            = new ViewTreeObserver.OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {
            if (SceneContainerFlag.isEnabled() && !mChildrenUpdateRequested) {
            if (SceneContainerFlag.isEnabled()) {
                getViewTreeObserver().removeOnPreDrawListener(this);
                return true;
            }
@@ -1083,6 +1085,10 @@ public class NotificationStackScrollLayout
        for (int i = 0; i < size; i++) {
            measureChild(getChildAt(i), childWidthSpec, childHeightSpec);
        }
        if (SceneContainerFlag.isEnabled()) {
            setMaxLayoutHeight(getMeasuredHeight());
            updateContentHeight();
        }
        Trace.endSection();
    }

@@ -1092,6 +1098,22 @@ public class NotificationStackScrollLayout
        super.requestLayout();
    }

    private void notifyStackHeightChangedListeners() {
        for (Runnable listener : mStackHeightChangedListeners) {
            listener.run();
        }
    }

    @Override
    public void addStackHeightChangedListener(@NonNull Runnable runnable) {
        mStackHeightChangedListeners.addIfAbsent(runnable);
    }

    @Override
    public void removeStackHeightChangedListener(@NonNull Runnable runnable) {
        mStackHeightChangedListeners.remove(runnable);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (!mSuppressChildrenMeasureAndLayout) {
@@ -1110,8 +1132,10 @@ public class NotificationStackScrollLayout
                        (int) height);
            }
        }
        if (!SceneContainerFlag.isEnabled()) {
            setMaxLayoutHeight(getHeight());
            updateContentHeight();
        }
        clampScrollPosition();
        requestChildrenUpdate();
        updateFirstAndLastBackgroundViews();
@@ -1176,11 +1200,6 @@ public class NotificationStackScrollLayout
        mScrollViewFields.setCurrentGestureOverscrollConsumer(consumer);
    }

    @Override
    public void setStackHeightConsumer(@Nullable Consumer<Float> consumer) {
        mScrollViewFields.setStackHeightConsumer(consumer);
    }

    @Override
    public void setHeadsUpHeightConsumer(@Nullable Consumer<Float> consumer) {
        mScrollViewFields.setHeadsUpHeightConsumer(consumer);
@@ -2404,16 +2423,25 @@ public class NotificationStackScrollLayout
                        /* notificationStackScrollLayout= */ this, mMaxDisplayedNotifications,
                        shelfIntrinsicHeight);
        mIntrinsicContentHeight = height;
        mScrollViewFields.sendStackHeight(height + footerIntrinsicHeight);

        // The topPadding can be bigger than the regular padding when qs is expanded, in that
        // state the maxPanelHeight and the contentHeight should be bigger
        mContentHeight =
                (int) (height + Math.max(mIntrinsicPadding, getTopPadding()) + mBottomPadding);
        mScrollViewFields.setIntrinsicStackHeight(
                (int) (mIntrinsicPadding + mIntrinsicContentHeight + footerIntrinsicHeight
                        + mBottomPadding));
        updateScrollability();
        clampScrollPosition();
        updateStackPosition();
        mAmbientState.setContentHeight(mContentHeight);

        notifyStackHeightChangedListeners();
    }

    @Override
    public int getIntrinsicStackHeight() {
        return mScrollViewFields.getIntrinsicStackHeight();
    }

    /**
Loading