diff --git a/openvidu-react-native-androidx/.buckconfig b/openvidu-react-native-androidx/.buckconfig new file mode 100644 index 00000000..934256cb --- /dev/null +++ b/openvidu-react-native-androidx/.buckconfig @@ -0,0 +1,6 @@ + +[android] + target = Google Inc.:Google APIs:23 + +[maven_repositories] + central = https://repo1.maven.org/maven2 diff --git a/openvidu-react-native-androidx/.flowconfig b/openvidu-react-native-androidx/.flowconfig new file mode 100644 index 00000000..47d80d95 --- /dev/null +++ b/openvidu-react-native-androidx/.flowconfig @@ -0,0 +1,69 @@ +[ignore] +; We fork some components by platform +.*/*[.]android.js + +; Ignore "BUCK" generated dirs +/\.buckd/ + +; Ignore unexpected extra "@providesModule" +.*/node_modules/.*/node_modules/fbjs/.* + +; Ignore duplicate module providers +; For RN Apps installed via npm, "Libraries" folder is inside +; "node_modules/react-native" but in the source repo it is in the root +.*/Libraries/react-native/React.js + +; Ignore polyfills +.*/Libraries/polyfills/.* + +; Ignore metro +.*/node_modules/metro/.* + +[include] + +[libs] +node_modules/react-native/Libraries/react-native/react-native-interface.js +node_modules/react-native/flow/ + +[options] +emoji=true + +esproposal.optional_chaining=enable +esproposal.nullish_coalescing=enable + +module.system=haste +module.system.haste.use_name_reducers=true +# get basename +module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1' +# strip .js or .js.flow suffix +module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1' +# strip .ios suffix +module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1' +module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1' +module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1' +module.system.haste.paths.blacklist=.*/__tests__/.* +module.system.haste.paths.blacklist=.*/__mocks__/.* +module.system.haste.paths.blacklist=/node_modules/react-native/Libraries/Animated/src/polyfills/.* +module.system.haste.paths.whitelist=/node_modules/react-native/Libraries/.* + +munge_underscores=true + +module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' + +module.file_ext=.js +module.file_ext=.jsx +module.file_ext=.json +module.file_ext=.native.js + +suppress_type=$FlowIssue +suppress_type=$FlowFixMe +suppress_type=$FlowFixMeProps +suppress_type=$FlowFixMeState + +suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) +suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ +suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy +suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError + +[version] +^0.92.0 diff --git a/openvidu-react-native-androidx/.gitattributes b/openvidu-react-native-androidx/.gitattributes new file mode 100644 index 00000000..d42ff183 --- /dev/null +++ b/openvidu-react-native-androidx/.gitattributes @@ -0,0 +1 @@ +*.pbxproj -text diff --git a/openvidu-react-native-androidx/.gitignore b/openvidu-react-native-androidx/.gitignore new file mode 100644 index 00000000..5d647565 --- /dev/null +++ b/openvidu-react-native-androidx/.gitignore @@ -0,0 +1,56 @@ +# OSX +# +.DS_Store + +# Xcode +# +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserstate +project.xcworkspace + +# Android/IntelliJ +# +build/ +.idea +.gradle +local.properties +*.iml + +# node.js +# +node_modules/ +npm-debug.log +yarn-error.log + +# BUCK +buck-out/ +\.buckd/ +*.keystore + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/ + +*/fastlane/report.xml +*/fastlane/Preview.html +*/fastlane/screenshots + +# Bundle artifact +*.jsbundle diff --git a/openvidu-react-native-androidx/.watchmanconfig b/openvidu-react-native-androidx/.watchmanconfig new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/openvidu-react-native-androidx/.watchmanconfig @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/openvidu-react-native-androidx/App.js b/openvidu-react-native-androidx/App.js new file mode 100644 index 00000000..20431f6c --- /dev/null +++ b/openvidu-react-native-androidx/App.js @@ -0,0 +1,499 @@ +/** + * Sample React Native App + * https://github.com/facebook/react-native + * + * @format + * @flow + */ + +import React, { Component } from 'react'; +import { + Platform, + TextInput, + ScrollView, + Button, + Alert, + Linking, + StyleSheet, + Text, + View, + Image, + PermissionsAndroid, +} from 'react-native'; + +import { OpenVidu } from 'openvidu-browser'; +import { RTCView } from './node_modules/openvidu-browser/node_modules/react-native-webrtc'; +import axios from 'axios'; + +const OPENVIDU_SERVER_URL = 'https://demos.openvidu.io:4443'; +const OPENVIDU_SERVER_SECRET = 'MY_SECRET'; + + +type Props = {}; +export default class App extends Component { + constructor(props) { + super(props); + + this.state = { + mySessionId: 'SessionA', + myUserName: 'Participant' + Math.floor(Math.random() * 100), + session: undefined, + mainStreamManager: undefined, + subscribers: [], + role: 'PUBLISHER', + mirror: true, + videoSource: undefined, + camera: true, + }; + } + + componentDidMount() {} + + componentWillUnmount() { + //this.leaveSession(); + } + + async checkAndroidPermissions() { + try { + const camera = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.CAMERA, { + title: 'Camera Permission', + message: 'OpenVidu needs access to your camera', + buttonNeutral: 'Ask Me Later', + buttonNegative: 'Cancel', + buttonPositive: 'OK', + }); + const audio = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.RECORD_AUDIO, { + title: 'Aduio Permission', + message: 'OpenVidu needs access to your microphone', + buttonNeutral: 'Ask Me Later', + buttonNegative: 'Cancel', + buttonPositive: 'OK', + }); + const storage = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE, { + title: 'STORAGE', + message: 'OpenVidu needs access to your storage ', + buttonNeutral: 'Ask Me Later', + buttonNegative: 'Cancel', + buttonPositive: 'OK', + }); + if (camera === PermissionsAndroid.RESULTS.GRANTED) { + console.log('You can use the camera'); + } else { + console.log('Camera permission denied'); + } + if (audio === PermissionsAndroid.RESULTS.GRANTED) { + console.log('You can use the audio'); + } else { + console.log('audio permission denied'); + } + if (storage === PermissionsAndroid.RESULTS.GRANTED) { + console.log('You can use the storage'); + } else { + console.log('storage permission denied'); + } + } catch (err) { + console.warn(err); + } + } + + joinSession() { + // --- 1) Get an OpenVidu object --- + + this.OV = new OpenVidu(); + + // --- 2) Init a session --- + + this.setState( + { + session: this.OV.initSession(), + }, + () => { + var mySession = this.state.session; + // --- 3) Specify the actions when events take place in the session --- + + // On every new Stream received... + mySession.on('streamCreated', (event) => { + // Subscribe to the Stream to receive it. Second parameter is undefined + // so OpenVidu doesn't create an HTML video by its own + const subscriber = mySession.subscribe(event.stream, undefined); + + var subscribers = this.state.subscribers; + + subscribers.push(subscriber); + // Update the state with the new subscribers + this.setState({ + subscribers: subscribers, + }); + }); + + // On every Stream destroyed... + mySession.on('streamDestroyed', (event) => { + event.preventDefault(); + // Remove the stream from 'subscribers' array + this.deleteSubscriber(event.stream.streamManager); + }); + + // --- 4) Connect to the session with a valid user token --- + // 'getToken' method is simulating what your server-side should do. + // 'token' parameter should be retrieved and returned by your own backend + this.getToken() + .then((token) => { + // First param is the token got from OpenVidu Server. Second param can be retrieved by every user on event + // 'streamCreated' (property Stream.connection.data), and will be appended to DOM as the user's nickname + mySession + .connect(token, { clientData: this.state.myUserName }) + .then(() => { + if (Platform.OS == 'android') { + this.checkAndroidPermissions(); + } + + // --- 5) Get your own camera stream --- + if (this.state.role !== 'SUBSCRIBER') { + const properties = { + audioSource: undefined, // The source of audio. If undefined default microphone + videoSource: undefined, // The source of video. If undefined default webcam + publishAudio: true, // Whether you want to start publishing with your audio unmuted or not + publishVideo: true, // Whether you want to start publishing with your video enabled or not + resolution: '640x480', // The resolution of your video + frameRate: 30, // The frame rate of your video + insertMode: 'APPEND', // How the video is inserted in the target element 'video-container' + }; + // Init a publisher passing undefined as targetElement (we don't want OpenVidu to insert a video + // element: we will manage it on our own) and with the desired properties + let publisher = this.OV.initPublisher(undefined, properties); + + // --- 6) Publish your stream --- + + // Set the main video in the page to display our webcam and store our Publisher + this.setState({ + mainStreamManager: publisher, + videoSource: !properties.videoSource ? '1' : properties.videoSource, // 0: back camera | 1: user camera | + }); + mySession.publish(publisher); + } + }) + .catch((error) => { + console.log('There was an error connecting to the session:', error.code, error.message); + }); + }) + .catch((error) => console.log('Error', error)); + }, + ); + } + + getNicknameTag(stream) { + // Gets the nickName of the user + if (stream.connection && JSON.parse(stream.connection.data) && JSON.parse(stream.connection.data).clientData) { + return JSON.parse(stream.connection.data).clientData; + } + return ''; + } + + deleteSubscriber(streamManager) { + setTimeout(() => { + let subscribers = this.state.subscribers; + const index = subscribers.indexOf(streamManager, 0); + if (index > -1) { + subscribers.splice(index, 1); + this.setState({ + subscribers: subscribers, + }); + } + }); + } + + leaveSession() { + // --- 7) Leave the session by calling 'disconnect' method over the Session object --- + + const mySession = this.state.session; + + if (mySession) { + mySession.disconnect(); + } + + // Empty all properties... + setTimeout(() => { + this.OV = null; + this.setState({ + session: undefined, + subscribers: [], + mySessionId: 'SessionA', + myUserName: 'Participant' + Math.floor(Math.random() * 100), + mainStreamManager: undefined, + publisher: undefined, + }); + }); + } + + toggleCamera() { + /** + * _switchCamera() Method provided by react-native-webrtc: + * This function allows to switch the front / back cameras in a video track on the fly, without the need for adding / removing tracks or renegotiating + */ + + this.state.mainStreamManager.stream + .getMediaStream() + .getVideoTracks()[0] + ._switchCamera(); + this.setState({ mirror: !this.state.mirror }); + + /** + * Traditional way: + * Renegotiating stream and init new publisher to change the camera + */ + /* + this.OV.getDevices().then(devices => { + console.log("DEVICES => ", devices); + let device = devices.filter(device => device.kind === 'videoinput' && device.deviceId !== this.state.videoSource)[0] + const properties = { + audioSource: undefined, + videoSource: device.deviceId, + publishAudio: true, + publishVideo: true, + resolution: '640x480', + frameRate: 30, + insertMode: 'APPEND', + } + + let publisher = this.OV.initPublisher(undefined, properties); + + this.state.session.unpublish(this.state.mainStreamManager); + + this.setState({ + videoSource : device.deviceId, + mainStreamManager: publisher, + mirror: !this.state.mirror + }); + this.state.session.publish(publisher); + }); + */ + } + + muteUnmuteCamera() { + this.state.mainStreamManager.publishVideo(!this.state.camera); + this.setState({ camera: !this.state.camera }); + } + + render() { + return ( + + {this.state.mainStreamManager ? ( + + + Session: {this.state.mySessionId} + {this.getNicknameTag(this.state.mainStreamManager.stream)} + { + if (!!rtcVideo) { + this.state.mainStreamManager.addVideoElement(rtcVideo); + } + }} + style={styles.selfView} + /> + + + +