diff --git a/packages/react-native/Libraries/Image/Image.android.js b/packages/react-native/Libraries/Image/Image.android.js index 06cd6677bafd28..41d36222863e47 100644 --- a/packages/react-native/Libraries/Image/Image.android.js +++ b/packages/react-native/Libraries/Image/Image.android.js @@ -186,9 +186,9 @@ let BaseImage: AbstractImageAndroid = ({ /* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was found * when making Flow check .android.js files. */ headers: (source?.[0]?.headers || source?.headers: ?{[string]: string}), - defaultSource: defaultSource ? defaultSource.uri : null, + defaultSource: defaultSource ? defaultSource : null, loadingIndicatorSrc: loadingIndicatorSource - ? loadingIndicatorSource.uri + ? loadingIndicatorSource : null, accessibilityLabel: props['aria-label'] ?? props.accessibilityLabel ?? props.alt, diff --git a/packages/react-native/Libraries/Image/ImageViewNativeComponent.js b/packages/react-native/Libraries/Image/ImageViewNativeComponent.js index 6ad7da9a8db642..f40dfd68c2af61 100644 --- a/packages/react-native/Libraries/Image/ImageViewNativeComponent.js +++ b/packages/react-native/Libraries/Image/ImageViewNativeComponent.js @@ -42,7 +42,7 @@ type ImageHostComponentProps = $ReadOnly<{ | ?$ReadOnlyArray>, headers?: ?{[string]: string}, defaultSource?: ?ImageSource | ?string, - loadingIndicatorSrc?: ?string, + loadingIndicatorSrc?: ?ImageSource | ?string, }>; interface NativeCommands { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index 88b97458b90760..b72afb96e00e74 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -170,7 +170,7 @@ public class FabricUIManager FabricSoLoader.staticInit(); } - @Nullable private FabricUIManagerBinding mBinding; + @Nullable public FabricUIManagerBinding mBinding; private final ReactApplicationContext mReactApplicationContext; private final MountingManager mMountingManager; private final FabricEventDispatcher mEventDispatcher; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.kt index 39399a66744f39..ec3949e24aff8f 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.kt @@ -955,8 +955,8 @@ public class ReactHostImpl( // as TurboModuleManager will handle any concurrent access instance.initializeEagerTurboModules() - log(method, "Loading JS Bundle") - instance.loadJSBundle(bundleLoader) +// log(method, "Loading JS Bundle") +// instance.loadJSBundle(bundleLoader) log(method, "Calling DevSupportManagerBase.onNewReactContextCreated(reactContext)") devSupportManager.onNewReactContextCreated(reactContext) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.kt index 403583f205acf3..34b708feda967a 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.kt @@ -130,13 +130,13 @@ public constructor( } @ReactProp(name = "defaultSource") - public fun setDefaultSource(view: ReactImageView, source: String?) { + public fun setDefaultSource(view: ReactImageView, source: ReadableMap?) { view.setDefaultSource(source) } // In JS this is Image.props.loadingIndicatorSource.uri @ReactProp(name = "loadingIndicatorSrc") - public fun setLoadingIndicatorSource(view: ReactImageView, source: String?) { + public fun setLoadingIndicatorSource(view: ReactImageView, source: ReadableMap?) { view.setLoadingIndicatorSource(source) } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.kt index db7a890ffdaa9d..da420580cac1d5 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.kt @@ -45,9 +45,11 @@ import com.facebook.imagepipeline.request.ImageRequest import com.facebook.imagepipeline.request.ImageRequest.RequestLevel import com.facebook.imagepipeline.request.ImageRequestBuilder import com.facebook.imagepipeline.request.Postprocessor +import com.facebook.react.BuildConfig import com.facebook.react.bridge.ReactContext import com.facebook.react.bridge.ReadableArray import com.facebook.react.bridge.ReadableMap +import com.facebook.react.bridge.SoftAssertions import com.facebook.react.common.annotations.UnstableReactNativeAPI import com.facebook.react.common.annotations.VisibleForTesting import com.facebook.react.common.build.ReactBuildConfig @@ -280,45 +282,12 @@ public class ReactImageView( } else if (sources.size() == 1) { // Optimize for the case where we have just one uri, case in which we don't need the sizes val source = checkNotNull(sources.getMap(0)) - val cacheControl = computeCacheControl(source.getString("cache")) - var imageSource = ImageSource(context, source.getString("uri"), cacheControl = cacheControl) - if (Uri.EMPTY == imageSource.uri) { - warnImageSource(source.getString("uri")) - imageSource = getTransparentBitmapImageSource(context) - } + val imageSource = readableMapToImageSource(source, includeSize = false) tmpSources.add(imageSource) } else { for (idx in 0 until sources.size()) { val source = sources.getMap(idx) ?: continue - val cacheControl = computeCacheControl(source.getString("cache")) - val uri = source.getString("uri") - val isForceCached = if (source.hasKey("isForceCached")) { - source.getBoolean("isForceCached") - } else { - false - } - val width = if (source.hasKey("width")) { - source.getDouble("width") - } else { - 0.0 - } - val height = if (source.hasKey("height")) { - source.getDouble("height") - } else { - 0.0 - } - var imageSource = - ImageSource( - context, - uri, - width, - height, - cacheControl, - isForceCached) - if (Uri.EMPTY == imageSource.uri) { - warnImageSource(uri) - imageSource = getTransparentBitmapImageSource(context) - } + val imageSource = readableMapToImageSource(source, includeSize = true) tmpSources.add(imageSource) } } @@ -351,16 +320,36 @@ public class ReactImageView( } } - public fun setDefaultSource(name: String?) { - val newDefaultDrawable = ResourceDrawableIdHelper.getResourceDrawable(context, name) + public fun setDefaultSource(source: ReadableMap?) { + var newDefaultDrawable: Drawable? = null + if (source != null) { + val imageSource = readableMapToImageSource(source, false) + SoftAssertions.assertCondition( + !BuildConfig.DEBUG && !imageSource.isResource, + "ReactImageView: Only local resources can be used as default image. Uri: ${imageSource.uri}" + ) + + newDefaultDrawable = ResourceDrawableIdHelper.getResourceDrawable(context, imageSource.source) + } + if (defaultImageDrawable != newDefaultDrawable) { defaultImageDrawable = newDefaultDrawable isDirty = true } } - public fun setLoadingIndicatorSource(name: String?) { - val drawable = ResourceDrawableIdHelper.getResourceDrawable(context, name) + public fun setLoadingIndicatorSource(source: ReadableMap?) { + var drawable: Drawable? = null + if (source != null) { + val imageSource = readableMapToImageSource(source, false) + SoftAssertions.assertCondition( + !BuildConfig.DEBUG && !imageSource.isResource, + "ReactImageView: Only local resources can be used as default image. Uri: ${imageSource.uri}" + ) + + drawable = ResourceDrawableIdHelper.getResourceDrawable(context, imageSource.source) + } + val newLoadingIndicatorSource = drawable?.let { AutoRotateDrawable(it, 1000) } if (loadingImageDrawable != newLoadingIndicatorSource) { loadingImageDrawable = newLoadingIndicatorSource @@ -577,6 +566,29 @@ public class ReactImageView( private val isTiled: Boolean get() = tileMode != TileMode.CLAMP + private fun readableMapToImageSource(source: ReadableMap, includeSize: Boolean = false): ImageSource { + val cacheControl = computeCacheControl(source.getString("cache")) + val uri = source.getString("uri") + var imageSource = if (includeSize) { + ImageSource( + context, + uri, + source.getDouble("width"), + source.getDouble("height"), + cacheControl, + ) + } else { + ImageSource(context, uri, cacheControl = cacheControl) + } + + if (Uri.EMPTY == imageSource.uri) { + warnImageSource(uri) + imageSource = getTransparentBitmapImageSource(context) + } + + return imageSource + } + private fun setSourceImage() { imageSource = null if (sources.isEmpty()) { diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/BaseTextProps.cpp b/packages/react-native/ReactCommon/react/renderer/components/text/BaseTextProps.cpp index ee1f1a8327aefb..2239af4a23a497 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/BaseTextProps.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/BaseTextProps.cpp @@ -426,19 +426,19 @@ void BaseTextProps::appendTextAttributesProps( if (textAttributes.fontWeight != oldProps->textAttributes.fontWeight) { result["fontWeight"] = textAttributes.fontWeight.has_value() ? toString(textAttributes.fontWeight.value()) - : nullptr; + : folly::dynamic(nullptr); } if (textAttributes.fontStyle != oldProps->textAttributes.fontStyle) { result["fontStyle"] = textAttributes.fontStyle.has_value() ? toString(textAttributes.fontStyle.value()) - : nullptr; + : folly::dynamic(nullptr); } if (textAttributes.fontVariant != oldProps->textAttributes.fontVariant) { result["fontVariant"] = textAttributes.fontVariant.has_value() ? toString(textAttributes.fontVariant.value()) - : nullptr; + : folly::dynamic(nullptr); } if (textAttributes.allowFontScaling != @@ -458,7 +458,7 @@ void BaseTextProps::appendTextAttributesProps( oldProps->textAttributes.dynamicTypeRamp) { result["dynamicTypeRamp"] = textAttributes.dynamicTypeRamp.has_value() ? toString(textAttributes.dynamicTypeRamp.value()) - : nullptr; + : folly::dynamic(nullptr); } if (!floatEquality( @@ -470,7 +470,7 @@ void BaseTextProps::appendTextAttributesProps( if (textAttributes.textTransform != oldProps->textAttributes.textTransform) { result["textTransform"] = textAttributes.textTransform.has_value() ? toString(textAttributes.textTransform.value()) - : nullptr; + : folly::dynamic(nullptr); } if (!floatEquality( @@ -481,7 +481,7 @@ void BaseTextProps::appendTextAttributesProps( if (textAttributes.alignment != oldProps->textAttributes.alignment) { result["textAlign"] = textAttributes.alignment.has_value() ? toString(textAttributes.alignment.value()) - : nullptr; + : folly::dynamic(nullptr); } if (textAttributes.baseWritingDirection != @@ -489,7 +489,7 @@ void BaseTextProps::appendTextAttributesProps( result["baseWritingDirection"] = textAttributes.baseWritingDirection.has_value() ? toString(textAttributes.baseWritingDirection.value()) - : nullptr; + : folly::dynamic(nullptr); } if (textAttributes.lineBreakStrategy != @@ -497,13 +497,13 @@ void BaseTextProps::appendTextAttributesProps( result["lineBreakStrategyIOS"] = textAttributes.lineBreakStrategy.has_value() ? toString(textAttributes.lineBreakStrategy.value()) - : nullptr; + : folly::dynamic(nullptr); } if (textAttributes.lineBreakMode != oldProps->textAttributes.lineBreakMode) { result["lineBreakModeIOS"] = textAttributes.lineBreakMode.has_value() ? toString(textAttributes.lineBreakMode.value()) - : nullptr; + : folly::dynamic(nullptr); } if (textAttributes.textDecorationColor != @@ -516,7 +516,7 @@ void BaseTextProps::appendTextAttributesProps( result["textDecorationLine"] = textAttributes.textDecorationLineType.has_value() ? toString(textAttributes.textDecorationLineType.value()) - : nullptr; + : folly::dynamic(nullptr); } if (textAttributes.textDecorationStyle != @@ -524,14 +524,14 @@ void BaseTextProps::appendTextAttributesProps( result["textDecorationStyle"] = textAttributes.textDecorationStyle.has_value() ? toString(textAttributes.textDecorationStyle.value()) - : nullptr; + : folly::dynamic(nullptr); } if (textAttributes.textShadowOffset != oldProps->textAttributes.textShadowOffset) { result["textShadowOffset"] = textAttributes.textShadowOffset.has_value() ? toDynamic(textAttributes.textShadowOffset.value()) - : nullptr; + : folly::dynamic(nullptr); } if (!floatEquality( @@ -561,13 +561,13 @@ void BaseTextProps::appendTextAttributesProps( oldProps->textAttributes.accessibilityRole) { result["accessibilityRole"] = textAttributes.accessibilityRole.has_value() ? toString(textAttributes.accessibilityRole.value()) - : nullptr; + : folly::dynamic(nullptr); } if (textAttributes.role != oldProps->textAttributes.role) { result["role"] = textAttributes.role.has_value() ? toString(textAttributes.role.value()) - : nullptr; + : folly::dynamic(nullptr); } if (textAttributes.opacity != oldProps->textAttributes.opacity) { diff --git a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.cpp b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.cpp index 2ae67fa33dfd76..3de86dfb2da0f5 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.cpp @@ -230,7 +230,7 @@ folly::dynamic ParagraphProps::getDiffProps(const Props* prevProps) const { result["textAlignVertical"] = paragraphAttributes.textAlignVertical.has_value() ? toString(paragraphAttributes.textAlignVertical.value()) - : nullptr; + : folly::dynamic(nullptr);; } if (isSelectable != oldProps->isSelectable) { diff --git a/packages/react-native/ReactCommon/react/renderer/imagemanager/primitives.h b/packages/react-native/ReactCommon/react/renderer/imagemanager/primitives.h index a89807bcb21568..7866ee8d9ebd68 100644 --- a/packages/react-native/ReactCommon/react/renderer/imagemanager/primitives.h +++ b/packages/react-native/ReactCommon/react/renderer/imagemanager/primitives.h @@ -59,9 +59,8 @@ class ImageSource { imageSourceResult["scale"] = scale; folly::dynamic sizeResult = folly::dynamic::object(); - sizeResult["width"] = size.width; - sizeResult["height"] = size.height; - imageSourceResult["size"] = sizeResult; + imageSourceResult["width"] = size.width; + imageSourceResult["height"] = size.height; imageSourceResult["body"] = body; imageSourceResult["method"] = method; diff --git a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterActivity.kt b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterActivity.kt index a40994fd3ecf9d..bde2a1b77d8ea3 100644 --- a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterActivity.kt +++ b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterActivity.kt @@ -7,101 +7,51 @@ package com.facebook.react.uiapp -import android.content.res.Configuration -import android.graphics.Color import android.os.Bundle -import android.view.View -import android.widget.FrameLayout -import androidx.core.graphics.drawable.toDrawable -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat -import com.facebook.react.FBRNTesterEndToEndHelper +import android.util.Log import com.facebook.react.ReactActivity -import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled -import com.facebook.react.defaults.DefaultReactActivityDelegate -import java.io.FileDescriptor -import java.io.PrintWriter - +import com.facebook.react.ReactInstanceEventListener +import com.facebook.react.bridge.ReactContext +import com.facebook.react.fabric.FabricUIManager +import com.facebook.react.uimanager.UIManagerHelper +import com.facebook.react.uimanager.common.UIManagerType + + +/** + * Reproduction case: + * A-B-C commit/mount chain, but commit B gets skipped + * What does skipped mean? + * - Does it mean the shadow tree commit is skipped + * - Or does it mean that executing the mount gets skipped? + * + * What i basically need or want is manual access to the UIManager.cpp and control all operations from there 😈 + */ internal class RNTesterActivity : ReactActivity() { - class RNTesterActivityDelegate(val activity: ReactActivity, mainComponentName: String) : - DefaultReactActivityDelegate(activity, mainComponentName, fabricEnabled) { - private val PARAM_ROUTE = "route" - private lateinit var initialProps: Bundle - - override fun onCreate(savedInstanceState: Bundle?) { - // Get remote param before calling super which uses it - val bundle = activity.intent?.extras - - if (bundle != null && bundle.containsKey(PARAM_ROUTE)) { - val routeUri = "rntester://example/${bundle.getString(PARAM_ROUTE)}Example" - initialProps = Bundle().apply { putString("exampleFromAppetizeParams", routeUri) } - } - FBRNTesterEndToEndHelper.onCreate(activity.application) - super.onCreate(savedInstanceState) - } - - override fun getLaunchOptions() = - if (this::initialProps.isInitialized) initialProps else Bundle() - } - - // set background color so it will show below transparent system bars on forced edge-to-edge - private fun maybeUpdateBackgroundColor() { - val isDarkMode = - resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == - Configuration.UI_MODE_NIGHT_YES - - val color = - if (isDarkMode) { - Color.rgb(11, 6, 0) - } else { - Color.rgb(243, 248, 255) - } - - window?.setBackgroundDrawable(color.toDrawable()) - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + + reactHost.addReactInstanceEventListener(object : ReactInstanceEventListener { - fullyDrawnReporter.addReporter() - maybeUpdateBackgroundColor() - - // register insets listener to update margins on the ReactRootView to avoid overlap w/ system - // bars - getReactDelegate()?.reactRootView?.let { rootView -> - val insetsType: Int = - WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() + override fun onReactContextInitialized(context: ReactContext) { + // Now we should be able to get dirty! + val uiManager = UIManagerHelper.getUIManager(context, UIManagerType.FABRIC) + ?: throw IllegalStateException("Fabric UIManager not found") - val windowInsetsListener = { view: View, windowInsets: WindowInsetsCompat -> - val insets = windowInsets.getInsets(insetsType) + val fabricUIManager = uiManager as? FabricUIManager + ?: throw IllegalStateException("UIManager is not a FabricUIManager") - (view.layoutParams as FrameLayout.LayoutParams).apply { - setMargins(insets.left, insets.top, insets.right, insets.bottom) - } - - WindowInsetsCompat.CONSUMED + runTest(fabricUIManager) } - ViewCompat.setOnApplyWindowInsetsListener(rootView, windowInsetsListener) - } - } - - override fun onConfigurationChanged(newConfig: Configuration) { - super.onConfigurationChanged(newConfig) - - // update background color on UI mode change - maybeUpdateBackgroundColor() + }) + + Log.d("HannoDebug", "RNTesterActivity onCreate"); } - override fun createReactActivityDelegate() = RNTesterActivityDelegate(this, mainComponentName) - override fun getMainComponentName() = "RNTesterApp" + override fun getMainComponentName() = "" - override fun dump( - prefix: String, - fd: FileDescriptor?, - writer: PrintWriter, - args: Array? - ) { - FBRNTesterEndToEndHelper.maybeDump(prefix, writer, args) + companion object { + @JvmStatic + external fun runTest(uiManager: FabricUIManager) } } diff --git a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.kt b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.kt index 0d9f7ff4146904..f4f7f603e7123d 100644 --- a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.kt +++ b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.kt @@ -49,7 +49,7 @@ internal class RNTesterApplication : Application(), ReactApplication { public override fun getBundleAssetName(): String = BuildConfig.BUNDLE_ASSET_NAME - override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG + override fun getUseDeveloperSupport(): Boolean = false public override fun getPackages(): List { return listOf( diff --git a/packages/rn-tester/android/app/src/main/jni/CMakeLists.txt b/packages/rn-tester/android/app/src/main/jni/CMakeLists.txt index 808c4ba3b7d60d..327603f0c97e3f 100644 --- a/packages/rn-tester/android/app/src/main/jni/CMakeLists.txt +++ b/packages/rn-tester/android/app/src/main/jni/CMakeLists.txt @@ -18,4 +18,5 @@ target_link_libraries(${CMAKE_PROJECT_NAME} sampleturbomodule) # RN Tester needs to link against the NativeCxxModuleExample target_link_libraries(${CMAKE_PROJECT_NAME} - nativecxxmoduleexample) + nativecxxmoduleexample + log) diff --git a/packages/rn-tester/android/app/src/main/jni/OnLoad.cpp b/packages/rn-tester/android/app/src/main/jni/OnLoad.cpp index 803d4bbc50c404..e1c9dd8f5433e0 100644 --- a/packages/rn-tester/android/app/src/main/jni/OnLoad.cpp +++ b/packages/rn-tester/android/app/src/main/jni/OnLoad.cpp @@ -13,6 +13,11 @@ #include #include #include +#include +#include +#include + +#include #ifdef REACT_NATIVE_APP_CODEGEN_HEADER #include REACT_NATIVE_APP_CODEGEN_HEADER @@ -24,6 +29,31 @@ namespace facebook { namespace react { +struct JRNTesterActivity : public jni::JavaClass { + static constexpr const char* kJavaDescriptor = + "Lcom/facebook/react/uiapp/RNTesterActivity;"; + + static void runTest( + jni::alias_ref, + jni::alias_ref javaUIManager) { + FabricUIManagerBinding* uiManagerBinding = javaUIManager->getBinding(); + if (uiManagerBinding == nullptr) { + __android_log_print(ANDROID_LOG_ERROR, "RNTesterActivity", "FabricUIManagerBinding is null"); + return; + } + + auto scheduler = uiManagerBinding->getScheduler(); + auto uiManager = scheduler->getUIManager(); + __android_log_print(ANDROID_LOG_INFO, "RNTesterActivity", "Successfully got UIManager!"); + } + + static void registerNatives() { + javaClassStatic()->registerNatives({ + makeNativeMethod("runTest", JRNTesterActivity::runTest), + }); + } +}; + void registerComponents( std::shared_ptr registry) { #ifdef REACT_NATIVE_APP_COMPONENT_REGISTRATION @@ -75,5 +105,6 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) { registerComponentDescriptorsFromEntryPoint = &facebook::react::registerComponents; facebook::react::SampleTurboModuleJSIBindings::registerNatives(); + facebook::react::JRNTesterActivity::registerNatives(); }); }