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

Commit ca07776d authored by Shawn Lee's avatar Shawn Lee Committed by Android (Google) Code Review
Browse files

Merge changes I423dd756,I9ab4950a into main

* changes:
  [flexiglass] Adds overscroll to notification stack content
  [flexiglass] Expose NestedScrollSource in PriorityNestedScrollConnection API
parents 9e9703a2 950261b7
Loading
Loading
Loading
Loading
+100 −0
Original line number Original line 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.animation.core.Animatable
import androidx.compose.animation.core.tween
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.offset
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.unit.IntOffset
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

@Composable
fun Modifier.stackVerticalOverscroll(
    coroutineScope: CoroutineScope,
    canScrollForward: () -> Boolean
): Modifier {
    val overscrollOffset = remember { Animatable(0f) }
    val stackNestedScrollConnection = remember {
        NotificationStackNestedScrollConnection(
            stackOffset = { overscrollOffset.value },
            canScrollForward = canScrollForward,
            onScroll = { offsetAvailable ->
                coroutineScope.launch {
                    overscrollOffset.snapTo(overscrollOffset.value + offsetAvailable * 0.3f)
                }
            },
            onStop = { velocityAvailable ->
                coroutineScope.launch {
                    overscrollOffset.animateTo(
                        targetValue = 0f,
                        initialVelocity = velocityAvailable,
                        animationSpec = tween()
                    )
                }
            }
        )
    }

    return this.then(
        Modifier.nestedScroll(stackNestedScrollConnection).offset {
            IntOffset(x = 0, y = overscrollOffset.value.roundToInt())
        }
    )
}

