diff --git a/README.md b/README.md index 5afefceda2f..f4f6cc5e9a5 100644 --- a/README.md +++ b/README.md @@ -15,24 +15,27 @@ To learn how to develop MetaMask-compatible applications, visit our [Developer D ### Building Locally The code is built using React-Native and running code locally requires a Mac or Linux OS. -- Install [sentry-cli](https://github.com/getsentry/sentry-cli) tools: `brew install getsentry/tools/sentry-cli` -- Install [Node.js](https://nodejs.org) **version 10 (latest stable) and yarn@1 (latest)** - - If you are using [nvm](https://github.com/creationix/nvm#installation) (recommended) running `nvm use` will automatically choose the right node version for you. +- Install [sentry-cli](https://github.com/getsentry/sentry-cli) tools: `brew install getsentry/tools/sentry-cli` -- Install the shared React Native dependencies (`React Native CLI`, _not_ `Expo CLI`) - - [macOS](https://facebook.github.io/react-native/docs/getting-started.html#installing-dependencies-1) - - [Linux](https://facebook.github.io/react-native/docs/getting-started.html#installing-dependencies-2) +- Install [Node.js](https://nodejs.org) **version 10 (latest stable) and yarn@1 (latest)** -- Install [cocoapods](https://guides.cocoapods.org/using/getting-started.html) by running: + - If you are using [nvm](https://github.com/creationix/nvm#installation) (recommended) running `nvm use` will automatically choose the right node version for you. -```bash +- Install the shared React Native dependencies (`React Native CLI`, _not_ `Expo CLI`) + + - [macOS](https://facebook.github.io/react-native/docs/getting-started.html#installing-dependencies-1) + - [Linux](https://facebook.github.io/react-native/docs/getting-started.html#installing-dependencies-2) + +- Install [cocoapods](https://guides.cocoapods.org/using/getting-started.html) by running: + +```bash sudo gem install cocoapods ``` -- _MetaMask Only:_ Rename the `.*.env.example` files (remove the `.example`) in the root of the project and fill in the appropriate values for each key. Get the values from another MetaMask Mobile developer. +- _MetaMask Only:_ Rename the `.*.env.example` files (remove the `.example`) in the root of the project and fill in the appropriate values for each key. Get the values from another MetaMask Mobile developer. -- Clone this repo and install our dependencies: +- Clone this repo and install our dependencies: ```bash git clone ... @@ -41,15 +44,17 @@ yarn install # this will run a lengthy postinstall flow cd ios && pod install && cd .. # install pods for iOS ``` -- _Non-MetaMask Only:_ In the project root folder run +- _Non-MetaMask Only:_ In the project root folder run + ``` cp .ios.env.example .ios.env && \ cp .android.env.example .android.env && \ cp .js.env.example .js.env - ``` -- _Non-MetaMask Only:_ Create an account and generate your own API key at [Infura](https://infura.io) in order to connect to main and test nets. Fill `MM_INFURA_PROJECT_ID` in `.js.env`. (App will run without it, but will not be able to connect to actual network.) +``` + +- _Non-MetaMask Only:_ Create an account and generate your own API key at [Infura](https://infura.io) in order to connect to main and test nets. Fill `MM_INFURA_PROJECT_ID` in `.js.env`. (App will run without it, but will not be able to connect to actual network.) -- Then, in one terminal, run: +- Then, in one terminal, run: ```bash yarn watch @@ -57,19 +62,26 @@ yarn watch #### Android -- Install the Android SDK, via [Android Studio](https://developer.android.com/studio). - - _MetaMask Only:_ To create production builds, you need to install Google Play Licensing Library via the SDK Manager in Android Studio. -- Linux only: - - Ensure that you have the `secret-tool` binary on your machine. - - Part of the [libsecret-tools](https://launchpad.net/ubuntu/bionic/+package/libsecret-tools) package on Debian/Ubuntu based distributions. -- Install the correct emulator - - Follow the instructions at: - - [React Native Getting Started - Android](https://facebook.github.io/react-native/docs/getting-started.html#installing-dependencies-1) _(React Native CLI Quickstart -> [your OS] -> Android)_ - - More details can be found [on the Android Developer site](https://developer.android.com/studio/run/emulator) - - You should use the following: - - **Android OS Version:** Latest, unless told otherwise - - **Device:** Google Pixel 3 -- Finally, start the emulator from Android Studio, and run: +- Install the Android SDK, via [Android Studio](https://developer.android.com/studio). + - _MetaMask Only:_ To create production builds, you need to install Google Play Licensing Library via the SDK Manager in Android Studio. +- Install the Android NDK, via [Android Studio](https://developer.android.com/studio)'s SDK Manager. + - In the SDK Manager, select the `SDK Tools` tab and install NDK version `17.2.4988734`. + - In the `android` directory, update the `local.properties` file by adding line: + ``` + ndk.dir=/Users/YOUR_HOME_DIRECTORY/Library/Android/sdk/ndk/17.2.4988734 + ``` + _(You may have to create local.properties if it doesn't exist.)_ +- Linux only: + - Ensure that you have the `secret-tool` binary on your machine. + - Part of the [libsecret-tools](https://launchpad.net/ubuntu/bionic/+package/libsecret-tools) package on Debian/Ubuntu based distributions. +- Install the correct emulator + - Follow the instructions at: + - [React Native Getting Started - Android](https://facebook.github.io/react-native/docs/getting-started.html#installing-dependencies-1) _(React Native CLI Quickstart -> [your OS] -> Android)_ + - More details can be found [on the Android Developer site](https://developer.android.com/studio/run/emulator) + - You should use the following: + - **Android OS Version:** Latest, unless told otherwise + - **Device:** Google Pixel 3 +- Finally, start the emulator from Android Studio, and run: ```bash yarn start:android @@ -77,12 +89,12 @@ yarn start:android #### iOS -- Install the iOS dependencies - - [React Native Getting Started - iOS](https://facebook.github.io/react-native/docs/getting-started.html#installing-dependencies-1) _(React Native CLI Quickstart -> [your OS] -> iOS)_ - - You do **not** need CocoaPods -- Install the correct simulator - - **iOS OS Version:** Latest, unless told otherwise - - **Device:** iPhone 11 Pro +- Install the iOS dependencies + - [React Native Getting Started - iOS](https://facebook.github.io/react-native/docs/getting-started.html#installing-dependencies-1) _(React Native CLI Quickstart -> [your OS] -> iOS)_ + - You do **not** need CocoaPods +- Install the correct simulator + - **iOS OS Version:** Latest, unless told otherwise + - **Device:** iPhone 11 Pro ```bash yarn start:ios @@ -117,48 +129,47 @@ If `yarn link` fails after going through these steps, try directly `yarn add`ing First, make sure you have the following running: -- `yarn watch` -- Your Android emulator or iOS simulator -- `yarn start:android` or `yarn start:ios` +- `yarn watch` +- Your Android emulator or iOS simulator +- `yarn start:android` or `yarn start:ios` Next, check that the React Native Debugger is working: -- Open your emulator or simulator, and select `Debug JS Remotely` (or something similar) from its developer menu -- To open the developer menu: - - iOS Simulator: `Cmd + D` - - Android Emulator - - macOS: `Cmd + M` - - Windows, Linux: `Ctrl + M` -- If it doesn't open automatically, try navigating to this URL in Chrome: http://localhost:8081/debugger-ui/ -- If these steps do not take you to the React Native Debugger, something is wrong +- Open your emulator or simulator, and select `Debug JS Remotely` (or something similar) from its developer menu +- To open the developer menu: + - iOS Simulator: `Cmd + D` + - Android Emulator + - macOS: `Cmd + M` - Windows, Linux: `Ctrl + M` +- If it doesn't open automatically, try navigating to this URL in Chrome: http://localhost:8081/debugger-ui/ +- If these steps do not take you to the React Native Debugger, something is wrong #### Debugging iOS (macOS Only) For more details, see [this page](https://medium.com/@mattcroak718/debugging-your-iphone-mobile-web-app-using-safari-development-tools-71240657c487). -- You should be able to inspect the mobile app using the console in the React Native Debugger in Chrome -- To debug a website (dapp) in the browser: - - Navigate to the website in the app's browser - - Open Safari - - Go to: _Preferences -> Advanced_ and select `Show Develop menu in menu bar` - - Select `Develop` in the menu bar - - Find your simulator in the second section from the top - - Select the relevant WebView from the list - - The simulator will highlight the WebView when you hover over it in Safari +- You should be able to inspect the mobile app using the console in the React Native Debugger in Chrome +- To debug a website (dapp) in the browser: + - Navigate to the website in the app's browser + - Open Safari + - Go to: _Preferences -> Advanced_ and select `Show Develop menu in menu bar` + - Select `Develop` in the menu bar + - Find your simulator in the second section from the top + - Select the relevant WebView from the list + - The simulator will highlight the WebView when you hover over it in Safari #### Debugging Android For more details, see [this page](https://developers.google.com/web/tools/chrome-devtools/remote-debugging/webviews). -- You should be able to inspect the mobile app using the console in the React Native Debugger in Chrome -- To debug a website (dapp) in the browser: - - Navigate to the website in the app's browser - - Go to chrome://inspect - - Select the relevant WebView under **Remote Target** +- You should be able to inspect the mobile app using the console in the React Native Debugger in Chrome +- To debug a website (dapp) in the browser: + - Navigate to the website in the app's browser + - Go to chrome://inspect + - Select the relevant WebView under **Remote Target** #### Miscellaneous -- [Troubleshooting for React Native](https://facebook.github.io/react-native/docs/troubleshooting#content) +- [Troubleshooting for React Native](https://facebook.github.io/react-native/docs/troubleshooting#content) ### Running Tests diff --git a/android/app/build.gradle b/android/app/build.gradle index 31f6cd53fec..400f070dd61 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -243,6 +243,10 @@ android { } } +configurations.all { + exclude group: 'com.facebook.react', module: 'react-native' +} + dependencies { implementation project(':lottie-react-native') @@ -251,18 +255,14 @@ dependencies { implementation 'androidx.multidex:multidex:2.0.1' implementation 'androidx.annotation:annotation:1.1.0' implementation 'androidx.appcompat:appcompat:1.2.0' - implementation "com.facebook.react:react-native:+" // From node_modules + // Replace 'com.facebook.react:react-native:+' with project(':ReactAndroid') to respect changes that we make in native Android code + implementation project(':ReactAndroid') implementation 'org.chromium:v8-android:+' - implementation 'com.google.android.gms:play-services-wallet:18.0.0' - implementation "io.branch.sdk.android:library:5.+" - implementation 'com.mixpanel.android:mixpanel-android:5.+' - androidTestImplementation('com.wix:detox:+') { transitive = true } androidTestImplementation 'junit:junit:4.12' - implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") { exclude group:'com.facebook.fbjni' diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 50dfee06afe..f3685baf67f 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -102,6 +102,8 @@ + + diff --git a/android/settings.gradle b/android/settings.gradle index 9f478a440e1..7949fc6de89 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -9,3 +9,5 @@ apply from: file("../node_modules/@react-native-community/cli-platform-android/n include ':react-native-video' project(':react-native-video').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-video/android-exoplayer') include ':app' +include ':ReactAndroid' +project(':ReactAndroid').projectDir = new File(rootProject.projectDir, '../node_modules/react-native/ReactAndroid') diff --git a/bitrise.yml b/bitrise.yml index 67559ee4a0a..4e4de838ed0 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -94,6 +94,7 @@ workflows: steps: - install-missing-android-tools@2: inputs: + - ndk_revision: $NDK_VERSION - gradlew_path: $PROJECT_LOCATION/gradlew - avd-manager@1: inputs: @@ -200,6 +201,7 @@ workflows: - destination: android/keystores/release.keystore - install-missing-android-tools@2: inputs: + - ndk_revision: $NDK_VERSION - gradlew_path: $PROJECT_LOCATION/gradlew - script@1: inputs: @@ -234,7 +236,7 @@ workflows: - package_name: $MM_ANDROID_PACKAGE_NAME envs: - opts: - is_expand: false + is_expand: false MM_ANDROID_PACKAGE_NAME: io.metamask ios_test_release_step: before_run: @@ -276,6 +278,9 @@ app: - opts: is_expand: false PROJECT_LOCATION: android + - opts: + is_expand: false + NDK_VERSION: 17b - opts: is_expand: false MODULE: app diff --git a/patches/react-native+0.63.4.patch b/patches/react-native+0.63.4.patch index 270cc7e1769..09066c1eaaa 100644 --- a/patches/react-native+0.63.4.patch +++ b/patches/react-native+0.63.4.patch @@ -28,3 +28,50 @@ index a4e0cc0..4f4c9ea 100644 const BatchedBridge = require('react-native/Libraries/BatchedBridge/BatchedBridge'); const {Recording: RecordingModule} = NativeModules; +diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java +index d1783a3..3c5852c 100644 +--- a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java ++++ b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java +@@ -29,6 +29,7 @@ import android.view.KeyEvent; + import android.view.MotionEvent; + import android.view.View; + import android.view.accessibility.AccessibilityNodeInfo; ++import android.view.inputmethod.BaseInputConnection; + import android.view.inputmethod.EditorInfo; + import android.view.inputmethod.InputConnection; + import android.view.inputmethod.InputMethodManager; +@@ -74,6 +75,12 @@ public class ReactEditText extends AppCompatEditText { + /** A count of events sent to JS or C++. */ + protected int mNativeEventCount; + ++ /** ++ * Taken from EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING We can't use that ++ * value directly as it was only added on Oreo, but we can apply the value ++ * anyway. ++ */ ++ private static final int IME_FLAG_NO_PERSONALIZED_LEARNING = 0x1000000; + private static final int UNSET = -1; + + private @Nullable ArrayList mListeners; +@@ -215,15 +222,17 @@ public class ReactEditText extends AppCompatEditText { + @Override + public InputConnection onCreateInputConnection(EditorInfo outAttrs) { + ReactContext reactContext = getReactContext(this); +- InputConnection inputConnection = super.onCreateInputConnection(outAttrs); ++ InputConnection inputConnection = new BaseInputConnection(this, false); + if (inputConnection != null && mOnKeyPress) { + inputConnection = + new ReactEditTextInputConnectionWrapper(inputConnection, reactContext, this); + } + +- if (isMultiline() && getBlurOnSubmit()) { +- // Remove IME_FLAG_NO_ENTER_ACTION to keep the original IME_OPTION +- outAttrs.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION; ++ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { ++ outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING; ++ } else { ++ // Cover OS versions below Oreo ++ outAttrs.imeOptions = IME_FLAG_NO_PERSONALIZED_LEARNING; + } + return inputConnection; + } diff --git a/patches/react-native-webview+11.0.2.patch b/patches/react-native-webview+11.0.2.patch index 01d46fe65a9..d9345f6803a 100644 --- a/patches/react-native-webview+11.0.2.patch +++ b/patches/react-native-webview+11.0.2.patch @@ -1,8 +1,8 @@ diff --git a/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java b/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java -index 2190ae7..c2414ce 100644 +index 2190ae7..236a4f2 100644 --- a/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java +++ b/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java -@@ -1,5 +1,27 @@ +@@ -1,5 +1,30 @@ package com.reactnativecommunity.webview; +import java.io.ByteArrayInputStream; @@ -23,6 +23,9 @@ index 2190ae7..c2414ce 100644 + +import android.util.Log; + ++import android.view.inputmethod.BaseInputConnection; ++import android.view.inputmethod.EditorInfo; ++import android.view.inputmethod.InputConnection; +import android.webkit.ServiceWorkerController; +import android.webkit.ServiceWorkerClient; + @@ -30,7 +33,7 @@ index 2190ae7..c2414ce 100644 import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.DownloadManager; -@@ -129,13 +151,19 @@ public class RNCWebViewManager extends SimpleViewManager { +@@ -129,13 +154,19 @@ public class RNCWebViewManager extends SimpleViewManager { public static final int COMMAND_LOAD_URL = 7; public static final int COMMAND_FOCUS = 8; @@ -51,7 +54,7 @@ index 2190ae7..c2414ce 100644 protected static final String HTML_MIME_TYPE = "text/html"; protected static final String JAVASCRIPT_INTERFACE = "ReactNativeWebView"; protected static final String HTTP_METHOD_POST = "POST"; -@@ -150,11 +178,20 @@ public class RNCWebViewManager extends SimpleViewManager { +@@ -150,11 +181,20 @@ public class RNCWebViewManager extends SimpleViewManager { protected @Nullable String mUserAgent = null; protected @Nullable String mUserAgentWithApplicationName = null; @@ -64,7 +67,7 @@ index 2190ae7..c2414ce 100644 public void configWebView(WebView webView) { } }; -+ ++ + httpClient = new Builder() + .followRedirects(false) + .followSslRedirects(false) @@ -72,7 +75,7 @@ index 2190ae7..c2414ce 100644 } public RNCWebViewManager(WebViewConfig webViewConfig) { -@@ -181,6 +218,7 @@ public class RNCWebViewManager extends SimpleViewManager { +@@ -181,6 +221,7 @@ public class RNCWebViewManager extends SimpleViewManager { @TargetApi(Build.VERSION_CODES.LOLLIPOP) protected WebView createViewInstance(ThemedReactContext reactContext) { RNCWebView webView = createRNCWebViewInstance(reactContext); @@ -80,7 +83,7 @@ index 2190ae7..c2414ce 100644 setupWebChromeClient(reactContext, webView); reactContext.addLifecycleEventListener(webView); mWebViewConfig.configWebView(webView); -@@ -246,9 +284,96 @@ public class RNCWebViewManager extends SimpleViewManager { +@@ -246,9 +287,96 @@ public class RNCWebViewManager extends SimpleViewManager { } }); @@ -141,7 +144,7 @@ index 2190ae7..c2414ce 100644 + Uri url = request.getUrl(); + String urlStr = url.toString(); + -+ if (onlyMainFrame && !request.isForMainFrame() || ++ if (onlyMainFrame && !request.isForMainFrame() || + urlStringLooksInvalid(urlStr)) { + return null;//super.shouldInterceptRequest(webView, request); + } @@ -151,23 +154,23 @@ index 2190ae7..c2414ce 100644 + .url(urlStr) + .header("User-Agent", userAgent) + .build(); -+ ++ + Response response = httpClient.newCall(req).execute(); -+ -+ ++ ++ + if (!responseRequiresJSInjection(response)) { + return null; + } -+ ++ + InputStream is = response.body().byteStream(); + MediaType contentType = response.body().contentType(); + Charset charset = contentType != null ? contentType.charset(StandardCharsets.UTF_8) : StandardCharsets.UTF_8; -+ ++ + RNCWebView reactWebView = (RNCWebView) webView; + if (response.code() == HttpURLConnection.HTTP_OK) { + is = new InputStreamWithInjectedJS(is, reactWebView.injectedJSBeforeContentLoaded, charset); + } -+ ++ + return new WebResourceResponse("text/html", charset.name(), is); + } catch (IOException e) { + return null; @@ -177,7 +180,7 @@ index 2190ae7..c2414ce 100644 @ReactProp(name = "javaScriptEnabled") public void setJavaScriptEnabled(WebView view, boolean enabled) { view.getSettings().setJavaScriptEnabled(enabled); -@@ -778,8 +903,94 @@ public class RNCWebViewManager extends SimpleViewManager { +@@ -778,8 +906,94 @@ public class RNCWebViewManager extends SimpleViewManager { } } @@ -273,7 +276,7 @@ index 2190ae7..c2414ce 100644 protected boolean mLastLoadFailed = false; protected @Nullable ReadableArray mUrlPrefixesForDefaultIntent; -@@ -808,9 +1019,6 @@ public class RNCWebViewManager extends SimpleViewManager { +@@ -808,9 +1022,6 @@ public class RNCWebViewManager extends SimpleViewManager { super.onPageStarted(webView, url, favicon); mLastLoadFailed = false; @@ -283,7 +286,7 @@ index 2190ae7..c2414ce 100644 dispatchEvent( webView, new TopLoadingStartEvent( -@@ -818,6 +1026,16 @@ public class RNCWebViewManager extends SimpleViewManager { +@@ -818,6 +1029,16 @@ public class RNCWebViewManager extends SimpleViewManager { createWebViewEvent(webView, url))); } @@ -300,19 +303,19 @@ index 2190ae7..c2414ce 100644 @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { final RNCWebView rncWebView = (RNCWebView) view; -@@ -870,6 +1088,21 @@ public class RNCWebViewManager extends SimpleViewManager { +@@ -870,6 +1091,21 @@ public class RNCWebViewManager extends SimpleViewManager { @TargetApi(Build.VERSION_CODES.N) @Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { -+ ++ + /* + * In order to follow redirects properly, we return null in interceptRequest(). + * Doing this breaks the web3 injection on the resulting page, so we have to reload to + * make sure web3 is available. + * */ -+ ++ + if (request.isForMainFrame() && request.isRedirect()) { + view.loadUrl(request.getUrl().toString()); + return true; @@ -322,6 +325,39 @@ index 2190ae7..c2414ce 100644 final String url = request.getUrl().toString(); return this.shouldOverrideUrlLoading(view, url); } +@@ -1232,6 +1468,12 @@ public class RNCWebViewManager extends SimpleViewManager { + private OnScrollDispatchHelper mOnScrollDispatchHelper; + protected boolean hasScrollEvent = false; + protected ProgressChangedFilter progressChangedFilter; ++ /** ++ * Taken from EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING We can't use that ++ * value directly as it was only added on Oreo, but we can apply the value ++ * anyway. ++ */ ++ private static final int IME_FLAG_NO_PERSONALIZED_LEARNING = 0x1000000; + + /** + * WebView must be created with an context of the current activity +@@ -1257,6 +1499,19 @@ public class RNCWebViewManager extends SimpleViewManager { + this.hasScrollEvent = hasScrollEvent; + } + ++ @Override ++ public InputConnection onCreateInputConnection(EditorInfo outAttrs) { ++ InputConnection inputConnection = new BaseInputConnection(this, false); ++ ++ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { ++ outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING; ++ } else { ++ // Cover OS versions below Oreo ++ outAttrs.imeOptions = IME_FLAG_NO_PERSONALIZED_LEARNING; ++ } ++ return inputConnection; ++ } ++ + @Override + public void onHostResume() { + // do nothing diff --git a/node_modules/react-native-webview/apple/RNCWebView.m b/node_modules/react-native-webview/apple/RNCWebView.m index 02005db..c9b51d9 100644 --- a/node_modules/react-native-webview/apple/RNCWebView.m