Integrate AndroidSwitch into Fabric on Android

Summary:
In this diff we integrate the Switch component on Android in Fabric. Since the component has a custom measure function, we need to write some C++ to call the measure method in Java.

The component isn't fully functional yet (setNativeProps isn't supported in Fabric) and has some problems with measuring itself. I will fix the component in the next diffs in this stack.

Reviewed By: JoshuaGross

Differential Revision: D17571258

fbshipit-source-id: be4e201495b9b197ddec44ee3484357bfb6225a8
This commit is contained in:
Oleksandr Melnykov 2019-10-03 03:12:46 -07:00 коммит произвёл Facebook Github Bot
Родитель 6aae8e0756
Коммит d0dd1aed29
8 изменённых файлов: 344 добавлений и 3 удалений

Просмотреть файл

@ -43,6 +43,6 @@ type NativeProps = $ReadOnly<{|
onChange?: BubblingEventHandler<SwitchChangeEvent>,
|}>;
export default (codegenNativeComponent<NativeProps>(
'AndroidSwitch',
): HostComponent<NativeProps>);
export default (codegenNativeComponent<NativeProps>('AndroidSwitch', {
interfaceOnly: true,
}): HostComponent<NativeProps>);

Просмотреть файл

@ -8,10 +8,12 @@
// switchview because switch is a keyword
package com.facebook.react.views.switchview;
import android.content.Context;
import android.view.View;
import android.widget.CompoundButton;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.uimanager.LayoutShadowNode;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
@ -177,4 +179,21 @@ public class ReactSwitchManager extends SimpleViewManager<ReactSwitch>
protected ViewManagerDelegate<ReactSwitch> getDelegate() {
return mDelegate;
}
@Override
public long measure(
Context context,
ReadableMap localData,
ReadableMap props,
ReadableMap state,
float width,
YogaMeasureMode widthMode,
float height,
YogaMeasureMode heightMode) {
ReactSwitch view = new ReactSwitch(context);
view.setShowText(false);
int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
view.measure(measureSpec, measureSpec);
return YogaMeasureOutput.make(view.getMeasuredWidth(), view.getMeasuredHeight());
}
}

Просмотреть файл

@ -0,0 +1,96 @@
load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_debug_preprocessor_flags")
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 = "androidswitch",
srcs = glob(
["**/*.cpp"],
exclude = glob(["tests/**/*.cpp"]),
),
headers = glob(
["**/*.h"],
exclude = glob(["tests/**/*.h"]),
),
header_namespace = "",
exported_headers = subdir_glob(
[
("", "*.h"),
("androidswitch", "*.h"),
],
prefix = "react/components/androidswitch",
),
compiler_flags = [
"-fexceptions",
"-frtti",
"-std=c++14",
"-Wall",
],
cxx_tests = [":tests"],
fbandroid_deps = [
react_native_target("jni/react/jni:jni"),
],
fbobjc_compiler_flags = APPLE_COMPILER_FLAGS,
fbobjc_preprocessor_flags = get_debug_preprocessor_flags() + get_apple_inspector_flags(),
force_static = True,
platforms = (ANDROID, APPLE, CXX),
preprocessor_flags = [
"-DLOG_TAG=\"ReactNative\"",
"-DWITH_FBSYSTRACE=1",
],
visibility = ["PUBLIC"],
deps = [
"fbsource//xplat/fbsystrace:fbsystrace",
"fbsource//xplat/folly:headers_only",
"fbsource//xplat/folly:memory",
"fbsource//xplat/folly:molly",
"fbsource//xplat/third-party/glog:glog",
YOGA_CXX_TARGET,
react_native_xplat_target("fabric/debug:debug"),
react_native_xplat_target("fabric/core:core"),
react_native_xplat_target("fabric/graphics:graphics"),
react_native_xplat_target("fabric/components/view:view"),
react_native_xplat_target("fabric/uimanager:uimanager"),
"fbsource//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 depends on `textlayoutmanager` which requires real an Emulator/Simulator to run.
# At the same time, the code of tests does not rely on the simulator capabilities and it would be wasteful to add `fbandroid_use_instrumentation_test = True`.
# (Beware of this option though.)
# ANDROID,
# APPLE,
CXX
),
deps = [
"fbsource//xplat/folly:molly",
"fbsource//xplat/third-party/gmock:gtest",
":androidswitch",
],
)

Просмотреть файл