fun NotificationStackNestedScrollConnection(
    stackOffset: () -> Float,
    canScrollForward: () -> Boolean,
    onStart: (Float) -> Unit = {},
    onScroll: (Float) -> Unit,
    onStop: (Float) -> Unit = {},
): PriorityNestedScrollConnection {
    return PriorityNestedScrollConnection(
        orientation = Orientation.Vertical,
        canStartPreScroll = { _, _ -> false },
        canStartPostScroll = { offsetAvailable, offsetBeforeStart ->
            offsetAvailable < 0f && offsetBeforeStart < 0f && !canScrollForward()
        },
        canStartPostFling = { velocityAvailable -> velocityAvailable < 0f && !canScrollForward() },
        canContinueScroll = { source ->
            if (source == NestedScrollSource.SideEffect) {
                stackOffset() > STACK_OVERSCROLL_FLING_MIN_OFFSET
            } else {
                true
            }
        },
        canScrollOnFling = true,
        onStart = { offsetAvailable -> onStart(offsetAvailable) },
        onScroll = { offsetAvailable ->
            onScroll(offsetAvailable)
            offsetAvailable
        },
        onStop = { velocityAvailable ->
            onStop(velocityAvailable)
            velocityAvailable
        },
    )
}
+2 −0
Original line number Original line Diff line number Diff line
@@ -474,6 +474,7 @@ fun SceneScope.NotificationScrollingStack(
                        .thenIf(shadeMode == ShadeMode.Single) {
                        .thenIf(shadeMode == ShadeMode.Single) {
                            Modifier.nestedScroll(scrimNestedScrollConnection)
                            Modifier.nestedScroll(scrimNestedScrollConnection)
                        }
                        }
                        .stackVerticalOverscroll(coroutineScope) { scrollState.canScrollForward }
                        .verticalScroll(scrollState)
                        .verticalScroll(scrollState)
                        .padding(top = topPadding)
                        .padding(top = topPadding)
                        .fillMaxWidth()
                        .fillMaxWidth()
@@ -671,3 +672,4 @@ private val DEBUG_HUN_COLOR = Color(0f, 0f, 1f, 0.2f)
private val DEBUG_BOX_COLOR = Color(0f, 1f, 0f, 0.2f)
private val DEBUG_BOX_COLOR = Color(0f, 1f, 0f, 0.2f)
private const val HUN_SNOOZE_POSITIONAL_THRESHOLD_FRACTION = 0.25f
private const val HUN_SNOOZE_POSITIONAL_THRESHOLD_FRACTION = 0.25f
private const val HUN_SNOOZE_VELOCITY_THRESHOLD = -70f
private const val HUN_SNOOZE_VELOCITY_THRESHOLD = -70f
internal const val STACK_OVERSCROLL_FLING_MIN_OFFSET = -100f
+5 −5
Original line number Original line Diff line number Diff line
@@ -38,7 +38,7 @@ class PriorityNestedScrollConnection(
    private val canStartPreScroll: (offsetAvailable: Offset, offsetBeforeStart: Offset) -> Boolean,
    private val canStartPreScroll: (offsetAvailable: Offset, offsetBeforeStart: Offset) -> Boolean,
    private val canStartPostScroll: (offsetAvailable: Offset, offsetBeforeStart: Offset) -> Boolean,
    private val canStartPostScroll: (offsetAvailable: Offset, offsetBeforeStart: Offset) -> Boolean,
    private val canStartPostFling: (velocityAvailable: Velocity) -> Boolean,
    private val canStartPostFling: (velocityAvailable: Velocity) -> Boolean,
    private val canContinueScroll: () -> Boolean,
    private val canContinueScroll: (source: NestedScrollSource) -> Boolean,
    private val canScrollOnFling: Boolean,
    private val canScrollOnFling: Boolean,
    private val onStart: (offsetAvailable: Offset) -> Unit,
    private val onStart: (offsetAvailable: Offset) -> Unit,
    private val onScroll: (offsetAvailable: Offset) -> Offset,
    private val onScroll: (offsetAvailable: Offset) -> Offset,
@@ -61,7 +61,7 @@ class PriorityNestedScrollConnection(


        if (
        if (
            isPriorityMode ||
            isPriorityMode ||
                (source == NestedScrollSource.Fling && !canScrollOnFling) ||
                (source == NestedScrollSource.SideEffect && !canScrollOnFling) ||
                !canStartPostScroll(available, offsetBeforeStart)
                !canStartPostScroll(available, offsetBeforeStart)
        ) {
        ) {
            // The priority mode cannot start so we won't consume the available offset.
            // The priority mode cannot start so we won't consume the available offset.
@@ -73,7 +73,7 @@ class PriorityNestedScrollConnection(


    override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
    override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
        if (!isPriorityMode) {
        if (!isPriorityMode) {
            if (source != NestedScrollSource.Fling || canScrollOnFling) {
            if (source == NestedScrollSource.UserInput || canScrollOnFling) {
                if (canStartPreScroll(available, offsetScrolledBeforePriorityMode)) {
                if (canStartPreScroll(available, offsetScrolledBeforePriorityMode)) {
                    return onPriorityStart(available)
                    return onPriorityStart(available)
                }
                }
@@ -84,7 +84,7 @@ class PriorityNestedScrollConnection(
            return Offset.Zero
            return Offset.Zero
        }
        }


        if (!canContinueScroll()) {
        if (!canContinueScroll(source)) {
            // Step 3a: We have lost priority and we no longer need to intercept scroll events.
            // Step 3a: We have lost priority and we no longer need to intercept scroll events.
            onPriorityStop(velocity = Velocity.Zero)
            onPriorityStop(velocity = Velocity.Zero)


@@ -170,7 +170,7 @@ fun PriorityNestedScrollConnection(
    canStartPreScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean,
    canStartPreScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean,
    canStartPostScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean,
    canStartPostScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean,
    canStartPostFling: (velocityAvailable: Float) -> Boolean,
    canStartPostFling: (velocityAvailable: Float) -> Boolean,
    canContinueScroll: () -> Boolean,
    canContinueScroll: (source: NestedScrollSource) -> Boolean,
    canScrollOnFling: Boolean,
    canScrollOnFling: Boolean,
    onStart: (offsetAvailable: Float) -> Unit,
    onStart: (offsetAvailable: Float) -> Unit,
    onScroll: (offsetAvailable: Float) -> Float,
    onScroll: (offsetAvailable: Float) -> Float,
+1 −1

File changed.

Contains only whitespace changes.