Skip to content

Commit

Permalink
Add visibility fix for nested litho view
Browse files Browse the repository at this point in the history
Summary: Add visibility fix for nested litho view

Reviewed By: adityasharat

Differential Revision: D57512293

fbshipit-source-id: b440351bc8913d4478567813dbc2b063e79de252
  • Loading branch information
jettbow authored and facebook-github-bot committed May 19, 2024
1 parent 26cea59 commit 73c2455
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 9 deletions.
18 changes: 13 additions & 5 deletions litho-core/src/main/java/com/facebook/litho/ComponentTree.java
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,9 @@ protected ComponentTree(Builder builder) {
config,
LithoTree.Companion.create(this, stateUpdater),
"root",
getLithoVisibilityEventsController(),
ComponentsConfiguration.defaultInstance.enableVisibilityFixForNestedLithoView
? builder.lithoVisibilityEventsController
: getLithoVisibilityEventsController(),
null,
builder.parentTreePropContainer);

Expand Down Expand Up @@ -409,8 +411,14 @@ protected ComponentTree(Builder builder) {
}
}

if (builder.mLifecycleProvider != null) {
subscribeToLifecycleProvider(builder.mLifecycleProvider);
if (ComponentsConfiguration.defaultInstance.enableVisibilityFixForNestedLithoView) {
if (mContext.getLithoVisibilityEventsController() != null) {
subscribeToLifecycleProvider(mContext.getLithoVisibilityEventsController());
}
} else {
if (builder.lithoVisibilityEventsController != null) {
subscribeToLifecycleProvider(builder.lithoVisibilityEventsController);
}
}

ComponentTreeDebugEventListener debugEventListener = config.componentsConfig.debugEventListener;
Expand Down Expand Up @@ -2955,7 +2963,7 @@ public static class Builder {
private @Nullable TreeState treeState;
private int overrideComponentTreeId = INVALID_ID;
private @Nullable MeasureListener mMeasureListener;
private @Nullable LithoVisibilityEventsController mLifecycleProvider;
private @Nullable LithoVisibilityEventsController lithoVisibilityEventsController;
private @Nullable RenderUnitIdGenerator mRenderUnitIdGenerator;
private @Nullable VisibilityBoundsTransformer visibilityBoundsTransformer;

Expand Down Expand Up @@ -3002,7 +3010,7 @@ public Builder withRoot(Component root) {

public Builder withLithoVisibilityEventsController(
@Nullable LithoVisibilityEventsController lifecycleProvider) {
mLifecycleProvider = lifecycleProvider;
lithoVisibilityEventsController = lifecycleProvider;
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.facebook.litho

import com.facebook.litho.LithoVisibilityEventsController.LithoVisibilityState
import com.facebook.litho.config.ComponentsConfiguration

/**
* LithoVisibilityEventsController implementation that can be used to subscribe a nested
Expand Down Expand Up @@ -53,7 +54,11 @@ class SimpleNestedTreeVisibilityEventsController(
LithoVisibilityState.HINT_VISIBLE -> moveToVisibilityState(LithoVisibilityState.HINT_VISIBLE)
LithoVisibilityState.HINT_INVISIBLE ->
moveToVisibilityState(LithoVisibilityState.HINT_INVISIBLE)
LithoVisibilityState.DESTROYED -> {}
LithoVisibilityState.DESTROYED -> {
if (ComponentsConfiguration.defaultInstance.enableVisibilityFixForNestedLithoView) {
moveToVisibilityState(LithoVisibilityState.DESTROYED)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ internal constructor(
@JvmField val useFineGrainedViewAttributesExtension: Boolean = false,
@JvmField val enableFacadeStateUpdater: Boolean = false,
@JvmField val skipSecondIsInWorkingRangeCheck: Boolean = false,
@JvmField val enableVisibilityFixForNestedLithoView: Boolean = false,
) {

val shouldAddRootHostViewOrDisableBgFgOutputs: Boolean =
Expand Down Expand Up @@ -310,6 +311,8 @@ internal constructor(
baseConfig.useFineGrainedViewAttributesExtension
private var enableFacadeStateUpdater = baseConfig.enableFacadeStateUpdater
private var skipSecondIsInWorkingRangeCheck = baseConfig.skipSecondIsInWorkingRangeCheck
private var enableVisibilityFixForNestedLithoView =
baseConfig.enableVisibilityFixForNestedLithoView

fun shouldNotifyVisibleBoundsChangeWhenNestedLithoViewBecomesInvisible(
enabled: Boolean
Expand Down Expand Up @@ -408,6 +411,10 @@ internal constructor(
skipSecondIsInWorkingRangeCheck = enabled
}

fun enableVisibilityFixForNestedLithoView(enabled: Boolean): Builder = also {
enableVisibilityFixForNestedLithoView = enabled
}

fun build(): ComponentsConfiguration {
return baseConfig.copy(
specsApiStateUpdateDuplicateDetectionEnabled =
Expand Down Expand Up @@ -442,7 +449,8 @@ internal constructor(
skipHostAlphaReset = skipHostAlphaReset,
useFineGrainedViewAttributesExtension = useFineGrainedViewAttributesExtension,
enableFacadeStateUpdater = enableFacadeStateUpdater,
skipSecondIsInWorkingRangeCheck = skipSecondIsInWorkingRangeCheck)
skipSecondIsInWorkingRangeCheck = skipSecondIsInWorkingRangeCheck,
enableVisibilityFixForNestedLithoView = enableVisibilityFixForNestedLithoView)
}
}
}
1 change: 1 addition & 0 deletions litho-it/src/test/java/com/facebook/litho/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ litho_robolectric4_test(
target = "8",
target_sdk_levels = ["33"],
deps = [
"//fbandroid/java/com/facebook/testing/robolectric:robolectric",
"//fbandroid/third-party/java/guava:guava",
"//third-party/kotlin/mockito-kotlin2:mockito-kotlin2",
LITHO_ANDROIDSUPPORT_RECYCLERVIEW_TARGET,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.facebook.litho

import com.facebook.litho.config.ComponentsConfiguration
import com.facebook.litho.core.height
import com.facebook.litho.core.width
import com.facebook.litho.kotlin.widget.Text
import com.facebook.litho.testing.LithoViewRule
import com.facebook.litho.visibility.onInvisible
import com.facebook.litho.visibility.onVisible
import com.facebook.litho.widget.collection.LazyList
import com.facebook.rendercore.sp
import com.facebook.testing.robolectric.WithTestDefaultsRunner
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.LooperMode

@LooperMode(LooperMode.Mode.LEGACY)
@RunWith(WithTestDefaultsRunner::class)
class LithoVisibilityEventsControllerForNestedViewTest {
@Rule
@JvmField
val lithoViewRule: LithoViewRule =
LithoViewRule(lithoVisibilityEventsController = { lithoVisibilityEventsControllerDelegate })
private val lithoVisibilityEventsControllerDelegate: LithoVisibilityEventsController =
LithoVisibilityEventsControllerDelegate()
private val invisibleTags: MutableSet<Int> = mutableSetOf()

@Before
fun setup() {
ComponentsConfiguration.defaultInstance =
ComponentsConfiguration.defaultInstance.copy(enableVisibilityFixForNestedLithoView = true)
}

@After
fun breakdown() {
ComponentsConfiguration.defaultInstance =
ComponentsConfiguration.defaultInstance.copy(enableVisibilityFixForNestedLithoView = false)
}

@Test
fun `test visibility events for nested LithoView`() {
val testLithoView =
lithoViewRule.render {
LazyList(style = Style.height(100.sp).width(100.sp)) {
for (i in 0 until 10) {
child(
component =
VisibilityTrackingComponent(tag = i, invisibleTracking = invisibleTags))
}
}
}
testLithoView.lithoView.subscribeComponentTreeToLifecycleProvider(
lithoVisibilityEventsControllerDelegate)

// testing initial state
assertThat(
testLithoView.lithoView.componentTree?.lithoVisibilityEventsController?.visibilityState)
.isEqualTo(LithoVisibilityEventsController.LithoVisibilityState.HINT_VISIBLE)
assertThat(invisibleTags).isEmpty()

// move to invisible
lithoVisibilityEventsControllerDelegate.moveToVisibilityState(
LithoVisibilityEventsController.LithoVisibilityState.HINT_INVISIBLE)
assertThat(invisibleTags).isEqualTo((0 until 10).toSet())

// move to visible
lithoVisibilityEventsControllerDelegate.moveToVisibilityState(
LithoVisibilityEventsController.LithoVisibilityState.HINT_VISIBLE)
assertThat(invisibleTags).isEmpty()
}
}

class VisibilityTrackingComponent(val invisibleTracking: MutableSet<Int>, val tag: Int) :
KComponent() {
override fun ComponentScope.render(): Component {

return Text(
text = "test",
style =
Style.height(5.sp)
.onVisible { invisibleTracking.remove(tag) }
.onInvisible { invisibleTracking.add(tag) })
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ import android.graphics.Rect
import com.facebook.litho.testing.LegacyLithoViewRule
import com.facebook.litho.testing.Whitebox
import com.facebook.litho.testing.exactly
import com.facebook.litho.testing.testrunner.LithoTestRunner
import com.facebook.litho.widget.LayoutSpecLifecycleTester
import com.facebook.litho.widget.MountSpecLifecycleTester
import com.facebook.testing.robolectric.WithTestDefaultsRunner
import java.util.ArrayList
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
Expand All @@ -32,7 +32,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.LooperMode

@RunWith(LithoTestRunner::class)
@RunWith(WithTestDefaultsRunner::class)
@LooperMode(LooperMode.Mode.LEGACY)
class LithoVisibilityEventsControllerTest {

Expand Down

0 comments on commit 73c2455

Please sign in to comment.