From 4493d7cd4fb1fe932a21a35b3774dd8f4bb81a6e Mon Sep 17 00:00:00 2001 From: Mateo Hrastnik Date: Tue, 4 Feb 2025 15:21:23 +0100 Subject: [PATCH 01/11] Inject dev machine IP on Android and improve error message when connection fails --- .../ReactAndroid/build.gradle.kts | 11 +++ .../devsupport/DevSupportManagerBase.java | 99 ++++++++++++++++--- .../modules/systeminfo/AndroidInfoHelpers.kt | 3 +- .../src/main/jni/react/jni/JSLoader.cpp | 11 ++- .../main/res/devsupport/values/strings.xml | 5 + 5 files changed, 110 insertions(+), 19 deletions(-) diff --git a/packages/react-native/ReactAndroid/build.gradle.kts b/packages/react-native/ReactAndroid/build.gradle.kts index 06b99bd36252ee..fbca96a2bd61cd 100644 --- a/packages/react-native/ReactAndroid/build.gradle.kts +++ b/packages/react-native/ReactAndroid/build.gradle.kts @@ -12,6 +12,8 @@ import com.facebook.react.tasks.internal.utils.* import de.undercouch.gradle.tasks.download.Download import java.nio.file.Paths import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import java.net.NetworkInterface +import java.net.Inet4Address plugins { id("maven-publish") @@ -467,6 +469,14 @@ fun enableWarningsAsErrors(): Boolean { return value?.toString()?.toBoolean() ?: false } +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() ?: "127.0.0.1" + val packageReactNdkLibsForBuck by tasks.registering(Copy::class) { dependsOn("mergeDebugNativeLibs") @@ -523,6 +533,7 @@ android { buildConfigField("int", "EXOPACKAGE_FLAGS", "0") buildConfigField("boolean", "UNSTABLE_ENABLE_FUSEBOX_RELEASE", "false") buildConfigField("boolean", "ENABLE_PERFETTO", "false") + buildConfigField("String", "REACT_NATIVE_DEV_SERVER_IP", "\"${getHostIpAddress()}\"") resValue("integer", "react_native_dev_server_port", reactNativeDevServerPort()) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java index 072c46ef18affe..4503fd3b035cb8 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java @@ -20,11 +20,13 @@ import android.graphics.Typeface; import android.hardware.SensorManager; import android.os.Build; +import android.text.InputType; import android.util.Pair; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; +import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.ListAdapter; @@ -62,6 +64,8 @@ import com.facebook.react.devsupport.interfaces.StackFrame; import com.facebook.react.modules.core.RCTNativeAppEventEmitter; import com.facebook.react.modules.debug.interfaces.DeveloperSettings; +import com.facebook.react.modules.systeminfo.AndroidInfoHelpers; +import com.facebook.react.packagerconnection.PackagerConnectionSettings; import com.facebook.react.packagerconnection.RequestHandler; import java.io.File; import java.net.MalformedURLException; @@ -376,24 +380,91 @@ public void onOptionSelected() { return; } - final EditText input = new EditText(context); - input.setHint("localhost:8081"); + PackagerConnectionSettings settings = mDevSettings.getPackagerConnectionSettings(); + final String currentHost = settings.getDebugServerHost(); + settings.setDebugServerHost(""); + final String defaultHost = settings.getDebugServerHost(); + settings.setDebugServerHost(currentHost); + + LinearLayout layout = new LinearLayout(context); + layout.setOrientation(LinearLayout.VERTICAL); + int paddingSmall = (int) (4 * context.getResources().getDisplayMetrics().density); + int paddingLarge = (int) (16 * context.getResources().getDisplayMetrics().density); + layout.setPadding(paddingLarge, paddingLarge, paddingLarge, paddingLarge); - AlertDialog bundleLocationDialog = + final TextView label = new TextView(context); + label.setText(mApplicationContext.getString(R.string.catalyst_change_bundle_location_input_label)); + label.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT)); + + final EditText input = new EditText(context); + // This makes it impossible to enter a newline in the input field + input.setInputType(InputType.TYPE_CLASS_TEXT); + input.setHint(mApplicationContext.getString(R.string.catalyst_change_bundle_location_input_hint)); + input.setBackgroundResource(android.R.drawable.edit_text); + input.setHintTextColor(0xFFCCCCCC); + input.setTextColor(0xFF000000); + input.setText(currentHost); + + final Button defaultHostSuggestion = new Button(context); + defaultHostSuggestion.setText(defaultHost); + defaultHostSuggestion.setTextSize(12); + defaultHostSuggestion.setAllCaps(false); + defaultHostSuggestion.setOnClickListener(v -> input.setText(defaultHost)); + + final Button localhostSuggestion = new Button(context); + localhostSuggestion.setText("localhost:8081"); + localhostSuggestion.setTextSize(12); + localhostSuggestion.setAllCaps(false); + localhostSuggestion.setOnClickListener(v -> input.setText("localhost:8081")); + + LinearLayout suggestionRow = new LinearLayout(context); + suggestionRow.setOrientation(LinearLayout.HORIZONTAL); + suggestionRow.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT + )); + suggestionRow.addView(defaultHostSuggestion); + suggestionRow.addView(localhostSuggestion); + + final TextView instructions = new TextView(context); + instructions.setText(mApplicationContext.getString(R.string.catalyst_change_bundle_location_instructions, AndroidInfoHelpers.getAdbReverseTcpCommand(mApplicationContext))); + final LinearLayout.LayoutParams instructionsParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT); + instructionsParams.setMargins(0, paddingSmall, 0, paddingLarge); + instructions.setLayoutParams(instructionsParams); + + final Button applyChangesButton = new Button(context); + applyChangesButton.setText(mApplicationContext.getString(R.string.catalyst_change_bundle_location_apply)); + + final Button cancelButton = new Button(context); + cancelButton.setText(mApplicationContext.getString(R.string.catalyst_change_bundle_location_cancel)); + + layout.addView(label); + layout.addView(input); + layout.addView(suggestionRow); + layout.addView(instructions); + layout.addView(applyChangesButton); + layout.addView(cancelButton); + + final AlertDialog bundleLocationDialog = new AlertDialog.Builder(context) .setTitle(mApplicationContext.getString(R.string.catalyst_change_bundle_location)) - .setView(input) - .setPositiveButton( - android.R.string.ok, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - String host = input.getText().toString(); - mDevSettings.getPackagerConnectionSettings().setDebugServerHost(host); - handleReloadJS(); - } - }) + .setView(layout) .create(); + + applyChangesButton.setOnClickListener(v -> { + String host = input.getText().toString(); + mDevSettings.getPackagerConnectionSettings().setDebugServerHost(host); + handleReloadJS(); + bundleLocationDialog.dismiss(); + }); + cancelButton.setOnClickListener(v -> { + bundleLocationDialog.dismiss(); + }); + bundleLocationDialog.show(); }); diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoHelpers.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoHelpers.kt index b85015aa724b06..76d34c03b84e75 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoHelpers.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoHelpers.kt @@ -10,6 +10,7 @@ package com.facebook.react.modules.systeminfo import android.content.Context import android.os.Build import com.facebook.common.logging.FLog +import com.facebook.react.BuildConfig import com.facebook.react.R import java.io.BufferedReader import java.io.InputStreamReader @@ -92,7 +93,7 @@ public object AndroidInfoHelpers { getMetroHostPropValue().isNotEmpty() -> getMetroHostPropValue() isRunningOnGenymotion() -> GENYMOTION_LOCALHOST isRunningOnStockEmulator() -> EMULATOR_LOCALHOST - else -> DEVICE_LOCALHOST + else -> BuildConfig.REACT_NATIVE_DEV_SERVER_IP } return String.format(Locale.US, "%s:%d", ipAddress, port) } diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/jni/JSLoader.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/jni/JSLoader.cpp index 03921b543c1307..f1b0689d790d2f 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/jni/JSLoader.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/jni/JSLoader.cpp @@ -88,10 +88,13 @@ loadScriptFromAssets(AAssetManager* manager, const std::string& assetName) { } throw std::runtime_error(folly::to( - "Unable to load script. Make sure you're " - "either running Metro (run 'npx react-native start') or that your bundle '", - assetName, - "' is packaged correctly for release.")); + "Unable to load script.\n\n" + "Make sure you're running Metro ('npx react-native start') or that your " + "bundle '", assetName, "' is packaged correctly for release.\n\n" + "The device must be on the same WiFi as your laptop to connect to Metro.\n\n" + "To use USB instead, shake the device to open the dev menu and set " + "the bundler location to \"localhost:8081\" and run:\n" + " adb reverse tcp:8081 tcp:8081")); } } // namespace facebook::react diff --git a/packages/react-native/ReactAndroid/src/main/res/devsupport/values/strings.xml b/packages/react-native/ReactAndroid/src/main/res/devsupport/values/strings.xml index 8eca4e032d1eb8..1749492fa76632 100644 --- a/packages/react-native/ReactAndroid/src/main/res/devsupport/values/strings.xml +++ b/packages/react-native/ReactAndroid/src/main/res/devsupport/values/strings.xml @@ -3,6 +3,11 @@ Reload Failed to load bundle. Try restarting the bundler or reconnecting your device. Change Bundle Location + Provide a custom bundler address and port: + 127.0.0.1:8081 + If you experience slow reloads or unstable network connection, you can route data via USB cable instead of WiFi:\n 1. Connect your device via USB\n 2. Set the bundle location to `localhost:8081`\n 3. Run this command in your terminal:\n      `%1$s` + Apply Changes + Cancel Failed to open DevTools. Please check that the dev server is running and reload the app. Open DevTools Connect to the bundler to debug JavaScript From b4c06e618971278d52b91d42047554abc47b8ffd Mon Sep 17 00:00:00 2001 From: Mateo Hrastnik Date: Wed, 5 Feb 2025 20:11:41 +0100 Subject: [PATCH 02/11] Move getHostIpAddress and logic to react-native-gradle-plugin --- .../com/facebook/react/utils/AgpConfiguratorUtils.kt | 12 ++++++++++++ packages/react-native/ReactAndroid/build.gradle.kts | 11 ----------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt index 39344aac7716cb..2475bfaad5f7ef 100644 --- a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt +++ b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt @@ -13,6 +13,8 @@ import com.facebook.react.ReactExtension import com.facebook.react.utils.ProjectUtils.isHermesEnabled import com.facebook.react.utils.ProjectUtils.isNewArchEnabled import java.io.File +import java.net.NetworkInterface +import java.net.Inet4Address import javax.xml.parsers.DocumentBuilder import javax.xml.parsers.DocumentBuilderFactory import org.gradle.api.Action @@ -34,6 +36,8 @@ internal object AgpConfiguratorUtils { project.isNewArchEnabled(extension).toString()) ext.defaultConfig.buildConfigField( "boolean", "IS_HERMES_ENABLED", project.isHermesEnabled.toString()) + ext.defaultConfig.buildConfigField( + "String", "REACT_NATIVE_DEV_SERVER_IP", "\"${getHostIpAddress()}\"") } } project.pluginManager.withPlugin("com.android.application", action) @@ -104,3 +108,11 @@ fun getPackageNameFromManifest(manifest: File): String? { return null } } + +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() ?: "127.0.0.1" \ No newline at end of file diff --git a/packages/react-native/ReactAndroid/build.gradle.kts b/packages/react-native/ReactAndroid/build.gradle.kts index fbca96a2bd61cd..06b99bd36252ee 100644 --- a/packages/react-native/ReactAndroid/build.gradle.kts +++ b/packages/react-native/ReactAndroid/build.gradle.kts @@ -12,8 +12,6 @@ import com.facebook.react.tasks.internal.utils.* import de.undercouch.gradle.tasks.download.Download import java.nio.file.Paths import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import java.net.NetworkInterface -import java.net.Inet4Address plugins { id("maven-publish") @@ -469,14 +467,6 @@ fun enableWarningsAsErrors(): Boolean { return value?.toString()?.toBoolean() ?: false } -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() ?: "127.0.0.1" - val packageReactNdkLibsForBuck by tasks.registering(Copy::class) { dependsOn("mergeDebugNativeLibs") @@ -533,7 +523,6 @@ android { buildConfigField("int", "EXOPACKAGE_FLAGS", "0") buildConfigField("boolean", "UNSTABLE_ENABLE_FUSEBOX_RELEASE", "false") buildConfigField("boolean", "ENABLE_PERFETTO", "false") - buildConfigField("String", "REACT_NATIVE_DEV_SERVER_IP", "\"${getHostIpAddress()}\"") resValue("integer", "react_native_dev_server_port", reactNativeDevServerPort()) From 0b19e3e4e57c0447c87d4a3019d053b122d9f75f Mon Sep 17 00:00:00 2001 From: Mateo Hrastnik Date: Wed, 5 Feb 2025 23:02:56 +0100 Subject: [PATCH 03/11] Refactor dev_server_ip detection to use resource instead of BuildConfig --- .../src/main/kotlin/com/facebook/react/ReactPlugin.kt | 4 ++-- .../com/facebook/react/utils/AgpConfiguratorUtils.kt | 7 +++---- packages/react-native/ReactAndroid/build.gradle.kts | 1 + .../react/modules/systeminfo/AndroidInfoHelpers.kt | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt index 0e56882578a759..73e6501690d0c5 100644 --- a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt +++ b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt @@ -16,7 +16,7 @@ import com.facebook.react.tasks.GenerateCodegenSchemaTask import com.facebook.react.tasks.GeneratePackageListTask import com.facebook.react.utils.AgpConfiguratorUtils.configureBuildConfigFieldsForApp import com.facebook.react.utils.AgpConfiguratorUtils.configureBuildConfigFieldsForLibraries -import com.facebook.react.utils.AgpConfiguratorUtils.configureDevPorts +import com.facebook.react.utils.AgpConfiguratorUtils.configureDevIpAndPorts import com.facebook.react.utils.AgpConfiguratorUtils.configureNamespaceForLibraries import com.facebook.react.utils.BackwardCompatUtils.configureBackwardCompatibilityReactMap import com.facebook.react.utils.DependencyUtils.configureDependencies @@ -70,7 +70,7 @@ class ReactPlugin : Plugin { configureReactNativeNdk(project, extension) configureBuildConfigFieldsForApp(project, extension) - configureDevPorts(project) + configureDevIpAndPorts(project) configureBackwardCompatibilityReactMap(project) configureJavaToolChains(project) diff --git a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt index 2475bfaad5f7ef..6c23c00b880469 100644 --- a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt +++ b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt @@ -36,8 +36,6 @@ internal object AgpConfiguratorUtils { project.isNewArchEnabled(extension).toString()) ext.defaultConfig.buildConfigField( "boolean", "IS_HERMES_ENABLED", project.isHermesEnabled.toString()) - ext.defaultConfig.buildConfigField( - "String", "REACT_NATIVE_DEV_SERVER_IP", "\"${getHostIpAddress()}\"") } } project.pluginManager.withPlugin("com.android.application", action) @@ -54,13 +52,14 @@ internal object AgpConfiguratorUtils { } } - fun configureDevPorts(project: Project) { + fun configureDevIpAndPorts(project: Project) { val devServerPort = project.properties["reactNativeDevServerPort"]?.toString() ?: DEFAULT_DEV_SERVER_PORT val action = Action { project.extensions.getByType(AndroidComponentsExtension::class.java).finalizeDsl { ext -> + ext.defaultConfig.resValue("string", "react_native_dev_server_ip", getHostIpAddress()) ext.defaultConfig.resValue("integer", "react_native_dev_server_port", devServerPort) } } @@ -115,4 +114,4 @@ fun getHostIpAddress(): String = .flatMap { it.inetAddresses.asSequence() } .filter { it is Inet4Address && !it.isLoopbackAddress } .map { it.hostAddress } - .firstOrNull() ?: "127.0.0.1" \ No newline at end of file + .firstOrNull() ?: "localhost" \ No newline at end of file diff --git a/packages/react-native/ReactAndroid/build.gradle.kts b/packages/react-native/ReactAndroid/build.gradle.kts index 06b99bd36252ee..4bbdc425898dfa 100644 --- a/packages/react-native/ReactAndroid/build.gradle.kts +++ b/packages/react-native/ReactAndroid/build.gradle.kts @@ -525,6 +525,7 @@ android { buildConfigField("boolean", "ENABLE_PERFETTO", "false") resValue("integer", "react_native_dev_server_port", reactNativeDevServerPort()) + resValue("string", "react_native_dev_server_ip", "localhost") testApplicationId = "com.facebook.react.tests.gradle" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoHelpers.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoHelpers.kt index 76d34c03b84e75..61daa0ddf6d5a8 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoHelpers.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoHelpers.kt @@ -31,10 +31,10 @@ public object AndroidInfoHelpers { private fun isRunningOnStockEmulator(): Boolean = Build.FINGERPRINT.contains("generic") || Build.FINGERPRINT.startsWith("google/sdk_gphone") - @JvmStatic public fun getServerHost(port: Int): String = getServerIpAddress(port) + @JvmStatic public fun getServerHost(context: Context, port: Int): String = getServerIpAddress(context, port) @JvmStatic - public fun getServerHost(context: Context): String = getServerIpAddress(getDevServerPort(context)) + public fun getServerHost(context: Context): String = getServerIpAddress(context, getDevServerPort(context)) @JvmStatic public fun getAdbReverseTcpCommand(port: Int): String = "adb reverse tcp:$port tcp:$port" @@ -87,13 +87,13 @@ public object AndroidInfoHelpers { private fun getDevServerPort(context: Context): Int = context.resources.getInteger(R.integer.react_native_dev_server_port) - private fun getServerIpAddress(port: Int): String { + private fun getServerIpAddress(context: Context, port: Int): String { val ipAddress: String = when { getMetroHostPropValue().isNotEmpty() -> getMetroHostPropValue() isRunningOnGenymotion() -> GENYMOTION_LOCALHOST isRunningOnStockEmulator() -> EMULATOR_LOCALHOST - else -> BuildConfig.REACT_NATIVE_DEV_SERVER_IP + else -> context.resources.getString(R.string.react_native_dev_server_ip) } return String.format(Locale.US, "%s:%d", ipAddress, port) } From 74ca8db8769ae600c5415c6e64de98043934e151 Mon Sep 17 00:00:00 2001 From: Mateo Hrastnik Date: Fri, 14 Feb 2025 13:38:41 +0100 Subject: [PATCH 04/11] Wording tweaks Co-authored-by: Alex Hunt --- .../ReactAndroid/src/main/jni/react/jni/JSLoader.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/jni/JSLoader.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/jni/JSLoader.cpp index f1b0689d790d2f..0858acaa129326 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/jni/JSLoader.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/jni/JSLoader.cpp @@ -89,10 +89,10 @@ loadScriptFromAssets(AAssetManager* manager, const std::string& assetName) { throw std::runtime_error(folly::to( "Unable to load script.\n\n" - "Make sure you're running Metro ('npx react-native start') or that your " + "Make sure you're running Metro or that your " "bundle '", assetName, "' is packaged correctly for release.\n\n" - "The device must be on the same WiFi as your laptop to connect to Metro.\n\n" - "To use USB instead, shake the device to open the dev menu and set " + "The device must be on the same Wi-Fi network as your laptop to connect to Metro.\n\n" + "To use USB instead, shake the device to open the Dev Menu and set " "the bundler location to \"localhost:8081\" and run:\n" " adb reverse tcp:8081 tcp:8081")); } From a09c6e62cf390a00efcc151076646f60cd569af7 Mon Sep 17 00:00:00 2001 From: Mateo Hrastnik Date: Fri, 14 Feb 2025 13:39:19 +0100 Subject: [PATCH 05/11] Wording tweak Co-authored-by: Alex Hunt --- .../kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt index 6c23c00b880469..d769f26cb00d04 100644 --- a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt +++ b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt @@ -52,7 +52,7 @@ internal object AgpConfiguratorUtils { } } - fun configureDevIpAndPorts(project: Project) { + fun configureDevServerLocation(project: Project) { val devServerPort = project.properties["reactNativeDevServerPort"]?.toString() ?: DEFAULT_DEV_SERVER_PORT From 5446ee885e14145084fcf1a12f860c08eb95224d Mon Sep 17 00:00:00 2001 From: Mateo Hrastnik Date: Fri, 14 Feb 2025 13:49:22 +0100 Subject: [PATCH 06/11] Rename configureDevIpAndPorts to configureDevServerLocation --- .../src/main/kotlin/com/facebook/react/ReactPlugin.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt index 73e6501690d0c5..3255ae47e940cf 100644 --- a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt +++ b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt @@ -16,7 +16,7 @@ import com.facebook.react.tasks.GenerateCodegenSchemaTask import com.facebook.react.tasks.GeneratePackageListTask import com.facebook.react.utils.AgpConfiguratorUtils.configureBuildConfigFieldsForApp import com.facebook.react.utils.AgpConfiguratorUtils.configureBuildConfigFieldsForLibraries -import com.facebook.react.utils.AgpConfiguratorUtils.configureDevIpAndPorts +import com.facebook.react.utils.AgpConfiguratorUtils.configureDevServerLocation import com.facebook.react.utils.AgpConfiguratorUtils.configureNamespaceForLibraries import com.facebook.react.utils.BackwardCompatUtils.configureBackwardCompatibilityReactMap import com.facebook.react.utils.DependencyUtils.configureDependencies @@ -70,7 +70,7 @@ class ReactPlugin : Plugin { configureReactNativeNdk(project, extension) configureBuildConfigFieldsForApp(project, extension) - configureDevIpAndPorts(project) + configureDevServerLocation(project) configureBackwardCompatibilityReactMap(project) configureJavaToolChains(project) From 8977048faef8cff467568f6e49b0a9dc3b938837 Mon Sep 17 00:00:00 2001 From: Mateo Hrastnik Date: Fri, 14 Feb 2025 18:46:44 +0100 Subject: [PATCH 07/11] Make getHostIpAddress internal Co-authored-by: Nicola Corti --- .../kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt index d769f26cb00d04..b700a19244a1fd 100644 --- a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt +++ b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt @@ -108,7 +108,7 @@ fun getPackageNameFromManifest(manifest: File): String? { } } -fun getHostIpAddress(): String = +internal fun getHostIpAddress(): String = NetworkInterface.getNetworkInterfaces().asSequence() .filter { it.isUp && !it.isLoopback } .flatMap { it.inetAddresses.asSequence() } From ca66afc5a2a1f02b2cb108bb2c359ce26c4dadf7 Mon Sep 17 00:00:00 2001 From: Mateo Hrastnik Date: Fri, 14 Feb 2025 18:49:34 +0100 Subject: [PATCH 08/11] Remove unused import of BuildConfig in AndroidInfoHelpers.kt --- .../com/facebook/react/modules/systeminfo/AndroidInfoHelpers.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoHelpers.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoHelpers.kt index 61daa0ddf6d5a8..27840255767107 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoHelpers.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoHelpers.kt @@ -10,7 +10,6 @@ package com.facebook.react.modules.systeminfo import android.content.Context import android.os.Build import com.facebook.common.logging.FLog -import com.facebook.react.BuildConfig import com.facebook.react.R import java.io.BufferedReader import java.io.InputStreamReader From 20a6abbca31eebef593a285bfbbca8807af21ceb Mon Sep 17 00:00:00 2001 From: Mateo Hrastnik Date: Fri, 14 Feb 2025 19:10:16 +0100 Subject: [PATCH 09/11] Replace "laptop" with "computer" --- .../ReactAndroid/src/main/jni/react/jni/JSLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/jni/JSLoader.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/jni/JSLoader.cpp index 0858acaa129326..4e9e01c3f6f16e 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/jni/JSLoader.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/jni/JSLoader.cpp @@ -91,7 +91,7 @@ loadScriptFromAssets(AAssetManager* manager, const std::string& assetName) { "Unable to load script.\n\n" "Make sure you're running Metro or that your " "bundle '", assetName, "' is packaged correctly for release.\n\n" - "The device must be on the same Wi-Fi network as your laptop to connect to Metro.\n\n" + "The device must be on the same Wi-Fi network as your computer to connect to Metro.\n\n" "To use USB instead, shake the device to open the Dev Menu and set " "the bundler location to \"localhost:8081\" and run:\n" " adb reverse tcp:8081 tcp:8081")); From afe89654249919fd80fb663cd31426d52315de9d Mon Sep 17 00:00:00 2001 From: Mateo Hrastnik Date: Fri, 14 Feb 2025 19:47:12 +0100 Subject: [PATCH 10/11] Support getServerHost(port: Int) without passing context --- .../react/modules/systeminfo/AndroidInfoHelpers.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoHelpers.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoHelpers.kt index 27840255767107..dbc270d703c5ea 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoHelpers.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/AndroidInfoHelpers.kt @@ -30,11 +30,13 @@ public object AndroidInfoHelpers { private fun isRunningOnStockEmulator(): Boolean = Build.FINGERPRINT.contains("generic") || Build.FINGERPRINT.startsWith("google/sdk_gphone") - @JvmStatic public fun getServerHost(context: Context, port: Int): String = getServerIpAddress(context, port) + @JvmStatic public fun getServerHost(port: Int): String = getServerIpAddress(null, port) @JvmStatic public fun getServerHost(context: Context): String = getServerIpAddress(context, getDevServerPort(context)) + @JvmStatic public fun getServerHost(context: Context, port: Int): String = getServerIpAddress(context, port) + @JvmStatic public fun getAdbReverseTcpCommand(port: Int): String = "adb reverse tcp:$port tcp:$port" @@ -86,12 +88,13 @@ public object AndroidInfoHelpers { private fun getDevServerPort(context: Context): Int = context.resources.getInteger(R.integer.react_native_dev_server_port) - private fun getServerIpAddress(context: Context, port: Int): String { + private fun getServerIpAddress(context: Context?, port: Int): String { val ipAddress: String = when { getMetroHostPropValue().isNotEmpty() -> getMetroHostPropValue() isRunningOnGenymotion() -> GENYMOTION_LOCALHOST isRunningOnStockEmulator() -> EMULATOR_LOCALHOST + context == null -> DEVICE_LOCALHOST else -> context.resources.getString(R.string.react_native_dev_server_ip) } return String.format(Locale.US, "%s:%d", ipAddress, port) From 9518dd26ce7eb9be616b173b6348c88521ef8f3c Mon Sep 17 00:00:00 2001 From: Mateo Hrastnik Date: Sat, 8 Mar 2025 00:22:51 +0100 Subject: [PATCH 11/11] Use string concatenation in runtime error to fix build issue --- .../ReactAndroid/src/main/jni/react/jni/JSLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/jni/JSLoader.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/jni/JSLoader.cpp index e32dbfe0003a61..b7cc0cf30b47a9 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/jni/JSLoader.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/jni/JSLoader.cpp @@ -89,7 +89,7 @@ loadScriptFromAssets(AAssetManager* manager, const std::string& assetName) { throw std::runtime_error( "Unable to load script.\n\n" "Make sure you're running Metro or that your " - "bundle '", assetName, "' is packaged correctly for release.\n\n" + "bundle '" + assetName + "' is packaged correctly for release.\n\n" "The device must be on the same Wi-Fi network as your computer to connect to Metro.\n\n" "To use USB instead, shake the device to open the Dev Menu and set " "the bundler location to \"localhost:8081\" and run:\n"