import com.android.Version import groovy.json.JsonSlurper buildscript { ext { rnsDefaultTargetSdkVersion = 34 rnsDefaultCompileSdkVersion = 34 rnsDefaultMinSdkVersion = 21 rnsDefaultKotlinVersion = '1.8.0' } ext.safeExtGet = {prop, fallback -> def props = (prop instanceof String) ? [prop] : prop def result = props.find { key -> return rootProject.ext.has(key) } return result ? rootProject.ext.get(result) : fallback } repositories { google() mavenCentral() } dependencies { classpath('com.android.tools.build:gradle:8.2.1') classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${safeExtGet('kotlinVersion', rnsDefaultKotlinVersion)}" classpath "com.diffplug.spotless:spotless-plugin-gradle:6.25.0" } } def isRunningInContextOfScreensRepo() { return project == rootProject } 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" } def areDebugLogsEnabled() { return project.hasProperty("rnsDebugLogsEnabled") && project.rnsDebugLogsEnabled == "true" } def resolveReactNativeDirectory() { def userDefinedRnDirPath = safeAppExtGet("REACT_NATIVE_NODE_MODULES_DIR", null) if (userDefinedRnDirPath != null) { return file(userDefinedRnDirPath) } File standardRnDirFile = file("$rootDir/../node_modules/react-native/") if (standardRnDirFile.exists()) { return standardRnDirFile } // This is legacy code, I'm not sure why it works in certain scenarios but it was reported that one of our // projects needs this. File legacyRnDirFile = file("$projectDir/../node_modules/react-native/") if (legacyRnDirFile.exists()) { return legacyRnDirFile } // We're in non standard setup, e.g. monorepo - try to use node resolver to locate the react-native package. String maybeRnPackagePath = providers.exec { workingDir(rootDir) commandLine("node", "--print", "require.resolve('react-native/package.json')") }.standardOutput.asText.get().trim() File nodeResolverRnDirFile = null // file() constructor fails in case string is null or blank if (maybeRnPackagePath != null && !maybeRnPackagePath.isBlank()) { File maybeRnPackageFile = file(maybeRnPackagePath) if (maybeRnPackageFile.exists()) { nodeResolverRnDirFile = maybeRnPackageFile.parentFile return nodeResolverRnDirFile } } throw new Exception("[RNScreens] Failed to resolve react-native directory. " + "Attempted locations: ${standardRnDirFile}, ${legacyRnDirFile} and ${nodeResolverRnDirFile}. " + "You should set project extension property (in `app/build.gradle`) `REACT_NATIVE_NODE_MODULES_DIR` with path to react-native.") } // spotless is only accessible within react-native-screens repo if (isRunningInContextOfScreensRepo()) { apply from: 'spotless.gradle' } if (isNewArchitectureEnabled()) { apply plugin: "com.facebook.react" } apply plugin: 'com.android.library' apply plugin: 'kotlin-android' def reactNativeArchitectures() { def value = project.getProperties().get("reactNativeArchitectures") return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"] } def safeAppExtGet(prop, fallback) { def appProject = rootProject.allprojects.find { it.plugins.hasPlugin('com.android.application') } appProject?.ext?.has(prop) ? appProject.ext.get(prop) : fallback } def reactNativeRootDir = resolveReactNativeDirectory() def reactProperties = new Properties() file("$reactNativeRootDir/ReactAndroid/gradle.properties").withInputStream { reactProperties.load(it) } def REACT_NATIVE_VERSION = reactProperties.getProperty("VERSION_NAME") def REACT_NATIVE_MINOR_VERSION = REACT_NATIVE_VERSION.startsWith("0.0.0-") ? 1000 : REACT_NATIVE_VERSION.split("\\.")[1].toInteger() def IS_NEW_ARCHITECTURE_ENABLED = isNewArchitectureEnabled() android { compileSdkVersion safeExtGet('compileSdkVersion', rnsDefaultCompileSdkVersion) namespace "com.swmansion.rnscreens" // 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 } defaultConfig { minSdkVersion safeExtGet(['minSdkVersion', 'minSdk'], rnsDefaultMinSdkVersion) targetSdkVersion safeExtGet(['targetSdkVersion', 'targetSdk'], rnsDefaultTargetSdkVersion) versionCode 1 versionName "1.0" buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", IS_NEW_ARCHITECTURE_ENABLED.toString() buildConfigField "boolean", "RNS_DEBUG_LOGGING", areDebugLogsEnabled().toString() ndk { abiFilters (*reactNativeArchitectures()) } externalNativeBuild { cmake { arguments "-DANDROID_STL=c++_shared", "-DRNS_NEW_ARCH_ENABLED=${IS_NEW_ARCHITECTURE_ENABLED}", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON" } } } buildFeatures { prefab true buildConfig true } externalNativeBuild { cmake { path "CMakeLists.txt" } } lintOptions { abortOnError false } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } 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 librnscreens_modules but we // are only allowed to specify exclude patterns) excludes = [ "META-INF", "META-INF/**", "**/libjsi.so", "**/libc++_shared.so", "**/libreact_render*.so", "**/libreactnativejni.so", "**/libreact_performance_timeline.so", // In 0.76 multiple react-native's libraries were merged and these are the main new artifacts we're using. // Some of above lib* names could be removed after we remove support for 0.76. // https://github.com/facebook/react-native/pull/43909 // https://github.com/facebook/react-native/pull/46059 "**/libfbjni.so", "**/libreactnative.so" ] } sourceSets.main { ext.androidResDir = "src/main/res" java { // Architecture specific if (IS_NEW_ARCHITECTURE_ENABLED) { srcDirs += [ "src/fabric/java", ] } else { srcDirs += [ "src/paper/java", ] } // Background color resolving if (REACT_NATIVE_MINOR_VERSION <= 74) { srcDirs += "src/versioned/backgroundcolor/74" } else if (REACT_NATIVE_MINOR_VERSION <= 76) { srcDirs += "src/versioned/backgroundcolor/76" } else { srcDirs += "src/versioned/backgroundcolor/latest" } // Native only classes that use PointerEvents if (REACT_NATIVE_MINOR_VERSION <= 77) { srcDirs += "src/versioned/pointerevents/77" } else { srcDirs += "src/versioned/pointerevents/latest" } // Edge-to-edge detection if (REACT_NATIVE_MINOR_VERSION <= 80) { srcDirs += "src/versioned/edge-to-edge/80" } else { srcDirs += "src/versioned/edge-to-edge/latest" } } res { if (safeExtGet(['compileSdkVersion', 'compileSdk'], rnsDefaultCompileSdkVersion) >= 33) { srcDirs = ["${androidResDir}/base", "${androidResDir}/v33"] } else { srcDirs = ["${androidResDir}/base"] } } } } repositories { maven { url "${reactNativeRootDir}/android" } mavenCentral() mavenLocal() google() } dependencies { implementation 'com.facebook.react:react-native:+' implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.fragment:fragment-ktx:1.6.1' implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'com.google.android.material:material:1.12.0' implementation "androidx.core:core-ktx:1.8.0" def COIL_VERSION = "3.0.4" implementation("io.coil-kt.coil3:coil:${COIL_VERSION}") implementation("io.coil-kt.coil3:coil-network-okhttp:${COIL_VERSION}") implementation("io.coil-kt.coil3:coil-svg:${COIL_VERSION}") constraints { implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1") { because("on older React Native versions this dependency conflicts with react-native-screens") } } }