diff --git a/android/.project b/android/.project new file mode 100644 index 00000000..d22beed7 --- /dev/null +++ b/android/.project @@ -0,0 +1,17 @@ + + + BlueWallet + Project android created by Buildship. + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + + org.eclipse.buildship.core.gradleprojectnature + + diff --git a/android/.settings/org.eclipse.buildship.core.prefs b/android/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 00000000..e8895216 --- /dev/null +++ b/android/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,2 @@ +connection.project.dir= +eclipse.preferences.version=1 diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index a73ac221..29140e2f 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -4,7 +4,10 @@ - + + + + + NSBluetoothAlwaysUsageDescription + In order to search for other BlueWallet users, we need your permission to scan for them. CFBundleDevelopmentRegion en CFBundleDisplayName @@ -56,18 +58,18 @@ NSAppleMusicUsageDescription This alert should not show up as we do not require this data - NSFaceIDUsageDescription - In order to confirm your identity, we need your permission to use FaceID. NSBluetoothPeripheralUsageDescription This alert should not show up as we do not require this data NSCalendarsUsageDescription This alert should not show up as we do not require this data NSCameraUsageDescription In order to quickly scan the recipient's address, we need your permission to use the camera to scan their QR Code. - NSLocationWhenInUseUsageDescription - This alert should not show up as we do not require this data + NSFaceIDUsageDescription + In order to confirm your identity, we need your permission to use FaceID. NSLocationAlwaysUsageDescription This alert should not show up as we do not require this data + NSLocationWhenInUseUsageDescription + This alert should not show up as we do not require this data NSMicrophoneUsageDescription This alert should not show up as we do not require this data NSMotionUsageDescription @@ -96,6 +98,12 @@ SimpleLineIcons.ttf Zocial.ttf + UIBackgroundModes + + bluetooth-central + bluetooth-peripheral + processing + UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 3ac28e18..452eafa3 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -10,13 +10,13 @@ PODS: - AppCenter/Crashes - AppCenterReactNativeShared - React - - AppCenter/Analytics (2.4.0): + - AppCenter/Analytics (2.5.0): - AppCenter/Core - - AppCenter/Core (2.4.0) - - AppCenter/Crashes (2.4.0): + - AppCenter/Core (2.5.0) + - AppCenter/Crashes (2.5.0): - AppCenter/Core - - AppCenterReactNativeShared (2.4.0): - - AppCenter/Core (= 2.4.0) + - AppCenterReactNativeShared (2.5.0): + - AppCenter/Core (= 2.5.0) - boost-for-react-native (1.63.0) - BVLinearGradient (2.5.4): - React @@ -84,6 +84,8 @@ PODS: - React-jsinspector (0.60.5) - react-native-biometrics (1.6.1): - React + - react-native-ble-manager (6.7.0): + - React - react-native-blur (0.8.0): - React - react-native-camera (3.4.0): @@ -184,6 +186,7 @@ DEPENDENCIES: - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`) - react-native-biometrics (from `../node_modules/react-native-biometrics`) + - react-native-ble-manager (from `../node_modules/react-native-ble-manager`) - "react-native-blur (from `../node_modules/@react-native-community/blur`)" - react-native-camera (from `../node_modules/react-native-camera`) - react-native-haptic-feedback (from `../node_modules/react-native-haptic-feedback`) @@ -262,6 +265,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/jsinspector" react-native-biometrics: :path: "../node_modules/react-native-biometrics" + react-native-ble-manager: + :path: "../node_modules/react-native-ble-manager" react-native-blur: :path: "../node_modules/@react-native-community/blur" react-native-camera: @@ -332,11 +337,11 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/yoga" SPEC CHECKSUMS: - AppCenter: 49a9ffe114c00e2bf5374aeda816d47eabf1978a + AppCenter: 637f180deefc61e8ab3f94223869ee50f61dabea appcenter: 4072e79b8d037d99056e6fc6556224b4525b5829 appcenter-analytics: 94be52eca805d586207d710aac95e0ddca8b3ddb appcenter-crashes: 30caece47856aee7c4c7449d4f4dae2d69795bd6 - AppCenterReactNativeShared: 57a66e6539e9abe6079ba8f93a002bc3f95788cc + AppCenterReactNativeShared: 99e7f662ec66b1cb41306ecf357aabac35931c08 boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c BVLinearGradient: 8cbc5155c978f2e43098818c91d206d07aae6d30 DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2 @@ -353,6 +358,7 @@ SPEC CHECKSUMS: React-jsiexecutor: 90ad2f9db09513fc763bc757fdc3c4ff8bde2a30 React-jsinspector: e08662d1bf5b129a3d556eb9ea343a3f40353ae4 react-native-biometrics: 4aaf49f9f8bd28c6aa3ec53534ca1b6b00486f6a + react-native-ble-manager: c61d563acabfc675d26ce55d3b6e2e84df950203 react-native-blur: cad4d93b364f91e7b7931b3fa935455487e5c33c react-native-camera: 203091b4bf99d48b788a0682ad573e8718724893 react-native-haptic-feedback: 22c9dc85fd8059f83bf9edd9212ac4bd4ae6074d diff --git a/package-lock.json b/package-lock.json index 0a34c6f3..2a708a61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11118,6 +11118,11 @@ "version": "git+https://github.com/BlueWallet/react-native-biometrics.git#8901d29b3d3443b0bc280c941d879ccf45ac01da", "from": "git+https://github.com/BlueWallet/react-native-biometrics.git" }, + "react-native-ble-manager": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/react-native-ble-manager/-/react-native-ble-manager-6.7.0.tgz", + "integrity": "sha512-hJuij3nWKBp4Ba7GaT8CzTGOp5i33czvU53Emvf6kBxCXNc2tBR55NogTtTC4DbTfrxNQHjr6Ppws7pDcH088w==" + }, "react-native-camera": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/react-native-camera/-/react-native-camera-3.4.0.tgz", @@ -11356,7 +11361,7 @@ } }, "react-native-tcp": { - "version": "git+https://github.com/aprock/react-native-tcp.git#c29cf0f29c14eab41182a37af54ef7b2409620b2", + "version": "git+https://github.com/aprock/react-native-tcp.git#6a3b1bc702bf1d40287274ac32698335a8fba61a", "from": "git+https://github.com/aprock/react-native-tcp.git", "requires": { "base64-js": "0.0.8", @@ -11989,7 +11994,7 @@ } }, "rn-nodeify": { - "version": "github:tradle/rn-nodeify#580705c3ee0227298d0d12dba413f7aa3bb24ebb", + "version": "github:tradle/rn-nodeify#300ca4460c3e4ffa01c45b3a122ce182dc1a0c5a", "from": "github:tradle/rn-nodeify", "dev": true, "requires": { diff --git a/package.json b/package.json index 614eba83..033f56ba 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ "react-localization": "1.0.13", "react-native": "0.60.5", "react-native-biometrics": "git+https://github.com/BlueWallet/react-native-biometrics.git", + "react-native-ble-manager": "^6.7.0", "react-native-camera": "3.4.0", "react-native-device-info": "4.0.1", "react-native-elements": "0.19.0", diff --git a/screen/wallets/list.js b/screen/wallets/list.js index 010f94b0..ae8764aa 100644 --- a/screen/wallets/list.js +++ b/screen/wallets/list.js @@ -1,5 +1,5 @@ import React, { Component } from 'react'; -import { View, TouchableOpacity, Text, FlatList, InteractionManager, RefreshControl, ScrollView } from 'react-native'; +import { View, TouchableOpacity, NativeEventEmitter, NativeModules, Text, FlatList, InteractionManager, RefreshControl, ScrollView } from 'react-native'; import { BlueLoading, SafeBlueArea, WalletsCarousel, BlueList, BlueHeaderDefaultMain, BlueTransactionListItem } from '../../BlueComponents'; import { Icon } from 'react-native-elements'; import { NavigationEvents } from 'react-navigation'; @@ -7,6 +7,9 @@ import ReactNativeHapticFeedback from 'react-native-haptic-feedback'; import PropTypes from 'prop-types'; import WalletGradient from '../../class/walletGradient'; import OnAppLaunch from '../../class/onAppLaunch'; +import BleManager from 'react-native-ble-manager'; +const BleManagerModule = NativeModules.BleManager; +const bleManagerEmitter = new NativeEventEmitter(BleManagerModule); let EV = require('../../events'); let A = require('../../analytics'); /** @type {AppStorage} */ @@ -38,15 +41,32 @@ export default class WalletsList extends Component { isFlatListRefreshControlHidden: true, wallets: BlueApp.getWallets().concat(false), lastSnappedTo: 0, + scanning:false, + peripherals: new Map(), }; EV(EV.enum.WALLETS_COUNT_CHANGED, this.redrawScreen.bind(this)); // here, when we receive TRANSACTIONS_COUNT_CHANGED we do not query // remote server, we just redraw the screen EV(EV.enum.TRANSACTIONS_COUNT_CHANGED, this.redrawScreen.bind(this)); + + this.handleDiscoverPeripheral = this.handleDiscoverPeripheral.bind(this); + this.handleStopScan = this.handleStopScan.bind(this); + this.handleUpdateValueForCharacteristic = this.handleUpdateValueForCharacteristic.bind(this); + this.handleDisconnectedPeripheral = this.handleDisconnectedPeripheral.bind(this); + this.handleAppStateChange = this.handleAppStateChange.bind(this); } componentDidMount() { + + BleManager.start({showAlert: true}).then((result) => this.startScan()); + + this.handlerDiscover = bleManagerEmitter.addListener('BleManagerDiscoverPeripheral', this.handleDiscoverPeripheral ); + this.handlerStop = bleManagerEmitter.addListener('BleManagerStopScan', this.handleStopScan ); + this.handlerDisconnect = bleManagerEmitter.addListener('BleManagerDisconnectPeripheral', this.handleDisconnectedPeripheral ); + this.handlerUpdate = bleManagerEmitter.addListener('BleManagerDidUpdateValueForCharacteristic', this.handleUpdateValueForCharacteristic ); + + this.redrawScreen(); // the idea is that upon wallet launch we will refresh // all balances and all transactions here: @@ -76,6 +96,152 @@ export default class WalletsList extends Component { }); } + handleDisconnectedPeripheral(data) { + let peripherals = this.state.peripherals; + let peripheral = peripherals.get(data.peripheral); + if (peripheral) { + peripheral.connected = false; + peripherals.set(peripheral.id, peripheral); + this.setState({peripherals}); + } + console.log('Disconnected from ' + data.peripheral); + } + + handleUpdateValueForCharacteristic(data) { + console.log('Received data from ' + data.peripheral + ' characteristic ' + data.characteristic, data.value); + } + + handleStopScan() { + console.log('Scan is stopped'); + this.setState({ scanning: false }); + } + + startScan() { + if (!this.state.scanning) { + console.warn('HERE') + //this.setState({peripherals: new Map()}); + BleManager.scan([], 3, true).then((results) => { + console.log('Scanning...'); + console.warn(results) + BleManager.connect('3EE7D2BD-0D0E-BBEC-7FC4-F53BBB23F6BB') + .then(() => { + // Success code + console.log('Connected'); + }) + .catch((error) => { + // Failure code + console.log(error); + }); + this.setState({scanning:true}); + }); + } + } + + retrieveConnected(){ + BleManager.getConnectedPeripherals([]).then((results) => { + if (results.length == 0) { + console.log('No connected peripherals') + } + console.log(results); + var peripherals = this.state.peripherals; + for (var i = 0; i < results.length; i++) { + var peripheral = results[i]; + peripheral.connected = true; + peripherals.set(peripheral.id, peripheral); + this.setState({ peripherals }); + } + }); + } + + + handleAppStateChange(nextAppState) { + if (this.state.appState.match(/inactive|background/) && nextAppState === 'active') { + console.log('App has come to the foreground!') + BleManager.getConnectedPeripherals([]).then((peripheralsArray) => { + console.log('Connected peripherals: ' + peripheralsArray.length); + }); + } + this.setState({appState: nextAppState}); + } + + handleDiscoverPeripheral(peripheral){ + var peripherals = this.state.peripherals; + console.log('Got ble peripheral', peripheral); + if (!peripheral.name) { + peripheral.name = 'NO NAME'; + } + peripherals.set(peripheral.id, peripheral); + this.setState({ peripherals }); + } + + test(peripheral) { + if (peripheral){ + if (peripheral.connected){ + BleManager.disconnect(peripheral.id); + }else{ + BleManager.connect(peripheral.id).then(() => { + let peripherals = this.state.peripherals; + let p = peripherals.get(peripheral.id); + if (p) { + p.connected = true; + peripherals.set(peripheral.id, p); + this.setState({peripherals}); + } + console.log('Connected to ' + peripheral.id); + + + setTimeout(() => { + + /* Test read current RSSI value + BleManager.retrieveServices(peripheral.id).then((peripheralData) => { + console.log('Retrieved peripheral services', peripheralData); + BleManager.readRSSI(peripheral.id).then((rssi) => { + console.log('Retrieved actual RSSI value', rssi); + }); + });*/ + + // Test using bleno's pizza example + // https://github.com/sandeepmistry/bleno/tree/master/examples/pizza + BleManager.retrieveServices(peripheral.id).then((peripheralInfo) => { + console.log(peripheralInfo); + var service = '13333333-3333-3333-3333-333333333337'; + var bakeCharacteristic = '13333333-3333-3333-3333-333333330003'; + var crustCharacteristic = '13333333-3333-3333-3333-333333330001'; + + setTimeout(() => { + BleManager.startNotification(peripheral.id, service, bakeCharacteristic).then(() => { + console.log('Started notification on ' + peripheral.id); + setTimeout(() => { + BleManager.write(peripheral.id, service, crustCharacteristic, [0]).then(() => { + console.log('Writed NORMAL crust'); + BleManager.write(peripheral.id, service, bakeCharacteristic, [1,95]).then(() => { + console.log('Writed 351 temperature, the pizza should be BAKED'); + /* + var PizzaBakeResult = { + HALF_BAKED: 0, + BAKED: 1, + CRISPY: 2, + BURNT: 3, + ON_FIRE: 4 + };*/ + }); + }); + + }, 500); + }).catch((error) => { + console.log('Notification error', error); + }); + }, 200); + }); + + }, 900); + }).catch((error) => { + console.log('Connection error', error); + }); + } + } + } + /** * Forcefully fetches TXs and balance for lastSnappedTo (i.e. current) wallet. * Triggered manually by user on pull-to-refresh.