google / horologist

Horologist is a group of libraries that aim to supplement Wear OS developers with features that are commonly required by developers but not yet available.
https://google.github.io/horologist/
Apache License 2.0
573 stars 95 forks source link

Using rotaryWithScroll and onRotaryInputAccumulatedWithFocus simultaneously #2112

Closed Pezcraft closed 2 months ago

Pezcraft commented 8 months ago

onRotaryInputAccumulatedWithFocus does nothing when rotaryWithScroll is used too. So what's the best option to react to scrolling events?

ScalingLazyColumn(
    modifier = Modifier
        .fillMaxSize()
        .rotaryWithScroll(columnState, focusRequester)
        .onRotaryInputAccumulatedWithFocus(focusRequester) {
            handleBlackscreen()
        },
    state = columnState.state
) {
    // ...
}
yschimke commented 8 months ago

They're both have the same input. So the can't. You will need to implement this yourself.

What are you wanting to happen?

Pezcraft commented 8 months ago

I want to reset a CountDownTimer whenever the user interacts with the app which includes scrolling.

yschimke commented 8 months ago

You will need to look at the implementations of those and use some of the lower level APIs. if you make a sample screen, I can more easily suggest a change.

Pezcraft commented 8 months ago
@OptIn(ExperimentalHorologistApi::class, ExperimentalWearFoundationApi::class)
@Composable
fun SampleScreen(
    modifier: Modifier = Modifier,
) {
    val columnState = rememberColumnState()
    val focusRequester = rememberActiveFocusRequester()

    var showBlackScreenNow by remember { mutableStateOf(false) }

    val screenTimeout: CountDownTimer = object : CountDownTimer(5000, 1000) {
        override fun onTick(millisUntilFinished: Long) {}
        override fun onFinish() {
            showBlackScreenNow = true
        }
    }

    Scaffold(modifier = modifier) {
        ScalingLazyColumn(
            modifier = Modifier
                .fillMaxSize()
                .rotaryWithScroll(columnState, focusRequester)
                .onRotaryScrollEvent {
                    if (showBlackScreenNow) {
                        showBlackScreenNow = false
                    }
                    screenTimeout.cancel()
                    screenTimeout.start()
                    true
                },
            state = columnState.state
        ) {
            item { Text(text = "...") }
        }

        if (showBlackScreenNow) {
            Column(
                modifier = Modifier
                    .fillMaxWidth()
                    .fillMaxHeight()
                    .wrapContentSize(Alignment.Center)
                    .clickable {
                        if (showBlackScreenNow) {
                            showBlackScreenNow = false
                        }
                        screenTimeout.cancel()
                        screenTimeout.start()
                    },
            ) {
                Box(
                    modifier = Modifier
                        .fillMaxWidth()
                        .fillMaxHeight()
                        .clip(RectangleShape)
                        .background(Color.Black, RectangleShape)
                )
            }
        }
    }
}
yschimke commented 8 months ago

Thanks, I'll take a look. But it might be a couple of days.

yschimke commented 3 months ago

Forgot about this, sorry. Will take a look.

yschimke commented 2 months ago

These APIs are now part of Wear Compose. You should see whether the Rotary support does what you need https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/rotary/RotaryScrollable.kt;l=118?q=RotaryScrollable&sq=

I expect this is by design, the existing rotary code normally consumes the events.

https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/rotary/RotaryInputModifier.kt;l=31?q=onRotaryScrollEvent

/**
 * Adding this [modifier][Modifier] to the [modifier][Modifier] parameter of a component will allow
 * it to intercept [RotaryScrollEvent]s if it (or one of its children) is focused.
 *
 * @param onRotaryScrollEvent This callback is invoked when the user interacts with the rotary side
 *   button or the bezel on a wear device. While implementing this callback, return true to stop
 *   propagation of this event. If you return false, the event will be sent to this
 *   [onRotaryScrollEvent]'s parent.
 * @return true if the event is consumed, false otherwise.
 */
fun Modifier.onRotaryScrollEvent(onRotaryScrollEvent: (RotaryScrollEvent) -> Boolean): Modifier =
    this then

Big apologies for the delay and responding and then just closing.