1 /* <lambda>null2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.notifications.ui.composable 18 19 import androidx.compose.foundation.gestures.Orientation 20 import androidx.compose.ui.input.nestedscroll.NestedScrollConnection 21 import com.android.compose.nestedscroll.PriorityNestedScrollConnection 22 23 /** 24 * A [NestedScrollConnection] that listens for all vertical scroll events and responds in the 25 * following way: 26 * - If you **scroll up**, it **first brings the [scrimOffset]** back to the [minScrimOffset] and 27 * then allows scrolling of the children (usually the content). 28 * - If you **scroll down**, it **first allows scrolling of the children** (usually the content) and 29 * then resets the [scrimOffset] to [maxScrimOffset]. 30 */ 31 fun NotificationScrimNestedScrollConnection( 32 scrimOffset: () -> Float, 33 snapScrimOffset: (Float) -> Unit, 34 animateScrimOffset: (Float) -> Unit, 35 minScrimOffset: () -> Float, 36 maxScrimOffset: Float, 37 contentHeight: () -> Float, 38 minVisibleScrimHeight: () -> Float, 39 isCurrentGestureOverscroll: () -> Boolean, 40 onStart: (Float) -> Unit = {}, <lambda>null41 onStop: (Float) -> Unit = {}, 42 ): PriorityNestedScrollConnection { 43 return PriorityNestedScrollConnection( 44 orientation = Orientation.Vertical, 45 // scrolling up and inner content is taller than the scrim, so scrim needs to 46 // expand; content can scroll once scrim is at the minScrimOffset. offsetBeforeStartnull47 canStartPreScroll = { offsetAvailable, offsetBeforeStart -> 48 offsetAvailable < 0 && 49 offsetBeforeStart == 0f && 50 contentHeight() > minVisibleScrimHeight() && 51 scrimOffset() > minScrimOffset() 52 }, 53 // scrolling down and content is done scrolling to top. After that, the scrim 54 // needs to collapse; collapse the scrim until it is at the maxScrimOffset. offsetAvailablenull55 canStartPostScroll = { offsetAvailable, _ -> 56 offsetAvailable > 0 && (scrimOffset() < maxScrimOffset || isCurrentGestureOverscroll()) 57 }, <lambda>null58 canStartPostFling = { false }, <lambda>null59 canContinueScroll = { 60 val currentHeight = scrimOffset() 61 minScrimOffset() < currentHeight && currentHeight < maxScrimOffset 62 }, 63 canScrollOnFling = true, offsetAvailablenull64 onStart = { offsetAvailable -> onStart(offsetAvailable) }, offsetAvailablenull65 onScroll = { offsetAvailable -> 66 val currentHeight = scrimOffset() 67 val amountConsumed = 68 if (offsetAvailable > 0) { 69 val amountLeft = maxScrimOffset - currentHeight 70 offsetAvailable.coerceAtMost(amountLeft) 71 } else { 72 val amountLeft = minScrimOffset() - currentHeight 73 offsetAvailable.coerceAtLeast(amountLeft) 74 } 75 snapScrimOffset(currentHeight + amountConsumed) 76 amountConsumed 77 }, 78 // Don't consume the velocity on pre/post fling velocityAvailablenull79 onStop = { velocityAvailable -> 80 onStop(velocityAvailable) 81 if (scrimOffset() < minScrimOffset()) { 82 animateScrimOffset(minScrimOffset()) 83 } 84 0f 85 }, 86 ) 87 } 88