Skip to content

Commit 73c2455

Browse files
jettbowfacebook-github-bot
authored andcommitted
Add visibility fix for nested litho view
Summary: Add visibility fix for nested litho view Reviewed By: adityasharat Differential Revision: D57512293 fbshipit-source-id: b440351bc8913d4478567813dbc2b063e79de252
1 parent 26cea59 commit 73c2455

File tree

6 files changed

+135
-9
lines changed

6 files changed

+135
-9
lines changed

litho-core/src/main/java/com/facebook/litho/ComponentTree.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,9 @@ protected ComponentTree(Builder builder) {
370370
config,
371371
LithoTree.Companion.create(this, stateUpdater),
372372
"root",
373-
getLithoVisibilityEventsController(),
373+
ComponentsConfiguration.defaultInstance.enableVisibilityFixForNestedLithoView
374+
? builder.lithoVisibilityEventsController
375+
: getLithoVisibilityEventsController(),
374376
null,
375377
builder.parentTreePropContainer);
376378

@@ -409,8 +411,14 @@ protected ComponentTree(Builder builder) {
409411
}
410412
}
411413

412-
if (builder.mLifecycleProvider != null) {
413-
subscribeToLifecycleProvider(builder.mLifecycleProvider);
414+
if (ComponentsConfiguration.defaultInstance.enableVisibilityFixForNestedLithoView) {
415+
if (mContext.getLithoVisibilityEventsController() != null) {
416+
subscribeToLifecycleProvider(mContext.getLithoVisibilityEventsController());
417+
}
418+
} else {
419+
if (builder.lithoVisibilityEventsController != null) {
420+
subscribeToLifecycleProvider(builder.lithoVisibilityEventsController);
421+
}
414422
}
415423

416424
ComponentTreeDebugEventListener debugEventListener = config.componentsConfig.debugEventListener;
@@ -2955,7 +2963,7 @@ public static class Builder {
29552963
private @Nullable TreeState treeState;
29562964
private int overrideComponentTreeId = INVALID_ID;
29572965
private @Nullable MeasureListener mMeasureListener;
2958-
private @Nullable LithoVisibilityEventsController mLifecycleProvider;
2966+
private @Nullable LithoVisibilityEventsController lithoVisibilityEventsController;
29592967
private @Nullable RenderUnitIdGenerator mRenderUnitIdGenerator;
29602968
private @Nullable VisibilityBoundsTransformer visibilityBoundsTransformer;
29612969

@@ -3002,7 +3010,7 @@ public Builder withRoot(Component root) {
30023010

30033011
public Builder withLithoVisibilityEventsController(
30043012
@Nullable LithoVisibilityEventsController lifecycleProvider) {
3005-
mLifecycleProvider = lifecycleProvider;
3013+
lithoVisibilityEventsController = lifecycleProvider;
30063014
return this;
30073015
}
30083016

litho-core/src/main/java/com/facebook/litho/SimpleNestedTreeVisibilityEventsController.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.facebook.litho
1818

1919
import com.facebook.litho.LithoVisibilityEventsController.LithoVisibilityState
20+
import com.facebook.litho.config.ComponentsConfiguration
2021

2122
/**
2223
* LithoVisibilityEventsController implementation that can be used to subscribe a nested
@@ -53,7 +54,11 @@ class SimpleNestedTreeVisibilityEventsController(
5354
LithoVisibilityState.HINT_VISIBLE -> moveToVisibilityState(LithoVisibilityState.HINT_VISIBLE)
5455
LithoVisibilityState.HINT_INVISIBLE ->
5556
moveToVisibilityState(LithoVisibilityState.HINT_INVISIBLE)
56-
LithoVisibilityState.DESTROYED -> {}
57+
LithoVisibilityState.DESTROYED -> {
58+
if (ComponentsConfiguration.defaultInstance.enableVisibilityFixForNestedLithoView) {
59+
moveToVisibilityState(LithoVisibilityState.DESTROYED)
60+
}
61+
}
5762
}
5863
}
5964
}

litho-core/src/main/java/com/facebook/litho/config/ComponentsConfiguration.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ internal constructor(
140140
@JvmField val useFineGrainedViewAttributesExtension: Boolean = false,
141141
@JvmField val enableFacadeStateUpdater: Boolean = false,
142142
@JvmField val skipSecondIsInWorkingRangeCheck: Boolean = false,
143+
@JvmField val enableVisibilityFixForNestedLithoView: Boolean = false,
143144
) {
144145

145146
val shouldAddRootHostViewOrDisableBgFgOutputs: Boolean =
@@ -310,6 +311,8 @@ internal constructor(
310311
baseConfig.useFineGrainedViewAttributesExtension
311312
private var enableFacadeStateUpdater = baseConfig.enableFacadeStateUpdater
312313
private var skipSecondIsInWorkingRangeCheck = baseConfig.skipSecondIsInWorkingRangeCheck
314+
private var enableVisibilityFixForNestedLithoView =
315+
baseConfig.enableVisibilityFixForNestedLithoView
313316

314317
fun shouldNotifyVisibleBoundsChangeWhenNestedLithoViewBecomesInvisible(
315318
enabled: Boolean
@@ -408,6 +411,10 @@ internal constructor(
408411
skipSecondIsInWorkingRangeCheck = enabled
409412
}
410413

414+
fun enableVisibilityFixForNestedLithoView(enabled: Boolean): Builder = also {
415+
enableVisibilityFixForNestedLithoView = enabled
416+
}
417+
411418
fun build(): ComponentsConfiguration {
412419
return baseConfig.copy(
413420
specsApiStateUpdateDuplicateDetectionEnabled =
@@ -442,7 +449,8 @@ internal constructor(
442449
skipHostAlphaReset = skipHostAlphaReset,
443450
useFineGrainedViewAttributesExtension = useFineGrainedViewAttributesExtension,
444451
enableFacadeStateUpdater = enableFacadeStateUpdater,
445-
skipSecondIsInWorkingRangeCheck = skipSecondIsInWorkingRangeCheck)
452+
skipSecondIsInWorkingRangeCheck = skipSecondIsInWorkingRangeCheck,
453+
enableVisibilityFixForNestedLithoView = enableVisibilityFixForNestedLithoView)
446454
}
447455
}
448456
}

litho-it/src/test/java/com/facebook/litho/BUCK

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ litho_robolectric4_test(
6262
target = "8",
6363
target_sdk_levels = ["33"],
6464
deps = [
65+
"//fbandroid/java/com/facebook/testing/robolectric:robolectric",
6566
"//fbandroid/third-party/java/guava:guava",
6667
"//third-party/kotlin/mockito-kotlin2:mockito-kotlin2",
6768
LITHO_ANDROIDSUPPORT_RECYCLERVIEW_TARGET,
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.facebook.litho
18+
19+
import com.facebook.litho.config.ComponentsConfiguration
20+
import com.facebook.litho.core.height
21+
import com.facebook.litho.core.width
22+
import com.facebook.litho.kotlin.widget.Text
23+
import com.facebook.litho.testing.LithoViewRule
24+
import com.facebook.litho.visibility.onInvisible
25+
import com.facebook.litho.visibility.onVisible
26+
import com.facebook.litho.widget.collection.LazyList
27+
import com.facebook.rendercore.sp
28+
import com.facebook.testing.robolectric.WithTestDefaultsRunner
29+
import org.assertj.core.api.Assertions.assertThat
30+
import org.junit.After
31+
import org.junit.Before
32+
import org.junit.Rule
33+
import org.junit.Test
34+
import org.junit.runner.RunWith
35+
import org.robolectric.annotation.LooperMode
36+
37+
@LooperMode(LooperMode.Mode.LEGACY)
38+
@RunWith(WithTestDefaultsRunner::class)
39+
class LithoVisibilityEventsControllerForNestedViewTest {
40+
@Rule
41+
@JvmField
42+
val lithoViewRule: LithoViewRule =
43+
LithoViewRule(lithoVisibilityEventsController = { lithoVisibilityEventsControllerDelegate })
44+
private val lithoVisibilityEventsControllerDelegate: LithoVisibilityEventsController =
45+
LithoVisibilityEventsControllerDelegate()
46+
private val invisibleTags: MutableSet<Int> = mutableSetOf()
47+
48+
@Before
49+
fun setup() {
50+
ComponentsConfiguration.defaultInstance =
51+
ComponentsConfiguration.defaultInstance.copy(enableVisibilityFixForNestedLithoView = true)
52+
}
53+
54+
@After
55+
fun breakdown() {
56+
ComponentsConfiguration.defaultInstance =
57+
ComponentsConfiguration.defaultInstance.copy(enableVisibilityFixForNestedLithoView = false)
58+
}
59+
60+
@Test
61+
fun `test visibility events for nested LithoView`() {
62+
val testLithoView =
63+
lithoViewRule.render {
64+
LazyList(style = Style.height(100.sp).width(100.sp)) {
65+
for (i in 0 until 10) {
66+
child(
67+
component =
68+
VisibilityTrackingComponent(tag = i, invisibleTracking = invisibleTags))
69+
}
70+
}
71+
}
72+
testLithoView.lithoView.subscribeComponentTreeToLifecycleProvider(
73+
lithoVisibilityEventsControllerDelegate)
74+
75+
// testing initial state
76+
assertThat(
77+
testLithoView.lithoView.componentTree?.lithoVisibilityEventsController?.visibilityState)
78+
.isEqualTo(LithoVisibilityEventsController.LithoVisibilityState.HINT_VISIBLE)
79+
assertThat(invisibleTags).isEmpty()
80+
81+
// move to invisible
82+
lithoVisibilityEventsControllerDelegate.moveToVisibilityState(
83+
LithoVisibilityEventsController.LithoVisibilityState.HINT_INVISIBLE)
84+
assertThat(invisibleTags).isEqualTo((0 until 10).toSet())
85+
86+
// move to visible
87+
lithoVisibilityEventsControllerDelegate.moveToVisibilityState(
88+
LithoVisibilityEventsController.LithoVisibilityState.HINT_VISIBLE)
89+
assertThat(invisibleTags).isEmpty()
90+
}
91+
}
92+
93+
class VisibilityTrackingComponent(val invisibleTracking: MutableSet<Int>, val tag: Int) :
94+
KComponent() {
95+
override fun ComponentScope.render(): Component {
96+
97+
return Text(
98+
text = "test",
99+
style =
100+
Style.height(5.sp)
101+
.onVisible { invisibleTracking.remove(tag) }
102+
.onInvisible { invisibleTracking.add(tag) })
103+
}
104+
}

litho-it/src/test/java/com/facebook/litho/LithoVisibilityEventsControllerTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ import android.graphics.Rect
2020
import com.facebook.litho.testing.LegacyLithoViewRule
2121
import com.facebook.litho.testing.Whitebox
2222
import com.facebook.litho.testing.exactly
23-
import com.facebook.litho.testing.testrunner.LithoTestRunner
2423
import com.facebook.litho.widget.LayoutSpecLifecycleTester
2524
import com.facebook.litho.widget.MountSpecLifecycleTester
25+
import com.facebook.testing.robolectric.WithTestDefaultsRunner
2626
import java.util.ArrayList
2727
import org.assertj.core.api.Assertions.assertThat
2828
import org.junit.After
@@ -32,7 +32,7 @@ import org.junit.Test
3232
import org.junit.runner.RunWith
3333
import org.robolectric.annotation.LooperMode
3434

35-
@RunWith(LithoTestRunner::class)
35+
@RunWith(WithTestDefaultsRunner::class)
3636
@LooperMode(LooperMode.Mode.LEGACY)
3737
class LithoVisibilityEventsControllerTest {
3838

0 commit comments

Comments
 (0)