From 62de5c001c1d5cf9435cb2ceab70a43a2f9dc3b1 Mon Sep 17 00:00:00 2001 From: David Vacca Date: Sat, 22 Aug 2020 01:43:55 -0700 Subject: [PATCH] Migrate AndroidProgressBarComponent to Fabric Summary: This diff migrates AndroidProgressBar component to Fabric changeLog: [internal] internal Reviewed By: sammy-SC Differential Revision: D23227857 fbshipit-source-id: c5cbbdcc36e63226286cd714d601f0d4690496b2 --- .../ProgressBarAndroidNativeComponent.js | 6 +- .../components/rncore/ComponentDescriptors.h | 1 - .../components/rncore/ShadowNodes.cpp | 1 - .../renderer/components/rncore/ShadowNodes.h | 9 -- .../ReactProgressBarViewManager.java | 40 ++++++ .../ComponentDescriptorRegistry.cpp | 4 - .../renderer/components/progressbar/BUCK | 121 ++++++++++++++++++ .../AndroidProgressBarComponentDescriptor.h | 53 ++++++++ .../AndroidProgressBarMeasurementsManager.cpp | 75 +++++++++++ .../AndroidProgressBarMeasurementsManager.h | 38 ++++++ .../AndroidProgressBarShadowNode.cpp | 35 +++++ .../AndroidProgressBarShadowNode.h | 46 +++++++ .../components/progressbar/conversions.h | 30 +++++ 13 files changed, 441 insertions(+), 18 deletions(-) create mode 100644 ReactCommon/react/renderer/components/progressbar/BUCK create mode 100644 ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/AndroidProgressBarComponentDescriptor.h create mode 100644 ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/AndroidProgressBarMeasurementsManager.cpp create mode 100644 ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/AndroidProgressBarMeasurementsManager.h create mode 100644 ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/AndroidProgressBarShadowNode.cpp create mode 100644 ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/AndroidProgressBarShadowNode.h create mode 100644 ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/conversions.h diff --git a/Libraries/Components/ProgressBarAndroid/ProgressBarAndroidNativeComponent.js b/Libraries/Components/ProgressBarAndroid/ProgressBarAndroidNativeComponent.js index c3efe138a71..0274e586679 100644 --- a/Libraries/Components/ProgressBarAndroid/ProgressBarAndroidNativeComponent.js +++ b/Libraries/Components/ProgressBarAndroid/ProgressBarAndroidNativeComponent.js @@ -30,6 +30,6 @@ type NativeProps = $ReadOnly<{| testID?: WithDefault, |}>; -export default (codegenNativeComponent( - 'AndroidProgressBar', -): HostComponent); +export default (codegenNativeComponent('AndroidProgressBar', { + interfaceOnly: true, +}): HostComponent); diff --git a/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/ComponentDescriptors.h b/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/ComponentDescriptors.h index a8efdeaed7c..cc3c195dd3a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/ComponentDescriptors.h +++ b/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/ComponentDescriptors.h @@ -20,7 +20,6 @@ using ActivityIndicatorViewComponentDescriptor = ConcreteComponentDescriptor; using AndroidDrawerLayoutComponentDescriptor = ConcreteComponentDescriptor; using RCTMaskedViewComponentDescriptor = ConcreteComponentDescriptor; -using AndroidProgressBarComponentDescriptor = ConcreteComponentDescriptor; using RCTProgressViewComponentDescriptor = ConcreteComponentDescriptor; using AndroidSwipeRefreshLayoutComponentDescriptor = ConcreteComponentDescriptor; using PullToRefreshViewComponentDescriptor = ConcreteComponentDescriptor; diff --git a/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/ShadowNodes.cpp b/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/ShadowNodes.cpp index 767f4779ed1..befdb19c30d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/ShadowNodes.cpp +++ b/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/ShadowNodes.cpp @@ -17,7 +17,6 @@ extern const char ActivityIndicatorViewComponentName[] = "ActivityIndicatorView" extern const char DatePickerComponentName[] = "DatePicker"; extern const char AndroidDrawerLayoutComponentName[] = "AndroidDrawerLayout"; extern const char RCTMaskedViewComponentName[] = "RCTMaskedView"; -extern const char AndroidProgressBarComponentName[] = "AndroidProgressBar"; extern const char RCTProgressViewComponentName[] = "RCTProgressView"; extern const char AndroidSwipeRefreshLayoutComponentName[] = "AndroidSwipeRefreshLayout"; extern const char PullToRefreshViewComponentName[] = "PullToRefreshView"; diff --git a/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/ShadowNodes.h b/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/ShadowNodes.h index 748f811c69d..f8a84f5836b 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/ShadowNodes.h +++ b/ReactAndroid/src/main/java/com/facebook/react/viewmanagers/jni/react/renderer/components/rncore/ShadowNodes.h @@ -55,15 +55,6 @@ using RCTMaskedViewShadowNode = ConcreteViewShadowNode< RCTMaskedViewComponentName, RCTMaskedViewProps>; -extern const char AndroidProgressBarComponentName[]; - -/* - * `ShadowNode` for component. - */ -using AndroidProgressBarShadowNode = ConcreteViewShadowNode< - AndroidProgressBarComponentName, - AndroidProgressBarProps>; - extern const char RCTProgressViewComponentName[]; /* diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/progressbar/ReactProgressBarViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/progressbar/ReactProgressBarViewManager.java index 12ff350d068..aae6bd5cfc4 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/progressbar/ReactProgressBarViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/progressbar/ReactProgressBarViewManager.java @@ -8,17 +8,25 @@ package com.facebook.react.views.progressbar; import android.content.Context; +import android.util.Pair; +import android.view.View; +import android.view.ViewGroup; import android.widget.ProgressBar; import androidx.annotation.Nullable; import com.facebook.react.bridge.JSApplicationIllegalArgumentException; +import com.facebook.react.bridge.ReadableMap; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.uimanager.BaseViewManager; +import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.ViewManagerDelegate; import com.facebook.react.uimanager.ViewProps; import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.viewmanagers.AndroidProgressBarManagerDelegate; import com.facebook.react.viewmanagers.AndroidProgressBarManagerInterface; +import com.facebook.yoga.YogaMeasureMode; +import com.facebook.yoga.YogaMeasureOutput; +import java.util.WeakHashMap; /** * Manages instances of ProgressBar. ProgressBar is wrapped in a ProgressBarContainerView because @@ -33,6 +41,8 @@ public class ReactProgressBarViewManager public static final String REACT_CLASS = "AndroidProgressBar"; + private final WeakHashMap> mMeasuredStyles = new WeakHashMap<>(); + /* package */ static final String PROP_STYLE = "styleAttr"; /* package */ static final String PROP_INDETERMINATE = "indeterminate"; /* package */ static final String PROP_PROGRESS = "progress"; @@ -154,4 +164,34 @@ public class ReactProgressBarViewManager throw new JSApplicationIllegalArgumentException("Unknown ProgressBar style: " + styleStr); } } + + @Override + public long measure( + Context context, + ReadableMap localData, + ReadableMap props, + ReadableMap state, + float width, + YogaMeasureMode widthMode, + float height, + YogaMeasureMode heightMode, + @Nullable float[] attachmentsPositions) { + + final Integer style = + ReactProgressBarViewManager.getStyleFromString(props.getString(PROP_STYLE)); + Pair value = mMeasuredStyles.get(style); + if (value == null) { + ProgressBar progressBar = ReactProgressBarViewManager.createProgressBar(context, style); + + final int spec = + View.MeasureSpec.makeMeasureSpec( + ViewGroup.LayoutParams.WRAP_CONTENT, View.MeasureSpec.UNSPECIFIED); + progressBar.measure(spec, spec); + value = Pair.create(progressBar.getMeasuredWidth(), progressBar.getMeasuredHeight()); + mMeasuredStyles.put(style, value); + } + + return YogaMeasureOutput.make( + PixelUtil.toDIPFromPixel(value.first), PixelUtil.toDIPFromPixel(value.second)); + } } diff --git a/ReactCommon/react/renderer/componentregistry/ComponentDescriptorRegistry.cpp b/ReactCommon/react/renderer/componentregistry/ComponentDescriptorRegistry.cpp index 8499e3ca24c..ede6ca80937 100644 --- a/ReactCommon/react/renderer/componentregistry/ComponentDescriptorRegistry.cpp +++ b/ReactCommon/react/renderer/componentregistry/ComponentDescriptorRegistry.cpp @@ -91,10 +91,6 @@ static std::string componentNameByReactViewName(std::string viewName) { return "PullToRefreshView"; } - if (viewName == "AndroidProgressBar") { - return "ActivityIndicatorView"; - } - // We need this temporarily for testing purposes until we have proper // implementation of core components. if (viewName == "ScrollContentView" || diff --git a/ReactCommon/react/renderer/components/progressbar/BUCK b/ReactCommon/react/renderer/components/progressbar/BUCK new file mode 100644 index 00000000000..8afe40dc616 --- /dev/null +++ b/ReactCommon/react/renderer/components/progressbar/BUCK @@ -0,0 +1,121 @@ +load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") +load( + "//tools/build_defs/oss:rn_defs.bzl", + "ANDROID", + "APPLE", + "CXX", + "YOGA_CXX_TARGET", + "fb_xplat_cxx_test", + "get_apple_compiler_flags", + "get_apple_inspector_flags", + "react_native_target", + "react_native_xplat_target", + "rn_xplat_cxx_library", + "subdir_glob", +) + +APPLE_COMPILER_FLAGS = get_apple_compiler_flags() + +rn_xplat_cxx_library( + name = "androidprogressbar", + srcs = glob( + ["**/*.cpp"], + exclude = glob([ + "tests/**/*.cpp", + ]), + ), + headers = [], + header_namespace = "", + exported_headers = subdir_glob( + [ + ("", "*.h"), + ], + prefix = "react/renderer/components/progressbar", + ), + compiler_flags = [ + "-fexceptions", + "-frtti", + "-std=c++14", + "-Wall", + ], + cxx_tests = [":tests"], + fbandroid_deps = [ + react_native_target("jni/react/jni:jni"), + ], + fbandroid_exported_headers = subdir_glob( + [ + ("", "*.h"), + ("android/react/renderer/components/progressbar", "*.h"), + ], + prefix = "react/renderer/components/progressbar", + ), + fbandroid_headers = glob( + ["android/react/renderer/components/progressbar/*.h"], + ), + fbandroid_srcs = glob( + ["android/react/renderer/components/progressbar/*.cpp"], + ), + fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, + fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), + force_static = True, + ios_exported_headers = subdir_glob( + [ + ("", "*.h"), + ("platform/ios", "*.h"), + ], + prefix = "react/renderer/components/progressbar", + ), + ios_headers = glob( + ["platform/ios/*.h"], + ), + ios_srcs = glob( + ["platform/ios/*.cpp"], + ), + labels = ["supermodule:xplat/default/public.react_native.infra"], + platforms = (ANDROID, APPLE, CXX), + preprocessor_flags = [ + "-DLOG_TAG=\"ReactNative\"", + "-DWITH_FBSYSTRACE=1", + ], + visibility = ["PUBLIC"], + deps = [ + "//third-party/glog:glog", + "//xplat/fbsystrace:fbsystrace", + "//xplat/folly:headers_only", + "//xplat/folly:memory", + "//xplat/folly:molly", + YOGA_CXX_TARGET, + react_native_xplat_target("react/renderer/debug:debug"), + react_native_xplat_target("react/renderer/core:core"), + react_native_xplat_target("react/renderer/components/image:image"), + react_native_xplat_target("react/renderer/components/view:view"), + react_native_xplat_target("react/renderer/graphics:graphics"), + react_native_xplat_target("react/renderer/imagemanager:imagemanager"), + react_native_xplat_target("react/renderer/uimanager:uimanager"), + react_native_xplat_target("react/renderer/componentregistry:componentregistry"), + "//xplat/js/react-native-github:generated_components-rncore", + ], +) + +fb_xplat_cxx_test( + name = "tests", + srcs = glob(["tests/**/*.cpp"]), + headers = glob(["tests/**/*.h"]), + compiler_flags = [ + "-fexceptions", + "-frtti", + "-std=c++14", + "-Wall", + ], + contacts = ["oncall+react_native@xmail.facebook.com"], + platforms = ( + # `Apple` and `Android` flavors are disabled because the module (built with those flavors) requires Emulator/Simulator (which is expensive and slow). At the same time, we don't really have tests here. + # ANDROID, + # APPLE, + CXX, + ), + deps = [ + "//xplat/folly:molly", + "//xplat/third-party/gmock:gtest", + ], +) diff --git a/ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/AndroidProgressBarComponentDescriptor.h b/ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/AndroidProgressBarComponentDescriptor.h new file mode 100644 index 00000000000..4b3802666a1 --- /dev/null +++ b/ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/AndroidProgressBarComponentDescriptor.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include "AndroidProgressBarMeasurementsManager.h" +#include "AndroidProgressBarShadowNode.h" + +namespace facebook { +namespace react { + +/* + * Descriptor for component. + */ +class AndroidProgressBarComponentDescriptor final + : public ConcreteComponentDescriptor { + public: + AndroidProgressBarComponentDescriptor( + ComponentDescriptorParameters const ¶meters) + : ConcreteComponentDescriptor(parameters), + measurementsManager_( + std::make_shared( + contextContainer_)) {} + + void adopt(UnsharedShadowNode shadowNode) const override { + ConcreteComponentDescriptor::adopt(shadowNode); + + assert(std::dynamic_pointer_cast(shadowNode)); + auto androidProgressBarShadowNode = + std::static_pointer_cast(shadowNode); + + // `AndroidProgressBarShadowNode` uses + // `AndroidProgressBarMeasurementsManager` to provide measurements to Yoga. + androidProgressBarShadowNode->setAndroidProgressBarMeasurementsManager( + measurementsManager_); + + // All `AndroidProgressBarShadowNode`s must have leaf Yoga nodes with + // properly setup measure function. + androidProgressBarShadowNode->enableMeasurement(); + } + + private: + const std::shared_ptr + measurementsManager_; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/AndroidProgressBarMeasurementsManager.cpp b/ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/AndroidProgressBarMeasurementsManager.cpp new file mode 100644 index 00000000000..9e4c2fdbe92 --- /dev/null +++ b/ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/AndroidProgressBarMeasurementsManager.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "AndroidProgressBarMeasurementsManager.h" + +#include +#include +#include + +using namespace facebook::jni; + +namespace facebook { +namespace react { + +Size AndroidProgressBarMeasurementsManager::measure( + SurfaceId surfaceId, + AndroidProgressBarProps props, + LayoutConstraints layoutConstraints) const { + { + std::lock_guard lock(mutex_); + if (hasBeenMeasured_) { + return cachedMeasurement_; + } + } + + const jni::global_ref &fabricUIManager = + contextContainer_->at>("FabricUIManager"); + + static auto measure = facebook::jni::findClassStatic( + "com/facebook/react/fabric/FabricUIManager") + ->getMethod("measure"); + + auto minimumSize = layoutConstraints.minimumSize; + auto maximumSize = layoutConstraints.maximumSize; + + local_ref componentName = make_jstring("AndroidProgressBar"); + + auto serialiazedProps = toDynamic(props); + local_ref propsRNM = + ReadableNativeMap::newObjectCxxArgs(serialiazedProps); + local_ref propsRM = + make_local(reinterpret_cast(propsRNM.get())); + + auto measurement = yogaMeassureToSize(measure( + fabricUIManager, + surfaceId, + componentName.get(), + nullptr, + propsRM.get(), + nullptr, + minimumSize.width, + maximumSize.width, + minimumSize.height, + maximumSize.height)); + + std::lock_guard lock(mutex_); + cachedMeasurement_ = measurement; + return measurement; +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/AndroidProgressBarMeasurementsManager.h b/ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/AndroidProgressBarMeasurementsManager.h new file mode 100644 index 00000000000..c2eb8191f77 --- /dev/null +++ b/ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/AndroidProgressBarMeasurementsManager.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace facebook { +namespace react { + +class AndroidProgressBarMeasurementsManager { + public: + AndroidProgressBarMeasurementsManager( + const ContextContainer::Shared &contextContainer) + : contextContainer_(contextContainer) {} + + Size measure( + SurfaceId surfaceId, + AndroidProgressBarProps props, + LayoutConstraints layoutConstraints) const; + + private: + const ContextContainer::Shared contextContainer_; + mutable std::mutex mutex_; + mutable bool hasBeenMeasured_ = false; + mutable Size cachedMeasurement_{}; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/AndroidProgressBarShadowNode.cpp b/ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/AndroidProgressBarShadowNode.cpp new file mode 100644 index 00000000000..0ab3970a71f --- /dev/null +++ b/ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/AndroidProgressBarShadowNode.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "AndroidProgressBarShadowNode.h" + +#include +#include + +namespace facebook { +namespace react { + +extern const char AndroidProgressBarComponentName[] = "AndroidProgressBar"; + +void AndroidProgressBarShadowNode::setAndroidProgressBarMeasurementsManager( + const std::shared_ptr + &measurementsManager) { + ensureUnsealed(); + measurementsManager_ = measurementsManager; +} + +#pragma mark - LayoutableShadowNode + +Size AndroidProgressBarShadowNode::measureContent( + LayoutContext const &layoutContext, + LayoutConstraints const &layoutConstraints) const { + return measurementsManager_->measure( + getSurfaceId(), getConcreteProps(), layoutConstraints); +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/AndroidProgressBarShadowNode.h b/ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/AndroidProgressBarShadowNode.h new file mode 100644 index 00000000000..e13f547eb66 --- /dev/null +++ b/ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/AndroidProgressBarShadowNode.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include + +namespace facebook { +namespace react { + +extern const char AndroidProgressBarComponentName[]; + +/* + * `ShadowNode` for component. + */ +class AndroidProgressBarShadowNode final : public ConcreteViewShadowNode< + AndroidProgressBarComponentName, + AndroidProgressBarProps, + AndroidProgressBarEventEmitter> { + public: + using ConcreteViewShadowNode::ConcreteViewShadowNode; + + // Associates a shared `AndroidProgressBarMeasurementsManager` with the node. + void setAndroidProgressBarMeasurementsManager( + const std::shared_ptr + &measurementsManager); + +#pragma mark - LayoutableShadowNode + + Size measureContent( + LayoutContext const &layoutContext, + LayoutConstraints const &layoutConstraints) const override; + + private: + std::shared_ptr measurementsManager_; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/conversions.h b/ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/conversions.h new file mode 100644 index 00000000000..cb70dc61169 --- /dev/null +++ b/ReactCommon/react/renderer/components/progressbar/android/react/renderer/components/progressbar/conversions.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include + +namespace facebook { +namespace react { + +#ifdef ANDROID +inline folly::dynamic toDynamic(AndroidProgressBarProps const &props) { + folly::dynamic serializedProps = folly::dynamic::object(); + serializedProps["styleAttr"] = props.styleAttr; + serializedProps["typeAttr"] = props.typeAttr; + serializedProps["indeterminate"] = props.indeterminate; + serializedProps["progress"] = props.progress; + serializedProps["animating"] = props.animating; + serializedProps["color"] = toDynamic(props.color); + serializedProps["testID"] = props.testID; + return serializedProps; +} +#endif + +} // namespace react +} // namespace facebook