From d4e82f9cb01c8f6e83cfb68ff1b2ba3b97508114 Mon Sep 17 00:00:00 2001 From: Michal Zielinski Date: Wed, 30 Oct 2024 04:23:43 -0700 Subject: [PATCH] Replace ProgressSpec with Progress Primitive Component Summary: As per title - this diff replaces `ProgressSpec` component with a Progress Primitive Component. In this change, we're: 1. Moving `Progress` Primitive Component from Litho sample app to litho widget module. 2. Renaming `Progress` Primitive Component to `ExperimentalProgress`. 3. Renaming `ProgressSpec` to `ProgressComponentSpec` 4. Introducing `ProgressSpec` LayoutSpec which returns either `ProgressComponentSpec` or `ExperimentalProgress` depending on MC value. Reviewed By: apowolny Differential Revision: D65139347 fbshipit-source-id: 5632801d3b62c7fa98cc8b611d4b6732fce1388c --- .../litho/config/ComponentsConfiguration.kt | 5 ++ .../litho/widget/ProgressSpecTest.java | 6 +- .../litho/widget/ExperimentalProgress.kt | 19 +++--- ...ssSpec.java => ProgressComponentSpec.java} | 4 +- .../com/facebook/litho/widget/ProgressSpec.kt | 62 +++++++++++++++++++ .../PrimitiveWidgetsExampleComponent.kt | 3 +- .../kotlin/primitives/widgets/ProgressTest.kt | 25 +++++--- 7 files changed, 100 insertions(+), 24 deletions(-) rename sample/src/main/java/com/facebook/samples/litho/kotlin/primitives/widgets/Progress.kt => litho-widget/src/main/java/com/facebook/litho/widget/ExperimentalProgress.kt (87%) rename litho-widget/src/main/java/com/facebook/litho/widget/{ProgressSpec.java => ProgressComponentSpec.java} (97%) create mode 100644 litho-widget/src/main/java/com/facebook/litho/widget/ProgressSpec.kt diff --git a/litho-core/src/main/java/com/facebook/litho/config/ComponentsConfiguration.kt b/litho-core/src/main/java/com/facebook/litho/config/ComponentsConfiguration.kt index b96f35127f9..95de2d6b0a9 100644 --- a/litho-core/src/main/java/com/facebook/litho/config/ComponentsConfiguration.kt +++ b/litho-core/src/main/java/com/facebook/litho/config/ComponentsConfiguration.kt @@ -280,6 +280,11 @@ internal constructor( */ @JvmField var usePrimitiveImage: Boolean = false + /** + * This flag is used to enable using PrimitiveComponent implementation of a Progress component. + */ + @JvmField var usePrimitiveProgress: Boolean = false + /** This config will enable logging of interactable components with 0 alpha */ @JvmField var isZeroAlphaLoggingEnabled: Boolean = false diff --git a/litho-it/src/test/com/facebook/litho/widget/ProgressSpecTest.java b/litho-it/src/test/com/facebook/litho/widget/ProgressSpecTest.java index 62c05766ad4..cf9619a9591 100644 --- a/litho-it/src/test/com/facebook/litho/widget/ProgressSpecTest.java +++ b/litho-it/src/test/com/facebook/litho/widget/ProgressSpecTest.java @@ -53,12 +53,12 @@ public void testUnsetSize() { View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); - assertThat(view.getMeasuredWidth()).isEqualTo(ProgressSpec.DEFAULT_SIZE); - assertThat(view.getMeasuredHeight()).isEqualTo(ProgressSpec.DEFAULT_SIZE); + assertThat(view.getMeasuredWidth()).isEqualTo(ProgressComponentSpec.DEFAULT_SIZE); + assertThat(view.getMeasuredHeight()).isEqualTo(ProgressComponentSpec.DEFAULT_SIZE); } private LithoView getMountedView() { - Progress.Builder progress = Progress.create(mContext); + ProgressComponent.Builder progress = ProgressComponent.create(mContext); return ComponentTestHelper.mountComponent(progress); } diff --git a/sample/src/main/java/com/facebook/samples/litho/kotlin/primitives/widgets/Progress.kt b/litho-widget/src/main/java/com/facebook/litho/widget/ExperimentalProgress.kt similarity index 87% rename from sample/src/main/java/com/facebook/samples/litho/kotlin/primitives/widgets/Progress.kt rename to litho-widget/src/main/java/com/facebook/litho/widget/ExperimentalProgress.kt index ed52c5c7e28..4367498bfdb 100644 --- a/sample/src/main/java/com/facebook/samples/litho/kotlin/primitives/widgets/Progress.kt +++ b/litho-widget/src/main/java/com/facebook/litho/widget/ExperimentalProgress.kt @@ -14,16 +14,17 @@ * limitations under the License. */ -package com.facebook.samples.litho.kotlin.primitives.widgets +package com.facebook.litho.widget import android.graphics.Color -import android.graphics.PorterDuff import android.graphics.drawable.Drawable +import androidx.core.graphics.BlendModeColorFilterCompat +import androidx.core.graphics.BlendModeCompat import com.facebook.litho.LithoPrimitive import com.facebook.litho.PrimitiveComponent import com.facebook.litho.PrimitiveComponentScope import com.facebook.litho.Style -import com.facebook.litho.widget.ProgressView +import com.facebook.litho.annotations.ExperimentalLithoApi import com.facebook.rendercore.Size import com.facebook.rendercore.SizeConstraints import com.facebook.rendercore.primitives.LayoutBehavior @@ -38,13 +39,13 @@ import com.facebook.rendercore.utils.withEqualDimensions * @param indeterminateDrawable Drawable to be shown to show progress. * @param color Tint color for the drawable. */ -class Progress( +@ExperimentalLithoApi +class ExperimentalProgress( private val color: Int = Color.TRANSPARENT, private val indeterminateDrawable: Drawable? = null, private val style: Style? = null ) : PrimitiveComponent() { - @Suppress("DEPRECATION") override fun PrimitiveComponentScope.render(): LithoPrimitive { return LithoPrimitive( layoutBehavior = ProgressLayoutBehavior, @@ -52,12 +53,13 @@ class Progress( MountBehavior(ViewAllocator { context -> ProgressView(context) }) { bind(indeterminateDrawable, color) { content -> val defaultIndeterminateDrawable = content.indeterminateDrawable + indeterminateDrawable?.let { content.indeterminateDrawable = indeterminateDrawable } content.indeterminateDrawable?.let { if (color != Color.TRANSPARENT) { - content.indeterminateDrawable - .mutate() - .setColorFilter(color, PorterDuff.Mode.MULTIPLY) + content.indeterminateDrawable.mutate().colorFilter = + BlendModeColorFilterCompat.createBlendModeColorFilterCompat( + color, BlendModeCompat.MODULATE) } } onUnbind { @@ -65,6 +67,7 @@ class Progress( if (color != Color.TRANSPARENT && content.indeterminateDrawable != null) { content.indeterminateDrawable.mutate().clearColorFilter() } + content.indeterminateDrawable = defaultIndeterminateDrawable } } diff --git a/litho-widget/src/main/java/com/facebook/litho/widget/ProgressSpec.java b/litho-widget/src/main/java/com/facebook/litho/widget/ProgressComponentSpec.java similarity index 97% rename from litho-widget/src/main/java/com/facebook/litho/widget/ProgressSpec.java rename to litho-widget/src/main/java/com/facebook/litho/widget/ProgressComponentSpec.java index 21d7f8cff0d..26ae4bfefd6 100644 --- a/litho-widget/src/main/java/com/facebook/litho/widget/ProgressSpec.java +++ b/litho-widget/src/main/java/com/facebook/litho/widget/ProgressComponentSpec.java @@ -51,7 +51,7 @@ * @prop color Tint color for the drawable. */ @MountSpec(isPureRender = true) -class ProgressSpec { +class ProgressComponentSpec { static final int DEFAULT_SIZE = 50; @@ -65,7 +65,7 @@ static void onLoadStyle(ComponentContext c, Output indeterminateDrawab @OnPrepare static void onPrepare( ComponentContext c, - @Prop(optional = true, resType = ResType.DRAWABLE) Drawable indeterminateDrawable, + @Prop(optional = true, resType = ResType.DRAWABLE) @Nullable Drawable indeterminateDrawable, Output resolvedIndeterminateDrawable) { if (indeterminateDrawable != null) { resolvedIndeterminateDrawable.set(indeterminateDrawable); diff --git a/litho-widget/src/main/java/com/facebook/litho/widget/ProgressSpec.kt b/litho-widget/src/main/java/com/facebook/litho/widget/ProgressSpec.kt new file mode 100644 index 00000000000..d213179f142 --- /dev/null +++ b/litho-widget/src/main/java/com/facebook/litho/widget/ProgressSpec.kt @@ -0,0 +1,62 @@ +/* + * 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.widget + +import android.graphics.Color +import android.graphics.drawable.Drawable +import com.facebook.litho.Component +import com.facebook.litho.ComponentContext +import com.facebook.litho.annotations.ExcuseMySpec +import com.facebook.litho.annotations.ExperimentalLithoApi +import com.facebook.litho.annotations.LayoutSpec +import com.facebook.litho.annotations.OnCreateLayout +import com.facebook.litho.annotations.Prop +import com.facebook.litho.annotations.PropDefault +import com.facebook.litho.annotations.Reason +import com.facebook.litho.annotations.ResType +import com.facebook.litho.config.ComponentsConfiguration + +/** + * Renders an infinitely spinning progress bar. + * + * @uidocs + * @prop indeterminateDrawable Drawable to be shown to show progress. + * @prop color Tint color for the drawable. + */ +@ExcuseMySpec(reason = Reason.J2K_CONVERSION) +@LayoutSpec +object ProgressSpec { + + @PropDefault val color: Int = Color.TRANSPARENT + + @OptIn(ExperimentalLithoApi::class) + @OnCreateLayout + fun onCreateLayout( + c: ComponentContext, + @Prop(optional = true, resType = ResType.COLOR) color: Int, + @Prop(optional = true, resType = ResType.DRAWABLE) indeterminateDrawable: Drawable?, + ): Component { + return if (ComponentsConfiguration.usePrimitiveProgress) { + ExperimentalProgress( + color = color, + indeterminateDrawable = indeterminateDrawable, + ) + } else { + ProgressComponent.create(c).color(color).indeterminateDrawable(indeterminateDrawable).build() + } + } +} diff --git a/sample/src/main/java/com/facebook/samples/litho/kotlin/primitives/widgets/PrimitiveWidgetsExampleComponent.kt b/sample/src/main/java/com/facebook/samples/litho/kotlin/primitives/widgets/PrimitiveWidgetsExampleComponent.kt index 61778ec0791..3a5d67a1ce5 100644 --- a/sample/src/main/java/com/facebook/samples/litho/kotlin/primitives/widgets/PrimitiveWidgetsExampleComponent.kt +++ b/sample/src/main/java/com/facebook/samples/litho/kotlin/primitives/widgets/PrimitiveWidgetsExampleComponent.kt @@ -32,6 +32,7 @@ import com.facebook.litho.kotlin.widget.Text import com.facebook.litho.view.background import com.facebook.litho.view.backgroundColor import com.facebook.litho.widget.ExperimentalImage +import com.facebook.litho.widget.ExperimentalProgress import com.facebook.rendercore.dp import com.facebook.rendercore.drawableRes import com.facebook.samples.litho.R.drawable.ic_launcher @@ -59,7 +60,7 @@ class PrimitiveWidgetsExampleComponent : KComponent() { ExperimentalImage( drawable = drawableRes(ic_launcher), style = Style.width(100.dp).height(100.dp))) child(Text("Progress")) - child(Progress(style = Style.width(100.dp).height(100.dp))) + child(ExperimentalProgress(style = Style.width(100.dp).height(100.dp))) child(Text("Horizontal Scroll")) child(HorizontalScroll { Row { getComponents(this) } }) child(Text("Vertical Scroll")) diff --git a/sample/src/test/java/com/facebook/samples/litho/kotlin/primitives/widgets/ProgressTest.kt b/sample/src/test/java/com/facebook/samples/litho/kotlin/primitives/widgets/ProgressTest.kt index 18fd79536b9..bfa94f39e4a 100644 --- a/sample/src/test/java/com/facebook/samples/litho/kotlin/primitives/widgets/ProgressTest.kt +++ b/sample/src/test/java/com/facebook/samples/litho/kotlin/primitives/widgets/ProgressTest.kt @@ -19,11 +19,13 @@ package com.facebook.samples.litho.kotlin.primitives.widgets import android.graphics.Color import android.graphics.drawable.ColorDrawable import com.facebook.litho.Style +import com.facebook.litho.annotations.ExperimentalLithoApi import com.facebook.litho.core.height import com.facebook.litho.core.width import com.facebook.litho.testing.LithoTestRule import com.facebook.litho.testing.assertj.LithoAssertions.assertThat import com.facebook.litho.testing.testrunner.LithoTestRunner +import com.facebook.litho.widget.ExperimentalProgress import com.facebook.litho.widget.ProgressView import com.facebook.rendercore.px import junit.framework.Assert.assertNotNull @@ -33,6 +35,7 @@ import org.junit.Test import org.junit.runner.RunWith /** Tests for [ProgressComponent] */ +@OptIn(ExperimentalLithoApi::class) @RunWith(LithoTestRunner::class) class ProgressTest { @@ -41,10 +44,10 @@ class ProgressTest { @Test fun `ProgressComponent should render`() { val testLithoView = - lithoViewRule.render { Progress(style = Style.width(100.px).height(100.px)) } + lithoViewRule.render { ExperimentalProgress(style = Style.width(100.px).height(100.px)) } // should find an Progress in the tree - assertNotNull(testLithoView.findComponent(Progress::class)) + assertNotNull(testLithoView.findComponent(ExperimentalProgress::class)) // should mount an Progress assertThat(testLithoView.lithoView.mountItemCount).isEqualTo(1) @@ -56,8 +59,8 @@ class ProgressTest { @Test fun `same instance should be equivalent`() { - val component = Progress() - val component2 = Progress() + val component = ExperimentalProgress() + val component2 = ExperimentalProgress() assertThat(component).isEquivalentTo(component2) assertThat(component).isEquivalentTo(component2, true) @@ -67,13 +70,15 @@ class ProgressTest { fun `components with same prop values should be equivalent`() { val colorDrawable = ColorDrawable(Color.RED) val color = Color.BLACK - val firstProgressWithColorDrawable = Progress(indeterminateDrawable = colorDrawable) - val secondProgressWithColorDrawable = Progress(indeterminateDrawable = colorDrawable) - val firstProgressWithColor = Progress(color = color) - val secondProgressWithColor = Progress(color = color) - val firstProgressWithBothParams = Progress(indeterminateDrawable = colorDrawable, color = color) + val firstProgressWithColorDrawable = ExperimentalProgress(indeterminateDrawable = colorDrawable) + val secondProgressWithColorDrawable = + ExperimentalProgress(indeterminateDrawable = colorDrawable) + val firstProgressWithColor = ExperimentalProgress(color = color) + val secondProgressWithColor = ExperimentalProgress(color = color) + val firstProgressWithBothParams = + ExperimentalProgress(indeterminateDrawable = colorDrawable, color = color) val secondProgressWithBothParams = - Progress(indeterminateDrawable = colorDrawable, color = color) + ExperimentalProgress(indeterminateDrawable = colorDrawable, color = color) assertThat(firstProgressWithColorDrawable).isEquivalentTo(secondProgressWithColorDrawable) assertThat(firstProgressWithColorDrawable).isEquivalentTo(secondProgressWithColorDrawable, true)