diff --git a/packages/react-native/Package.swift b/packages/react-native/Package.swift index b4d72ba0dc45..4ca8eefdb9ca 100644 --- a/packages/react-native/Package.swift +++ b/packages/react-native/Package.swift @@ -416,6 +416,8 @@ let reactFabricComponents = RNTarget( name: .reactFabricComponents, path: "ReactCommon/react/renderer", excludedPaths: [ + "components/modal/platform/android", + "components/modal/platform/cxx", "components/view/platform/android", "components/view/platform/windows", "components/view/platform/macos", diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/statusbar/StatusBarModule.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/statusbar/StatusBarModule.kt index 07bba9ded311..bd5fcc8022dd 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/statusbar/StatusBarModule.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/statusbar/StatusBarModule.kt @@ -13,8 +13,6 @@ import android.os.Build import android.view.View import android.view.WindowInsetsController import android.view.WindowManager -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat import com.facebook.common.logging.FLog import com.facebook.fbreact.specs.NativeStatusBarManagerAndroidSpec import com.facebook.react.bridge.GuardedRunnable @@ -23,6 +21,7 @@ import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.UiThreadUtil import com.facebook.react.common.ReactConstants import com.facebook.react.module.annotations.ReactModule +import com.facebook.react.uimanager.DisplayMetricsHolder.getStatusBarHeightPx import com.facebook.react.uimanager.PixelUtil import com.facebook.react.views.view.setStatusBarTranslucency import com.facebook.react.views.view.setStatusBarVisibility @@ -34,32 +33,17 @@ internal class StatusBarModule(reactContext: ReactApplicationContext?) : @Suppress("DEPRECATION") override fun getTypedExportedConstants(): Map { + val currentActivity = reactApplicationContext.currentActivity val statusBarColor = - reactApplicationContext.getCurrentActivity()?.window?.statusBarColor?.let { color -> + currentActivity?.window?.statusBarColor?.let { color -> String.format("#%06X", 0xFFFFFF and color) } ?: "black" return mapOf( - HEIGHT_KEY to PixelUtil.toDIPFromPixel(getStatusBarHeightPx()), + HEIGHT_KEY to PixelUtil.toDIPFromPixel(getStatusBarHeightPx(currentActivity).toFloat()), DEFAULT_BACKGROUND_COLOR_KEY to statusBarColor, ) } - private fun getStatusBarHeightPx(): Float { - val windowInsets = - reactApplicationContext - .getCurrentActivity() - ?.window - ?.decorView - ?.let(ViewCompat::getRootWindowInsets) ?: return 0f - return windowInsets - .getInsets( - WindowInsetsCompat.Type.statusBars() or - WindowInsetsCompat.Type.navigationBars() or - WindowInsetsCompat.Type.displayCutout()) - .top - .toFloat() - } - @Suppress("DEPRECATION") override fun setColor(colorDouble: Double, animated: Boolean) { val color = colorDouble.toInt() diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/DisplayMetricsHolder.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/DisplayMetricsHolder.kt index 0826e95bb2c4..9f6c9310a690 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/DisplayMetricsHolder.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/DisplayMetricsHolder.kt @@ -7,9 +7,12 @@ package com.facebook.react.uimanager +import android.app.Activity import android.content.Context import android.util.DisplayMetrics import android.view.WindowManager +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat import com.facebook.react.bridge.WritableMap import com.facebook.react.bridge.WritableNativeMap @@ -98,4 +101,14 @@ public object DisplayMetricsHolder { putDouble("fontScale", fontScale) putDouble("densityDpi", displayMetrics.densityDpi.toDouble()) } + + internal fun getStatusBarHeightPx(activity: Activity?): Int { + val windowInsets = activity?.window?.decorView?.let(ViewCompat::getRootWindowInsets) ?: return 0 + return windowInsets + .getInsets( + WindowInsetsCompat.Type.statusBars() or + WindowInsetsCompat.Type.navigationBars() or + WindowInsetsCompat.Type.displayCutout()) + .top + } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.kt index 293ea5c0d88f..7f2798ae02e3 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.kt @@ -40,6 +40,8 @@ import com.facebook.react.bridge.WritableNativeMap import com.facebook.react.common.annotations.VisibleForTesting import com.facebook.react.common.build.ReactBuildConfig import com.facebook.react.config.ReactFeatureFlags +import com.facebook.react.uimanager.DisplayMetricsHolder +import com.facebook.react.uimanager.DisplayMetricsHolder.getStatusBarHeightPx import com.facebook.react.uimanager.JSPointerDispatcher import com.facebook.react.uimanager.JSTouchDispatcher import com.facebook.react.uimanager.PixelUtil.pxToDp @@ -53,6 +55,7 @@ import com.facebook.react.views.modal.ReactModalHostView.DialogRootViewGroup import com.facebook.react.views.view.ReactViewGroup import com.facebook.react.views.view.setStatusBarTranslucency import com.facebook.react.views.view.setSystemBarsTranslucency +import com.facebook.yoga.annotations.DoNotStrip /** * ReactModalHostView is a view that sits in the view hierarchy representing a Modal view. @@ -122,6 +125,7 @@ public class ReactModalHostView(context: ThemedReactContext) : private var createNewDialog = false init { + initStatusBarHeight(context) dialogRootViewGroup = DialogRootViewGroup(context) } @@ -461,6 +465,26 @@ public class ReactModalHostView(context: ThemedReactContext) : private companion object { private const val TAG = "ReactModalHost" + + // We store the status bar height to be able to properly position + // the modal on the first render. + private var statusBarHeight = 0 + + private fun initStatusBarHeight(reactContext: ReactContext) { + statusBarHeight = getStatusBarHeightPx(reactContext.currentActivity) + } + + @JvmStatic + @DoNotStrip + private fun getScreenDisplayMetricsWithoutInsets(): Long { + val displayMetrics = DisplayMetricsHolder.getScreenDisplayMetrics() + return encodeFloatsToLong( + displayMetrics.widthPixels.toFloat().pxToDp(), + (displayMetrics.heightPixels - statusBarHeight).toFloat().pxToDp()) + } + + private fun encodeFloatsToLong(width: Float, height: Float): Long = + (width.toRawBits().toLong()) shl 32 or (height.toRawBits().toLong()) } /** @@ -476,6 +500,7 @@ public class ReactModalHostView(context: ThemedReactContext) : */ public class DialogRootViewGroup internal constructor(context: Context) : ReactViewGroup(context), RootView { + internal var stateWrapper: StateWrapper? = null internal var eventDispatcher: EventDispatcher? = null diff --git a/packages/react-native/ReactCommon/React-FabricComponents.podspec b/packages/react-native/ReactCommon/React-FabricComponents.podspec index cf039719e3c6..0b87e5b3b552 100644 --- a/packages/react-native/ReactCommon/React-FabricComponents.podspec +++ b/packages/react-native/ReactCommon/React-FabricComponents.podspec @@ -89,7 +89,7 @@ Pod::Spec.new do |s| end ss.subspec "modal" do |sss| - sss.source_files = "react/renderer/components/modal/**/*.{m,mm,cpp,h}" + sss.source_files = "react/renderer/components/modal/*.{m,mm,cpp,h}" sss.exclude_files = "react/renderer/components/modal/tests" sss.header_dir = "react/renderer/components/modal" end diff --git a/packages/react-native/ReactCommon/react/renderer/components/modal/CMakeLists.txt b/packages/react-native/ReactCommon/react/renderer/components/modal/CMakeLists.txt index a996222eadce..b11531023a22 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/modal/CMakeLists.txt +++ b/packages/react-native/ReactCommon/react/renderer/components/modal/CMakeLists.txt @@ -8,7 +8,10 @@ set(CMAKE_VERBOSE_MAKEFILE on) include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake) -file(GLOB rrc_modal_SRC CONFIGURE_DEPENDS *.cpp) +file(GLOB rrc_modal_SRC CONFIGURE_DEPENDS + *.cpp + platform/android/*.cpp) + add_library(rrc_modal STATIC ${rrc_modal_SRC}) target_include_directories(rrc_modal PUBLIC ${REACT_COMMON_DIR}) diff --git a/packages/react-native/ReactCommon/react/renderer/components/modal/ModalHostViewComponentDescriptor.h b/packages/react-native/ReactCommon/react/renderer/components/modal/ModalHostViewComponentDescriptor.h index 9ecdc54de4cf..db62b76165c2 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/modal/ModalHostViewComponentDescriptor.h +++ b/packages/react-native/ReactCommon/react/renderer/components/modal/ModalHostViewComponentDescriptor.h @@ -30,8 +30,9 @@ class ModalHostViewComponentDescriptor final *shadowNode.getState()) .getData(); - layoutableShadowNode.setSize( - Size{stateData.screenSize.width, stateData.screenSize.height}); + layoutableShadowNode.setSize(Size{ + .width = stateData.screenSize.width, + .height = stateData.screenSize.height}); layoutableShadowNode.setPositionType(YGPositionTypeAbsolute); ConcreteComponentDescriptor::adopt(shadowNode); diff --git a/packages/react-native/ReactCommon/react/renderer/components/modal/ModalHostViewState.h b/packages/react-native/ReactCommon/react/renderer/components/modal/ModalHostViewState.h index 183188de443b..76879cf7347b 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/modal/ModalHostViewState.h +++ b/packages/react-native/ReactCommon/react/renderer/components/modal/ModalHostViewState.h @@ -9,15 +9,12 @@ #include #include +#include "ModalHostViewUtils.h" #ifdef RN_SERIALIZABLE_STATE #include #endif -#if defined(__APPLE__) && TARGET_OS_IOS -#include "ModalHostViewUtils.h" -#endif - namespace facebook::react { /* @@ -27,12 +24,7 @@ class ModalHostViewState final { public: using Shared = std::shared_ptr; -#if defined(__APPLE__) && TARGET_OS_IOS - ModalHostViewState() : screenSize(RCTModalHostViewScreenSize()) { -#else - ModalHostViewState(){ -#endif - }; + ModalHostViewState() : screenSize(ModalHostViewScreenSize()) {} ModalHostViewState(Size screenSize_) : screenSize(screenSize_){}; #ifdef RN_SERIALIZABLE_STATE @@ -40,8 +32,8 @@ class ModalHostViewState final { const ModalHostViewState& previousState, folly::dynamic data) : screenSize(Size{ - (Float)data["screenWidth"].getDouble(), - (Float)data["screenHeight"].getDouble()}){}; + .width = (Float)data["screenWidth"].getDouble(), + .height = (Float)data["screenHeight"].getDouble()}){}; #endif const Size screenSize{}; diff --git a/packages/react-native/ReactCommon/react/renderer/components/modal/ModalHostViewUtils.h b/packages/react-native/ReactCommon/react/renderer/components/modal/ModalHostViewUtils.h index fc038ad33de5..86b375d850c3 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/modal/ModalHostViewUtils.h +++ b/packages/react-native/ReactCommon/react/renderer/components/modal/ModalHostViewUtils.h @@ -7,10 +7,10 @@ #pragma once -#include +#include namespace facebook::react { -Size RCTModalHostViewScreenSize(void); +Size ModalHostViewScreenSize(void); } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/components/modal/ModalHostViewUtils.mm b/packages/react-native/ReactCommon/react/renderer/components/modal/ModalHostViewUtils.mm index d2c3560dca8f..7444e3a76e72 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/modal/ModalHostViewUtils.mm +++ b/packages/react-native/ReactCommon/react/renderer/components/modal/ModalHostViewUtils.mm @@ -11,7 +11,7 @@ namespace facebook::react { -Size RCTModalHostViewScreenSize(void) +Size ModalHostViewScreenSize(void) { CGSize screenSize = RCTScreenSize(); return {screenSize.width, screenSize.height}; diff --git a/packages/react-native/ReactCommon/react/renderer/components/modal/platform/android/JReactModalHostView.h b/packages/react-native/ReactCommon/react/renderer/components/modal/platform/android/JReactModalHostView.h new file mode 100644 index 000000000000..f4300046116a --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/components/modal/platform/android/JReactModalHostView.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) Meta Platforms, Inc. and 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 + +namespace facebook::react { + +class JReactModalHostView + : public facebook::jni::JavaClass { + public: + static auto constexpr kJavaDescriptor = + "Lcom/facebook/react/views/modal/ReactModalHostView;"; + + static Size getDisplayMetrics() { + static auto method = + JReactModalHostView::javaClassStatic()->getStaticMethod( + "getScreenDisplayMetricsWithoutInsets"); + auto result = method(javaClassStatic()); + + // Inspired from yogaMeassureToSize from conversions.h + int32_t wBits = 0xFFFFFFFF & (result >> 32); + int32_t hBits = 0xFFFFFFFF & result; + + auto* measuredWidth = reinterpret_cast(&wBits); + auto* measuredHeight = reinterpret_cast(&hBits); + + return Size{.width = *measuredWidth, .height = *measuredHeight}; + } +}; + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/components/modal/platform/android/ModalHostViewUtils.cpp b/packages/react-native/ReactCommon/react/renderer/components/modal/platform/android/ModalHostViewUtils.cpp new file mode 100644 index 000000000000..50e2ce89584d --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/components/modal/platform/android/ModalHostViewUtils.cpp @@ -0,0 +1,18 @@ +/* + * Copyright (c) Meta Platforms, Inc. and 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 "JReactModalHostView.h" + +namespace facebook::react { + +Size ModalHostViewScreenSize() { + return JReactModalHostView::getDisplayMetrics(); +} + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/components/modal/platform/cxx/ModalHostViewUtils.cpp b/packages/react-native/ReactCommon/react/renderer/components/modal/platform/cxx/ModalHostViewUtils.cpp new file mode 100644 index 000000000000..a661b73ccfa5 --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/components/modal/platform/cxx/ModalHostViewUtils.cpp @@ -0,0 +1,17 @@ +/* + * Copyright (c) Meta Platforms, Inc. and 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 + +namespace facebook::react { + +Size ModalHostViewScreenSize() { + return Size{0, 0}; +} + +} // namespace facebook::react