Fixed
Status Update
Comments
ap...@google.com <ap...@google.com> #2
Project: platform/frameworks/support
Branch: androidx-main
commit 2ae4491c96160cde4b70863202542499c68da4b7
Author: Louis Pullen-Freilich <lpf@google.com>
Date: Sat Feb 06 21:48:44 2021
InteractionState -> [Mutable]InteractionSource rework
This CL replaces InteractionState with [Mutable]InteractionSource - interfaces responsible for emitting / observing Interaction events. InteractionSource exposes interactions: Flow<Interaction>, which allows consumers to observe the ordered stream of Interactions and change how components appear accordingly.
This resolves a few issues with the existing InteractionState implementation:
- No handling for multiple sources of the same Interaction
For example, a compound component with two `clickable`s - if one `clickable` is pressed, then the other, then the first `clickable` is released, this would appear as though the entire InteractionState was not pressed.
- Lossy compression of events into state
InteractionState turns a stream events into a snapshot state at a given point in time. Complex components / indications such as Ripple care about the ordering of events, and trying to reconstruct ordering from a snapshot state is a bit messy and can lose information.
- Batched state writes
State<> is not a great fit for representing events as the name suggests - one of the reasons for this is that state writes are batched, so multiple changes (such as multiple fast presses) will be batched into one write. This means that we might skip showing ripples for presses if events happen too closely together.
- Lack of detailed information for events
For simple components, just knowing if the component is pressed or not can be enough to change how it appears, but for complicated components it can be important to know if a press was stopped / cancelled - this sort of information did not exist in InteractionState. Additionally metadata such as press position can be important, but there was no scalable way to support this. Now Interactions can just be unique instances with metadata, so Pressed() can contain a press position just as a property for consumers that need to know this information.
As well as the `interactions` Flow, simple extension functions on InteractionSource (collectIsDraggedAsState, collectIsFocusedAsState, collectIsPressedAsState) have been added for cases when components only care about the binary state of a particular interaction, and not the entire stream of events.
MutableInteractionSource exposes `emit()` and `tryEmit()` methods that allow emitting Interactions to the `interactions` Flow.
This CL also changes the existing Interactions to be classes, and adds further classes representing stop / cancel events for the initial events.
Bug: b/152525426
Fixes: b/171913923
Fixes: b/171710801
Fixes: b/174852378
Test: InteractionSourceTest
Test: updateApi
Relnote: "InteractionState has been replaced with [Mutable]InteractionSource - interfaces responsible for emitting / collecting Interaction events. Instead of passing `interactionState = remember { InteractionState() }` to components such as Button and Modifier.clickable(), use `interactionSource = remember { MutableInteractionSource() }`. Instead of: `Interaction.Pressed in interactionState` you should instead use the extension functions on InteractionSource, such as InteractionSource.collectIsPressedAsState(). For complex use cases you can use InteractionSource.interactions to observe the stream of Interactions. See the InteractionSource documentation and samples for more information."
Change-Id: I85965d0dba39d1740c097915d1d1a367eea2a78c
M compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/MultiDimensionalAnimationDemo.kt
M compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SingleValueAnimationDemo.kt
M compose/foundation/foundation/api/current.txt
M compose/foundation/foundation/api/public_plus_experimental_current.txt
M compose/foundation/foundation/api/restricted_current.txt
M compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/FoundationDemos.kt
M compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt
M compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeVariousInputField.kt
M compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/FocusableSample.kt
M compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/IndicationSamples.kt
A compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/InteractionSourceSample.kt
D compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/InteractionStateSample.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ClickableTest.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/DraggableTest.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/FocusableTest.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/IndicationTest.kt
A compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/InteractionSourceTest.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/SelectableTest.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ToggleableTest.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/TextFieldInteractionsTest.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/textfield/TextFieldScrollTest.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/textfield/TextFieldTest.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Indication.kt
D compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Interaction.kt
D compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/InteractionState.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
A compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/interaction/DragInteraction.kt
A compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/interaction/FocusInteraction.kt
A compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/interaction/Interaction.kt
A compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/interaction/InteractionSource.kt
A compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/interaction/PressInteraction.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/selection/Selectable.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/selection/Toggleable.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicTextField.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldGestureModifiers.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldPressGestureFilter.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldScroll.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionContainer.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt
M compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/Scrollbar.desktop.kt
D compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/InteractionStateTest.kt
M compose/material/material-ripple/src/commonMain/kotlin/androidx/compose/material/ripple/Ripple.kt
M compose/material/material-ripple/src/commonMain/kotlin/androidx/compose/material/ripple/RippleAnimation.kt
M compose/material/material-ripple/src/commonMain/kotlin/androidx/compose/material/ripple/RippleTheme.kt
M compose/material/material/api/current.txt
M compose/material/material/api/public_plus_experimental_current.txt
M compose/material/material/api/restricted_current.txt
M compose/material/material/integration-tests/material-studies/src/main/java/androidx/compose/material/studies/rally/TopAppBar.kt
M compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomNavigationScreenshotTest.kt
M compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/MaterialRippleThemeTest.kt
M compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SwipeableTest.kt
M compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/TabScreenshotTest.kt
M compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldTest.kt
M compose/material/material/src/androidMain/kotlin/androidx/compose/material/AndroidMenu.android.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomNavigation.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/Button.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/Checkbox.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/Elevation.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/FloatingActionButton.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/IconButton.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/Menu.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/RadioButton.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/Swipeable.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/Switch.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/Tab.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldDefaults.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldImpl.kt
M compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopMenu.desktop.kt
M compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/ScrollGestureFilterDemo.kt
https://android-review.googlesource.com/1579423
Branch: androidx-main
commit 2ae4491c96160cde4b70863202542499c68da4b7
Author: Louis Pullen-Freilich <lpf@google.com>
Date: Sat Feb 06 21:48:44 2021
InteractionState -> [Mutable]InteractionSource rework
This CL replaces InteractionState with [Mutable]InteractionSource - interfaces responsible for emitting / observing Interaction events. InteractionSource exposes interactions: Flow<Interaction>, which allows consumers to observe the ordered stream of Interactions and change how components appear accordingly.
This resolves a few issues with the existing InteractionState implementation:
- No handling for multiple sources of the same Interaction
For example, a compound component with two `clickable`s - if one `clickable` is pressed, then the other, then the first `clickable` is released, this would appear as though the entire InteractionState was not pressed.
- Lossy compression of events into state
InteractionState turns a stream events into a snapshot state at a given point in time. Complex components / indications such as Ripple care about the ordering of events, and trying to reconstruct ordering from a snapshot state is a bit messy and can lose information.
- Batched state writes
State<> is not a great fit for representing events as the name suggests - one of the reasons for this is that state writes are batched, so multiple changes (such as multiple fast presses) will be batched into one write. This means that we might skip showing ripples for presses if events happen too closely together.
- Lack of detailed information for events
For simple components, just knowing if the component is pressed or not can be enough to change how it appears, but for complicated components it can be important to know if a press was stopped / cancelled - this sort of information did not exist in InteractionState. Additionally metadata such as press position can be important, but there was no scalable way to support this. Now Interactions can just be unique instances with metadata, so Pressed() can contain a press position just as a property for consumers that need to know this information.
As well as the `interactions` Flow, simple extension functions on InteractionSource (collectIsDraggedAsState, collectIsFocusedAsState, collectIsPressedAsState) have been added for cases when components only care about the binary state of a particular interaction, and not the entire stream of events.
MutableInteractionSource exposes `emit()` and `tryEmit()` methods that allow emitting Interactions to the `interactions` Flow.
This CL also changes the existing Interactions to be classes, and adds further classes representing stop / cancel events for the initial events.
Bug:
Fixes:
Fixes:
Fixes:
Test: InteractionSourceTest
Test: updateApi
Relnote: "InteractionState has been replaced with [Mutable]InteractionSource - interfaces responsible for emitting / collecting Interaction events. Instead of passing `interactionState = remember { InteractionState() }` to components such as Button and Modifier.clickable(), use `interactionSource = remember { MutableInteractionSource() }`. Instead of: `Interaction.Pressed in interactionState` you should instead use the extension functions on InteractionSource, such as InteractionSource.collectIsPressedAsState(). For complex use cases you can use InteractionSource.interactions to observe the stream of Interactions. See the InteractionSource documentation and samples for more information."
Change-Id: I85965d0dba39d1740c097915d1d1a367eea2a78c
M compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/MultiDimensionalAnimationDemo.kt
M compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/SingleValueAnimationDemo.kt
M compose/foundation/foundation/api/current.txt
M compose/foundation/foundation/api/public_plus_experimental_current.txt
M compose/foundation/foundation/api/restricted_current.txt
M compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/FoundationDemos.kt
M compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/ListDemos.kt
M compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/ComposeVariousInputField.kt
M compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/FocusableSample.kt
M compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/IndicationSamples.kt
A compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/InteractionSourceSample.kt
D compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/InteractionStateSample.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ClickableTest.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/DraggableTest.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/FocusableTest.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/IndicationTest.kt
A compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/InteractionSourceTest.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/SelectableTest.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ToggleableTest.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/TextFieldInteractionsTest.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/textfield/TextFieldScrollTest.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/textfield/TextFieldTest.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Indication.kt
D compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Interaction.kt
D compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/InteractionState.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
A compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/interaction/DragInteraction.kt
A compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/interaction/FocusInteraction.kt
A compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/interaction/Interaction.kt
A compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/interaction/InteractionSource.kt
A compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/interaction/PressInteraction.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/selection/Selectable.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/selection/Toggleable.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicTextField.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldGestureModifiers.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldPressGestureFilter.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldScroll.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionContainer.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt
M compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/Scrollbar.desktop.kt
D compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/InteractionStateTest.kt
M compose/material/material-ripple/src/commonMain/kotlin/androidx/compose/material/ripple/Ripple.kt
M compose/material/material-ripple/src/commonMain/kotlin/androidx/compose/material/ripple/RippleAnimation.kt
M compose/material/material-ripple/src/commonMain/kotlin/androidx/compose/material/ripple/RippleTheme.kt
M compose/material/material/api/current.txt
M compose/material/material/api/public_plus_experimental_current.txt
M compose/material/material/api/restricted_current.txt
M compose/material/material/integration-tests/material-studies/src/main/java/androidx/compose/material/studies/rally/TopAppBar.kt
M compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomNavigationScreenshotTest.kt
M compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/MaterialRippleThemeTest.kt
M compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SwipeableTest.kt
M compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/TabScreenshotTest.kt
M compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldTest.kt
M compose/material/material/src/androidMain/kotlin/androidx/compose/material/AndroidMenu.android.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomNavigation.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/Button.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/Checkbox.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/Elevation.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/FloatingActionButton.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/IconButton.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/Menu.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/RadioButton.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/Swipeable.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/Switch.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/Tab.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldDefaults.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextFieldImpl.kt
M compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopMenu.desktop.kt
M compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/ScrollGestureFilterDemo.kt
Description
We added this initially to support storing a press position so that indications (such as
Ripple
) know where to draw the ripple from. Because this just ends up storing a value for a given interaction in a map, there's little safety / guarantee that there is a position, and it is not flexible for other metadata outside of position.Additionally, this causes problems when hoisting
InteractionState
and sharing between components, for example a clickableListItem
that contains aButton
:Because the press position is stored per-Interaction and not local to the component, the same position will be used to draw both ripples. This causes strange effects as the ripples appear to start from different places, and if you click on the bigger component then the ripple on the smaller component will start from outside the bounds of the component, since the position is just an absolute offset.
A more common use case is a
ListItem
with aRadioButton
- this is less bad as theRadioButton
(and other selection controls) use a bounded ripple, so the ripple position never changes - but if clicking on theRadioButton
then the ripple for theListItem
will appear in the wrong place.We should figure out:
a) What the correct / expected behavior is - where the ripple of the other component should begin, and how this can be customized
b) How to change the APIs to support the above