diff --git a/android/build.gradle b/android/build.gradle index d9a423404..cba67f804 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -2,6 +2,18 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' +def isNewArchitectureEnabled() { + // To opt-in for the New Architecture, you can either: + // - Set `newArchEnabled` to true inside the `gradle.properties` file + // - Invoke gradle with `-newArchEnabled=true` + // - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true` + return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true" +} + +if (isNewArchitectureEnabled()) { + apply plugin: 'com.facebook.react' +} + def _ext = rootProject.ext def _reactNativeVersion = _ext.has('reactNative') ? _ext.reactNative : '+' @@ -16,6 +28,14 @@ def _androidTestRunnerVersion = _ext.has('androidTestRunnerVersion') ? _ext.andr android { compileSdkVersion _compileSdkVersion + // Used to override the NDK path/version on internal CI or by allowing + // users to customize the NDK path/version from their root project (e.g. for M1 support) + if (rootProject.hasProperty("ndkPath")) { + ndkPath rootProject.ext.ndkPath + } + if (rootProject.hasProperty("ndkVersion")) { + ndkVersion rootProject.ext.ndkVersion + } buildToolsVersion _buildToolsVersion compileOptions { @@ -28,6 +48,16 @@ android { debug.java.srcDirs += 'src/debug/kotlin' test.java.srcDirs += 'src/test/kotlin' androidTest.java.srcDirs += 'src/androidTest/kotlin' + + if (isNewArchitectureEnabled()) { + main.java.srcDirs += 'src/fabric/java' + main.java.srcDirs += "${project.buildDir}/generated/source/codegen/java" + } else { + // this folder also includes files from codegen so the library can compile with + // codegen turned off + // TODO: This won't work for now!!!! + main.java.srcDirs += 'src/paper/java' + } } defaultConfig { @@ -36,6 +66,40 @@ android { versionCode 1 versionName "1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() + if (isNewArchitectureEnabled()) { + var appProject = rootProject.allprojects.find {it.plugins.hasPlugin('com.android.application')} + externalNativeBuild { + ndkBuild { + arguments "APP_PLATFORM=android-21", + "APP_STL=c++_shared", + "NDK_TOOLCHAIN_VERSION=clang", + "GENERATED_SRC_DIR=${appProject.buildDir}/generated/source", + "PROJECT_BUILD_DIR=${appProject.buildDir}", + "REACT_ANDROID_DIR=${appProject.rootDir}/../node_modules/react-native/ReactAndroid", + "REACT_ANDROID_BUILD_DIR=${appProject.rootDir}/../node_modules/react-native/ReactAndroid/build" + cFlags "-Wall", "-Werror", "-fexceptions", "-frtti", "-DWITH_INSPECTOR=1" + cppFlags "-std=c++17" + targets "rnflashlist_modules" + } + } + } + } + + if (isNewArchitectureEnabled()) { + externalNativeBuild { + ndkBuild { + path "src/main/jni/Android.mk" + } + } + } + + packagingOptions { + // For some reason gradle only complains about the duplicated version of libreact_render libraries + // while there are more libraries copied in intermediates folder of the lib build directory, we exclude + // only the ones that make the build fail (ideally we should only include librngesturehandler_modules but we + // are only allowed to specify exclude patterns) + exclude "**/libreact_render*.so" } lintOptions { @@ -48,7 +112,11 @@ android { } dependencies { - compileOnly "com.facebook.react:react-native:${_reactNativeVersion}" + if (isNewArchitectureEnabled()) { + implementation project(':ReactAndroid') + } else { + compileOnly "com.facebook.react:react-native:${_reactNativeVersion}" + } implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${_kotlinVersion}" testImplementation "junit:junit:${_junitVersion}" testImplementation "org.mockito.kotlin:mockito-kotlin:${_mockitoVersion}" @@ -57,3 +125,13 @@ dependencies { androidTestImplementation("androidx.test:runner:${_androidTestRunnerVersion}") androidTestImplementation("androidx.test:rules:${_androidTestRunnerVersion}") } + +if (isNewArchitectureEnabled()) { + react { + reactRoot = rootProject.file("../node_modules/react-native/") + jsRootDir = file("../src/fabric/") + codegenDir = rootProject.file("../node_modules/react-native-codegen/") + libraryName = "rnflashlist" + codegenJavaPackageName = "com.shopify.reactnative.flash_list" + } +} diff --git a/android/src/fabric/java/com/shopify/reactnative/flash_list/FlashListComponentsRegistry.java b/android/src/fabric/java/com/shopify/reactnative/flash_list/FlashListComponentsRegistry.java new file mode 100644 index 000000000..eb3fdec80 --- /dev/null +++ b/android/src/fabric/java/com/shopify/reactnative/flash_list/FlashListComponentsRegistry.java @@ -0,0 +1,29 @@ +package com.shopify.reactnative.flash_list.react; + +import com.facebook.jni.HybridData; +import com.facebook.proguard.annotations.DoNotStrip; +import com.facebook.react.fabric.ComponentFactory; +import com.facebook.soloader.SoLoader; + +@DoNotStrip +public class FlashListComponentsRegistry { + static { + SoLoader.loadLibrary("fabricjni"); + SoLoader.loadLibrary("flashlist_modules"); + } + + @DoNotStrip private final HybridData mHybridData; + + @DoNotStrip + private native HybridData initHybrid(ComponentFactory componentFactory); + + @DoNotStrip + private FlashListComponentsRegistry(ComponentFactory componentFactory) { + mHybridData = initHybrid(componentFactory); + } + + @DoNotStrip + public static FlashListComponentsRegistry register(ComponentFactory componentFactory) { + return new FlashListComponentsRegistry(componentFactory); + } +} diff --git a/android/src/main/jni/Android.mk b/android/src/main/jni/Android.mk new file mode 100644 index 000000000..40b503fc9 --- /dev/null +++ b/android/src/main/jni/Android.mk @@ -0,0 +1,53 @@ +GESTURE_HANDLER_MAIN_THIS_DIR := $(call my-dir) + +include $(REACT_ANDROID_DIR)/Android-prebuilt.mk + +# libreact_render_uimanager +include $(CLEAR_VARS) +LOCAL_MODULE := libreact_render_uimanager +LOCAL_SRC_FILES := $(REACT_NDK_EXPORT_DIR)/$(TARGET_ARCH_ABI)/libreact_render_uimanager.so +LOCAL_EXPORT_C_INCLUDES := \ + $(REACT_COMMON_DIR)/react/renderer/uimanager +include $(PREBUILT_SHARED_LIBRARY) +# end libreact_render_uimanager + +include $(GESTURE_HANDLER_MAIN_THIS_DIR)/../../../build/generated/source/codegen/jni/Android.mk + +include $(CLEAR_VARS) + +LOCAL_PATH := $(GESTURE_HANDLER_MAIN_THIS_DIR) +LOCAL_MODULE := rnflashlist_modules + +LOCAL_C_INCLUDES := $(LOCAL_PATH) $(GENERATED_SRC_DIR)/codegen/jni +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) $(wildcard $(GENERATED_SRC_DIR)/codegen/jni/*.cpp) +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) $(GENERATED_SRC_DIR)/codegen/jni + +# Please note as one of the library listed is libreact_codegen_samplelibrary +# This name will be generated as libreact_codegen_ +# where is the one you specified in the Gradle configuration +LOCAL_SHARED_LIBRARIES := libjsi \ + libfbjni \ + libglog \ + libfolly_json \ + libyoga \ + libreact_nativemodule_core \ + libturbomodulejsijni \ + librrc_view \ + libreact_render_core \ + libreact_render_graphics \ + libfabricjni \ + libfolly_futures \ + libreact_debug \ + libreact_render_componentregistry \ + libreact_render_debug \ + libruntimeexecutor \ + libreact_render_mapbuffer \ + libreact_render_uimanager \ + libreact_codegen_rncore \ + libreact_codegen_rnflashlist + +LOCAL_CFLAGS := \ + -DLOG_TAG=\"ReactNative\" +LOCAL_CFLAGS += -fexceptions -frtti -std=c++17 -Wall + +include $(BUILD_SHARED_LIBRARY) diff --git a/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutView.kt b/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutView.kt index 38391f973..b0be07e5c 100644 --- a/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutView.kt +++ b/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutView.kt @@ -2,7 +2,6 @@ package com.shopify.reactnative.flash_list import android.content.Context import android.graphics.Canvas -import android.util.DisplayMetrics import android.view.View import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.ReactContext diff --git a/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutViewManager.kt b/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutViewManager.kt index 4cac04a63..2273ac6e2 100644 --- a/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutViewManager.kt +++ b/android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutViewManager.kt @@ -1,17 +1,25 @@ package com.shopify.reactnative.flash_list +import android.util.Log import com.facebook.react.module.annotations.ReactModule import com.facebook.react.uimanager.ThemedReactContext import com.facebook.react.uimanager.annotations.ReactProp -import com.facebook.react.views.view.ReactViewGroup -import com.facebook.react.views.view.ReactViewManager import com.facebook.react.common.MapBuilder +import com.facebook.react.uimanager.ViewGroupManager +import com.facebook.react.uimanager.ViewManagerDelegate +import com.facebook.react.viewmanagers.AutoLayoutViewManagerDelegate +import com.facebook.react.viewmanagers.AutoLayoutViewManagerInterface import kotlin.math.roundToInt /** ViewManager for AutoLayoutView - Container for all RecyclerListView children. Automatically removes all gaps and overlaps for GridLayouts with flexible spans. * Note: This cannot work for masonry layouts i.e, pinterest like layout */ @ReactModule(name = AutoLayoutViewManager.REACT_CLASS) -class AutoLayoutViewManager: ReactViewManager() { +class AutoLayoutViewManager: ViewGroupManager(), AutoLayoutViewManagerInterface { + private val mDelegate: ViewManagerDelegate + + init { + mDelegate = AutoLayoutViewManagerDelegate(this) + } companion object { const val REACT_CLASS = "AutoLayoutView" @@ -21,7 +29,7 @@ class AutoLayoutViewManager: ReactViewManager() { return REACT_CLASS } - override fun createViewInstance(context: ThemedReactContext): ReactViewGroup { + override fun createViewInstance(context: ThemedReactContext): AutoLayoutView { return AutoLayoutView(context).also { it.pixelDensity = context.resources.displayMetrics.density.toDouble() } } @@ -34,27 +42,27 @@ class AutoLayoutViewManager: ReactViewManager() { } @ReactProp(name = "horizontal") - fun setHorizontal(view: AutoLayoutView, isHorizontal: Boolean) { + override fun setHorizontal(view: AutoLayoutView, isHorizontal: Boolean) { view.alShadow.horizontal = isHorizontal } @ReactProp(name = "scrollOffset") - fun setScrollOffset(view: AutoLayoutView, scrollOffset: Double) { + override fun setScrollOffset(view: AutoLayoutView, scrollOffset: Double) { view.alShadow.scrollOffset = convertToPixelLayout(scrollOffset, view.pixelDensity) } @ReactProp(name = "windowSize") - fun setWindowSize(view: AutoLayoutView, windowSize: Double) { + override fun setWindowSize(view: AutoLayoutView, windowSize: Double) { view.alShadow.windowSize = convertToPixelLayout(windowSize, view.pixelDensity) } @ReactProp(name = "renderAheadOffset") - fun setRenderAheadOffset(view: AutoLayoutView, renderOffset: Double) { + override fun setRenderAheadOffset(view: AutoLayoutView, renderOffset: Double) { view.alShadow.renderOffset = convertToPixelLayout(renderOffset, view.pixelDensity) } @ReactProp(name = "enableInstrumentation") - fun setEnableInstrumentation(view: AutoLayoutView, enableInstrumentation: Boolean) { + override fun setEnableInstrumentation(view: AutoLayoutView, enableInstrumentation: Boolean) { view.enableInstrumentation = enableInstrumentation } diff --git a/android/src/main/kotlin/com/shopify/reactnative/flash_list/CellContainerManager.kt b/android/src/main/kotlin/com/shopify/reactnative/flash_list/CellContainerManager.kt index 1434caaae..1c2c74daf 100644 --- a/android/src/main/kotlin/com/shopify/reactnative/flash_list/CellContainerManager.kt +++ b/android/src/main/kotlin/com/shopify/reactnative/flash_list/CellContainerManager.kt @@ -1,13 +1,22 @@ package com.shopify.reactnative.flash_list +import android.util.Log import com.facebook.react.module.annotations.ReactModule import com.facebook.react.uimanager.ThemedReactContext +import com.facebook.react.uimanager.ViewGroupManager +import com.facebook.react.uimanager.ViewManagerDelegate import com.facebook.react.uimanager.annotations.ReactProp -import com.facebook.react.views.view.ReactViewGroup -import com.facebook.react.views.view.ReactViewManager +import com.facebook.react.viewmanagers.CellContainerManagerDelegate +import com.facebook.react.viewmanagers.CellContainerManagerInterface + +@ReactModule(name = CellContainerManager.REACT_CLASS) +class CellContainerManager: ViewGroupManager(), CellContainerManagerInterface { + private val mDelegate: ViewManagerDelegate + + init { + mDelegate = CellContainerManagerDelegate(this) + } -@ReactModule(name = AutoLayoutViewManager.REACT_CLASS) -class CellContainerManager: ReactViewManager() { companion object { const val REACT_CLASS = "CellContainer" } @@ -16,12 +25,12 @@ class CellContainerManager: ReactViewManager() { return REACT_CLASS } - override fun createViewInstance(context: ThemedReactContext): ReactViewGroup { + override fun createViewInstance(context: ThemedReactContext): CellContainerImpl { return CellContainerImpl(context) } @ReactProp(name = "index") - fun setIndex(view: CellContainerImpl, index: Int) { + override fun setIndex(view: CellContainerImpl, index: Int) { view.index = index } } diff --git a/android/src/main/kotlin/com/shopify/reactnative/flash_list/FlashListPackage.kt b/android/src/main/kotlin/com/shopify/reactnative/flash_list/FlashListPackage.kt index dc118ef42..bd2ac0f5a 100644 --- a/android/src/main/kotlin/com/shopify/reactnative/flash_list/FlashListPackage.kt +++ b/android/src/main/kotlin/com/shopify/reactnative/flash_list/FlashListPackage.kt @@ -4,9 +4,17 @@ import com.facebook.react.ReactPackage import com.facebook.react.bridge.NativeModule import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.uimanager.ViewManager +import com.facebook.soloader.SoLoader class ReactNativeFlashListPackage : ReactPackage { override fun createNativeModules(reactContext: ReactApplicationContext): List { + if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { + // For Fabric, we load c++ native library here, this triggers gesture handler's + // Fabric component registration which is necessary in order to avoid asking users + // to manually add init calls in their application code. + // This should no longer be needed if RN's autolink mechanism has Fabric support + SoLoader.loadLibrary("rnflashlist_modules") + } return listOf() } diff --git a/fixture/android/app/build.gradle b/fixture/android/app/build.gradle index f6c4be2cb..3a186bc27 100644 --- a/fixture/android/app/build.gradle +++ b/fixture/android/app/build.gradle @@ -1,7 +1,7 @@ apply plugin: "com.android.application" -apply plugin: 'kotlin-android' import com.android.build.OutputFile +import org.apache.tools.ant.taskdefs.condition.Os /** * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets @@ -79,7 +79,7 @@ import com.android.build.OutputFile */ project.ext.react = [ - enableHermes: false, // clean and rebuild if changing + enableHermes: true, // clean and rebuild if changing ] apply from: "../../node_modules/react-native/react.gradle" @@ -115,42 +115,112 @@ def jscFlavor = 'org.webkit:android-jsc:+' /** * Whether to enable the Hermes VM. * - * This should be set on project.ext.react and mirrored here. If it is not set + * This should be set on project.ext.react and that value will be read here. If it is not set * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode * and the benefits of using Hermes will therefore be sharply reduced. */ def enableHermes = project.ext.react.get("enableHermes", false); /** - * Architectures to build native code for in debug. + * Architectures to build native code for. */ -def nativeArchitectures = project.getProperties().get("reactNativeDebugArchitectures") +def reactNativeArchitectures() { + def value = project.getProperties().get("reactNativeArchitectures") + return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"] +} android { - testOptions { - unitTests.all { - useJUnitPlatform() - } - } ndkVersion rootProject.ext.ndkVersion compileSdkVersion rootProject.ext.compileSdkVersion defaultConfig { applicationId "com.flatlistpro" - minSdkVersion 24 + minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0" - testBuildType System.getProperty('testBuildType', 'debug') - testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' + buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() + + if (isNewArchitectureEnabled()) { + // We configure the NDK build only if you decide to opt-in for the New Architecture. + externalNativeBuild { + ndkBuild { + arguments "APP_PLATFORM=android-21", + "APP_STL=c++_shared", + "NDK_TOOLCHAIN_VERSION=clang", + "GENERATED_SRC_DIR=$buildDir/generated/source", + "PROJECT_BUILD_DIR=$buildDir", + "REACT_ANDROID_DIR=$rootDir/../node_modules/react-native/ReactAndroid", + "REACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build" + cFlags "-Wall", "-Werror", "-fexceptions", "-frtti", "-DWITH_INSPECTOR=1" + cppFlags "-std=c++17" + // Make sure this target name is the same you specify inside the + // src/main/jni/Android.mk file for the `LOCAL_MODULE` variable. + targets "flatlistpro_appmodules" + + // Fix for windows limit on number of character in file paths and in command lines + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + arguments "NDK_OUT=${rootProject.projectDir.getParent()}\\.cxx", + "NDK_APP_SHORT_COMMANDS=true" + } + } + } + if (!enableSeparateBuildPerCPUArchitecture) { + ndk { + abiFilters (*reactNativeArchitectures()) + } + } + } } + + if (isNewArchitectureEnabled()) { + // We configure the NDK build only if you decide to opt-in for the New Architecture. + externalNativeBuild { + ndkBuild { + path "$projectDir/src/main/jni/Android.mk" + } + } + def reactAndroidProjectDir = project(':ReactAndroid').projectDir + def packageReactNdkDebugLibs = tasks.register("packageReactNdkDebugLibs", Copy) { + dependsOn(":ReactAndroid:packageReactNdkDebugLibsForBuck") + from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib") + into("$buildDir/react-ndk/exported") + } + def packageReactNdkReleaseLibs = tasks.register("packageReactNdkReleaseLibs", Copy) { + dependsOn(":ReactAndroid:packageReactNdkReleaseLibsForBuck") + from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib") + into("$buildDir/react-ndk/exported") + } + afterEvaluate { + // If you wish to add a custom TurboModule or component locally, + // you should uncomment this line. + // preBuild.dependsOn("generateCodegenArtifactsFromSchema") + preDebugBuild.dependsOn(packageReactNdkDebugLibs) + preReleaseBuild.dependsOn(packageReactNdkReleaseLibs) + + // Due to a bug inside AGP, we have to explicitly set a dependency + // between configureNdkBuild* tasks and the preBuild tasks. + // This can be removed once this is solved: https://issuetracker.google.com/issues/207403732 + configureNdkBuildRelease.dependsOn(preReleaseBuild) + configureNdkBuildDebug.dependsOn(preDebugBuild) + reactNativeArchitectures().each { architecture -> + tasks.findByName("configureNdkBuildDebug[${architecture}]")?.configure { + dependsOn("preDebugBuild") + } + tasks.findByName("configureNdkBuildRelease[${architecture}]")?.configure { + dependsOn("preReleaseBuild") + } + } + } + } + splits { abi { reset() enable enableSeparateBuildPerCPUArchitecture universalApk false // If true, also generate a universal APK - include "armeabi-v7a", "x86", "arm64-v8a", "x86_64" + include (*reactNativeArchitectures()) } } signingConfigs { @@ -164,11 +234,6 @@ android { buildTypes { debug { signingConfig signingConfigs.debug - if (nativeArchitectures) { - ndk { - abiFilters nativeArchitectures.split(',') - } - } } release { // Caution! In production, you need to generate your own keystore file. @@ -198,13 +263,11 @@ android { dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) + //noinspection GradleDynamicVersion implementation "com.facebook.react:react-native:+" // From node_modules implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" - testImplementation 'androidx.test:core-ktx:1.3.0' - testImplementation 'org.junit.jupiter:junit-jupiter:5.8.2' - testImplementation 'com.google.code.gson:gson:2.8.9' debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") { exclude group:'com.facebook.fbjni' @@ -226,8 +289,18 @@ dependencies { } else { implementation jscFlavor } - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - androidTestImplementation('com.wix:detox:+') +} + +if (isNewArchitectureEnabled()) { + // If new architecture is enabled, we let you build RN from source + // Otherwise we fallback to a prebuilt .aar bundled in the NPM package. + // This will be applied to all the imported transtitive dependency. + configurations.all { + resolutionStrategy.dependencySubstitution { + substitute(module("com.facebook.react:react-native")) + .using(project(":ReactAndroid")).because("On New Architecture we're building React Native from source") + } + } } // Run this once to be able to run the application with BUCK @@ -238,3 +311,11 @@ task copyDownloadableDepsToLibs(type: Copy) { } apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) + +def isNewArchitectureEnabled() { + // To opt-in for the New Architecture, you can either: + // - Set `newArchEnabled` to true inside the `gradle.properties` file + // - Invoke gradle with `-newArchEnabled=true` + // - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true` + return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true" +} diff --git a/fixture/android/app/src/debug/java/com/flatlistpro/ReactNativeFlipper.java b/fixture/android/app/src/debug/java/com/flatlistpro/ReactNativeFlipper.java index f736b6d08..1db23bfb6 100644 --- a/fixture/android/app/src/debug/java/com/flatlistpro/ReactNativeFlipper.java +++ b/fixture/android/app/src/debug/java/com/flatlistpro/ReactNativeFlipper.java @@ -19,6 +19,7 @@ import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; import com.facebook.flipper.plugins.react.ReactFlipperPlugin; import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; +import com.facebook.react.ReactInstanceEventListener; import com.facebook.react.ReactInstanceManager; import com.facebook.react.bridge.ReactContext; import com.facebook.react.modules.network.NetworkingModule; @@ -51,7 +52,7 @@ public void apply(OkHttpClient.Builder builder) { ReactContext reactContext = reactInstanceManager.getCurrentReactContext(); if (reactContext == null) { reactInstanceManager.addReactInstanceEventListener( - new ReactInstanceManager.ReactInstanceEventListener() { + new ReactInstanceEventListener() { @Override public void onReactContextInitialized(ReactContext reactContext) { reactInstanceManager.removeReactInstanceEventListener(this); diff --git a/fixture/android/app/src/main/AndroidManifest.xml b/fixture/android/app/src/main/AndroidManifest.xml index c4f7495a3..666c47e4c 100644 --- a/fixture/android/app/src/main/AndroidManifest.xml +++ b/fixture/android/app/src/main/AndroidManifest.xml @@ -15,9 +15,10 @@ + android:windowSoftInputMode="adjustResize" + android:exported="true"> diff --git a/fixture/android/app/src/main/java/com/flatlistpro/MainActivity.java b/fixture/android/app/src/main/java/com/flatlistpro/MainActivity.java index 28f2114ff..5f2bde79f 100644 --- a/fixture/android/app/src/main/java/com/flatlistpro/MainActivity.java +++ b/fixture/android/app/src/main/java/com/flatlistpro/MainActivity.java @@ -1,6 +1,8 @@ package com.flatlistpro; import com.facebook.react.ReactActivity; +import com.facebook.react.ReactActivityDelegate; +import com.facebook.react.ReactRootView; import android.os.Bundle; public class MainActivity extends ReactActivity { @@ -14,6 +16,27 @@ protected String getMainComponentName() { return "FlatListPro"; } + /** + * Returns the instance of the {@link ReactActivityDelegate}. There the RootView is created and + * you can specify the rendered you wish to use (Fabric or the older renderer). + */ + @Override + protected ReactActivityDelegate createReactActivityDelegate() { + return new MainActivityDelegate(this, getMainComponentName()); + } + public static class MainActivityDelegate extends ReactActivityDelegate { + public MainActivityDelegate(ReactActivity activity, String mainComponentName) { + super(activity, mainComponentName); + } + @Override + protected ReactRootView createRootView() { + ReactRootView reactRootView = new ReactRootView(getContext()); + // If you opted-in for the New Architecture, we enable the Fabric Renderer. + reactRootView.setIsFabric(BuildConfig.IS_NEW_ARCHITECTURE_ENABLED); + return reactRootView; + } + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(null); diff --git a/fixture/android/app/src/main/java/com/flatlistpro/MainApplication.java b/fixture/android/app/src/main/java/com/flatlistpro/MainApplication.java index e9e52652a..362121c9d 100644 --- a/fixture/android/app/src/main/java/com/flatlistpro/MainApplication.java +++ b/fixture/android/app/src/main/java/com/flatlistpro/MainApplication.java @@ -7,12 +7,14 @@ import com.facebook.react.ReactInstanceManager; import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; +import com.facebook.react.config.ReactFeatureFlags; import com.facebook.soloader.SoLoader; +import com.flatlistpro.newarchitecture.MainApplicationReactNativeHost; import java.lang.reflect.InvocationTargetException; import java.util.List; import com.facebook.react.bridge.JSIModulePackage; -import com.swmansion.reanimated.ReanimatedJSIModulePackage; +//import com.swmansion.reanimated.ReanimatedJSIModulePackage; public class MainApplication extends Application implements ReactApplication { @@ -27,7 +29,6 @@ public boolean getUseDeveloperSupport() { protected List getPackages() { @SuppressWarnings("UnnecessaryLocalVariable") List packages = new PackageList(this).getPackages(); - packages.add(new com.flatlistpro.AppPackage()); // packages.add(new com.shopify.reactnative.flash_list.FlashListPackage()); return packages; } @@ -37,20 +38,29 @@ protected String getJSMainModuleName() { return "index"; } - @Override - protected JSIModulePackage getJSIModulePackage() { - return new ReanimatedJSIModulePackage(); // <- add - } +// @Override +// protected JSIModulePackage getJSIModulePackage() { +// return new ReanimatedJSIModulePackage(); // <- add +// } }; + + private final ReactNativeHost mNewArchitectureNativeHost = new MainApplicationReactNativeHost(this); + @Override public ReactNativeHost getReactNativeHost() { - return mReactNativeHost; + if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { + return mNewArchitectureNativeHost; + } else { + return mReactNativeHost; + } } @Override public void onCreate() { super.onCreate(); + // If you opted-in for the New Architecture, we enable the TurboModule system + ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; SoLoader.init(this, /* native exopackage */ false); initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); } diff --git a/fixture/android/app/src/main/java/com/flatlistpro/newarchitecture/MainApplicationReactNativeHost.java b/fixture/android/app/src/main/java/com/flatlistpro/newarchitecture/MainApplicationReactNativeHost.java new file mode 100644 index 000000000..ec899feec --- /dev/null +++ b/fixture/android/app/src/main/java/com/flatlistpro/newarchitecture/MainApplicationReactNativeHost.java @@ -0,0 +1,103 @@ +package com.flatlistpro.newarchitecture; +import android.app.Application; +import androidx.annotation.NonNull; +import com.facebook.react.PackageList; +import com.facebook.react.ReactInstanceManager; +import com.facebook.react.ReactNativeHost; +import com.facebook.react.ReactPackage; +import com.facebook.react.ReactPackageTurboModuleManagerDelegate; +import com.facebook.react.bridge.JSIModulePackage; +import com.facebook.react.bridge.JSIModuleProvider; +import com.facebook.react.bridge.JSIModuleSpec; +import com.facebook.react.bridge.JSIModuleType; +import com.facebook.react.bridge.JavaScriptContextHolder; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.UIManager; +import com.facebook.react.fabric.ComponentFactory; +import com.facebook.react.fabric.CoreComponentsRegistry; +import com.facebook.react.fabric.EmptyReactNativeConfig; +import com.facebook.react.fabric.FabricJSIModuleProvider; +import com.facebook.react.uimanager.ViewManagerRegistry; +import com.flatlistpro.BuildConfig; +import com.flatlistpro.newarchitecture.components.MainComponentsRegistry; +import com.flatlistpro.newarchitecture.modules.MainApplicationTurboModuleManagerDelegate; +import java.util.ArrayList; +import java.util.List; +/** + * A {@link ReactNativeHost} that helps you load everything needed for the New Architecture, both + * TurboModule delegates and the Fabric Renderer. + * + *

Please note that this class is used ONLY if you opt-in for the New Architecture (see the + * `newArchEnabled` property). Is ignored otherwise. + */ +public class MainApplicationReactNativeHost extends ReactNativeHost { + public MainApplicationReactNativeHost(Application application) { + super(application); + } + @Override + public boolean getUseDeveloperSupport() { + return BuildConfig.DEBUG; + } + @Override + protected List getPackages() { + List packages = new PackageList(this).getPackages(); + // Packages that cannot be autolinked yet can be added manually here, for example: + // packages.add(new MyReactNativePackage()); + // TurboModules must also be loaded here providing a valid TurboReactPackage implementation: + // packages.add(new TurboReactPackage() { ... }); + // If you have custom Fabric Components, their ViewManagers should also be loaded here + // inside a ReactPackage. + return packages; + } + @Override + protected String getJSMainModuleName() { + return "index"; + } + @NonNull + @Override + protected ReactPackageTurboModuleManagerDelegate.Builder + getReactPackageTurboModuleManagerDelegateBuilder() { + // Here we provide the ReactPackageTurboModuleManagerDelegate Builder. This is necessary + // for the new architecture and to use TurboModules correctly. + return new MainApplicationTurboModuleManagerDelegate.Builder(); + } + @Override + protected JSIModulePackage getJSIModulePackage() { + return new JSIModulePackage() { + @Override + public List getJSIModules( + final ReactApplicationContext reactApplicationContext, + final JavaScriptContextHolder jsContext) { + final List specs = new ArrayList<>(); + // Here we provide a new JSIModuleSpec that will be responsible of providing the + // custom Fabric Components. + specs.add( + new JSIModuleSpec() { + @Override + public JSIModuleType getJSIModuleType() { + return JSIModuleType.UIManager; + } + @Override + public JSIModuleProvider getJSIModuleProvider() { + final ComponentFactory componentFactory = new ComponentFactory(); + CoreComponentsRegistry.register(componentFactory); + // Here we register a Components Registry. + // The one that is generated with the template contains no components + // and just provides you the one from React Native core. + MainComponentsRegistry.register(componentFactory); + final ReactInstanceManager reactInstanceManager = getReactInstanceManager(); + ViewManagerRegistry viewManagerRegistry = + new ViewManagerRegistry( + reactInstanceManager.getOrCreateViewManagers(reactApplicationContext)); + return new FabricJSIModuleProvider( + reactApplicationContext, + componentFactory, + new EmptyReactNativeConfig(), + viewManagerRegistry); + } + }); + return specs; + } + }; + } +} diff --git a/fixture/android/app/src/main/java/com/flatlistpro/newarchitecture/components/MainComponentsRegistry.java b/fixture/android/app/src/main/java/com/flatlistpro/newarchitecture/components/MainComponentsRegistry.java new file mode 100644 index 000000000..03f5c641a --- /dev/null +++ b/fixture/android/app/src/main/java/com/flatlistpro/newarchitecture/components/MainComponentsRegistry.java @@ -0,0 +1,30 @@ +package com.flatlistpro.newarchitecture.components; +import com.facebook.jni.HybridData; +import com.facebook.proguard.annotations.DoNotStrip; +import com.facebook.react.fabric.ComponentFactory; +import com.facebook.soloader.SoLoader; +/** + * Class responsible to load the custom Fabric Components. This class has native methods and needs a + * corresponding C++ implementation/header file to work correctly (already placed inside the jni/ + * folder for you). + * + *

Please note that this class is used ONLY if you opt-in for the New Architecture (see the + * `newArchEnabled` property). Is ignored otherwise. + */ +@DoNotStrip +public class MainComponentsRegistry { + static { + SoLoader.loadLibrary("fabricjni"); + } + @DoNotStrip private final HybridData mHybridData; + @DoNotStrip + private native HybridData initHybrid(ComponentFactory componentFactory); + @DoNotStrip + private MainComponentsRegistry(ComponentFactory componentFactory) { + mHybridData = initHybrid(componentFactory); + } + @DoNotStrip + public static MainComponentsRegistry register(ComponentFactory componentFactory) { + return new MainComponentsRegistry(componentFactory); + } +} diff --git a/fixture/android/app/src/main/java/com/flatlistpro/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate.java b/fixture/android/app/src/main/java/com/flatlistpro/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate.java new file mode 100644 index 000000000..e9876059d --- /dev/null +++ b/fixture/android/app/src/main/java/com/flatlistpro/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate.java @@ -0,0 +1,40 @@ +package com.flatlistpro.newarchitecture.modules; +import com.facebook.jni.HybridData; +import com.facebook.react.ReactPackage; +import com.facebook.react.ReactPackageTurboModuleManagerDelegate; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.soloader.SoLoader; +import java.util.List; +/** + * Class responsible to load the TurboModules. This class has native methods and needs a + * corresponding C++ implementation/header file to work correctly (already placed inside the jni/ + * folder for you). + * + *

Please note that this class is used ONLY if you opt-in for the New Architecture (see the + * `newArchEnabled` property). Is ignored otherwise. + */ +public class MainApplicationTurboModuleManagerDelegate + extends ReactPackageTurboModuleManagerDelegate { + private static volatile boolean sIsSoLibraryLoaded; + protected MainApplicationTurboModuleManagerDelegate( + ReactApplicationContext reactApplicationContext, List packages) { + super(reactApplicationContext, packages); + } + protected native HybridData initHybrid(); + native boolean canCreateTurboModule(String moduleName); + public static class Builder extends ReactPackageTurboModuleManagerDelegate.Builder { + protected MainApplicationTurboModuleManagerDelegate build( + ReactApplicationContext context, List packages) { + return new MainApplicationTurboModuleManagerDelegate(context, packages); + } + } + @Override + protected synchronized void maybeLoadOtherSoLibraries() { + if (!sIsSoLibraryLoaded) { + // If you change the name of your application .so file in the Android.mk file, + // make sure you update the name here as well. + SoLoader.loadLibrary("flatlistpro_appmodules"); + sIsSoLibraryLoaded = true; + } + } +} diff --git a/fixture/android/app/src/main/jni/Android.mk b/fixture/android/app/src/main/jni/Android.mk new file mode 100644 index 000000000..6439a5775 --- /dev/null +++ b/fixture/android/app/src/main/jni/Android.mk @@ -0,0 +1,50 @@ +THIS_DIR := $(call my-dir) + +include $(REACT_ANDROID_DIR)/Android-prebuilt.mk + +# If you wish to add a custom TurboModule or Fabric component in your app you +# will have to include the following autogenerated makefile. +# include $(GENERATED_SRC_DIR)/codegen/jni/Android.mk +include $(CLEAR_VARS) + +LOCAL_PATH := $(THIS_DIR) + +# You can customize the name of your application .so file here. +LOCAL_MODULE := flatlistpro_appmodules + +LOCAL_C_INCLUDES := $(LOCAL_PATH) +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) + +# If you wish to add a custom TurboModule or Fabric component in your app you +# will have to uncomment those lines to include the generated source +# files from the codegen (placed in $(GENERATED_SRC_DIR)/codegen/jni) +# +# LOCAL_C_INCLUDES += $(GENERATED_SRC_DIR)/codegen/jni +# LOCAL_SRC_FILES += $(wildcard $(GENERATED_SRC_DIR)/codegen/jni/*.cpp) +# LOCAL_EXPORT_C_INCLUDES += $(GENERATED_SRC_DIR)/codegen/jni + +# Here you should add any native library you wish to depend on. +LOCAL_SHARED_LIBRARIES := \ + libfabricjni \ + libfbjni \ + libfolly_futures \ + libfolly_json \ + libglog \ + libjsi \ + libreact_render_mapbuffer \ + libreact_codegen_rncore \ + libreact_debug \ + libreact_nativemodule_core \ + libreact_render_componentregistry \ + libreact_render_core \ + libreact_render_debug \ + libreact_render_graphics \ + librrc_view \ + libruntimeexecutor \ + libturbomodulejsijni \ + libyoga + +LOCAL_CFLAGS := -DLOG_TAG=\"ReactNative\" -fexceptions -frtti -std=c++17 -Wall + +include $(BUILD_SHARED_LIBRARY) \ No newline at end of file diff --git a/fixture/android/app/src/main/jni/MainApplicationModuleProvider.cpp b/fixture/android/app/src/main/jni/MainApplicationModuleProvider.cpp new file mode 100644 index 000000000..640a5baac --- /dev/null +++ b/fixture/android/app/src/main/jni/MainApplicationModuleProvider.cpp @@ -0,0 +1,20 @@ +#include "MainApplicationModuleProvider.h" +#include +namespace facebook { +namespace react { +std::shared_ptr MainApplicationModuleProvider( + const std::string moduleName, + const JavaTurboModule::InitParams ¶ms) { + // Here you can provide your own module provider for TurboModules coming from + // either your application or from external libraries. The approach to follow + // is similar to the following (for a library called `samplelibrary`: + // + // auto module = samplelibrary_ModuleProvider(moduleName, params); + // if (module != nullptr) { + // return module; + // } + // return rncore_ModuleProvider(moduleName, params); + return rncore_ModuleProvider(moduleName, params); +} +} // namespace react +} // namespace facebook diff --git a/fixture/android/app/src/main/jni/MainApplicationModuleProvider.h b/fixture/android/app/src/main/jni/MainApplicationModuleProvider.h new file mode 100644 index 000000000..522258e1f --- /dev/null +++ b/fixture/android/app/src/main/jni/MainApplicationModuleProvider.h @@ -0,0 +1,11 @@ +#pragma once +#include +#include +#include +namespace facebook { +namespace react { +std::shared_ptr MainApplicationModuleProvider( + const std::string moduleName, + const JavaTurboModule::InitParams ¶ms); +} // namespace react +} // namespace facebook diff --git a/fixture/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.cpp b/fixture/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.cpp new file mode 100644 index 000000000..88e992ce7 --- /dev/null +++ b/fixture/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.cpp @@ -0,0 +1,38 @@ +#include "MainApplicationTurboModuleManagerDelegate.h" +#include "MainApplicationModuleProvider.h" +namespace facebook { +namespace react { +jni::local_ref +MainApplicationTurboModuleManagerDelegate::initHybrid( + jni::alias_ref) { + return makeCxxInstance(); +} +void MainApplicationTurboModuleManagerDelegate::registerNatives() { + registerHybrid({ + makeNativeMethod( + "initHybrid", MainApplicationTurboModuleManagerDelegate::initHybrid), + makeNativeMethod( + "canCreateTurboModule", + MainApplicationTurboModuleManagerDelegate::canCreateTurboModule), + }); +} +std::shared_ptr +MainApplicationTurboModuleManagerDelegate::getTurboModule( + const std::string name, + const std::shared_ptr jsInvoker) { + // Not implemented yet: provide pure-C++ NativeModules here. + return nullptr; +} +std::shared_ptr +MainApplicationTurboModuleManagerDelegate::getTurboModule( + const std::string name, + const JavaTurboModule::InitParams ¶ms) { + return MainApplicationModuleProvider(name, params); +} +bool MainApplicationTurboModuleManagerDelegate::canCreateTurboModule( + std::string name) { + return getTurboModule(name, nullptr) != nullptr || + getTurboModule(name, {.moduleName = name}) != nullptr; +} +} // namespace react +} // namespace facebook diff --git a/fixture/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.h b/fixture/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.h new file mode 100644 index 000000000..1afdbbff1 --- /dev/null +++ b/fixture/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.h @@ -0,0 +1,30 @@ +#include +#include +#include +#include +namespace facebook { +namespace react { +class MainApplicationTurboModuleManagerDelegate + : public jni::HybridClass< + MainApplicationTurboModuleManagerDelegate, + TurboModuleManagerDelegate> { + public: + // Adapt it to the package you used for your Java class. + static constexpr auto kJavaDescriptor = + "Lcom/flatlistpro/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate;"; + static jni::local_ref initHybrid(jni::alias_ref); + static void registerNatives(); + std::shared_ptr getTurboModule( + const std::string name, + const std::shared_ptr jsInvoker) override; + std::shared_ptr getTurboModule( + const std::string name, + const JavaTurboModule::InitParams ¶ms) override; + /** + * Test-only method. Allows user to verify whether a TurboModule can be + * created by instances of this class. + */ + bool canCreateTurboModule(std::string name); +}; +} // namespace react +} // namespace facebook diff --git a/fixture/android/app/src/main/jni/MainComponentsRegistry.cpp b/fixture/android/app/src/main/jni/MainComponentsRegistry.cpp new file mode 100644 index 000000000..d62dcb558 --- /dev/null +++ b/fixture/android/app/src/main/jni/MainComponentsRegistry.cpp @@ -0,0 +1,48 @@ +#include "MainComponentsRegistry.h" +#include +#include +#include +#include +namespace facebook { +namespace react { +MainComponentsRegistry::MainComponentsRegistry(ComponentFactory *delegate) {} +std::shared_ptr +MainComponentsRegistry::sharedProviderRegistry() { + auto providerRegistry = CoreComponentsRegistry::sharedProviderRegistry(); + // Custom Fabric Components go here. You can register custom + // components coming from your App or from 3rd party libraries here. + // + // providerRegistry->add(concreteComponentDescriptorProvider< + // AocViewerComponentDescriptor>()); + return providerRegistry; +} +jni::local_ref +MainComponentsRegistry::initHybrid( + jni::alias_ref, + ComponentFactory *delegate) { + auto instance = makeCxxInstance(delegate); + auto buildRegistryFunction = + [](EventDispatcher::Weak const &eventDispatcher, + ContextContainer::Shared const &contextContainer) + -> ComponentDescriptorRegistry::Shared { + auto registry = MainComponentsRegistry::sharedProviderRegistry() + ->createComponentDescriptorRegistry( + {eventDispatcher, contextContainer}); + auto mutableRegistry = + std::const_pointer_cast(registry); + mutableRegistry->setFallbackComponentDescriptor( + std::make_shared( + ComponentDescriptorParameters{ + eventDispatcher, contextContainer, nullptr})); + return registry; + }; + delegate->buildRegistryFunction = buildRegistryFunction; + return instance; +} +void MainComponentsRegistry::registerNatives() { + registerHybrid({ + makeNativeMethod("initHybrid", MainComponentsRegistry::initHybrid), + }); +} +} // namespace react +} // namespace facebook diff --git a/fixture/android/app/src/main/jni/MainComponentsRegistry.h b/fixture/android/app/src/main/jni/MainComponentsRegistry.h new file mode 100644 index 000000000..14d278450 --- /dev/null +++ b/fixture/android/app/src/main/jni/MainComponentsRegistry.h @@ -0,0 +1,24 @@ +#pragma once +#include +#include +#include +#include +namespace facebook { +namespace react { +class MainComponentsRegistry + : public facebook::jni::HybridClass { + public: + // Adapt it to the package you used for your Java class. + constexpr static auto kJavaDescriptor = + "Lcom/flatlistpro/newarchitecture/components/MainComponentsRegistry;"; + static void registerNatives(); + MainComponentsRegistry(ComponentFactory *delegate); + private: + static std::shared_ptr + sharedProviderRegistry(); + static jni::local_ref initHybrid( + jni::alias_ref, + ComponentFactory *delegate); +}; +} // namespace react +} // namespace facebook diff --git a/fixture/android/app/src/main/jni/OnLoad.cpp b/fixture/android/app/src/main/jni/OnLoad.cpp new file mode 100644 index 000000000..ddd99a4c4 --- /dev/null +++ b/fixture/android/app/src/main/jni/OnLoad.cpp @@ -0,0 +1,10 @@ +#include +#include "MainApplicationTurboModuleManagerDelegate.h" +#include "MainComponentsRegistry.h" +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { + return facebook::jni::initialize(vm, [] { + facebook::react::MainApplicationTurboModuleManagerDelegate:: + registerNatives(); + facebook::react::MainComponentsRegistry::registerNatives(); + }); +} diff --git a/fixture/android/build.gradle b/fixture/android/build.gradle index 9a95cb8fa..02dd580fb 100644 --- a/fixture/android/build.gradle +++ b/fixture/android/build.gradle @@ -2,20 +2,28 @@ buildscript { ext { - buildToolsVersion = "30.0.2" + buildToolsVersion = "31.0.0" minSdkVersion = 21 - compileSdkVersion = 30 - targetSdkVersion = 30 - ndkVersion = "21.4.7075529" - kotlin_version = "1.4.10" + compileSdkVersion = 31 + targetSdkVersion = 31 + kotlin_version = "1.6.10" + if (System.properties['os.arch'] == "aarch64") { + // For M1 Users we need to use the NDK 24 which added support for aarch64 + ndkVersion = "24.0.8215888" + } else { + // Otherwise we default to the side-by-side NDK version from AGP. + ndkVersion = "21.4.7075529" + } } repositories { google() mavenCentral() } dependencies { - classpath("com.android.tools.build:gradle:4.2.2") + classpath("com.android.tools.build:gradle:7.0.4") + classpath("com.facebook.react:react-native-gradle-plugin") + classpath("de.undercouch:gradle-download-task:4.1.2") // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" diff --git a/fixture/android/gradle.properties b/fixture/android/gradle.properties index 455cd1480..a4bcffe81 100644 --- a/fixture/android/gradle.properties +++ b/fixture/android/gradle.properties @@ -9,7 +9,8 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -# Default value: -Xmx10248m -XX:MaxPermSize=256m +# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m +org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m # When configured, Gradle will run in incubating parallel mode. @@ -25,6 +26,14 @@ android.useAndroidX=true android.enableJetifier=true # Version of flipper SDK to use with React Native -FLIPPER_VERSION=0.99.0 - -org.gradle.jvmargs=-Xmx4g -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +FLIPPER_VERSION=0.125.0 +# Use this property to specify which architecture you want to build. +# You can also override it from the CLI using +# ./gradlew -PreactNativeArchitectures=x86_64 +reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 +# Use this property to enable support to the new architecture. +# This will allow you to use TurboModules and the Fabric render in +# your application. You should enable this flag either if you want +# to write custom TurboModules/Fabric components OR use libraries that +# are providing them. +newArchEnabled=true diff --git a/fixture/android/gradle/wrapper/gradle-wrapper.properties b/fixture/android/gradle/wrapper/gradle-wrapper.properties index 7665b0fa9..669386b87 100644 --- a/fixture/android/gradle/wrapper/gradle-wrapper.properties +++ b/fixture/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/fixture/android/gradlew b/fixture/android/gradlew index 4f906e0c8..1b6c78733 100755 --- a/fixture/android/gradlew +++ b/fixture/android/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,67 +17,101 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -106,80 +140,95 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/fixture/android/settings.gradle b/fixture/android/settings.gradle index 224835917..836828195 100644 --- a/fixture/android/settings.gradle +++ b/fixture/android/settings.gradle @@ -3,3 +3,8 @@ apply from: file("../node_modules/@react-native-community/cli-platform-android/n include ':app' include ':flash_list' project(':flash_list').projectDir = new File(rootProject.projectDir, '../../android') +includeBuild('../node_modules/react-native-gradle-plugin') +if (settings.hasProperty("newArchEnabled") && settings.newArchEnabled == "true") { + include(":ReactAndroid") + project(":ReactAndroid").projectDir = file('../node_modules/react-native/ReactAndroid') +} diff --git a/fixture/package.json b/fixture/package.json index d0d322467..4336ef8e4 100644 --- a/fixture/package.json +++ b/fixture/package.json @@ -20,12 +20,13 @@ "@shopify/flash-list": "link:../", "@shopify/react-native-performance-lists-profiler": "^0.0.10", "react": "17.0.2", - "react-native": "0.68.0", + "react-native": "0.68.1", "react-native-codegen": "^0.0.14", "react-native-fast-image": "^8.5.11", "react-native-flipper": "^0.142.0", "react-native-gesture-handler": "^2.4.1", - "react-native-safe-area-context": "^4.1.4", + "react-native-gradle-plugin": "^0.0.7", + "react-native-safe-area-context": "^4.2.5", "react-native-screens": "^3.13.1" }, "devDependencies": { diff --git a/fixture/yarn.lock b/fixture/yarn.lock index 2d45a2732..cf0826e7a 100644 --- a/fixture/yarn.lock +++ b/fixture/yarn.lock @@ -5597,17 +5597,20 @@ react-native-gesture-handler@^2.4.1: lodash "^4.17.21" prop-types "^15.7.2" -react-native-gradle-plugin@^0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.0.5.tgz#1f20d437b140eda65b6e3bdf6eb102bbab1a5a10" - integrity sha512-kGupXo+pD2mB6Z+Oyowor3qlCroiS32FNGoiGQdwU19u8o+NNhEZKwoKfC5Qt03bMZSmFlcAlTyf79vrS2BZKQ== - dependencies: - react-native-codegen "*" +react-native-gradle-plugin@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.0.6.tgz#b61a9234ad2f61430937911003cddd7e15c72b45" + integrity sha512-eIlgtsmDp1jLC24dRn43hB3kEcZVqx6DUQbR0N1ABXGnMEafm9I3V3dUUeD1vh+Dy5WqijSoEwLNUPLgu5zDMg== + +react-native-gradle-plugin@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.0.7.tgz#96602f909745239deab7b589443f14fce5da2056" + integrity sha512-+4JpbIx42zGTONhBTIXSyfyHICHC29VTvhkkoUOJAh/XHPEixpuBduYgf6Y4y9wsN1ARlQhBBoptTvXvAFQf5g== -react-native-safe-area-context@^4.1.4: - version "4.2.4" - resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-4.2.4.tgz#4df42819759c4d3c74252c8678c2772cfa2271a6" - integrity sha512-OOX+W2G4YYufvryonn6Kw6YnyT8ZThkxPHZBD04NLHaZmicUaaDVII/PZ3M5fD1o5N62+T+8K4bCS5Un2ggvkA== +react-native-safe-area-context@^4.2.5: + version "4.2.5" + resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-4.2.5.tgz#23006dc1a398bb825d7d795c27f1c46119efe8a5" + integrity sha512-nUil2de1gk/8ZB9IzIxFyGCiKeAYcHzJb/Tks2NzSkev1qH4MNR05DWYDSmW6vLT+y4mospLVyG/H5dyUd+KQQ== react-native-screens@^3.13.1: version "3.13.1" @@ -5617,10 +5620,10 @@ react-native-screens@^3.13.1: react-freeze "^1.0.0" warn-once "^0.1.0" -react-native@0.68.0: - version "0.68.0" - resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.68.0.tgz#00204818c7fa5a9802ee73f3905a6d9b1c945a31" - integrity sha512-Qi8KpG9rqiU0hVp05GKkuRe8iAVhblYMwpnwG3wkBi99Z/X8iZ0jD1b1UW0/y6oesmCyGQAxpsB36imU8zg1AQ== +react-native@0.68.1: + version "0.68.1" + resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.68.1.tgz#c3d92f89028cdc2453fe7cd2d532b3f68d1c27c8" + integrity sha512-5gfvslo5NO2Ece2k/q41eVOK3ca4u1QAOf+qM+auvOiUA4/QR5Yr0WfSGbRpUr2GaFgv7qP11F4+elCravg7uQ== dependencies: "@jest/create-cache-key-function" "^27.0.1" "@react-native-community/cli" "^7.0.3" @@ -5645,13 +5648,13 @@ react-native@0.68.0: promise "^8.0.3" react-devtools-core "^4.23.0" react-native-codegen "^0.0.13" - react-native-gradle-plugin "^0.0.5" + react-native-gradle-plugin "^0.0.6" react-refresh "^0.4.0" react-shallow-renderer "16.14.1" regenerator-runtime "^0.13.2" scheduler "^0.20.2" stacktrace-parser "^0.1.3" - use-subscription "^1.0.0" + use-subscription ">=1.0.0 <1.6.0" whatwg-fetch "^3.0.0" ws "^6.1.4" @@ -6664,7 +6667,7 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= -use-subscription@^1.0.0: +"use-subscription@>=1.0.0 <1.6.0": version "1.5.1" resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.5.1.tgz#73501107f02fad84c6dd57965beb0b75c68c42d1" integrity sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA== diff --git a/package.json b/package.json index 9bcf67134..ecb2e6414 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "jestSetup.js" ], "dependencies": { + "react-native-gradle-plugin": "^0.0.7", "recyclerlistview": "3.2.0-beta.4" }, "codegenConfig": { diff --git a/src/FlashList.tsx b/src/FlashList.tsx index 12650767e..c44d76350 100644 --- a/src/FlashList.tsx +++ b/src/FlashList.tsx @@ -536,11 +536,12 @@ class FlashList extends React.PureComponent< }; private itemContainer = (props: any, parentProps: any) => { + console.log(props.style); return (