mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
9ad5e72b77
Summary: This is an incomplete effort to migrate from libfb to libfbjni. This is needed to restore the compatibility with Flipper and other FB Android projects that make use of FBJNI. Effectively, the outcome is that `fbjni` no longer has a checked-in copy here, but instead relies on the public artifacts published at github.com/facebookincubator/fbjni that can be deduplicated at build-time. **A non-exhaustive list of tasks:** * [X] Gradle builds the SDK and RNTester for Android. * [X] Buck build for rntester works in OSS. * [ ] Move from `java-only` release to full `fbjni` release. This requires finding a solution for stripping out `.so` files that the old `Android.mk` insists on including in the final artifacts and will clash with the full distribution. * [ ] Import this and fix potential internal build issues. * [ ] Verify that the changes made to the Hermes integration don't have any unintended consequences. ## Changelog [Android] [Changed] - Migrated from libfb to libfbjni for JNI calls Pull Request resolved: https://github.com/facebook/react-native/pull/27729 Test Plan: - CI is already passing again for Gradle and Buck in OSS. - After applying the following patch, RNTester builds and works with the latest Flipper SDK: ``` diff --git a/RNTester/android/app/build.gradle b/RNTester/android/app/build.gradle index b8a6437d7..eac942104 100644 --- a/RNTester/android/app/build.gradle +++ b/RNTester/android/app/build.gradle @@ -170,10 +170,19 @@ dependencies { debugImplementation files(hermesPath + "hermes-debug.aar") releaseImplementation files(hermesPath + "hermes-release.aar") - debugImplementation("com.facebook.flipper:flipper:0.23.4") { + debugImplementation("com.facebook.flipper🐬+") { exclude group:'com.facebook.yoga' - exclude group:'com.facebook.flipper', module: 'fbjni' - exclude group:'com.facebook.litho', module: 'litho-annotations' + exclude group:'com.facebook.fbjni' + } + + debugImplementation("com.facebook.flipper:flipper-network-plugin:+") { + exclude group:'com.facebook.yoga' + exclude group:'com.facebook.fbjni' + } + + debugImplementation("com.facebook.flipper:flipper-fresco-plugin:+") { + exclude group:'com.facebook.yoga' + exclude group:'com.facebook.fbjni' } if (useIntlJsc) { ``` Reviewed By: mdvacca Differential Revision: D19345270 Pulled By: passy fbshipit-source-id: 33811e7f97f44f2ec5999e1c35339909dc4fd3b1
166 lines
5.8 KiB
C++
166 lines
5.8 KiB
C++
/*
|
|
* 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 "AndroidTextInputShadowNode.h"
|
|
|
|
#include <fbjni/fbjni.h>
|
|
#include <react/attributedstring/AttributedStringBox.h>
|
|
#include <react/attributedstring/TextAttributes.h>
|
|
#include <react/components/text/BaseTextShadowNode.h>
|
|
#include <react/core/LayoutConstraints.h>
|
|
#include <react/core/LayoutContext.h>
|
|
#include <react/core/conversions.h>
|
|
#include <react/jni/ReadableNativeMap.h>
|
|
|
|
using namespace facebook::jni;
|
|
|
|
namespace facebook {
|
|
namespace react {
|
|
|
|
extern const char AndroidTextInputComponentName[] = "AndroidTextInput";
|
|
|
|
void AndroidTextInputShadowNode::setContextContainer(
|
|
ContextContainer *contextContainer) {
|
|
ensureUnsealed();
|
|
contextContainer_ = contextContainer;
|
|
}
|
|
|
|
AttributedString AndroidTextInputShadowNode::getAttributedString() const {
|
|
// Use BaseTextShadowNode to get attributed string from children
|
|
auto childTextAttributes = TextAttributes::defaultTextAttributes();
|
|
childTextAttributes.apply(getProps()->textAttributes);
|
|
auto attributedString =
|
|
BaseTextShadowNode::getAttributedString(childTextAttributes, *this);
|
|
|
|
// BaseTextShadowNode only gets children. We must detect and prepend text
|
|
// value attributes manually.
|
|
if (!getProps()->text.empty()) {
|
|
auto textAttributes = TextAttributes::defaultTextAttributes();
|
|
textAttributes.apply(getProps()->textAttributes);
|
|
auto fragment = AttributedString::Fragment{};
|
|
fragment.string = getProps()->text;
|
|
fragment.textAttributes = textAttributes;
|
|
// If the TextInput opacity is 0 < n < 1, the opacity of the TextInput and
|
|
// text value's background will stack. This is a hack/workaround to prevent
|
|
// that effect.
|
|
fragment.textAttributes.backgroundColor = clearColor();
|
|
fragment.parentShadowView = ShadowView(*this);
|
|
attributedString.prependFragment(fragment);
|
|
}
|
|
|
|
return attributedString;
|
|
}
|
|
|
|
// For measurement purposes, we want to make sure that there's at least a
|
|
// single character in the string so that the measured height is greater
|
|
// than zero. Otherwise, empty TextInputs with no placeholder don't
|
|
// display at all.
|
|
AttributedString AndroidTextInputShadowNode::getPlaceholderAttributedString()
|
|
const {
|
|
// Return placeholder text, since text and children are empty.
|
|
auto textAttributedString = AttributedString{};
|
|
auto fragment = AttributedString::Fragment{};
|
|
fragment.string = getProps()->placeholder;
|
|
|
|
if (fragment.string.empty()) {
|
|
fragment.string = " ";
|
|
}
|
|
|
|
auto textAttributes = TextAttributes::defaultTextAttributes();
|
|
textAttributes.apply(getProps()->textAttributes);
|
|
|
|
// If there's no text, it's possible that this Fragment isn't actually
|
|
// appended to the AttributedString (see implementation of appendFragment)
|
|
fragment.textAttributes = textAttributes;
|
|
fragment.parentShadowView = ShadowView(*this);
|
|
textAttributedString.appendFragment(fragment);
|
|
|
|
return textAttributedString;
|
|
}
|
|
|
|
void AndroidTextInputShadowNode::setTextLayoutManager(
|
|
SharedTextLayoutManager textLayoutManager) {
|
|
ensureUnsealed();
|
|
textLayoutManager_ = textLayoutManager;
|
|
}
|
|
|
|
void AndroidTextInputShadowNode::updateStateIfNeeded() {
|
|
ensureUnsealed();
|
|
|
|
auto reactTreeAttributedString = getAttributedString();
|
|
auto const &state = getStateData();
|
|
|
|
assert(textLayoutManager_);
|
|
assert(
|
|
(!state.layoutManager || state.layoutManager == textLayoutManager_) &&
|
|
"`StateData` refers to a different `TextLayoutManager`");
|
|
|
|
// Tree is often out of sync with the value of the TextInput.
|
|
// This is by design - don't change the value of the TextInput in the State,
|
|
// and therefore in Java, unless the tree itself changes.
|
|
if (state.reactTreeAttributedString == reactTreeAttributedString &&
|
|
state.layoutManager == textLayoutManager_) {
|
|
return;
|
|
}
|
|
|
|
// Store default TextAttributes in state.
|
|
// In the case where the TextInput is completely empty (no value, no
|
|
// defaultValue, no placeholder, no children) there are therefore no fragments
|
|
// in the AttributedString, and when State is updated, it needs some way to
|
|
// reconstruct a Fragment with default TextAttributes.
|
|
auto defaultTextAttributes = TextAttributes::defaultTextAttributes();
|
|
defaultTextAttributes.apply(getProps()->textAttributes);
|
|
|
|
// Even if we're here and updating state, it may be only to update the layout
|
|
// manager If that is the case, make sure we don't update text: pass in the
|
|
// current attributedString unchanged, and pass in zero for the "event count"
|
|
// so no changes are applied There's no way to prevent a state update from
|
|
// flowing to Java, so we just ensure it's a noop in those cases.
|
|
setStateData(AndroidTextInputState{
|
|
(state.reactTreeAttributedString == reactTreeAttributedString
|
|
? 0
|
|
: getProps()->mostRecentEventCount),
|
|
(state.reactTreeAttributedString == reactTreeAttributedString
|
|
? state.attributedString
|
|
: reactTreeAttributedString),
|
|
reactTreeAttributedString,
|
|
getProps()->paragraphAttributes,
|
|
defaultTextAttributes,
|
|
ShadowView(*this),
|
|
textLayoutManager_});
|
|
}
|
|
|
|
#pragma mark - LayoutableShadowNode
|
|
|
|
Size AndroidTextInputShadowNode::measure(
|
|
LayoutConstraints layoutConstraints) const {
|
|
auto const &state = getStateData();
|
|
|
|
AttributedString attributedString = state.attributedString;
|
|
|
|
if (attributedString.isEmpty()) {
|
|
attributedString = getPlaceholderAttributedString();
|
|
}
|
|
|
|
if (attributedString.isEmpty()) {
|
|
return {0, 0};
|
|
}
|
|
|
|
return textLayoutManager_->measure(
|
|
AttributedStringBox{attributedString},
|
|
getProps()->paragraphAttributes,
|
|
layoutConstraints);
|
|
}
|
|
|
|
void AndroidTextInputShadowNode::layout(LayoutContext layoutContext) {
|
|
updateStateIfNeeded();
|
|
ConcreteViewShadowNode::layout(layoutContext);
|
|
}
|
|
|
|
} // namespace react
|
|
} // namespace facebook
|