Status Update
Comments
lp...@google.com <lp...@google.com> #2
Branch: androidx-master-dev
commit c60f33e229e31ab328ef6b59dab63b264954831c
Author: Alexandre Elias <aelias@google.com>
Date: Fri Jul 10 16:23:09 2020
Semantics no-op cleanups
Partly in response to lmr's broad code review, I did a pass of
superficial API/implementation cleanup. The main changes are:
- I changed each Boolean SemanticsProperty where false is equivalent to
not being present to take "Unit" instead. This is conceptually
clearer: it avoids questions like "can I cancel out a semantics from a
merged child by setting it to false?" Because "property = Unit" looks
weird, I also changed the style of these to "property()".
- I moved the Semantics id generator closer to where it's used, in
SemanticsModifierCore. I made it internal and an AtomicInt.
(Note that integer ids are heavily used in the Android
AccessibilityNodeInfo APIs so I can't simply remove them entirely.)
- I deleted dead code. Some examples include SemanticsHintOverrides,
a public API not connected to anything, and SemanticsPropertyKey
merge() open method which is never called. (In both cases I have
a different plan in mind for accessibility.)
Fixes: 145951226
Fixes: 145955412
Test: existing tests
Relnote: "Single-value semantics properties now use a calling style.
For example, 'semantics { hidden = true }' is now written as:
'semantics { hidden() }'."
Change-Id: Ic1afd12ea22c926babc9662f1804d80b33aa0cfc
M ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/LayoutNodeModifierBenchmark.kt
M ui/ui-core/api/0.1.0-dev15.txt
M ui/ui-core/api/current.txt
M ui/ui-core/api/public_plus_experimental_0.1.0-dev15.txt
M ui/ui-core/api/public_plus_experimental_current.txt
M ui/ui-core/api/restricted_0.1.0-dev15.txt
M ui/ui-core/api/restricted_current.txt
M ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/graphics/vector/VectorTest.kt
M ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/semantics/SemanticsTests.kt
M ui/ui-core/src/androidMain/kotlin/androidx/ui/core/AndroidActuals.kt
M ui/ui-core/src/androidMain/kotlin/androidx/ui/core/AndroidComposeView.kt
M ui/ui-core/src/androidMain/kotlin/androidx/ui/core/AndroidComposeViewAccessibilityDelegateCompat.kt
M ui/ui-core/src/androidMain/kotlin/androidx/ui/core/AndroidPopup.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/core/Expect.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/core/semantics/SemanticsConfiguration.kt
D ui/ui-core/src/commonMain/kotlin/androidx/ui/core/semantics/SemanticsHintOverrides.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/core/semantics/SemanticsModifier.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/core/semantics/SemanticsNode.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/core/semantics/SemanticsOwner.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/core/semantics/SemanticsWrapper.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/semantics/SemanticsProperties.kt
M ui/ui-foundation/api/0.1.0-dev15.txt
M ui/ui-foundation/api/current.txt
M ui/ui-foundation/api/public_plus_experimental_0.1.0-dev15.txt
M ui/ui-foundation/api/public_plus_experimental_current.txt
M ui/ui-foundation/api/restricted_0.1.0-dev15.txt
M ui/ui-foundation/api/restricted_current.txt
M ui/ui-foundation/src/main/java/androidx/ui/foundation/Clickable.kt
M ui/ui-foundation/src/main/java/androidx/ui/foundation/Dialog.kt
M ui/ui-foundation/src/main/java/androidx/ui/foundation/Scroller.kt
M ui/ui-foundation/src/main/java/androidx/ui/foundation/selection/Selectable.kt
M ui/ui-foundation/src/main/java/androidx/ui/foundation/selection/Toggleable.kt
M ui/ui-foundation/src/main/java/androidx/ui/foundation/semantics/FoundationSemanticsProperties.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/ButtonTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/CardTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/CheckboxScreenshotTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/RadioButtonScreenshotTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/ScaffoldTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/SnackbarTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/SurfaceTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/ripple/RippleIndicationTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/textfield/TextFieldScreenshotTest.kt
M ui/ui-material/src/main/java/androidx/ui/material/AppBar.kt
M ui/ui-material/src/main/java/androidx/ui/material/TextFieldImpl.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/AssertsTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/CallSemanticsActionTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/ErrorMessagesTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/FindersTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/PrintToStringTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/ScrollToTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/TextActionsTest.kt
M ui/ui-test/src/main/java/androidx/ui/test/Actions.kt
M ui/ui-test/src/main/java/androidx/ui/test/Filters.kt
M ui/ui-text/api/0.1.0-dev15.txt
M ui/ui-text/api/current.txt
M ui/ui-text/api/public_plus_experimental_0.1.0-dev15.txt
M ui/ui-text/api/public_plus_experimental_current.txt
M ui/ui-text/api/restricted_0.1.0-dev15.txt
M ui/ui-text/api/restricted_current.txt
M ui/ui-text/src/commonMain/kotlin/androidx/ui/text/CoreTextField.kt
M ui/ui-text/src/commonMain/kotlin/androidx/ui/text/TextSemanticsProperties.kt
ap...@google.com <ap...@google.com> #3
Branch: androidx-main
commit b9c029ef8bc7d6abe73b72bc4fb00e02da90b366
Author: Louis Pullen-Freilich <lpf@google.com>
Date: Mon Oct 18 23:43:49 2021
Delays PressInteractions if the Compose view is inside a scrolling ViewGroup
Updates ComposeView and other ViewGroups to correctly set shouldDelayChildPressedState to false
Bug:
Test: ClickableInScrollableViewGroupTest.kt
Change-Id: Ib7e000139d2bf39c9453fde38efbe2d84a6eac9a
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/selection/Toggleable.kt
M compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
M compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/Clickable.desktop.kt
A compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ClickableInScrollableViewGroupTest.kt
M compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidViewsHandler.android.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ClickableTest.kt
M compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ComposeView.android.kt
M compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt
M compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/Clickable.android.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
ap...@google.com <ap...@google.com> #4
Branch: androidx-main
commit 262448965ac6cc81652e47e0a1534ae14ff7014e
Author: Louis Pullen-Freilich <lpf@google.com>
Date: Mon Oct 18 17:18:06 2021
No longer delays PressInteractions within Modifier.scrollable()
As a result ripples in non-scrollable containers will now instantly start to ripple on a down event, instead of waiting for a delay.
Bug:
Test: tests
Relnote: "Ripples and other indications will now only be delayed if they are inside a Modifier.scrollable() container, instead of always being delayed for a down event."
Change-Id: Ibefe01bcdef89e01b6e9f7edf9fe13622450f487
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/selection/Toggleable.kt
M compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/CardTest.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ClickableTest.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ToggleableTest.kt
M compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ButtonScreenshotTest.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/IndicationTest.kt
M compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SwitchScreenshotTest.kt
M compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SurfaceTest.kt
M compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/RadioButtonScreenshotTest.kt
M compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/CheckboxScreenshotTest.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/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/FloatingActionButtonScreenshotTest.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
lp...@google.com <lp...@google.com>
ma...@google.com <ma...@google.com>
ap...@google.com <ap...@google.com> #5
Branch: androidx-main
commit ea035f9a4c64467bc175122c8f9efafae978080b
Author: Mariano Martin <marianomartin@google.com>
Date: Wed Mar 02 11:04:52 2022
Added a Modifier to query ancestors scroll info
Test: Added tests
Fixes: 223405612, 203141462
Relnote: """Added an Modifier API to query ancestors scroll info.
Used in Clickable to correctly delay press interactions, when getures could become scroll events.
Fixed Clickables not correctly delaying ripples, when used inside an Scrollable ViewGroup.
Updated Drawers and Sheets to correctly delay presses in case gestures can become scroll events.
"""
Change-Id: I2ba9d6d55f853e5d2775fe9a9f15e7a41d41e359
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ClickableInScrollableViewGroupTest.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt
M compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/Clickable.android.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.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
M compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/Clickable.desktop.kt
M compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/DrawerTest.kt
M compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt
M compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/DismissibleNavigationDrawerTest.kt
M compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ModalNavigationDrawerTest.kt
M compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationDrawer.kt
M compose/ui/ui/api/current.txt
M compose/ui/ui/api/public_plus_experimental_current.txt
M compose/ui/ui/api/restricted_current.txt
A compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/ScrollableContainerSample.kt
A compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/nestedscroll/ScrollContainerInfoTest.kt
M compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/AndroidViewTest.kt
M compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/ComposeViewTest.kt
M compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
M compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt
A compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/ScrollContainerInfo.kt
ju...@google.com <ju...@google.com> #6
The following release(s) address this bug.It is possible this bug has only been partially addressed:
androidx.compose.foundation:foundation:1.4.0-alpha03
androidx.compose.material:material:1.4.0-alpha03
androidx.compose.material3:material3:1.1.0-alpha03
androidx.compose.ui:ui:1.4.0-alpha03
jo...@muzz.com <jo...@muzz.com> #7
We have clickable items within a swipeable Composable and have ended up applying a scrollable modifier which never scrolls in order to apply this local modifier and have the clickable items delay the ripple in case the user is performing a swipe.
If not should swipeable or draggable use the same modifier local to say the composable is inside a scrollable container?
lp...@google.com <lp...@google.com> #8
Yes, we have plans to make it public in the future, but it is not prioritized currently.
We don't want to globally enable this for draggable / swipeable, because the ordering can get tricky and not every component would need this, but we would explicitly add it for known components where it is required, such as in a drawer / a sheet.
jd...@google.com <jd...@google.com> #9
Reopening this bug because we would need ModifierLocalScrollableContainer
to be public so we can set it in our implementation of go/sysui-transition-layout. We have a custom gesture handler to implement transitions between different SystemUI scenes, and currently the ripple is instantly started when swiping down on clickables which has a noticeable impact on jank.
Any change this can be prioritized? :-) It looks like everything is already implement and "only" exposing a public API is missing.
lp...@google.com <lp...@google.com> #10
That said there might be other use cases that care about scrollable containers separately, such as scrolling screenshots, so it's unclear what the overlap is and what the API(s) that would accomplish these use cases in a consistent manner are. I might be able to do a brief exploration though to see what we would need to do to unblock at least some of the current use cases.
For your custom gesture handler, what are the 'rules' for the transitions here - does it convert horizontal / vertical swipes past a touch slop into a transition?
jd...@google.com <jd...@google.com> #11
Thanks for the quick reply!
For your custom gesture handler, what are the 'rules' for the transitions here - does it convert horizontal / vertical swipes past a touch slop into a transition?
Yes that's exactly what it does, using awaitHorizontalTouchSlopOrCancellation
and awaitVerticalTouchSlopOrCancellation
:-) We had to fork Draggable
because we also needed to support multiple fingers down. See the code in
jd...@google.com <jd...@google.com> #12
@lpf any update on this by any chance? :-)
lp...@google.com <lp...@google.com> #13
jd...@google.com <jd...@google.com> #14
Any news? :-) Pinging this again because this is preventing us from correctling measuring performance on some of our benchmarks: when the ripple is triggered during a transition, it's causing a lot of frames to be marked as dropped. See
lp...@google.com <lp...@google.com> #15
There's work planned in this space in our general tracker, but not at a high priority compared to other ongoing work.
For the benchmark case specifically, you could still end up in cases where the ripple is triggered if the gesture is 'slow' so that the move doesn't start until after the minimum press time: what specifically is your goal here for the benchmarks?
jd...@google.com <jd...@google.com> #16
what specifically is your goal here for the benchmarks?
I want the benchmark to be stable and have the least number of dropped frames. But we also want to make sure that in most cases this path is not triggered in production, so I don't want to just disable ripples in the benchmark either. Once we can disable the ripple inside our custom draggable, I'll make sure that the benchmark gesture is fast enough to not trigger the ripple.
jj...@google.com <jj...@google.com> #17
FYI this is real jank, not just showing up in the benchmark.
lp...@google.com <lp...@google.com> #18
I'll make sure that the benchmark gesture is fast enough to not trigger the ripple.
Hard to say without looking into some method traces for this, but it is likely that most of the cost isn't in drawing the ripple itself (because this is all done on the render thread), but in creating the ripple node in preparation to draw it. So even if you were to delay the interaction, it might still be slower regardless.
Do you have a link to the component with the ripple?
jj...@google.com <jj...@google.com> #19
updates. Some history here:
On Fri, May 24, 2024 at 6:21 PM lpf <buganizer-system+lpf@google.com> wrote:
lp...@google.com <lp...@google.com> #20
Not sure I understand the discussion in that bug, but it sounds like a framework issue on some API levels? Is there something actionable from the Compose side to mitigate this issue?
jj...@google.com <jj...@google.com> #21
Friendly ping on this - note this is a clear regression from the View framework. View's are able to handle this properly.
jd...@google.com <jd...@google.com> #22
To make sure there is no misunderstanding of the bug, here is an example that shows that the ripple is not triggered when dragging a clickable View
but that is is triggered when dragging a clickable Composable :-) See the video below.
import android.widget.Button
import androidx.compose.foundation.border
import androidx.compose.foundation.gestures.draggable2D
import androidx.compose.foundation.gestures.rememberDraggable2DState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round
import androidx.compose.ui.viewinterop.AndroidView
@Composable
fun DraggableDemo(modifier: Modifier = Modifier) {
Column(modifier.fillMaxSize()) {
var offset by remember { mutableStateOf(Offset.Zero) }
Box(
Modifier.offset { offset.round() }
.draggable2D(rememberDraggable2DState { offset += it })
.size(200.dp)
.border(1.dp, Color.Red),
contentAlignment = Alignment.Center,
) {
Column {
Button(onClick = {}) { Text("Compose button") }
AndroidView(factory = { Button(it).apply { text = "View button" } })
}
}
}
}
lp...@google.com <lp...@google.com> #23
For this issue specifically, all AndroidViews inside Compose are treated defensively as if they might scroll, so ripples will always be delayed (even if there is nothing scrollable in the hierarchy). In the future when we have public API we would implement this to follow the same logic used within compose.
For the compose case, we only apply this logic to scrollable() and other scrolling containers, not to draggable. If the 'issue' here is that you want the compose case to not instantly show a ripple, as a workaround if you add an empty scrollable with zero size somewhere in the hierarchy above the rippling component, it might work.
Note that this work is tracked in our backlog, but not being actively worked on / won't be for a minimum of a few months.
jd...@google.com <jd...@google.com> #24
For the compose case, we only apply this logic to scrollable() and other scrolling containers, not to draggable
Any chance we can have an early experimental version of a public API to be able to do the same in our custom pointer inputs/draggables?
lp...@google.com <lp...@google.com> #25
Current update:
There are some similar issues in the same problem space that we aim to solve together, rather than trying to patch over these issues with different hard-to-maintain APIs. Basically there's a general issue here of gesture coordination, where high level gestures (e.g 'drag') need some level of coordination between each other. This is true for this case, cases like handling touch slop for multiple lazy lists, etc.
The current loose idea is building some higher level abstraction around gestures, built on top of pointerInput. E.g you could imagine some Modifier.gesture() API, that allows for some higher level logic, and communication between gestures in a hierarchy. As mentioned previously this is on our backlog, and this should solve this specific bug too when we get around to implementing it
Description
In b/168524931 we globally added a delay before emitting a
PressInteraction
in case we are in a scrollable container, and in case the 'down' event is actually part of a scroll. This is important so that we don't show a ripple if the user is actually beginning a scroll motion.However, currently because we don't have any 'in scrollable container' metadata, this is globally enabled, whether or not we actually need to delay. This has the unfortunate effect of delaying
PressInteraction
s unnecessarily, and hence causing ripples and other indication to be delayed and feel laggy.With the recently added
ModifierLocal
APIs, we can now provide and consume this metadata, so we should now implement support for this so we only delayPressInteraction
s if necessary.Compose work:
ModifierLocal
to represent scrollable container metadataModifier.scrollable
- this is used in other APIs such as lazy lists andModifier.verticalScroll
, so just setting it here should cover all cases.PressInteraction
, so that we only delay if we are truly inside a scrollable containerInterop work:
ComposeView
is in a scrollable container, as even if there are no scrollable containers in the Compose part of the hierarchy, if theComposeView
itself is in a scrollableViewGroup
, we should still delay pressesAndroidViewHolder
should also consume this metadata and use it to overrideViewGroup#shouldDelayChildPressedState()
, so anyView
s hosted inside of Compose can also delay their pressed state if they are inside scrollable Compose containers, or inside non-scrollable Compose containers inside a non-scrollableAndroidView
inside a scrollable Compose container...Because the interop work requires APIs between
ui
andfoundation
(presumably theModifierLocal
needs to be public API inui
, so we can use it fromfoundation
), for now we can just ignore theAndroidViewHolder
support, and just queryLocalView
instead of setting this metadata at theComposeView
layer to unblock the most common cases.