From 7cedccdb8d07c87841f96fb327a01568a1a48835 Mon Sep 17 00:00:00 2001 From: Rick Hanlon Date: Thu, 16 Apr 2020 08:39:12 -0700 Subject: [PATCH] Add "Open Debugger" and "Open React DevTools" to Android dev menu Summary: This diff introduces a new "Open Debugger" menu item for VMs that support on device debugging and for opening the React DevTools in Flipper. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D20784279 fbshipit-source-id: caecdace00007224692d994a75c106842c8b2acb --- .../react/testing/ReactSettingsForTests.java | 2 +- .../react/devsupport/DevInternalSettings.java | 2 +- .../react/devsupport/DevServerHelper.java | 40 ++++++++++++ .../devsupport/DevSupportManagerBase.java | 61 +++++++++++++++---- .../debug/interfaces/DeveloperSettings.java | 2 +- .../main/res/devsupport/values/strings.xml | 5 +- 6 files changed, 96 insertions(+), 16 deletions(-) diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactSettingsForTests.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactSettingsForTests.java index cffb8eec4c5..de7f8cc1b76 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactSettingsForTests.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactSettingsForTests.java @@ -37,7 +37,7 @@ public class ReactSettingsForTests implements DeveloperSettings { } @Override - public boolean isNuclideJSDebugEnabled() { + public boolean isDeviceDebugEnabled() { return false; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevInternalSettings.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevInternalSettings.java index 9d060babc18..b63b37cdc9c 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevInternalSettings.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevInternalSettings.java @@ -105,7 +105,7 @@ public class DevInternalSettings } @Override - public boolean isNuclideJSDebugEnabled() { + public boolean isDeviceDebugEnabled() { return ReactBuildConfig.IS_INTERNAL_BUILD && ReactBuildConfig.DEBUG; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java index f80ae648404..5c5bad9eb4a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.java @@ -7,10 +7,12 @@ package com.facebook.react.devsupport; +import android.content.Context; import android.os.AsyncTask; import androidx.annotation.Nullable; import com.facebook.common.logging.FLog; import com.facebook.infer.annotation.Assertions; +import com.facebook.react.bridge.ReactContext; import com.facebook.react.common.ReactConstants; import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener; import com.facebook.react.devsupport.interfaces.PackagerStatusCallback; @@ -23,6 +25,7 @@ import com.facebook.react.packagerconnection.ReconnectingWebSocket.ConnectionCal import com.facebook.react.packagerconnection.RequestHandler; import com.facebook.react.packagerconnection.RequestOnlyHandler; import com.facebook.react.packagerconnection.Responder; +import com.facebook.react.util.RNLog; import java.io.File; import java.io.IOException; import java.util.Arrays; @@ -247,6 +250,38 @@ public class DevServerHelper { }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } + public void openUrl(final ReactContext context, final String url, final String errorMessage) { + new AsyncTask() { + @Override + protected Boolean doInBackground(Void... ignore) { + return doSync(); + } + + public boolean doSync() { + try { + String openUrlEndpoint = getOpenUrlEndpoint(context); + String jsonString = new JSONObject().put("url", url).toString(); + RequestBody body = RequestBody.create(MediaType.parse("application/json"), jsonString); + + Request request = new Request.Builder().url(openUrlEndpoint).post(body).build(); + OkHttpClient client = new OkHttpClient(); + client.newCall(request).execute(); + return true; + } catch (JSONException | IOException e) { + FLog.e(ReactConstants.TAG, "Failed to open URL" + url, e); + return false; + } + } + + @Override + protected void onPostExecute(Boolean result) { + if (!result) { + RNLog.w(context, errorMessage); + } + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + public void symbolicateStackTrace( Iterable stackFrames, final SymbolicationListener listener) { try { @@ -345,6 +380,11 @@ public class DevServerHelper { mBundleDownloader.downloadBundleFromURL(callback, outputFile, bundleURL, bundleInfo); } + private String getOpenUrlEndpoint(Context context) { + return String.format( + Locale.US, "http://%s/open-url", AndroidInfoHelpers.getServerHost(context)); + } + public void downloadBundleFromURL( DevBundleDownloadListener callback, File outputFile, diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java index 249a359230d..8a99c522ba9 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java @@ -71,6 +71,9 @@ public abstract class DevSupportManagerBase private static final int JSEXCEPTION_ERROR_COOKIE = -1; private static final String JS_BUNDLE_FILE_NAME = "ReactNativeDevBundle.js"; private static final String RELOAD_APP_ACTION_SUFFIX = ".RELOAD_APP_ACTION"; + private static final String FLIPPER_DEBUGGER_URL = + "flipper://null/Hermesdebuggerrn?device=React%20Native"; + private static final String FLIPPER_DEVTOOLS_URL = "flipper://null/React?device=React%20Native"; private boolean mIsSamplingProfilerEnabled = false; private enum ErrorType { @@ -432,17 +435,53 @@ public abstract class DevSupportManagerBase handleReloadJS(); } }); - options.put( - mDevSettings.isRemoteJSDebugEnabled() - ? mApplicationContext.getString(R.string.catalyst_debug_stop) - : mApplicationContext.getString(R.string.catalyst_debug), - new DevOptionHandler() { - @Override - public void onOptionSelected() { - mDevSettings.setRemoteJSDebugEnabled(!mDevSettings.isRemoteJSDebugEnabled()); - handleReloadJS(); - } - }); + if (mDevSettings.isDeviceDebugEnabled()) { + // For on-device debugging we link out to Flipper. + // Since we're assuming Flipper is available, also include the DevTools. + + // Reset the old debugger setting so no one gets stuck. + // TODO: Remove in a few weeks. + if (mDevSettings.isRemoteJSDebugEnabled()) { + mDevSettings.setRemoteJSDebugEnabled(false); + handleReloadJS(); + } + options.put( + mApplicationContext.getString(R.string.catalyst_debug_open), + new DevOptionHandler() { + @Override + public void onOptionSelected() { + mDevServerHelper.openUrl( + mCurrentContext, + FLIPPER_DEBUGGER_URL, + mApplicationContext.getString(R.string.catalyst_open_flipper_error)); + } + }); + options.put( + mApplicationContext.getString(R.string.catalyst_devtools_open), + new DevOptionHandler() { + @Override + public void onOptionSelected() { + mDevServerHelper.openUrl( + mCurrentContext, + FLIPPER_DEVTOOLS_URL, + mApplicationContext.getString(R.string.catalyst_open_flipper_error)); + } + }); + } else { + // For remote debugging, we open up Chrome running the app in a web worker. + // Note that this requires async communication, which will not work for Turbo Modules. + options.put( + mDevSettings.isRemoteJSDebugEnabled() + ? mApplicationContext.getString(R.string.catalyst_debug_stop) + : mApplicationContext.getString(R.string.catalyst_debug), + new DevOptionHandler() { + @Override + public void onOptionSelected() { + mDevSettings.setRemoteJSDebugEnabled(!mDevSettings.isRemoteJSDebugEnabled()); + handleReloadJS(); + } + }); + } options.put( mApplicationContext.getString(R.string.catalyst_change_bundle_location), new DevOptionHandler() { diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/debug/interfaces/DeveloperSettings.java b/ReactAndroid/src/main/java/com/facebook/react/modules/debug/interfaces/DeveloperSettings.java index bc5e19d7c9e..118c8fdb276 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/debug/interfaces/DeveloperSettings.java +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/debug/interfaces/DeveloperSettings.java @@ -26,7 +26,7 @@ public interface DeveloperSettings { boolean isElementInspectorEnabled(); /** @return Whether Nuclide JS debugging is enabled. */ - boolean isNuclideJSDebugEnabled(); + boolean isDeviceDebugEnabled(); /** @return Whether remote JS debugging is enabled. */ boolean isRemoteJSDebugEnabled(); diff --git a/ReactAndroid/src/main/res/devsupport/values/strings.xml b/ReactAndroid/src/main/res/devsupport/values/strings.xml index 82db0a9b7ec..3571c835e31 100644 --- a/ReactAndroid/src/main/res/devsupport/values/strings.xml +++ b/ReactAndroid/src/main/res/devsupport/values/strings.xml @@ -3,14 +3,15 @@ Reload Failed to load bundle. Try restarting the bundler or reconnecting your device. Change Bundle Location + Failed to open Flipper. Please check that Metro is running. + Open React DevTools + Open Debugger Debug Stop Debugging Connecting to debugger... Failed to connect to debugger! Debug with Chrome Stop Chrome Debugging - Debug with Nuclide - Failed to communicate with the bundler to enabling debugging with Nuclide. Enable Fast Refresh Disable Fast Refresh Disabling Fast Refresh because it requires a development bundle.