/* * 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. */ package com.facebook.react.utils import com.android.build.api.variant.ApplicationAndroidComponentsExtension import com.android.build.api.variant.LibraryAndroidComponentsExtension import com.android.build.gradle.LibraryExtension import com.facebook.react.ReactExtension import com.facebook.react.utils.ProjectUtils.isEdgeToEdgeEnabled import com.facebook.react.utils.ProjectUtils.isHermesEnabled import com.facebook.react.utils.ProjectUtils.isNewArchEnabled import java.io.File import java.net.Inet4Address import java.net.NetworkInterface import javax.xml.parsers.DocumentBuilder import javax.xml.parsers.DocumentBuilderFactory import kotlin.plus import org.gradle.api.Action import org.gradle.api.Project import org.gradle.api.plugins.AppliedPlugin import org.w3c.dom.Element @Suppress("UnstableApiUsage") internal object AgpConfiguratorUtils { fun configureBuildTypesForApp(project: Project) { val action = Action { project.extensions .getByType(ApplicationAndroidComponentsExtension::class.java) .finalizeDsl { ext -> ext.buildTypes { val debug = getByName("debug").apply { manifestPlaceholders["usesCleartextTraffic"] = "true" } getByName("release").apply { manifestPlaceholders["usesCleartextTraffic"] = "false" } maybeCreate("debugOptimized").apply { manifestPlaceholders["usesCleartextTraffic"] = "true" initWith(debug) externalNativeBuild { cmake { arguments("-DCMAKE_BUILD_TYPE=Release") matchingFallbacks += listOf("release") } } } } } } project.pluginManager.withPlugin("com.android.application", action) } fun configureBuildConfigFieldsForApp(project: Project, extension: ReactExtension) { val action = Action { project.extensions .getByType(ApplicationAndroidComponentsExtension::class.java) .finalizeDsl { ext -> ext.buildFeatures.buildConfig = true ext.defaultConfig.buildConfigField( "boolean", "IS_NEW_ARCHITECTURE_ENABLED", project.isNewArchEnabled(extension).toString()) ext.defaultConfig.buildConfigField( "boolean", "IS_HERMES_ENABLED", project.isHermesEnabled.toString()) ext.defaultConfig.buildConfigField( "boolean", "IS_EDGE_TO_EDGE_ENABLED", project.isEdgeToEdgeEnabled.toString()) } } project.pluginManager.withPlugin("com.android.application", action) project.pluginManager.withPlugin("com.android.library", action) } fun configureBuildConfigFieldsForLibraries(appProject: Project) { appProject.rootProject.allprojects { subproject -> subproject.pluginManager.withPlugin("com.android.library") { subproject.extensions .getByType(LibraryAndroidComponentsExtension::class.java) .finalizeDsl { ext -> ext.buildFeatures.buildConfig = true } } } } fun configureDevServerLocation(project: Project) { val devServerPort = project.properties["reactNativeDevServerPort"]?.toString() ?: DEFAULT_DEV_SERVER_PORT val action = Action { project.extensions .getByType(ApplicationAndroidComponentsExtension::class.java) .finalizeDsl { ext -> ext.defaultConfig.resValue( "string", "react_native_dev_server_ip", getHostIpAddress()) ext.defaultConfig.resValue("integer", "react_native_dev_server_port", devServerPort) } } project.pluginManager.withPlugin("com.android.application", action) project.pluginManager.withPlugin("com.android.library", action) } fun configureNamespaceForLibraries(appProject: Project) { appProject.rootProject.allprojects { subproject -> subproject.pluginManager.withPlugin("com.android.library") { subproject.extensions .getByType(LibraryAndroidComponentsExtension::class.java) .finalizeDsl { ext -> if (ext.namespace == null) { val android = subproject.extensions.getByType(LibraryExtension::class.java) val manifestFile = android.sourceSets.getByName("main").manifest.srcFile manifestFile .takeIf { it.exists() } ?.let { file -> getPackageNameFromManifest(file)?.let { packageName -> ext.namespace = packageName } } } } } } } } const val DEFAULT_DEV_SERVER_PORT = "8081" fun getPackageNameFromManifest(manifest: File): String? { val factory: DocumentBuilderFactory = DocumentBuilderFactory.newInstance() val builder: DocumentBuilder = factory.newDocumentBuilder() try { val xmlDocument = builder.parse(manifest) val manifestElement = xmlDocument.getElementsByTagName("manifest").item(0) as? Element val packageName = manifestElement?.getAttribute("package") return if (packageName.isNullOrEmpty()) null else packageName } catch (e: Exception) { return null } } internal fun getHostIpAddress(): String = NetworkInterface.getNetworkInterfaces() .asSequence() .filter { it.isUp && !it.isLoopback } .flatMap { it.inetAddresses.asSequence() } .filter { it is Inet4Address && !it.isLoopbackAddress } .map { it.hostAddress } .firstOrNull() ?: "localhost"