@ -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 "AndroidSwitchMeasurementsManager.h"
#include "AndroidSwitchShadowNode.h"
#include <react/core/ConcreteComponentDescriptor.h>
namespace facebook {
namespace react {
/*
* Descriptor for <AndroidSwitch> component.
*/
class AndroidSwitchComponentDescriptor final
: public ConcreteComponentDescriptor<AndroidSwitchShadowNode> {
public:
AndroidSwitchComponentDescriptor(
EventDispatcher::Weak eventDispatcher,
ContextContainer::Shared const &contextContainer)
: ConcreteComponentDescriptor(eventDispatcher),
measurementsManager_(std::make_shared<AndroidSwitchMeasurementsManager>(
contextContainer)) {}
void adopt(UnsharedShadowNode shadowNode) const override {
ConcreteComponentDescriptor::adopt(shadowNode);
assert(std::dynamic_pointer_cast<AndroidSwitchShadowNode>(shadowNode));
auto androidSwitchShadowNode =
std::static_pointer_cast<AndroidSwitchShadowNode>(shadowNode);
// `AndroidSwitchShadowNode` uses `AndroidSwitchMeasurementsManager` to
// provide measurements to Yoga.
androidSwitchShadowNode->setAndroidSwitchMeasurementsManager(
measurementsManager_);
// All `AndroidSwitchShadowNode`s must have leaf Yoga nodes with properly
// setup measure function.
androidSwitchShadowNode->enableMeasurement();
}
private:
const std::shared_ptr<AndroidSwitchMeasurementsManager> measurementsManager_;
};
} // namespace react
} // namespace facebook

Просмотреть файл

@ -0,0 +1,65 @@
/**
* 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 "AndroidSwitchMeasurementsManager.h"
#include <fb/fbjni.h>
#include <react/core/conversions.h>
#include <react/jni/ReadableNativeMap.h>
using namespace facebook::jni;
namespace facebook {
namespace react {
Size AndroidSwitchMeasurementsManager::measure(
LayoutConstraints layoutConstraints) const {
{
std::lock_guard<std::mutex> lock(mutex_);
if (hasBeenMeasured_) {
return cachedMeasurement_;
}
}
const jni::global_ref<jobject> &fabricUIManager =
contextContainer_->at<jni::global_ref<jobject>>("FabricUIManager");
static auto measure =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<jlong(
jstring,
ReadableMap::javaobject,
ReadableMap::javaobject,
ReadableMap::javaobject,
jfloat,
jfloat,
jfloat,
jfloat)>("measure");
auto minimumSize = layoutConstraints.minimumSize;
auto maximumSize = layoutConstraints.maximumSize;
local_ref<JString> componentName = make_jstring("AndroidSwitch");
auto measurement = yogaMeassureToSize(measure(
fabricUIManager,
componentName.get(),
nullptr,
nullptr,
nullptr,
minimumSize.width,
maximumSize.width,
minimumSize.height,
maximumSize.height));
std::lock_guard<std::mutex> lock(mutex_);
cachedMeasurement_ = measurement;
return measurement;
}
} // namespace react
} // namespace facebook

Просмотреть файл

@ -0,0 +1,33 @@
/**
* 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 <react/core/ConcreteComponentDescriptor.h>
#include <react/core/LayoutConstraints.h>
#include <react/utils/ContextContainer.h>
namespace facebook {
namespace react {
class AndroidSwitchMeasurementsManager {
public:
AndroidSwitchMeasurementsManager(
const ContextContainer::Shared &contextContainer)
: contextContainer_(contextContainer) {}
Size measure(LayoutConstraints layoutConstraints) const;
private:
const ContextContainer::Shared contextContainer_;
mutable std::mutex mutex_;
mutable bool hasBeenMeasured_ = false;
mutable Size cachedMeasurement_{};
};
} // namespace react
} // namespace facebook

Просмотреть файл

@ -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 "AndroidSwitchShadowNode.h"
namespace facebook {
namespace react {
extern const char AndroidSwitchComponentName[] = "AndroidSwitch";
void AndroidSwitchShadowNode::setAndroidSwitchMeasurementsManager(
const std::shared_ptr<AndroidSwitchMeasurementsManager>
&measurementsManager) {
ensureUnsealed();
measurementsManager_ = measurementsManager;
}
#pragma mark - LayoutableShadowNode
Size AndroidSwitchShadowNode::measure(
LayoutConstraints layoutConstraints) const {
return measurementsManager_->measure(layoutConstraints);
}
} // namespace react
} // namespace facebook

Просмотреть файл

@ -0,0 +1,45 @@
/**
* 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 "AndroidSwitchMeasurementsManager.h"
#include <react/components/rncore/EventEmitters.h>
#include <react/components/rncore/Props.h>
#include <react/components/view/ConcreteViewShadowNode.h>
namespace facebook {
namespace react {
extern const char AndroidSwitchComponentName[];
/*
* `ShadowNode` for <AndroidSwitch> component.
*/
class AndroidSwitchShadowNode final : public ConcreteViewShadowNode<
AndroidSwitchComponentName,
AndroidSwitchProps,
AndroidSwitchEventEmitter> {
public:
using ConcreteViewShadowNode::ConcreteViewShadowNode;
// Associates a shared `AndroidSwitchMeasurementsManager` with the node.
void setAndroidSwitchMeasurementsManager(
const std::shared_ptr<AndroidSwitchMeasurementsManager>
&measurementsManager);
#pragma mark - LayoutableShadowNode
Size measure(LayoutConstraints layoutConstraints) const override;
private:
std::shared_ptr<AndroidSwitchMeasurementsManager> measurementsManager_;
};
} // namespace react
} // namespace facebook