Fixed
Status Update
Comments
je...@google.com <je...@google.com> #2
I believe inheritance will be supported long-term, but is not yet. The error is probably expected to fire in both A and B in the near-term. Chuck can confirm
je...@google.com <je...@google.com>
kl...@google.com <kl...@google.com> #3
1. We plan to support inheritance but we don't yet.
2. A is not reported nor is A transformed to add @Model behavior. This is a bug. We should report A as and error as well and, eventually, both should work and produce the classes with the expected semantics.
2. A is not reported nor is A transformed to add @Model behavior. This is a bug. We should report A as and error as well and, eventually, both should work and produce the classes with the expected semantics.
kl...@google.com <kl...@google.com>
kl...@google.com <kl...@google.com> #4
Project: platform/frameworks/support
Branch: androidx-master-dev
commit fcf76b3241844f18106d6e850004952046d4bfb6
Author: Leland Richardson <lelandr@google.com>
Date: Wed May 13 16:58:59 2020
Deprecate @Model
Relnote: “
@Model annotation is now deprecated. Use state and mutableStateOf as alternatives. This deprecation decision was reached after much careful discussion.
Justification
=============
Rationale includes but is not limited to:
- Reduces API surface area and concepts we need to teach
- More closely aligns with other comparable toolkits (Swift UI, React, Flutter)
- Reversible decision. We can always bring @Model back later.
- Removes corner-case usage and difficult to answer questions about configuring @Model as things we need to handle
- @Model data classes, equals, hashcode, etc.
- How do I have some properties “observed” and others not?
- How do I specify structural vs. referential equality to be used in observation?
- Reduces “magic” in the system. Would reduce the likelihood of someone assuming system was smarter than it is (ie, it knowing how to diff a list)
- Makes the granularity of observation more intuitive.
- Improves refactorability from variable -> property on class
- Potentially opens up possibilities to do hand-crafted State-specific optimizations
- More closely aligns with the rest of the ecosystem and reduces ambiguity towards immutable or us “embracing mutable state”
Migration Notes
===============
Almost all existing usages of @Model are fairly trivially transformed in one of two ways. The example below has a @Model class with two properties just for the sake of example, and has it being used in a composable.
```
@Model class Position(
var x: Int,
var y: Int
)
@Composable fun Example() {
var p = remember { Position(0, 0) }
PositionChanger(
position=p,
onXChange={ p.x = it }
onYChange={ p.y = it }
)
}
```
Alternative 1: Use State<OriginalClass> and create copies.
----------------------------------------------------------
This approach is made easier with Kotlin’s data classes. Essentially, make all previously `var` properties into `val` properties of a data class, and then use `state` instead of `remember`, and assign the state value to cloned copies of the original using the data class `copy(...)` convenience method.
It’s important to note that this approach only works when the only mutations to that class were done in the same scope that the `State` instance is created. If the class is internally mutating itself outside of the scope of usage, and you are relying on the observation of that, then the next approach is the one you will want to use.
```
data class Position(
val x: Int,
val y: Int
)
@Composable fun Example() {
var p by state { Position(0, 0) }
PositionChanger(
position=p,
onXChange={ p = p.copy(x=it) }
onYChange={ p = p.copy(y=it) }
)
}
```
Alternative 2: Use mutableStateOf and property delegates
--------------------------------------------------------
This approach is made easier with Kotlin’s property delegates and the `mutableStateOf` API which allows you to create MutableState instances outside of composition. Essentially, replace all `var` properties of the original class with `var` properties with `mutableStateOf` as their property delegate. This has the advantage that the usage of the class will not change at all, only the internal implementation of it. The behavior is not completely identical to the original example though, as each property is now observed/subscribed to individually, so the recompositions you see after this refactor could be more narrow (a good thing).
```
class Position(x: Int, y: Int) {
var x by mutableStateOf(x)
var y by mutableStateOf(y)
}
// source of Example is identical to original
@Composable fun Example() {
var p = remember { Position(0, 0) }
PositionChanger(
position=p,
onXChange={ p.x = it }
onYChange={ p.y = it }
)
}
```
“
Bug: 156546430
Bug: 152993135
Bug: 152050010
Bug: 148866188
Bug: 148422703
Bug: 148394427
Bug: 146362815
Bug: 146342522
Bug: 143413369
Bug: 135715219
Bug: 126418732
Bug: 147088098
Bug: 143263925
Bug: 139653744
Change-Id: I409e8c158841eae1dd548b33f1ec80bb609cba31
M compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/frames/FrameDiagnosticTests.kt
M compose/compose-runtime/api/0.1.0-dev12.txt
M compose/compose-runtime/api/current.txt
M compose/compose-runtime/api/public_plus_experimental_0.1.0-dev12.txt
M compose/compose-runtime/api/public_plus_experimental_current.txt
M compose/compose-runtime/api/restricted_0.1.0-dev12.txt
M compose/compose-runtime/api/restricted_current.txt
M compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/ComposeBenchmark.kt
M compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/SiblingBenchmark.kt
M compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/dbmonster/DbMonster.kt
M compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/realworld4/RealWorld4_DataModels.kt
M compose/compose-runtime/samples/src/main/java/androidx/compose/samples/ModelSamples.kt
M compose/compose-runtime/src/commonMain/kotlin/androidx/compose/FrameManager.kt
M compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Model.kt
M compose/compose-runtime/src/commonMain/kotlin/androidx/compose/MutableState.kt
M compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Observe.kt
M compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Recompose.kt
M compose/compose-runtime/src/commonMain/kotlin/androidx/compose/StableMarker.kt
M ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/ModelObserverBenchmark.kt
M ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/RadioGroupBenchmark.kt
M ui/integration-tests/demos/src/main/java/androidx/ui/demos/DemoColorPalette.kt
M ui/integration-tests/src/main/java/androidx/ui/integration/test/foundation/RectsInColumnSharedModelTestCase.kt
M ui/ui-android-view/integration-tests/android-view-demos/src/main/java/androidx/ui/androidview/demos/WebComponentActivity.kt
M ui/ui-animation/src/main/java/androidx/ui/animation/AnimatedValueEffects.kt
M ui/ui-animation/src/main/java/androidx/ui/animation/Transition.kt
M ui/ui-core/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt
M ui/ui-core/src/androidTest/java/androidx/ui/core/test/AndroidViewCompatTest.kt
M ui/ui-core/src/androidTest/java/androidx/ui/core/test/ClipTest.kt
M ui/ui-core/src/androidTest/java/androidx/ui/core/test/DrawShadowTest.kt
M ui/ui-core/src/androidTest/java/androidx/ui/core/test/ModelReadsTest.kt
M ui/ui-core/src/androidTest/java/androidx/ui/core/test/OpacityTest.kt
D ui/ui-core/src/androidTest/java/androidx/ui/core/test/ValueModel.kt
M ui/ui-core/src/androidTest/java/androidx/ui/core/test/WithConstraintsTest.kt
M ui/ui-core/src/androidTest/java/androidx/ui/res/ResourcesTest.kt
M ui/ui-core/src/androidTest/java/androidx/ui/semantics/SemanticsTests.kt
M ui/ui-core/src/main/java/androidx/ui/core/ModelObserver.kt
M ui/ui-core/src/main/java/androidx/ui/res/Resources.kt
M ui/ui-core/src/test/java/androidx/ui/core/selection/SelectionManagerDragTest.kt
M ui/ui-core/src/test/java/androidx/ui/core/selection/SelectionManagerLongPressDragTest.kt
M ui/ui-core/src/test/java/androidx/ui/core/selection/SelectionManagerTest.kt
M ui/ui-foundation/src/androidTest/java/androidx/ui/foundation/DeterminateProgressTest.kt
M ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/ContainerTest.kt
M ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/OnPositionedTest.kt
M ui/ui-material/api/0.1.0-dev12.txt
M ui/ui-material/api/current.txt
M ui/ui-material/api/public_plus_experimental_0.1.0-dev12.txt
M ui/ui-material/api/public_plus_experimental_current.txt
M ui/ui-material/api/restricted_0.1.0-dev12.txt
M ui/ui-material/api/restricted_current.txt
M ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/DynamicThemeActivity.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/DrawerTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/ProgressIndicatorTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/RadioGroupUiTest.kt
M ui/ui-material/src/main/java/androidx/ui/material/Color.kt
M ui/ui-material/src/main/java/androidx/ui/material/Scaffold.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/AndroidComposeTestCaseRunnerTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/MultipleComposeRootsTest.kt
M ui/ui-text/src/main/java/androidx/ui/text/CoreText.kt
M ui/ui-text/src/main/java/androidx/ui/text/CoreTextField.kt
https://android-review.googlesource.com/1311293
Branch: androidx-master-dev
commit fcf76b3241844f18106d6e850004952046d4bfb6
Author: Leland Richardson <lelandr@google.com>
Date: Wed May 13 16:58:59 2020
Deprecate @Model
Relnote: “
@Model annotation is now deprecated. Use state and mutableStateOf as alternatives. This deprecation decision was reached after much careful discussion.
Justification
=============
Rationale includes but is not limited to:
- Reduces API surface area and concepts we need to teach
- More closely aligns with other comparable toolkits (Swift UI, React, Flutter)
- Reversible decision. We can always bring @Model back later.
- Removes corner-case usage and difficult to answer questions about configuring @Model as things we need to handle
- @Model data classes, equals, hashcode, etc.
- How do I have some properties “observed” and others not?
- How do I specify structural vs. referential equality to be used in observation?
- Reduces “magic” in the system. Would reduce the likelihood of someone assuming system was smarter than it is (ie, it knowing how to diff a list)
- Makes the granularity of observation more intuitive.
- Improves refactorability from variable -> property on class
- Potentially opens up possibilities to do hand-crafted State-specific optimizations
- More closely aligns with the rest of the ecosystem and reduces ambiguity towards immutable or us “embracing mutable state”
Migration Notes
===============
Almost all existing usages of @Model are fairly trivially transformed in one of two ways. The example below has a @Model class with two properties just for the sake of example, and has it being used in a composable.
```
@Model class Position(
var x: Int,
var y: Int
)
@Composable fun Example() {
var p = remember { Position(0, 0) }
PositionChanger(
position=p,
onXChange={ p.x = it }
onYChange={ p.y = it }
)
}
```
Alternative 1: Use State<OriginalClass> and create copies.
----------------------------------------------------------
This approach is made easier with Kotlin’s data classes. Essentially, make all previously `var` properties into `val` properties of a data class, and then use `state` instead of `remember`, and assign the state value to cloned copies of the original using the data class `copy(...)` convenience method.
It’s important to note that this approach only works when the only mutations to that class were done in the same scope that the `State` instance is created. If the class is internally mutating itself outside of the scope of usage, and you are relying on the observation of that, then the next approach is the one you will want to use.
```
data class Position(
val x: Int,
val y: Int
)
@Composable fun Example() {
var p by state { Position(0, 0) }
PositionChanger(
position=p,
onXChange={ p = p.copy(x=it) }
onYChange={ p = p.copy(y=it) }
)
}
```
Alternative 2: Use mutableStateOf and property delegates
--------------------------------------------------------
This approach is made easier with Kotlin’s property delegates and the `mutableStateOf` API which allows you to create MutableState instances outside of composition. Essentially, replace all `var` properties of the original class with `var` properties with `mutableStateOf` as their property delegate. This has the advantage that the usage of the class will not change at all, only the internal implementation of it. The behavior is not completely identical to the original example though, as each property is now observed/subscribed to individually, so the recompositions you see after this refactor could be more narrow (a good thing).
```
class Position(x: Int, y: Int) {
var x by mutableStateOf(x)
var y by mutableStateOf(y)
}
// source of Example is identical to original
@Composable fun Example() {
var p = remember { Position(0, 0) }
PositionChanger(
position=p,
onXChange={ p.x = it }
onYChange={ p.y = it }
)
}
```
“
Bug: 156546430
Bug: 152993135
Bug: 152050010
Bug: 148866188
Bug: 148422703
Bug: 148394427
Bug: 146362815
Bug: 146342522
Bug: 143413369
Bug: 135715219
Bug: 126418732
Bug: 147088098
Bug: 143263925
Bug: 139653744
Change-Id: I409e8c158841eae1dd548b33f1ec80bb609cba31
M compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/frames/FrameDiagnosticTests.kt
M compose/compose-runtime/api/0.1.0-dev12.txt
M compose/compose-runtime/api/current.txt
M compose/compose-runtime/api/public_plus_experimental_0.1.0-dev12.txt
M compose/compose-runtime/api/public_plus_experimental_current.txt
M compose/compose-runtime/api/restricted_0.1.0-dev12.txt
M compose/compose-runtime/api/restricted_current.txt
M compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/ComposeBenchmark.kt
M compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/SiblingBenchmark.kt
M compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/dbmonster/DbMonster.kt
M compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/realworld4/RealWorld4_DataModels.kt
M compose/compose-runtime/samples/src/main/java/androidx/compose/samples/ModelSamples.kt
M compose/compose-runtime/src/commonMain/kotlin/androidx/compose/FrameManager.kt
M compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Model.kt
M compose/compose-runtime/src/commonMain/kotlin/androidx/compose/MutableState.kt
M compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Observe.kt
M compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Recompose.kt
M compose/compose-runtime/src/commonMain/kotlin/androidx/compose/StableMarker.kt
M ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/ModelObserverBenchmark.kt
M ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/RadioGroupBenchmark.kt
M ui/integration-tests/demos/src/main/java/androidx/ui/demos/DemoColorPalette.kt
M ui/integration-tests/src/main/java/androidx/ui/integration/test/foundation/RectsInColumnSharedModelTestCase.kt
M ui/ui-android-view/integration-tests/android-view-demos/src/main/java/androidx/ui/androidview/demos/WebComponentActivity.kt
M ui/ui-animation/src/main/java/androidx/ui/animation/AnimatedValueEffects.kt
M ui/ui-animation/src/main/java/androidx/ui/animation/Transition.kt
M ui/ui-core/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt
M ui/ui-core/src/androidTest/java/androidx/ui/core/test/AndroidViewCompatTest.kt
M ui/ui-core/src/androidTest/java/androidx/ui/core/test/ClipTest.kt
M ui/ui-core/src/androidTest/java/androidx/ui/core/test/DrawShadowTest.kt
M ui/ui-core/src/androidTest/java/androidx/ui/core/test/ModelReadsTest.kt
M ui/ui-core/src/androidTest/java/androidx/ui/core/test/OpacityTest.kt
D ui/ui-core/src/androidTest/java/androidx/ui/core/test/ValueModel.kt
M ui/ui-core/src/androidTest/java/androidx/ui/core/test/WithConstraintsTest.kt
M ui/ui-core/src/androidTest/java/androidx/ui/res/ResourcesTest.kt
M ui/ui-core/src/androidTest/java/androidx/ui/semantics/SemanticsTests.kt
M ui/ui-core/src/main/java/androidx/ui/core/ModelObserver.kt
M ui/ui-core/src/main/java/androidx/ui/res/Resources.kt
M ui/ui-core/src/test/java/androidx/ui/core/selection/SelectionManagerDragTest.kt
M ui/ui-core/src/test/java/androidx/ui/core/selection/SelectionManagerLongPressDragTest.kt
M ui/ui-core/src/test/java/androidx/ui/core/selection/SelectionManagerTest.kt
M ui/ui-foundation/src/androidTest/java/androidx/ui/foundation/DeterminateProgressTest.kt
M ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/ContainerTest.kt
M ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/OnPositionedTest.kt
M ui/ui-material/api/0.1.0-dev12.txt
M ui/ui-material/api/current.txt
M ui/ui-material/api/public_plus_experimental_0.1.0-dev12.txt
M ui/ui-material/api/public_plus_experimental_current.txt
M ui/ui-material/api/restricted_0.1.0-dev12.txt
M ui/ui-material/api/restricted_current.txt
M ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/DynamicThemeActivity.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/DrawerTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/ProgressIndicatorTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/RadioGroupUiTest.kt
M ui/ui-material/src/main/java/androidx/ui/material/Color.kt
M ui/ui-material/src/main/java/androidx/ui/material/Scaffold.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/AndroidComposeTestCaseRunnerTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/MultipleComposeRootsTest.kt
M ui/ui-text/src/main/java/androidx/ui/text/CoreText.kt
M ui/ui-text/src/main/java/androidx/ui/text/CoreTextField.kt
kl...@google.com <kl...@google.com> #5
ap...@google.com <ap...@google.com> #6
Project: platform/frameworks/support
Branch: androidx-main
commit 98f2641f492c7ffc91e77e39d60b77909669f0ea
Author: Zach Klippenstein <klippenstein@google.com>
Date: Wed Nov 09 16:48:00 2022
Send apply notifications after Recomposer finishes frame.
This fixes a bug where updating the intrinsics changes would be delayed
a frame. Changes to the intrinsics measure policy in a
layout node during apply changes was not applied until _after_ the
layout pass, which might result in not invalidating layout that should
have been that frame. And since those changes would only be applied for
the next frame, it would invalidate that frame unnecessarily. In other
words, intrinsics changes would be delayed a frame.
This is a prerequisite for b/222093277 (aosp/2243539).
Test: RemeasureWithIntrinsicsRealClockTest (the current test does not
catch this bug because of b/222093277 ).
Test: RecomposerTests
Relnote: "Snapshot apply notifications are now sent after the
`Recomposer` finishes applying changes."
Change-Id: Iad6c0dcd163a5a8f9c5aec426da3d4f701ca509f
M compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
M compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionTests.kt
M compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/EffectsTests.kt
M compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/RecomposerTests.kt
A compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RemeasureWithIntrinsicsRealClockTest.kt
M compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RemeasureWithIntrinsicsTest.kt
https://android-review.googlesource.com/2296013
Branch: androidx-main
commit 98f2641f492c7ffc91e77e39d60b77909669f0ea
Author: Zach Klippenstein <klippenstein@google.com>
Date: Wed Nov 09 16:48:00 2022
Send apply notifications after Recomposer finishes frame.
This fixes a bug where updating the intrinsics changes would be delayed
a frame. Changes to the intrinsics measure policy in a
layout node during apply changes was not applied until _after_ the
layout pass, which might result in not invalidating layout that should
have been that frame. And since those changes would only be applied for
the next frame, it would invalidate that frame unnecessarily. In other
words, intrinsics changes would be delayed a frame.
This is a prerequisite for
Test: RemeasureWithIntrinsicsRealClockTest (the current test does not
catch this bug because of
Test: RecomposerTests
Relnote: "Snapshot apply notifications are now sent after the
`Recomposer` finishes applying changes."
Change-Id: Iad6c0dcd163a5a8f9c5aec426da3d4f701ca509f
M compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
M compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionTests.kt
M compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/EffectsTests.kt
M compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/RecomposerTests.kt
A compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RemeasureWithIntrinsicsRealClockTest.kt
M compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/RemeasureWithIntrinsicsTest.kt
ap...@google.com <ap...@google.com> #7
Project: platform/frameworks/support
Branch: androidx-main
commit f61c557f4526dfa0e33d070de9c262e788184e8a
Author: Zach Klippenstein <klippenstein@google.com>
Date: Thu Oct 20 10:33:11 2022
Defer dispatching coroutines resumed during animation callbacks in UI tests.
This is a workaround for the fact that we use an unconfined dispatcher
in UI tests ( b/254115946 ), which prevents us from being able to perform
layout passes for frames in some cases ( b/222093277 ).
TestMonotonicFrameClock gets a continuation interceptor which wraps continuations with
behavior that will usually no-op, but if the continuation is resumed
while frame callbacks are running, it will defer resuming the
underlying continuation until all the frame callbacks have finished.
This matches how continuations are dispatched in non-test code, since
that code does not use an unconfined dispatcher.
In order to preserve test delay skipping, the interceptor implements
Delay and delegates it to the underlying dispatcher, just like
ApplyingContinuationInterceptor is already doing.
This CL also adds a callback that runs after each frame in
TestMonotonicFrameClock. This change was originally in aosp/2121435.
This allows us to perform measure and layout after each frame if
necessary, which aligns the measure/layout timing with production.
Bug: b/254115946
Bug: b/222093277
Bug: b/255802670
Test: PhaseOrderingTest, TestMonotonicFrameClockTest
Relnote: "In UI tests using a Compose rule, continuations resumed during
`withFrameNanos` callbacks will not be dispatched until after all
frame callbacks have finished running. This matches the behavior of
compose when running normally. However, tests that rely on the old
behavior may fail. This should only affect code that calls
`withFrameNanos` or `withFrameMillis` directly, and has logic
outside of callback passed to those functions that may need to be
moved inside the callbacks. See the animation test changes in this
CL for examples."
Relnote: "Added optional `onPerformTraversals: (Long) -> Unit` parameter to
`TestMonotonicFrameClock` constructor and factory function to run
code after `withFrameNanos` callbacks but before resuming callers'
coroutines."
Change-Id: Idb41309445a030c91e8e4ae05daa9642b450505c
M compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/SingleValueAnimationTest.kt
M compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/AnimatedContentTest.kt
M compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/CrossfadeTest.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableFocusableInteractionTest.kt
M compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunner.android.kt
M compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/ComposeUiTest.android.kt
M compose/ui/ui-test-junit4/src/commonMain/kotlin/androidx/compose/ui/test/ApplyingContinuationInterceptor.kt
M compose/ui/ui-test/api/public_plus_experimental_current.txt
M compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/PhaseOrderingTest.kt
M compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/TestMonotonicFrameClockTest.kt
A compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/internal/DelayPropagatingContinuationInterceptorWrapper.kt
A compose/ui/ui-test/src/jvmMain/kotlin/androidx/compose/ui/test/FrameDeferringContinuationInterceptor.kt
M compose/ui/ui-test/src/jvmMain/kotlin/androidx/compose/ui/test/TestMonotonicFrameClock.jvm.kt
https://android-review.googlesource.com/2260389
Branch: androidx-main
commit f61c557f4526dfa0e33d070de9c262e788184e8a
Author: Zach Klippenstein <klippenstein@google.com>
Date: Thu Oct 20 10:33:11 2022
Defer dispatching coroutines resumed during animation callbacks in UI tests.
This is a workaround for the fact that we use an unconfined dispatcher
in UI tests (
layout passes for frames in some cases (
TestMonotonicFrameClock gets a continuation interceptor which wraps continuations with
behavior that will usually no-op, but if the continuation is resumed
while frame callbacks are running, it will defer resuming the
underlying continuation until all the frame callbacks have finished.
This matches how continuations are dispatched in non-test code, since
that code does not use an unconfined dispatcher.
In order to preserve test delay skipping, the interceptor implements
Delay and delegates it to the underlying dispatcher, just like
ApplyingContinuationInterceptor is already doing.
This CL also adds a callback that runs after each frame in
TestMonotonicFrameClock. This change was originally in aosp/2121435.
This allows us to perform measure and layout after each frame if
necessary, which aligns the measure/layout timing with production.
Bug:
Bug:
Bug:
Test: PhaseOrderingTest, TestMonotonicFrameClockTest
Relnote: "In UI tests using a Compose rule, continuations resumed during
`withFrameNanos` callbacks will not be dispatched until after all
frame callbacks have finished running. This matches the behavior of
compose when running normally. However, tests that rely on the old
behavior may fail. This should only affect code that calls
`withFrameNanos` or `withFrameMillis` directly, and has logic
outside of callback passed to those functions that may need to be
moved inside the callbacks. See the animation test changes in this
CL for examples."
Relnote: "Added optional `onPerformTraversals: (Long) -> Unit` parameter to
`TestMonotonicFrameClock` constructor and factory function to run
code after `withFrameNanos` callbacks but before resuming callers'
coroutines."
Change-Id: Idb41309445a030c91e8e4ae05daa9642b450505c
M compose/animation/animation-core/src/androidAndroidTest/kotlin/androidx/compose/animation/core/SingleValueAnimationTest.kt
M compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/AnimatedContentTest.kt
M compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/CrossfadeTest.kt
M compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableFocusableInteractionTest.kt
M compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunner.android.kt
M compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/ComposeUiTest.android.kt
M compose/ui/ui-test-junit4/src/commonMain/kotlin/androidx/compose/ui/test/ApplyingContinuationInterceptor.kt
M compose/ui/ui-test/api/public_plus_experimental_current.txt
M compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/PhaseOrderingTest.kt
M compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/TestMonotonicFrameClockTest.kt
A compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/internal/DelayPropagatingContinuationInterceptorWrapper.kt
A compose/ui/ui-test/src/jvmMain/kotlin/androidx/compose/ui/test/FrameDeferringContinuationInterceptor.kt
M compose/ui/ui-test/src/jvmMain/kotlin/androidx/compose/ui/test/TestMonotonicFrameClock.jvm.kt
kl...@google.com <kl...@google.com> #8
Since this bug is in the test runtime, you might expect that fixing it would expose actual bugs that tests weren't catching, because they weren't able to. And it did!
- Coroutine continuations were being dispatched immediately even during frame callbacks (
,b/254115946 , aosp/2260389). This was a bug in the test runtime itself, but it's a bug that even external Compose tests would hit so it's still a production bug in a sense.b/255802670 - Intrinsics were writing a state object in applyChanges that was not getting picked up until the next frame, this was a bug in
Recomposer
(aosp/2296013). - The
ConstraintLayout
composable was triggering an additional, unnecessary recomposition whenever a helper changed ( , aosp/2319364).b/261270254
ap...@google.com <ap...@google.com> #9
Project: platform/frameworks/support
Branch: androidx-main
commit 9f9434a17870acd76c6c692dab9ffa425d1eab04
Author: Zach Klippenstein <klippenstein@google.com>
Date: Sat Dec 03 08:00:35 2022
Fix ConstraintLayout causing extra recomposition when helper changes.
Fixes: b/261270254
Bug: b/222093277
Test: ConstraintLayoutTest
Relnote: "Fixed issue where ConstraintLayout always triggered an
unnecessary extra recomposition when helpers were changed."
Change-Id: Id83ada3e963b20180c324f25b6db7c53f810463f
M constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/ConstraintLayoutTest.kt
M constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayout.kt
https://android-review.googlesource.com/2319364
Branch: androidx-main
commit 9f9434a17870acd76c6c692dab9ffa425d1eab04
Author: Zach Klippenstein <klippenstein@google.com>
Date: Sat Dec 03 08:00:35 2022
Fix ConstraintLayout causing extra recomposition when helper changes.
Fixes:
Bug:
Test: ConstraintLayoutTest
Relnote: "Fixed issue where ConstraintLayout always triggered an
unnecessary extra recomposition when helpers were changed."
Change-Id: Id83ada3e963b20180c324f25b6db7c53f810463f
M constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/ConstraintLayoutTest.kt
M constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayout.kt
ap...@google.com <ap...@google.com> #10
Project: platform/frameworks/support
Branch: androidx-main
commit 0253b7d395277c6a4669cbcb1ff7bdc5583cb6c6
Author: Zach Klippenstein <klippenstein@google.com>
Date: Tue Nov 22 14:33:04 2022
Make UI tests run layout passes between frames
Checks if a local measure/layout is scheduled after execution of each
frame. With a local measure/layout I mean a layout pass for which an
Android View layout pass has not been scheduled. It is local because it
only affects composables, and not any View outside the host View.
Fixes: b/222093277
Bug: b/219885899
Test: AnimatedVisibilityTest (new in ui-test-junit4)
Test: PhaseOrderingTest
Test: TestRuleExecutesLayoutPassesWhenWaitingForIdleTest
Test: BasicCurvedTextTest
Test: this change is also exercised by nearly all tests
Relnote: "Android Compose UI tests will now run layout passes for each frame when
executing frames to get to idle (e.g. via `waitForIdle`). This may
affect tests that assert on individual frames of layout animations."
Change-Id: I8ea08728a395665f9ec7965579797e537b2c35e5
M compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BackdropScaffoldTest.kt
M compose/ui/ui-test-junit4/build.gradle
M compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/ComposeUiTest.android.kt
M compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/PhaseOrderingTest.kt
M compose/ui/ui/api/public_plus_experimental_current.txt
A compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/TestRuleExecutesLayoutPassesWhenWaitingForIdleTest.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/node/RootForTest.kt
M wear/compose/compose-foundation/src/androidAndroidTest/kotlin/androidx/wear/compose/foundation/BasicCurvedTextTest.kt
https://android-review.googlesource.com/2243539
Branch: androidx-main
commit 0253b7d395277c6a4669cbcb1ff7bdc5583cb6c6
Author: Zach Klippenstein <klippenstein@google.com>
Date: Tue Nov 22 14:33:04 2022
Make UI tests run layout passes between frames
Checks if a local measure/layout is scheduled after execution of each
frame. With a local measure/layout I mean a layout pass for which an
Android View layout pass has not been scheduled. It is local because it
only affects composables, and not any View outside the host View.
Fixes:
Bug:
Test: AnimatedVisibilityTest (new in ui-test-junit4)
Test: PhaseOrderingTest
Test: TestRuleExecutesLayoutPassesWhenWaitingForIdleTest
Test: BasicCurvedTextTest
Test: this change is also exercised by nearly all tests
Relnote: "Android Compose UI tests will now run layout passes for each frame when
executing frames to get to idle (e.g. via `waitForIdle`). This may
affect tests that assert on individual frames of layout animations."
Change-Id: I8ea08728a395665f9ec7965579797e537b2c35e5
M compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BackdropScaffoldTest.kt
M compose/ui/ui-test-junit4/build.gradle
M compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/ComposeUiTest.android.kt
M compose/ui/ui-test/src/androidAndroidTest/kotlin/androidx/compose/ui/test/PhaseOrderingTest.kt
M compose/ui/ui/api/public_plus_experimental_current.txt
A compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/TestRuleExecutesLayoutPassesWhenWaitingForIdleTest.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/node/RootForTest.kt
M wear/compose/compose-foundation/src/androidAndroidTest/kotlin/androidx/wear/compose/foundation/BasicCurvedTextTest.kt
ju...@google.com <ju...@google.com> #11
The following release(s) address this bug.It is possible this bug has only been partially addressed:
androidx.compose.animation:animation:1.4.0-alpha03
androidx.compose.animation:animation-core:1.4.0-alpha03
androidx.compose.foundation:foundation:1.4.0-alpha03
androidx.compose.runtime:runtime:1.4.0-alpha03
androidx.compose.ui:ui:1.4.0-alpha03
androidx.compose.ui:ui-test:1.4.0-alpha03
androidx.compose.ui:ui-test-junit4:1.4.0-alpha03
na...@google.com <na...@google.com> #12
The following release(s) address this bug.It is possible this bug has only been partially addressed:
androidx.compose.material:material:1.4.0-alpha04
androidx.compose.ui:ui:1.4.0-alpha04
androidx.compose.ui:ui-test:1.4.0-alpha04
androidx.compose.ui:ui-test-junit4:1.4.0-alpha04
androidx.wear.compose:compose-foundation:1.2.0-alpha02
pr...@google.com <pr...@google.com> #13
The following release(s) address this bug.It is possible this bug has only been partially addressed:
androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha06
Description
When we're awaiting idleness, we forward ("pump") the clock to reach quiescence faster. But in many cases we also have to wait for measure/layout, which is still driven by the Choreographer. If we can perform a measure/layout pass ourselves when we detect that's the thing we're waiting for, we can potentially reach idleness faster.