Status Update
Comments
si...@google.com <si...@google.com> #2
We might just need to implement equals and hashcode for the modifier
si...@google.com <si...@google.com>
si...@google.com <si...@google.com> #3
Hi, I can't reproduce this behavior. It is possible that the composable in your code is recomposing because of a different reason. Is the tag you pass for example read from a class variable? That would cause your composable to be recomposed because the compiler cannot infer that all input to the testTag is @Stable.
The test below shows that adding testTag
does not prevent Compose to skip the composable:
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.width
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.produceState
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.delay
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class TestTagTest {
@get:Rule
val rule = createComposeRule()
@Stable
private class RecompositionCounter {
var withoutTag = 0
var withTag = 0
}
@Test
fun test() {
val counter = RecompositionCounter()
rule.setContent {
val state = produceState(initialValue = 0) {
repeat(10) {
delay(500)
value++
}
}
Column {
Text(text = "value: ${state.value}")
MyComponent(modifier = Modifier.width(200.dp), "Data1", counter)
MyComponent(modifier = Modifier.width(200.dp).testTag("Data2"), "Data2", counter)
}
}
repeat(10) {
rule.waitForIdle()
rule.mainClock.advanceTimeBy(500)
}
rule.onNodeWithText("value: 10").assertExists()
assertThat(counter.withoutTag).isEqualTo(1)
assertThat(counter.withTag).isEqualTo(1)
}
@Composable
private fun MyComponent(modifier: Modifier = Modifier, text: String, rc: RecompositionCounter) {
if (text == "Data1") {
rc.withoutTag++
} else if (text == "Data2") {
rc.withTag++
}
Text(modifier = modifier, text = text)
}
}
mo...@google.com <mo...@google.com> #4
Hi,
I have change the code a bit so I can use it outside a test on a ComponentActivity.
Here the code:
package de.telekom.testapp
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.width
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.delay
@Stable
private data class RecompositionCounter(
var withoutTag: Int = 0,
var withTag: Int = 0
)
@Composable
fun TestModifier() {
val counter = remember { RecompositionCounter() }
val state = produceState(initialValue = 0) {
repeat(10) {
delay(500)
value++
}
}
Column {
Text(text = "value: ${state.value} $counter")
MyComponent(modifier = Modifier.width(200.dp), "Data1", counter)
MyComponent(
modifier = Modifier
.width(200.dp)
.testTag("Data2"), "Data2", counter
)
}
}
@Composable
private fun MyComponent(modifier: Modifier = Modifier, text: String, rc: RecompositionCounter) {
if (text == "Data1") {
rc.withoutTag++
} else if (text == "Data2") {
rc.withTag++
}
Text(modifier = modifier, text = text)
}
And the counter "withTag" still increments. Attached you find a screenshot after 10 repeats.
si...@google.com <si...@google.com> #5
Additional info
compose version: '1.1.0-beta02' gradle: gradle-7.2-all Emulator: API 26 x86
si...@google.com <si...@google.com> #6
Hmm, this is odd. I can reproduce the bug when using the latest snapshot build of Compose from androidx.dev (
mo...@google.com <mo...@google.com> #7
Here is what happened:
The Live Literals feature in Android Studio switches out literals with state values. This will influence static inference by the compiler.
Without Live Literals on, the compiler will infer that Modifier.testTag("tag")
is only using static input and will not even call equals on it to verify if the value has changed (and consequently if parts of the code can be skipped); it knows that it will never change. With Live Literals on however, Modifier.testTag("tag")
is no longer considered static and during composition Modifier.testTag("tag") == Modifier.testTag("tag")
will be false, resulting in not skipping that part of the code.
This is a consequence of the underlying Modifier.composed(..)
, which is not equal to another Modifier.composed(..)
with exactly the same lambda.
For now, you can turn off Live Literals by adding @file:NoLiveLiterals
to the impacted files when benchmarking compositions. Also, in release builds Live Literals will always be turned off.
I filed
mo...@google.com <mo...@google.com> #8
In release builds or with @file:NoLiveLiterals it works.
Thanks for the details.
se...@google.com <se...@google.com> #9
Branch: androidx-main
commit 23ca74cafdbfa98898d8dfd5836ce81f02f48a59
Author: Alexandre Elias <aelias@google.com>
Date: Wed Jul 13 15:52:22 2022
Make Modifier.semantics stateless
This consolidates two other changes (
composed {} from the semantics modifier.
The semantics id is now generated lazily in the corresponding
LayoutNode. This allows us to create the semantics modifier without the
need for a composition context. In turn that means that semantics
modifiers are now comparable with each other, such that for example
Modifier.testTag("foo") == Modifier.testTag("foo").
When lambdas are part of the semantics, for example as an onClick
action, the semantics modifiers will still compare unequal though, even
when the lambdas do the exact same thing:
Modifier.semantics{onClick={true}} != Modifier.semantics{onClick={true}}
Bug: 203559524
Test: ./gradlew compose:ui:ui:test && \
./gradlew compose:ui:ui:cC
Relnote: "Deprecated `SemanticsModifier.id` and moved the semantics id
to `LayoutInfo.semanticsId` instead."
Change-Id: Iac808fc0e3ff14f1c1a95ee3f1f24cd436245a0e
M compose/ui/ui/api/restricted_current.ignore
M compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt
M compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt
M compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
M compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsModifier.kt
M compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextControllerTest.kt
M compose/ui/ui/api/current.txt
M compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/semantics/SemanticsTests.kt
M compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/SkiaBasedOwner.skiko.kt
M compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
M compose/ui/ui/api/current.ignore
M compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
M compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
M compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutInfo.kt
M compose/ui/ui/api/restricted_current.txt
M compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextSelectionLongPressDragTest.kt
M compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt
M compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsEntity.kt
M compose/ui/ui/api/public_plus_experimental_current.txt
se...@google.com <se...@google.com> #10
I didn't specifically test the testTag/skipping combination, but I think the above change probably fixed it in Compose 1.3. Feel free to reopen if it didn't.
ma...@google.com <ma...@google.com> #11
Coming back to this to give me 5 cents.
Modifier.scrollable
itself doesn't have any notion of layout at all, so I'm unsure if that would be the right place for it. Thing to node is that we also need to provide a proper nestedscroll support (+ account for nested scroll as well, as you want your toolbar to collapse if the textField is below the soft input, don't you?).
It seems like we would need to account for that in the scrollable implementations, e.g Modifier.verticalScroll
or LazyColumn
, as they also might want to process it differently. Some bits might live in the Modifier.scrollable
as well, but it's mostly a layout thing I suppose.
Another alternative: could it be possible to utilise semantics for this purpose? I don't know how idiomatic it would be but it seems like it is somewhat related. WE could basically ask whatever container to scroll to the bounds (which we could retrieve from the layout node) and the scroll bit will just work out of the box if the semantics are set right.
ra...@google.com <ra...@google.com>
ma...@google.com <ma...@google.com> #12
Ralston, let me know if you need any help or input from the scrolling side! Thanks!
ap...@google.com <ap...@google.com> #13
Branch: androidx-main
commit 421a738e446c6118de69765225a1508bdfd39372
Author: Ralston Da Silva <ralu@google.com>
Date: Wed May 05 06:33:28 2021
Golden images for RelocationRequester Modifier
This CL adds snapshots for the RelocationRequesterModifierTest
Bug: 178211874
Change-Id: I337d81b2176be1da4b7645b941c48c92e0d3075e
Test: ./gradlew compose:ui:ui:connectedCheck -P android.testInstrumentationRunnerArguments.class=androidx.compose.ui.layout.RelocationRequesterModifierTest
A compose/ui/ui/bringIntoParentBounds_blueBoxBottom_cuttlefish.png
A compose/ui/ui/bringIntoParentBounds_blueBoxCenterHorizontal_cuttlefish.png
A compose/ui/ui/bringIntoParentBounds_blueBoxCenterVertical_cuttlefish.png
A compose/ui/ui/bringIntoParentBounds_blueBoxLeft_cuttlefish.png
A compose/ui/ui/bringIntoParentBounds_blueBoxRight_cuttlefish.png
A compose/ui/ui/bringIntoParentBounds_blueBoxTop_cuttlefish.png
A compose/ui/ui/bringIntoParentBounds_blueBox_cuttlefish.png
A compose/ui/ui/bringIntoParentBounds_grayRectangleHorizontal_cuttlefish.png
A compose/ui/ui/bringIntoParentBounds_grayRectangleVertical_cuttlefish.png
ap...@google.com <ap...@google.com> #14
Branch: androidx-main
commit bd6db32649c6060ddead0d1f7e55636e60c9ff61
Author: Ralston Da Silva <ralu@google.com>
Date: Mon Apr 19 17:54:18 2021
Relocation Modifier
This is a modifier that can be used to bring an item into view by requesting that
all its scrollable parents scroll so that this item is visible.
Bug: 178211874
Test: ./gradlew compose:ui:ui:connectedCheck -P android.testInstrumentationRunnerArguments.class=androidx.compose.ui.layout.RelocationRequesterModifierTest
Relnote: Added Experimental Modifier.relocate()
Change-Id: I48322e5f84f5d688f92c7a397e9b1328b3824f63
M compose/ui/ui/api/1.0.0-beta08.txt
M compose/ui/ui/api/current.txt
M compose/ui/ui/api/public_plus_experimental_1.0.0-beta08.txt
M compose/ui/ui/api/public_plus_experimental_current.txt
M compose/ui/ui/api/restricted_1.0.0-beta08.txt
M compose/ui/ui/api/restricted_current.txt
M compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/UiDemos.kt
A compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/scroll/BringIntoParentBoundsDemo.kt
A compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/RelocationSamples.kt
A compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RelocationRequesterModifierTest.kt
A compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/RelocationRequester.kt
A compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/RelocationRequesterModifier.kt
ap...@google.com <ap...@google.com> #15
Branch: androidx-main
commit 42303ba13af7818755606f2a3ebf931914b2b50e
Author: Ralston Da Silva <ralu@google.com>
Date: Thu May 20 15:12:54 2021
Bring item into View
This CL renames RelocationRequester.bringIntoParentBounds() to RelocationRequester.bringIntoView.
It also supports the use-case where they keyobard could be hiding the element, and brings
it into view by calling view.requestRectangleOnScreen.
Bug: 178211874
Test: ./gradlew compose:ui:ui:connectedCheck -P android.testInstrumentationRunnerArguments.class=androidx.compose.ui.layout.RelocationRequesterModifierTest
Relnote: N/A
Change-Id: I4124888de3b67fda965372380b5a166406817ce1
M compose/ui/ui/api/public_plus_experimental_1.0.0-beta08.txt
M compose/ui/ui/api/public_plus_experimental_current.txt
M compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/UiDemos.kt
M compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/scroll/BringIntoViewDemo.kt
A compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/scroll/RequestRectangleOnScreeenDemo.kt
M compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/RelocationSamples.kt
M compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt
M compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RelocationRequesterModifierTest.kt
M compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
M compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/RelocationRequester.kt
M compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/RelocationRequesterModifier.kt
M compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt
M compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwner.desktop.kt
M compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
ni...@google.com <ni...@google.com> #16
Bulk edit: This bug is currently marked as blocking Compose 1.0. Once rc01 is cut, all changes will need to go through our cherry-pick process.
If you intend to fix it in Compose 1.0, no action is needed except prioritizing finishing this work.
Otherwise, please remove this bug from the appropriate 1.0 hotlist (likely
Thanks!
ra...@google.com <ra...@google.com> #17
For this fix we need TextField to use RelocationRequester.bringIntoView() which is an experimental UI API, and it so it can't be fixed for Compose 1.0
ap...@google.com <ap...@google.com> #18
Branch: androidx-main
commit e40d3772b9a36196f94dfaef0483e3fc52951b53
Author: Ralston Da Silva <ralu@google.com>
Date: Fri Jun 04 18:02:01 2021
Implement bringIntoView
We initially tried to implement bringIntoView by making changes
to nested scroll. This CL adds a Modifier.onRelocationRequest
that responds to requests from Modifier.relocationRequester
more details at go/compose-bringIntoView
Bug: 178211874
Test: ./gradlew compose:ui:ui:connectedCheck -P android.testInstrumentationRunnerArguments.class=androidx.compose.ui.layout.RelocationModifierTest
Relnote: Added experimental modifier to handle relocation requests.
Change-Id: I65a97bd7fca7271781efe31fcc9cb387e9857b51
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
M compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/UiDemos.kt
M compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/scroll/BringIntoViewDemo.kt
M compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/scroll/RequestRectangleOnScreeenDemo.kt
M compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/RelocationSamples.kt
M compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInputEventProcessorTest.kt
A compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RelocationModifierTest.kt
M compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RelocationRequesterModifierTest.kt
M compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
A compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/RelocationModifier.kt
M compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/RelocationRequester.kt
M compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/RelocationRequesterModifier.kt
M compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
M compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeWrapper.kt
A compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedRelocationNode.kt
A compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedRelocationRequesterNode.kt
M compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt
M compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopOwner.desktop.kt
M compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
si...@google.com <si...@google.com>
so...@google.com <so...@google.com> #19
ap...@google.com <ap...@google.com> #20
Branch: androidx-main
commit a5c92f31181087df9a6a7100111d4248b94e75d4
Author: Zach Klippenstein <klippenstein@google.com>
Date: Fri Jan 28 12:36:11 2022
Change CoreTextField to use BringIntoViewRequester.
CoreTextField needs to ensure the cursor is in the visible bounds of all
scrollable parents, including the one in the text field itself.
Originally, it tried doing this by going through some of the text
service abstractions and ultimately calling
`View.requestRectangleOnScreen`. However, composable parents also need
to be aware of the request, so the correct way to do this is to use the
relatively recent `BringIntoViewRequester`. I'm not sure why all the
indirection through text services was done, it isn't necessary and
obscures the code, so I just made `CoreTextField` directly call
`BringIntoViewRequester.bringIntoView()`. This helps with
but doesn't completely fix it, due to
Bug:
Fixes:
Test: Tested manually.
Test: Refactored TextFieldDelegate unit tests to use the new code path:
./gradlew :compose:f:f:test
Relnote: "`notifyFocusedRect` methods in `TextInputSession` and
`TextInputService` are now deprecated and won't be called. Use
`BringIntoViewRequester` instead."
Change-Id: Ia4302c5f6ee79eec30a9f42c149da8775e1ed57e
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Focusable.kt
M compose/ui/ui-text/api/restricted_current.txt
M compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/text/TextFieldToggleTextTestCase.kt
M compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.android.kt
A compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextFieldBringIntoViewTest.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/TextFieldDelegate.kt
M compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/TextInputService.kt
M compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
M compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextFieldDelegateTest.kt
M compose/ui/ui-text/api/public_plus_experimental_current.txt
M compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextInputServiceTest.kt
M compose/ui/ui-text/api/current.txt
ap...@google.com <ap...@google.com> #21
Branch: androidx-main
commit b47ac54b27a3e77b40f672cbcd5a713f464a4d27
Author: Zach Klippenstein <klippenstein@google.com>
Date: Thu Feb 03 12:09:04 2022
Revert removing implementations of notifyFocusedRect.
The notifyFocusedRect methods on TextInputService and TextInputSession
were deprecated and had their implementations replaced by no-ops in the
change aosp/1959647. This was probably too aggressive, so this change
adds the old implementations back in but leaves them deprecated, in case
any third-party code was calling them. The implementations were actually
broken before, and this change doesn't try to fix them, it just restores
the previous broken behavior.
Discussion thread about revert:
Bug:
Bug:
Test: Old tests restored.
Change-Id: I385749b629fd35b72bd148122ab1ec0f6d4ffa96
M compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/TextInputService.kt
M compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.android.kt
M compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextInputServiceTest.kt
da...@gmail.com <da...@gmail.com> #22
Is there something that has to be done from the developer perspective for the textfield to scroll into view when the keyboard comes up to cover them?
Description
What the function does is:
- calls requestRectangle on View.
Therefore it is something that we can bubble up from current node to parents and then the View system until the provided area is visible on screen.
Not sure how compose scrollers would react to this and if it is possible.
Related tickets