├── android
├── README
├── app
│ ├── .gitignore
│ ├── gradle.keystore
│ ├── src
│ │ ├── main
│ │ │ ├── res
│ │ │ │ ├── raw
│ │ │ │ │ ├── call.mp3
│ │ │ │ │ └── start.mp3
│ │ │ │ ├── drawable
│ │ │ │ │ ├── call_ans.png
│ │ │ │ │ ├── call_answer.png
│ │ │ │ │ ├── call_hangup.png
│ │ │ │ │ ├── call_answer_p.png
│ │ │ │ │ ├── call_hangup_p.png
│ │ │ │ │ ├── switch_camera.png
│ │ │ │ │ ├── btn_login_selector.xml
│ │ │ │ │ ├── call_answer_x.xml
│ │ │ │ │ └── call_hangup_x.xml
│ │ │ │ ├── drawable-hdpi
│ │ │ │ │ ├── call_in.png
│ │ │ │ │ ├── call_out.png
│ │ │ │ │ ├── ic_share.png
│ │ │ │ │ ├── ic_account.png
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ ├── avatar_contact.png
│ │ │ │ │ ├── bg_background.png
│ │ │ │ │ ├── btn_login_normal.png
│ │ │ │ │ ├── btn_login_pressed.png
│ │ │ │ │ └── callin_not_answer.png
│ │ │ │ ├── drawable-xhdpi
│ │ │ │ │ ├── ic_account.png
│ │ │ │ │ ├── bg_background.png
│ │ │ │ │ └── ic_activity_back.png
│ │ │ │ ├── values-large
│ │ │ │ │ └── dimens.xml
│ │ │ │ ├── values
│ │ │ │ │ ├── colors.xml
│ │ │ │ │ ├── dimens.xml
│ │ │ │ │ ├── strings.xml
│ │ │ │ │ └── styles.xml
│ │ │ │ ├── values-w820dp
│ │ │ │ │ └── dimens.xml
│ │ │ │ └── layout
│ │ │ │ │ └── activity_login.xml
│ │ │ ├── assets
│ │ │ │ └── fonts
│ │ │ │ │ ├── Entypo.ttf
│ │ │ │ │ ├── Zocial.ttf
│ │ │ │ │ ├── jitsi.ttf
│ │ │ │ │ ├── EvilIcons.ttf
│ │ │ │ │ ├── Ionicons.ttf
│ │ │ │ │ ├── Octicons.ttf
│ │ │ │ │ ├── FontAwesome.ttf
│ │ │ │ │ ├── Foundation.ttf
│ │ │ │ │ ├── MaterialIcons.ttf
│ │ │ │ │ ├── SimpleLineIcons.ttf
│ │ │ │ │ └── MaterialCommunityIcons.ttf
│ │ │ ├── jniLibs
│ │ │ │ ├── x86
│ │ │ │ │ └── libasync_tcp.so
│ │ │ │ └── armeabi-v7a
│ │ │ │ │ └── libasync_tcp.so
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── beetle
│ │ │ │ └── conference
│ │ │ │ ├── LoginActivity.java
│ │ │ │ └── GroupVOIPActivity.java
│ │ └── androidTest
│ │ │ └── java
│ │ │ └── com
│ │ │ └── beetle
│ │ │ └── conference
│ │ │ └── ApplicationTest.java
│ └── build.gradle
├── asynctcp
│ ├── .gitignore
│ ├── src
│ │ ├── main
│ │ │ ├── res
│ │ │ │ ├── values
│ │ │ │ │ └── strings.xml
│ │ │ │ ├── drawable-hdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── drawable-mdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── drawable-xhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ └── drawable-xxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── beetle
│ │ │ │ ├── TCPReadCallback.java
│ │ │ │ ├── TCPConnectCallback.java
│ │ │ │ ├── AsyncTCP.java
│ │ │ │ └── AsyncTCPTest.java
│ │ └── androidTest
│ │ │ └── java
│ │ │ └── com
│ │ │ └── beetle
│ │ │ └── asynctcp
│ │ │ └── ApplicationTest.java
│ └── build.gradle
├── imsdk
│ ├── src
│ │ ├── main
│ │ │ ├── res
│ │ │ │ ├── values
│ │ │ │ │ └── strings.xml
│ │ │ │ ├── drawable-hdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── drawable-mdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── drawable-xhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ └── drawable-xxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── beetle
│ │ │ │ └── im
│ │ │ │ ├── RTMessageObserver.java
│ │ │ │ ├── VOIPObserver.java
│ │ │ │ ├── RoomMessageObserver.java
│ │ │ │ ├── SystemMessageObserver.java
│ │ │ │ ├── IMServiceObserver.java
│ │ │ │ ├── RTMessage.java
│ │ │ │ ├── VOIPControl.java
│ │ │ │ ├── LoginPointObserver.java
│ │ │ │ ├── RoomMessage.java
│ │ │ │ ├── SyncKeyHandler.java
│ │ │ │ ├── PeerMessageHandler.java
│ │ │ │ ├── CustomerMessage.java
│ │ │ │ ├── PeerMessageObserver.java
│ │ │ │ ├── GroupMessageObserver.java
│ │ │ │ ├── IMMessage.java
│ │ │ │ ├── LoginPoint.java
│ │ │ │ ├── GroupMessageHandler.java
│ │ │ │ ├── CustomerMessageHandler.java
│ │ │ │ ├── CustomerMessageObserver.java
│ │ │ │ ├── Timer.java
│ │ │ │ └── BytePacket.java
│ │ └── androidTest
│ │ │ └── java
│ │ │ └── com
│ │ │ └── beetle
│ │ │ └── im
│ │ │ └── ApplicationTest.java
│ └── build.gradle
├── .gitignore
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gen_key.sh
├── settings.gradle
├── build.gradle
├── gradle.properties
├── gradlew.bat
└── gradlew
├── index.ios.js
├── index.android.js
├── ios
├── conference
│ ├── en.lproj
│ │ └── InfoPlist.strings
│ ├── zh-Hans.lproj
│ │ └── InfoPlist.strings
│ ├── jitsi.ttf
│ ├── sound
│ │ ├── apns.caf
│ │ ├── call.mp3
│ │ ├── end.mp3
│ │ └── start.mp3
│ ├── Images.xcassets
│ │ ├── Contents.json
│ │ ├── bg.imageset
│ │ │ ├── bg.png
│ │ │ ├── bg@2x.png
│ │ │ ├── bg@3x.png
│ │ │ └── Contents.json
│ │ ├── switch.imageset
│ │ │ ├── switch.png
│ │ │ ├── switch@2x.png
│ │ │ └── Contents.json
│ │ ├── Call_Ans.imageset
│ │ │ ├── Call_answer.png
│ │ │ └── Contents.json
│ │ ├── accept_nor.imageset
│ │ │ ├── accept_nor.png
│ │ │ └── Contents.json
│ │ ├── accept_pre.imageset
│ │ │ ├── accept_pre.png
│ │ │ └── Contents.json
│ │ ├── refuse_nor.imageset
│ │ │ ├── refuse_nor.png
│ │ │ └── Contents.json
│ │ ├── refuse_pre.imageset
│ │ │ ├── refuse_pre.png
│ │ │ └── Contents.json
│ │ ├── Call_hangup.imageset
│ │ │ ├── Call_hangup.png
│ │ │ └── Contents.json
│ │ ├── Call_Ans_p.imageset
│ │ │ ├── Call_answer_p.png
│ │ │ └── Contents.json
│ │ ├── Call_hangup_p.imageset
│ │ │ ├── Call_hangup_p.png
│ │ │ └── Contents.json
│ │ ├── Call_background2.imageset
│ │ │ ├── Call_background2.png
│ │ │ ├── Call_background2@2x.png
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── AppDelegate.h
│ ├── main.m
│ ├── conference-Prefix.pch
│ ├── controller
│ │ ├── GroupVOIPViewController.h
│ │ └── GroupVOIOPViewController.m
│ ├── ViewController.h
│ ├── AppDelegate.m
│ ├── conference-Info.plist
│ └── ViewController.m
├── conferenceTests
│ ├── en.lproj
│ │ └── InfoPlist.strings
│ ├── zh-Hans.lproj
│ │ └── InfoPlist.strings
│ ├── conferenceTests.m
│ └── conferenceTests-Info.plist
├── Podfile
├── Podfile.lock
└── imsdk
│ ├── imsdkTests
│ └── Info.plist
│ └── imsdk
│ ├── AsyncTCP.h
│ ├── util.h
│ ├── TCPConnection.h
│ ├── GOReachability.h
│ ├── Message.h
│ ├── util.c
│ ├── IMService.h
│ ├── AsyncTCP.m
│ ├── Message.m
│ ├── TCPConnection.m
│ └── GOReachability.m
├── react
├── index.js
├── polyfills-webrtc.js
├── participant.js
├── RTCPeerConnection.js
├── adapter.js
└── .eslintrc.js
├── .gitignore
└── package.json
/android/README:
--------------------------------------------------------------------------------
1 | Android VOIP
--------------------------------------------------------------------------------
/android/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/android/asynctcp/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/index.ios.js:
--------------------------------------------------------------------------------
1 | export * from './react/index';
2 |
--------------------------------------------------------------------------------
/index.android.js:
--------------------------------------------------------------------------------
1 | export * from './react/index';
2 |
--------------------------------------------------------------------------------
/ios/conference/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/ios/conference/zh-Hans.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/ios/conferenceTests/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/ios/conference/jitsi.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/ios/conference/jitsi.ttf
--------------------------------------------------------------------------------
/ios/conferenceTests/zh-Hans.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/android/app/gradle.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/gradle.keystore
--------------------------------------------------------------------------------
/ios/conference/sound/apns.caf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/ios/conference/sound/apns.caf
--------------------------------------------------------------------------------
/ios/conference/sound/call.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/ios/conference/sound/call.mp3
--------------------------------------------------------------------------------
/ios/conference/sound/end.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/ios/conference/sound/end.mp3
--------------------------------------------------------------------------------
/ios/conference/sound/start.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/ios/conference/sound/start.mp3
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | platform :ios, '8.0'
2 | target 'conference' do
3 | pod 'MBProgressHUD'
4 | pod 'Toast'
5 | end
6 |
--------------------------------------------------------------------------------
/android/imsdk/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | IMSDK
3 |
4 |
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | /local.properties
3 | /.idea/workspace.xml
4 | /.idea/libraries
5 | .DS_Store
6 | /build
7 | *~
8 |
--------------------------------------------------------------------------------
/android/app/src/main/res/raw/call.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/res/raw/call.mp3
--------------------------------------------------------------------------------
/android/asynctcp/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AsyncTCP
3 |
4 |
--------------------------------------------------------------------------------
/android/app/src/main/res/raw/start.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/res/raw/start.mp3
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Entypo.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/assets/fonts/Entypo.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Zocial.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/assets/fonts/Zocial.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/jitsi.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/assets/fonts/jitsi.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/EvilIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/assets/fonts/EvilIcons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Ionicons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/assets/fonts/Ionicons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Octicons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/assets/fonts/Octicons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/call_ans.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/res/drawable/call_ans.png
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/FontAwesome.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/assets/fonts/FontAwesome.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Foundation.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/assets/fonts/Foundation.ttf
--------------------------------------------------------------------------------
/android/app/src/main/jniLibs/x86/libasync_tcp.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/jniLibs/x86/libasync_tcp.so
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-hdpi/call_in.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/res/drawable-hdpi/call_in.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/call_answer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/res/drawable/call_answer.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/call_hangup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/res/drawable/call_hangup.png
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/bg.imageset/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/ios/conference/Images.xcassets/bg.imageset/bg.png
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/MaterialIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/assets/fonts/MaterialIcons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-hdpi/call_out.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/res/drawable-hdpi/call_out.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-hdpi/ic_share.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/res/drawable-hdpi/ic_share.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/call_answer_p.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/res/drawable/call_answer_p.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/call_hangup_p.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/res/drawable/call_hangup_p.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/switch_camera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/res/drawable/switch_camera.png
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/bg.imageset/bg@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/ios/conference/Images.xcassets/bg.imageset/bg@2x.png
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/bg.imageset/bg@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/ios/conference/Images.xcassets/bg.imageset/bg@3x.png
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/SimpleLineIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/assets/fonts/SimpleLineIcons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-hdpi/ic_account.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/res/drawable-hdpi/ic_account.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xhdpi/ic_account.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/res/drawable-xhdpi/ic_account.png
--------------------------------------------------------------------------------
/android/app/src/main/jniLibs/armeabi-v7a/libasync_tcp.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/jniLibs/armeabi-v7a/libasync_tcp.so
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-hdpi/avatar_contact.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/res/drawable-hdpi/avatar_contact.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-hdpi/bg_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/res/drawable-hdpi/bg_background.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xhdpi/bg_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/res/drawable-xhdpi/bg_background.png
--------------------------------------------------------------------------------
/android/imsdk/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/imsdk/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/imsdk/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/imsdk/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/imsdk/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/imsdk/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/switch.imageset/switch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/ios/conference/Images.xcassets/switch.imageset/switch.png
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-hdpi/btn_login_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/res/drawable-hdpi/btn_login_normal.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-hdpi/btn_login_pressed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/res/drawable-hdpi/btn_login_pressed.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-hdpi/callin_not_answer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/res/drawable-hdpi/callin_not_answer.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xhdpi/ic_activity_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/app/src/main/res/drawable-xhdpi/ic_activity_back.png
--------------------------------------------------------------------------------
/android/asynctcp/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/android/asynctcp/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/asynctcp/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/asynctcp/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/asynctcp/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/asynctcp/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/asynctcp/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/gen_key.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | BASEDIR=$(dirname $0)
3 | keytool -genkey -v -keystore $BASEDIR/app/gradle.keystore -alias gradle -keyalg RSA -keysize 2048 -validity 10000
4 |
--------------------------------------------------------------------------------
/android/imsdk/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/imsdk/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/switch.imageset/switch@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/ios/conference/Images.xcassets/switch.imageset/switch@2x.png
--------------------------------------------------------------------------------
/android/asynctcp/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/android/asynctcp/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/react/index.js:
--------------------------------------------------------------------------------
1 | import { AppRegistry } from 'react-native';
2 | import GroupCall from './GroupCall.js';
3 | AppRegistry.registerComponent('GroupCall', () => GroupCall);
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/Call_Ans.imageset/Call_answer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/ios/conference/Images.xcassets/Call_Ans.imageset/Call_answer.png
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/accept_nor.imageset/accept_nor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/ios/conference/Images.xcassets/accept_nor.imageset/accept_nor.png
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/accept_pre.imageset/accept_pre.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/ios/conference/Images.xcassets/accept_pre.imageset/accept_pre.png
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/refuse_nor.imageset/refuse_nor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/ios/conference/Images.xcassets/refuse_nor.imageset/refuse_nor.png
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/refuse_pre.imageset/refuse_pre.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/ios/conference/Images.xcassets/refuse_pre.imageset/refuse_pre.png
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/Call_hangup.imageset/Call_hangup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/ios/conference/Images.xcassets/Call_hangup.imageset/Call_hangup.png
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/Call_Ans_p.imageset/Call_answer_p.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/ios/conference/Images.xcassets/Call_Ans_p.imageset/Call_answer_p.png
--------------------------------------------------------------------------------
/android/asynctcp/src/main/java/com/beetle/TCPReadCallback.java:
--------------------------------------------------------------------------------
1 | package com.beetle;
2 |
3 |
4 | public interface TCPReadCallback {
5 |
6 | public void onRead(Object tcp, byte[] data);
7 |
8 | }
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/Call_hangup_p.imageset/Call_hangup_p.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/ios/conference/Images.xcassets/Call_hangup_p.imageset/Call_hangup_p.png
--------------------------------------------------------------------------------
/android/asynctcp/src/main/java/com/beetle/TCPConnectCallback.java:
--------------------------------------------------------------------------------
1 | package com.beetle;
2 |
3 | public interface TCPConnectCallback {
4 |
5 | public void onConnect(Object tcp, int status);
6 |
7 | }
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/Call_background2.imageset/Call_background2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/ios/conference/Images.xcassets/Call_background2.imageset/Call_background2.png
--------------------------------------------------------------------------------
/android/imsdk/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/Call_background2.imageset/Call_background2@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoBelieveIO/conference/HEAD/ios/conference/Images.xcassets/Call_background2.imageset/Call_background2@2x.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values-large/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 8dp
4 | 16dp
5 | 16dp
6 |
7 |
--------------------------------------------------------------------------------
/android/imsdk/src/main/java/com/beetle/im/RTMessageObserver.java:
--------------------------------------------------------------------------------
1 | package com.beetle.im;
2 |
3 | /**
4 | * Created by houxh on 16/1/25.
5 | */
6 | public interface RTMessageObserver {
7 | void onRTMessage(RTMessage rt);
8 | }
9 |
--------------------------------------------------------------------------------
/android/imsdk/src/main/java/com/beetle/im/VOIPObserver.java:
--------------------------------------------------------------------------------
1 | package com.beetle.im;
2 |
3 | /**
4 | * Created by houxh on 14-12-31.
5 | */
6 | public interface VOIPObserver {
7 | public void onVOIPControl(VOIPControl ctl);
8 | }
9 |
--------------------------------------------------------------------------------
/android/imsdk/src/main/java/com/beetle/im/RoomMessageObserver.java:
--------------------------------------------------------------------------------
1 | package com.beetle.im;
2 |
3 | /**
4 | * Created by houxh on 16/5/14.
5 | */
6 | public interface RoomMessageObserver {
7 | public void onRoomMessage(RoomMessage msg);
8 | }
9 |
--------------------------------------------------------------------------------
/android/imsdk/src/main/java/com/beetle/im/SystemMessageObserver.java:
--------------------------------------------------------------------------------
1 | package com.beetle.im;
2 |
3 | /**
4 | * Created by houxh on 16/1/16.
5 | */
6 | public interface SystemMessageObserver {
7 | public void onSystemMessage(String sm);
8 | }
9 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'conference'
2 |
3 | include ':app'
4 |
5 |
6 | include ':react-native-webrtc'
7 | project(':react-native-webrtc').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webrtc/android')
8 |
--------------------------------------------------------------------------------
/android/imsdk/src/main/java/com/beetle/im/IMServiceObserver.java:
--------------------------------------------------------------------------------
1 | package com.beetle.im;
2 |
3 | /**
4 | * Created by houxh on 14-7-23.
5 | */
6 | public interface IMServiceObserver {
7 | public void onConnectState(IMService.ConnectState state);
8 | }
9 |
--------------------------------------------------------------------------------
/android/imsdk/src/main/java/com/beetle/im/RTMessage.java:
--------------------------------------------------------------------------------
1 | package com.beetle.im;
2 |
3 | /**
4 | * Created by houxh on 16/1/25.
5 | */
6 | public class RTMessage {
7 | public long sender;
8 | public long receiver;
9 | public String content;
10 | }
11 |
--------------------------------------------------------------------------------
/android/imsdk/src/main/java/com/beetle/im/VOIPControl.java:
--------------------------------------------------------------------------------
1 | package com.beetle.im;
2 |
3 | /**
4 | * Created by houxh on 14-12-31.
5 | */
6 | public class VOIPControl {
7 | public long sender;
8 | public long receiver;
9 | public byte[] content;
10 | }
--------------------------------------------------------------------------------
/android/imsdk/src/main/java/com/beetle/im/LoginPointObserver.java:
--------------------------------------------------------------------------------
1 | package com.beetle.im;
2 |
3 | /**
4 | * Created by houxh on 14-7-23.
5 | */
6 | public interface LoginPointObserver {
7 | //当前用户ID在其它地方登录
8 | public void onLoginPoint(LoginPoint lp);
9 |
10 | }
--------------------------------------------------------------------------------
/android/imsdk/src/main/java/com/beetle/im/RoomMessage.java:
--------------------------------------------------------------------------------
1 | package com.beetle.im;
2 |
3 | /**
4 | * Created by houxh on 16/5/14.
5 | */
6 | public class RoomMessage {
7 | public long sender;
8 | public long receiver;
9 | public String content;
10 | }
11 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Aug 15 12:52:53 CST 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | xcuserdata
2 | project.xcworkspace
3 | *~
4 | Pods
5 | conference.xcworkspace
6 | node_modules
7 | /android/local.properties
8 | /android/.idea/workspace.xml
9 | /android/.idea/libraries
10 | /android/.idea
11 | android/imsdk/build/
12 | .gradle
13 | *.iml
14 | .DS_Store
15 | yarn.lock
--------------------------------------------------------------------------------
/android/imsdk/src/main/java/com/beetle/im/SyncKeyHandler.java:
--------------------------------------------------------------------------------
1 | package com.beetle.im;
2 |
3 | /**
4 | * Created by houxh on 2016/11/2.
5 | */
6 |
7 | public interface SyncKeyHandler {
8 | boolean saveSyncKey(long syncKey);
9 | boolean saveGroupSyncKey(long groupID, long syncKey);
10 | }
11 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #18b000
4 | #138d00
5 | #18b000
6 | #66FFFFFF
7 |
--------------------------------------------------------------------------------
/ios/conference/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // Face
4 | //
5 | // Created by houxh on 14-10-13.
6 | // Copyright (c) 2014年 beetle. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 | @property (strong, nonatomic) UIWindow *window;
13 | @end
14 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/btn_login_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/imsdk/src/main/java/com/beetle/im/PeerMessageHandler.java:
--------------------------------------------------------------------------------
1 | package com.beetle.im;
2 |
3 | /**
4 | * Created by houxh on 14-7-23.
5 | */
6 | public interface PeerMessageHandler {
7 | public boolean handleMessage(IMMessage msg, long uid);
8 | public boolean handleMessageACK(int msgLocalID, long uid);
9 | public boolean handleMessageFailure(int msgLocalID, long uid);
10 | }
--------------------------------------------------------------------------------
/android/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/ios/conference/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // Face
4 | //
5 | // Created by houxh on 14-10-13.
6 | // Copyright (c) 2014年 beetle. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #import "AppDelegate.h"
12 |
13 | int main(int argc, char * argv[])
14 | {
15 | @autoreleasepool {
16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/call_answer_x.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/call_hangup_x.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/android/imsdk/src/main/java/com/beetle/im/CustomerMessage.java:
--------------------------------------------------------------------------------
1 | package com.beetle.im;
2 |
3 | /**
4 | * Created by houxh on 16/1/19.
5 | */
6 | public class CustomerMessage {
7 | //未被序列化
8 | public int msgLocalID;
9 |
10 | public long customerAppID;
11 | public long customerID;
12 | public long storeID;
13 | public long sellerID;
14 | public int timestamp;
15 | public String content;
16 | }
17 |
--------------------------------------------------------------------------------
/android/imsdk/src/main/java/com/beetle/im/PeerMessageObserver.java:
--------------------------------------------------------------------------------
1 | package com.beetle.im;
2 |
3 | /**
4 | * Created by houxh on 14-7-23.
5 | */
6 | public interface PeerMessageObserver {
7 | public void onPeerInputting(long uid);
8 |
9 | public void onPeerMessage(IMMessage msg);
10 | public void onPeerMessageACK(int msgLocalID, long uid);
11 | public void onPeerMessageFailure(int msgLocalID, long uid);
12 | }
--------------------------------------------------------------------------------
/android/imsdk/src/main/java/com/beetle/im/GroupMessageObserver.java:
--------------------------------------------------------------------------------
1 | package com.beetle.im;
2 |
3 | /**
4 | * Created by houxh on 14-7-23.
5 | */
6 | public interface GroupMessageObserver {
7 | public void onGroupMessage(IMMessage msg);
8 | public void onGroupMessageACK(int msgLocalID, long uid);
9 | public void onGroupMessageFailure(int msgLocalID, long uid);
10 | public void onGroupNotification(String notification);
11 | }
--------------------------------------------------------------------------------
/android/imsdk/src/main/java/com/beetle/im/IMMessage.java:
--------------------------------------------------------------------------------
1 | package com.beetle.im;
2 |
3 | import android.util.Log;
4 |
5 | import java.util.ArrayList;
6 | import java.util.Arrays;
7 |
8 | /**
9 | * Created by houxh on 14-7-23.
10 | */
11 |
12 | public class IMMessage {
13 | public long sender;
14 | public long receiver;
15 | public int timestamp;
16 | public int msgLocalID;
17 | public String content;
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/android/imsdk/src/main/java/com/beetle/im/LoginPoint.java:
--------------------------------------------------------------------------------
1 | package com.beetle.im;
2 |
3 | /**
4 | * Created by houxh on 15/2/3.
5 | */
6 | public class LoginPoint {
7 | public static final int PLATFORM_IOS = 1;
8 | public static final int PLATFORM_ANDROID = 2;
9 | public static final int PLATFORM_WEB = 3;
10 |
11 | public int upTimestamp;//上线时间戳
12 | public int platformID;//平台id
13 | public String deviceID;//设备id
14 | }
15 |
--------------------------------------------------------------------------------
/android/imsdk/src/main/java/com/beetle/im/GroupMessageHandler.java:
--------------------------------------------------------------------------------
1 | package com.beetle.im;
2 |
3 | /**
4 | * Created by houxh on 15/3/21.
5 | */
6 | public interface GroupMessageHandler {
7 | public boolean handleMessage(IMMessage msg);
8 | public boolean handleMessageACK(int msgLocalID, long uid);
9 | public boolean handleMessageFailure(int msgLocalID, long uid);
10 | public boolean handleGroupNotification(String notification);
11 | }
12 |
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/Call_Ans.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Call_answer.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/accept_nor.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x",
6 | "filename" : "accept_nor.png"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/accept_pre.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x",
6 | "filename" : "accept_pre.png"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/refuse_nor.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "refuse_nor.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/refuse_pre.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "refuse_pre.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/ios/conference/conference-Prefix.pch:
--------------------------------------------------------------------------------
1 | //
2 | // Prefix header
3 | //
4 | // The contents of this file are implicitly included at the beginning of every source file.
5 | //
6 |
7 | #import
8 |
9 | #ifndef __IPHONE_5_0
10 | #warning "This project uses features only available in iOS SDK 5.0 and later."
11 | #endif
12 |
13 | #ifdef __OBJC__
14 | #import
15 | #import
16 | #endif
17 |
18 |
19 |
--------------------------------------------------------------------------------
/android/imsdk/src/androidTest/java/com/beetle/im/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.beetle.im;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/android/imsdk/src/main/java/com/beetle/im/CustomerMessageHandler.java:
--------------------------------------------------------------------------------
1 | package com.beetle.im;
2 |
3 | /**
4 | * Created by houxh on 16/1/17.
5 | */
6 | public interface CustomerMessageHandler {
7 | public boolean handleCustomerSupportMessage(CustomerMessage msg);
8 | public boolean handleMessage(CustomerMessage msg);
9 | public boolean handleMessageACK(CustomerMessage msg);
10 | public boolean handleMessageFailure(CustomerMessage msg);
11 | }
12 |
--------------------------------------------------------------------------------
/android/imsdk/src/main/java/com/beetle/im/CustomerMessageObserver.java:
--------------------------------------------------------------------------------
1 | package com.beetle.im;
2 |
3 | /**
4 | * Created by houxh on 16/1/18.
5 | */
6 | public interface CustomerMessageObserver {
7 | public void onCustomerSupportMessage(CustomerMessage msg);
8 | public void onCustomerMessage(CustomerMessage msg);
9 | public void onCustomerMessageACK(CustomerMessage msg);
10 | public void onCustomerMessageFailure(CustomerMessage msg);
11 | }
12 |
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/Call_Ans_p.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Call_answer_p.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/Call_hangup.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Call_hangup.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/Call_hangup_p.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Call_hangup_p.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/android/app/src/androidTest/java/com/beetle/conference/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.beetle.conference;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/android/asynctcp/src/androidTest/java/com/beetle/asynctcp/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.beetle.asynctcp;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - MBProgressHUD (1.0.0)
3 | - Toast (3.1.0)
4 |
5 | DEPENDENCIES:
6 | - MBProgressHUD
7 | - Toast
8 |
9 | SPEC REPOS:
10 | https://github.com/cocoapods/specs.git:
11 | - MBProgressHUD
12 | - Toast
13 |
14 | SPEC CHECKSUMS:
15 | MBProgressHUD: 4890f671c94e8a0f3cf959aa731e9de2f036d71a
16 | Toast: 14a93686d6c8bfe2727afd342414e35660a8a1f3
17 |
18 | PODFILE CHECKSUM: a0a2548c5f5b5612b89250f2739cddc5230d4d4e
19 |
20 | COCOAPODS: 1.5.3
21 |
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/switch.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "switch.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "switch@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/bg.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "bg.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "bg@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "bg@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/Call_background2.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Call_background2.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Call_background2@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/ios/conference/controller/GroupVOIPViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ConferenceViewController.h
3 | // Face
4 | //
5 | // Created by houxh on 2016/12/7.
6 | // Copyright © 2016年 beetle. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface GroupVOIPViewController : UIViewController
12 |
13 | +(int64_t)controllerCount;
14 |
15 | @property(nonatomic, assign) int64_t currentUID;
16 | @property(nonatomic, copy) NSString *channelID;
17 | @property(nonatomic, copy) NSString *token;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/ios/conference/ViewController.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2014-2015, GoBelieve
3 | All rights reserved.
4 |
5 | This source code is licensed under the BSD-style license found in the
6 | LICENSE file in the root directory of this source tree. An additional grant
7 | of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | @interface ViewController : UIViewController
13 |
14 |
15 | @end
16 |
17 |
--------------------------------------------------------------------------------
/android/imsdk/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion '25.0.0'
6 |
7 | defaultConfig {
8 | minSdkVersion 9
9 | targetSdkVersion 23
10 | }
11 |
12 | compileOptions {
13 | sourceCompatibility JavaVersion.VERSION_1_6
14 | targetCompatibility JavaVersion.VERSION_1_6
15 | }
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 | }
23 |
24 | dependencies {
25 | compile fileTree(dir: 'libs', include: ['*.jar'])
26 | compile project(':asynctcp')
27 | }
28 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 8dp
5 | 8dp
6 | 16dp
7 | 16dp
8 | 16dp
9 |
10 |
11 | 26sp
12 | 24sp
13 | 22sp
14 | 20sp
15 | 18sp
16 | 16sp
17 | 14sp
18 | 12sp
19 | 10sp
20 |
21 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | google()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.1.3'
10 |
11 | // NOTE: Do not place your application dependencies here; they belong
12 | // in the individual module build.gradle files
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | jcenter()
19 | maven {
20 | // All of React Native (JS, Android binaries) is installed from npm
21 | url "$rootDir/../node_modules/react-native/android"
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/ios/conferenceTests/conferenceTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // FaceTests.m
3 | // FaceTests
4 | //
5 | // Created by houxh on 14-10-13.
6 | // Copyright (c) 2014年 beetle. All rights reserved.
7 | //
8 |
9 | #import
10 | @interface FaceTests : XCTestCase
11 |
12 | @end
13 |
14 | @implementation FaceTests
15 |
16 | - (void)setUp
17 | {
18 | [super setUp];
19 | // Put setup code here. This method is called before the invocation of each test method in the class.
20 | }
21 |
22 | - (void)tearDown
23 | {
24 | // Put teardown code here. This method is called after the invocation of each test method in the class.
25 | [super tearDown];
26 | }
27 |
28 |
29 | - (void)testHistoryDB
30 | {
31 |
32 |
33 | }
34 |
35 | @end
36 |
--------------------------------------------------------------------------------
/android/asynctcp/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion '25.0.0'
6 | defaultConfig {
7 | minSdkVersion 9
8 | targetSdkVersion 23
9 | versionCode 1
10 | versionName "1.0"
11 | }
12 |
13 | compileOptions {
14 | sourceCompatibility JavaVersion.VERSION_1_7
15 | targetCompatibility JavaVersion.VERSION_1_7
16 | }
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | }
24 |
25 | dependencies {
26 | compile fileTree(dir: 'libs', include: ['*.jar'])
27 | }
28 |
--------------------------------------------------------------------------------
/ios/conferenceTests/conferenceTests-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundlePackageType
14 | BNDL
15 | CFBundleShortVersionString
16 | 1.0
17 | CFBundleSignature
18 | ????
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | ## Project-wide Gradle settings.
2 | #
3 | # For more details on how to configure your build environment visit
4 | # http://www.gradle.org/docs/current/userguide/build_environment.html
5 | #
6 | # Specifies the JVM arguments used for the daemon process.
7 | # The setting is particularly useful for tweaking memory settings.
8 | # Default value: -Xmx1024m -XX:MaxPermSize=256m
9 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
10 | #
11 | # When configured, Gradle will run in incubating parallel mode.
12 | # This option should only be used with decoupled projects. More details, visit
13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
14 | # org.gradle.parallel=true
15 | #Sun Dec 11 09:58:21 CST 2016
16 | android.useDeprecatedNdk=true
17 |
--------------------------------------------------------------------------------
/ios/imsdk/imsdkTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | com.beetle.$(PRODUCT_NAME:rfc1034identifier)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "conference",
3 | "version": "0.0.0",
4 | "description": "conference demo",
5 | "keywords": [
6 | "webrtc"
7 | ],
8 | "author": "",
9 | "readmeFilename": "README.md",
10 | "scripts": {
11 | "lint": "jshint . && eslint .",
12 | "validate": "npm ls",
13 | "start": "node node_modules/react-native/local-cli/cli.js start"
14 | },
15 | "dependencies": {
16 | "events": "^1.1.1",
17 | "freeice": "^2.1.2",
18 | "hark": "1.1.3",
19 | "inherits": "^2.0.1",
20 | "merge": "^1.2.0",
21 | "react": "16.3.1",
22 | "react-dom": "15.4.2",
23 | "react-native": "0.55.4",
24 | "react-native-permissions": "~1.1.1",
25 | "react-native-webrtc": "1.63.0",
26 | "sdp-translator": "^0.1.15",
27 | "ua-parser-js": "^0.7.7",
28 | "uuid": "~2.0.1"
29 | },
30 | "license": "Apache-2.0"
31 | }
32 |
--------------------------------------------------------------------------------
/android/asynctcp/src/main/java/com/beetle/AsyncTCP.java:
--------------------------------------------------------------------------------
1 | package com.beetle;
2 |
3 | import java.lang.ref.WeakReference;
4 |
5 | public class AsyncTCP {
6 | private int sock;
7 | private int events;
8 |
9 | private byte[] data;
10 | private boolean connecting;
11 |
12 | private TCPConnectCallback connectCallback;
13 | private TCPReadCallback readCallback;
14 | private long self;
15 |
16 |
17 | public void setConnectCallback(TCPConnectCallback cb) {
18 | connectCallback = cb;
19 | }
20 | public void setReadCallback(TCPReadCallback cb) {
21 | readCallback = cb;
22 | }
23 | public native boolean connect(String host, int port);
24 | public native void close();
25 |
26 | public native void writeData(byte[] bytes);
27 |
28 | public native void startRead();
29 |
30 |
31 | static {
32 | System.loadLibrary("async_tcp");
33 | }
34 | }
--------------------------------------------------------------------------------
/ios/imsdk/imsdk/AsyncTCP.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2014-2015, GoBelieve
3 | All rights reserved.
4 |
5 | This source code is licensed under the BSD-style license found in the
6 | LICENSE file in the root directory of this source tree. An additional grant
7 | of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | @class AsyncTCP;
13 | typedef void(^ConnectCB)(AsyncTCP *tcp, int err);
14 | typedef void(^ReadCB)(AsyncTCP *tcp, NSData *data, int err);
15 | typedef void(^CloseCB)(AsyncTCP *tcp, int err);
16 |
17 | @interface AsyncTCP : NSObject
18 | -(BOOL)connect:(NSString*)host port:(int)port cb:(ConnectCB)cb;
19 | -(void)close;
20 | -(void)write:(NSData*)data;
21 | -(void)flush;
22 | -(void)startRead:(ReadCB)cb;
23 | @end
24 |
25 |
26 |
--------------------------------------------------------------------------------
/ios/imsdk/imsdk/util.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2014-2015, GoBelieve
3 | All rights reserved.
4 |
5 | This source code is licensed under the BSD-style license found in the
6 | LICENSE file in the root directory of this source tree. An additional grant
7 | of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #ifndef IM_UTIL_H
11 | #define IM_UTIL_H
12 |
13 | void writeInt32(int32_t v, void *p);
14 | int32_t readInt32(const void *p);
15 |
16 | void writeInt64(int64_t v, void *p);
17 | int64_t readInt64(const void *p);
18 |
19 | void writeInt16(int16_t v, void *p);
20 | int16_t readInt16(const void *p);
21 |
22 | int lookupAddr(const char *host, int port, struct sockaddr_in *addr);
23 |
24 |
25 | int sock_nonblock(int fd, int set);
26 | int write_data(int fd, uint8_t *bytes, int len);
27 |
28 | #endif
29 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | conference
4 | 发送用户id
5 | Connection error
6 | OK
7 | Switch front/back camera
8 | Slide to change capture format
9 | Camera2 only supports capturing to texture. Either disable Camera2 or enable capturing to texture in the options.
10 | 接收用户id
11 | 1号参会者
12 | 2号参会者
13 | 3号参会者
14 | 4号参会者
15 | 登 录
16 |
17 |
--------------------------------------------------------------------------------
/ios/conference/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | }
43 | ],
44 | "info" : {
45 | "version" : 1,
46 | "author" : "xcode"
47 | }
48 | }
--------------------------------------------------------------------------------
/react/polyfills-webrtc.js:
--------------------------------------------------------------------------------
1 | import {
2 | MediaStream,
3 | MediaStreamTrack,
4 | RTCSessionDescription,
5 | RTCIceCandidate,
6 | getUserMedia
7 | } from 'react-native-webrtc';
8 |
9 | import RTCPeerConnection from './RTCPeerConnection';
10 |
11 | (global => {
12 | if (typeof global.webkitMediaStream === 'undefined') {
13 | global.webkitMediaStream = MediaStream;
14 | }
15 | if (typeof global.MediaStreamTrack === 'undefined') {
16 | global.MediaStreamTrack = MediaStreamTrack;
17 | }
18 | if (typeof global.webkitRTCPeerConnection === 'undefined') {
19 | global.webkitRTCPeerConnection = RTCPeerConnection;
20 | }
21 | if (typeof global.RTCSessionDescription === 'undefined') {
22 | global.RTCSessionDescription = RTCSessionDescription;
23 | }
24 |
25 | if (typeof global.RTCIceCandidate == 'undefined') {
26 | global.RTCIceCandidate = RTCIceCandidate;
27 | }
28 |
29 | const navigator = global.navigator;
30 |
31 | if (navigator) {
32 | if (typeof navigator.webkitGetUserMedia === 'undefined') {
33 | navigator.webkitGetUserMedia = getUserMedia;
34 | }
35 | }
36 |
37 | })(global || window || this); // eslint-disable-line no-invalid-this
38 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
17 |
18 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/ios/imsdk/imsdk/TCPConnection.h:
--------------------------------------------------------------------------------
1 | //
2 | // TCPConnection.h
3 | // podcasting
4 | //
5 | // Created by houxh on 15/6/25.
6 | // Copyright (c) 2015年 beetle. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #define STATE_UNCONNECTED 0
12 | #define STATE_CONNECTING 1
13 | #define STATE_CONNECTED 2
14 | #define STATE_CONNECTFAIL 3
15 |
16 | @protocol TCPConnectionObserver
17 | @optional
18 | //同IM服务器连接的状态变更通知
19 | -(void)onConnectState:(int)state;
20 |
21 | @end
22 |
23 |
24 | @class AsyncTCP;
25 | @interface TCPConnection : NSObject
26 | //public
27 | @property(nonatomic, assign)int connectState;
28 | @property(nonatomic, copy) NSString *host;
29 |
30 | //protect
31 | @property(nonatomic)int port;
32 | @property(nonatomic, assign)int heartbeatHZ;
33 | @property(nonatomic)AsyncTCP *tcp;
34 |
35 | //subclass override
36 | -(void)sendPing;
37 |
38 |
39 | -(BOOL)handleData:(NSData*)data;
40 |
41 | -(void)onConnect;
42 | -(void)onClose;
43 |
44 |
45 |
46 | //protect method
47 | -(void)ping;
48 | -(void)pong;
49 | -(void)reconnect2S;
50 |
51 | //public method
52 | -(void)start;
53 | -(void)stop;
54 |
55 | -(void)enterForeground;
56 | -(void)enterBackground;
57 |
58 | -(void)addConnectionObserver:(id)ob;
59 | -(void)removeConnectionObserver:(id)ob;
60 |
61 | -(void)startRechabilityNotifier;
62 | @end
63 |
--------------------------------------------------------------------------------
/react/participant.js:
--------------------------------------------------------------------------------
1 | /*
2 | * (C) Copyright 2014 Kurento (http://kurento.org/)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 |
18 | function Participant(name, sendMessage) {
19 | console.log("participant name:", name);
20 | this.name = name;
21 |
22 | this.offerToReceiveVideo = function(error, offerSdp, wp){
23 | if (error) return console.error ("sdp offer error")
24 | console.log('Invoking SDP offer callback function');
25 | var msg = { id : "receiveVideoFrom",
26 | sender : name,
27 | sdpOffer : offerSdp
28 | };
29 | sendMessage(msg);
30 | }
31 |
32 |
33 | this.onIceCandidate = function (candidate, wp) {
34 | console.log("Local candidate" + JSON.stringify(candidate));
35 |
36 | var message = {
37 | id: 'onIceCandidate',
38 | candidate: candidate,
39 | name: name
40 | };
41 | sendMessage(message);
42 | }
43 |
44 | Object.defineProperty(this, 'rtcPeer', { writable: true});
45 |
46 | this.dispose = function() {
47 | console.log('Disposing participant ' + this.name);
48 | this.rtcPeer.dispose();
49 | };
50 | }
51 |
52 | module.exports = Participant;
53 |
--------------------------------------------------------------------------------
/android/imsdk/src/main/java/com/beetle/im/Timer.java:
--------------------------------------------------------------------------------
1 | package com.beetle.im;
2 |
3 | import android.os.Handler;
4 | import android.os.Message;
5 | import android.util.Log;
6 | import static android.os.SystemClock.uptimeMillis;
7 |
8 | /**
9 | * Created by houxh on 14-7-21.
10 | */
11 | public abstract class Timer {
12 | private static final int WHAT = 0;
13 |
14 | private long start;
15 | private long interval;
16 | private boolean active = false;
17 |
18 | class TimerHandler extends Handler {
19 | @Override
20 | public void handleMessage(Message msg) {
21 | if (!active) {
22 | return;
23 | }
24 |
25 | Timer.this.fire();
26 | if (Timer.this.interval != -1) {
27 | long t = uptimeMillis() + Timer.this.interval;
28 | boolean b = this.sendEmptyMessageAtTime(WHAT, t);
29 | }
30 | }
31 | }
32 | private Handler handler = new TimerHandler();
33 |
34 | public void setTimer(long start, long interval) {
35 | this.start = start;
36 | this.interval = interval;
37 | if (active) {
38 | handler.removeMessages(WHAT);
39 | handler.sendEmptyMessageAtTime(WHAT, start);
40 | }
41 | }
42 |
43 | public void setTimer(long start) {
44 | this.start = start;
45 | this.interval = -1;
46 | if (active) {
47 | handler.removeMessages(WHAT);
48 | handler.sendEmptyMessageAtTime(WHAT, start);
49 | }
50 | }
51 |
52 | public void resume() {
53 | active = true;
54 | handler.sendEmptyMessageAtTime(WHAT, start);
55 | }
56 |
57 | public void suspend() {
58 | active = false;
59 | handler.removeMessages(WHAT);
60 | }
61 |
62 | protected abstract void fire();
63 | }
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | apply from: '../../node_modules/react-native/react.gradle'
4 |
5 | android {
6 | compileSdkVersion 23
7 | buildToolsVersion '27.0.3'
8 | useLibrary 'org.apache.http.legacy'
9 |
10 | defaultConfig {
11 | applicationId "com.beetle.conference"
12 | minSdkVersion 16
13 | targetSdkVersion 23
14 | versionCode 10
15 | versionName "1.0"
16 |
17 | ndk {
18 | abiFilters 'armeabi-v7a', 'x86'
19 | }
20 | packagingOptions {
21 | // The project react-native does not provide 64-bit binaries at the
22 | // time of this writing. Unfortunately, packaging any 64-bit
23 | // binaries into the .apk will crash the app at runtime on 64-bit
24 | // platforms.
25 | exclude 'lib/x86_64/libjingle_peerconnection_so.so'
26 | exclude 'lib/arm64-v8a/libjingle_peerconnection_so.so'
27 | }
28 | }
29 | signingConfigs {
30 | app {
31 | storeFile file("gradle.keystore")
32 | storePassword "gradle"
33 | keyAlias "gradle"
34 | keyPassword "gradle"
35 | }
36 | }
37 |
38 | compileOptions {
39 | sourceCompatibility JavaVersion.VERSION_1_7
40 | targetCompatibility JavaVersion.VERSION_1_7
41 | }
42 |
43 | buildTypes {
44 | release {
45 | minifyEnabled false
46 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
47 | signingConfig signingConfigs.app
48 | }
49 | }
50 | }
51 |
52 | dependencies {
53 | implementation fileTree(include: ['*.jar'], dir: 'libs')
54 |
55 | implementation 'com.facebook.react:react-native:+'
56 | implementation project(':react-native-webrtc')
57 |
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/android/asynctcp/src/main/java/com/beetle/AsyncTCPTest.java:
--------------------------------------------------------------------------------
1 | package com.beetle;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.os.Looper;
6 | import android.util.Log;
7 | import android.view.View;
8 | import android.view.View.OnClickListener;
9 | import android.widget.Button;
10 |
11 |
12 |
13 | public class AsyncTCPTest extends Activity {
14 | AsyncTCP tcp;
15 | byte[] recvBuf = new byte[0];
16 | @Override
17 | public void onCreate(Bundle savedInstanceState)
18 | {
19 | super.onCreate(savedInstanceState);
20 | Button bt = new Button(this);
21 | bt.setText( "start" );
22 | setContentView(bt);
23 |
24 |
25 | bt.setOnClickListener(new OnClickListener() {
26 | @Override
27 | public void onClick(View arg0) {
28 | test();
29 | }
30 | });
31 | }
32 |
33 |
34 | public void test() {
35 | if (tcp != null) return;
36 | tcp = new AsyncTCP();
37 |
38 |
39 | TCPConnectCallback cb = new TCPConnectCallback() {
40 | public void onConnect(Object tcp1, int status) {
41 | if (status != 0) {
42 | Log.i("Beetle", "connect error");
43 | tcp.close();
44 | return;
45 | }
46 | Log.i("Beetle", "connected");
47 | byte[] data = "GET / HTTP/1.1\r\nHost: www.baidu.com\r\nConnection: close\r\n\r\n".getBytes();
48 | tcp.writeData(data);
49 |
50 | tcp.startRead();
51 | }
52 | };
53 | TCPReadCallback read_cb = new TCPReadCallback() {
54 | public void onRead(Object tcp1, byte[] data) {
55 | if (data.length == 0) {
56 | try {
57 | String result = new String(recvBuf, "UTF-8");
58 | Log.i("Beetle", result);
59 | } catch(Exception e) {
60 |
61 | }
62 | Log.i("Beetle", "tcp closed");
63 | tcp.close();
64 | return;
65 | }
66 |
67 | byte[] result = new byte[recvBuf.length + data.length];
68 | System.arraycopy(recvBuf, 0, result, 0, recvBuf.length);
69 | System.arraycopy(data, 0, result, recvBuf.length, data.length);
70 | recvBuf = result;
71 | Log.i("Beetle", "recv data");
72 | }
73 | };
74 | tcp.setConnectCallback(cb);
75 | tcp.setReadCallback(read_cb);
76 | tcp.connect("www.baidu.com", 80);
77 | }
78 | }
--------------------------------------------------------------------------------
/ios/conference/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // Face
4 | //
5 | // Created by houxh on 14-10-13.
6 | // Copyright (c) 2014年 beetle. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 | #import "ViewController.h"
11 | @implementation AppDelegate
12 |
13 |
14 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
15 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
16 | self.window.backgroundColor = [UIColor whiteColor];
17 | [self.window makeKeyAndVisible];
18 |
19 | ViewController *mainViewController = [[ViewController alloc] init];
20 | self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:mainViewController];
21 |
22 | return YES;
23 | }
24 |
25 | - (void)applicationWillResignActive:(UIApplication *)application {
26 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
27 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
28 | }
29 |
30 | - (void)applicationDidEnterBackground:(UIApplication *)application {
31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
33 | }
34 |
35 | - (void)applicationWillEnterForeground:(UIApplication *)application {
36 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
37 | }
38 |
39 | - (void)applicationDidBecomeActive:(UIApplication *)application {
40 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
41 | }
42 |
43 | - (void)applicationWillTerminate:(UIApplication *)application {
44 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
45 | }
46 |
47 |
48 |
49 | @end
50 |
--------------------------------------------------------------------------------
/ios/conference/conference-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | conference
9 | CFBundleExecutable
10 | ${EXECUTABLE_NAME}
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | conference
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1.0.0
25 | LSRequiresIPhoneOS
26 |
27 | NSAppTransportSecurity
28 |
29 | NSAllowsArbitraryLoads
30 |
31 |
32 | NSCameraUsageDescription
33 | This app requires access to camera.
34 | NSContactsUsageDescription
35 | This app requires access to contacts.
36 | NSMicrophoneUsageDescription
37 | This app requires access to microphone.
38 | UIAppFonts
39 |
40 | jitsi.ttf
41 | FontAwesome.ttf
42 | Entypo.ttf
43 | EvilIcons.ttf
44 | Foundation.ttf
45 | Ionicons.ttf
46 | MaterialCommunityIcons.ttf
47 | MaterialIcons.ttf
48 | Octicons.ttf
49 | SimpleLineIcons.ttf
50 | Zocial.ttf
51 |
52 | UIBackgroundModes
53 |
54 | UILaunchStoryboardName
55 | LaunchScreen
56 | UIRequiredDeviceCapabilities
57 |
58 | armv7
59 |
60 | UIStatusBarTintParameters
61 |
62 | UINavigationBar
63 |
64 | Style
65 | UIBarStyleDefault
66 | Translucent
67 |
68 |
69 |
70 | UISupportedInterfaceOrientations
71 |
72 | UIInterfaceOrientationPortrait
73 |
74 | UIViewControllerBasedStatusBarAppearance
75 |
76 |
77 |
--------------------------------------------------------------------------------
/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/ios/imsdk/imsdk/GOReachability.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2014-2015, GoBelieve
3 | All rights reserved.
4 |
5 | This source code is licensed under the BSD-style license found in the
6 | LICENSE file in the root directory of this source tree. An additional grant
7 | of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 | #import
12 |
13 | /**
14 | * Does ARC support GCD objects?
15 | * It does if the minimum deployment target is iOS 6+ or Mac OS X 8+
16 | *
17 | * @see http://opensource.apple.com/source/libdispatch/libdispatch-228.18/os/object.h
18 | **/
19 | #if OS_OBJECT_USE_OBJC
20 | #define NEEDS_DISPATCH_RETAIN_RELEASE 0
21 | #else
22 | #define NEEDS_DISPATCH_RETAIN_RELEASE 1
23 | #endif
24 |
25 | /**
26 | * Create NS_ENUM macro if it does not exist on the targeted version of iOS or OS X.
27 | *
28 | * @see http://nshipster.com/ns_enum-ns_options/
29 | **/
30 | #ifndef NS_ENUM
31 | #define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
32 | #endif
33 |
34 | typedef NS_ENUM(NSInteger, NetworkStatus) {
35 | // Apple NetworkStatus Compatible Names.
36 | NotReachable = 0,
37 | ReachableViaWiFi = 2,
38 | ReachableViaWWAN = 1
39 | };
40 |
41 | @class GOReachability;
42 |
43 | typedef void (^NetworkReachable)(GOReachability * reachability);
44 | typedef void (^NetworkUnreachable)(GOReachability * reachability);
45 |
46 | @interface GOReachability : NSObject
47 |
48 | @property (nonatomic, copy) NetworkReachable reachableBlock;
49 | @property (nonatomic, copy) NetworkUnreachable unreachableBlock;
50 |
51 |
52 | @property (nonatomic, assign) BOOL reachableOnWWAN;
53 |
54 | +(GOReachability*)reachabilityWithHostname:(NSString*)hostname;
55 | // This is identical to the function above, but is here to maintain
56 | //compatibility with Apples original code. (see .m)
57 | +(GOReachability*)reachabilityWithHostName:(NSString*)hostname;
58 | +(GOReachability*)reachabilityForInternetConnection;
59 | +(GOReachability*)reachabilityWithAddress:(const struct sockaddr_in*)hostAddress;
60 | +(GOReachability*)reachabilityForLocalWiFi;
61 |
62 | -(GOReachability *)initWithReachabilityRef:(SCNetworkReachabilityRef)ref;
63 |
64 | -(BOOL)startNotifier;
65 | -(void)stopNotifier;
66 |
67 | -(BOOL)isReachable;
68 | -(BOOL)isReachableViaWWAN;
69 | -(BOOL)isReachableViaWiFi;
70 |
71 | // WWAN may be available, but not active until a connection has been established.
72 | // WiFi may require a connection for VPN on Demand.
73 | -(BOOL)isConnectionRequired; // Identical DDG variant.
74 | -(BOOL)connectionRequired; // Apple's routine.
75 | // Dynamic, on demand connection?
76 | -(BOOL)isConnectionOnDemand;
77 | // Is user intervention required?
78 | -(BOOL)isInterventionRequired;
79 |
80 | -(NetworkStatus)currentReachabilityStatus;
81 | -(SCNetworkReachabilityFlags)reachabilityFlags;
82 | -(NSString*)currentReachabilityString;
83 | -(NSString*)currentReachabilityFlags;
84 |
85 | @end
86 |
--------------------------------------------------------------------------------
/android/app/src/main/res/layout/activity_login.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
11 |
14 |
18 |
19 |
28 |
29 |
36 |
37 |
38 |
39 |
42 |
46 |
47 |
48 |
56 |
57 |
63 |
64 |
65 |
66 |
67 |
71 |
79 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/ios/imsdk/imsdk/Message.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2014-2015, GoBelieve
3 | All rights reserved.
4 |
5 | This source code is licensed under the BSD-style license found in the
6 | LICENSE file in the root directory of this source tree. An additional grant
7 | of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 |
12 | #define MSG_HEARTBEAT 1
13 |
14 | #define MSG_AUTH_STATUS 3
15 | #define MSG_IM 4
16 | #define MSG_ACK 5
17 |
18 | #define MSG_GROUP_NOTIFICATION 7
19 | #define MSG_GROUP_IM 8
20 |
21 | #define MSG_INPUTING 10
22 |
23 | #define MSG_PING 13
24 | #define MSG_PONG 14
25 | #define MSG_AUTH_TOKEN 15
26 |
27 | #define MSG_RT 17
28 | #define MSG_ENTER_ROOM 18
29 | #define MSG_LEAVE_ROOM 19
30 | #define MSG_ROOM_IM 20
31 | #define MSG_SYSTEM 21
32 | #define MSG_UNREAD_COUNT 22
33 |
34 | #define MSG_CUSTOMER 24
35 | #define MSG_CUSTOMER_SUPPORT 25
36 |
37 |
38 | //客户端->服务端
39 | #define MSG_SYNC 26 //同步消息
40 | //服务端->客服端
41 | #define MSG_SYNC_BEGIN 27
42 | #define MSG_SYNC_END 28
43 | //通知客户端有新消息
44 | #define MSG_SYNC_NOTIFY 29
45 |
46 | //客户端->服务端
47 | #define MSG_SYNC_GROUP 30//同步超级群消息
48 | //服务端->客服端
49 | #define MSG_SYNC_GROUP_BEGIN 31
50 | #define MSG_SYNC_GROUP_END 32
51 | //通知客户端有新消息
52 | #define MSG_SYNC_GROUP_NOTIFY 33
53 |
54 | //客服端->服务端
55 | #define MSG_SYNC_KEY 34
56 | #define MSG_GROUP_SYNC_KEY 35
57 |
58 |
59 | #define MSG_VOIP_CONTROL 64
60 |
61 | #define PLATFORM_IOS 1
62 | #define PLATFORM_ANDROID 2
63 | #define PLATFORM_WEB 3
64 |
65 |
66 |
67 |
68 | @interface IMMessage : NSObject
69 | @property(nonatomic, assign)int64_t sender;
70 | @property(nonatomic, assign)int64_t receiver;
71 | @property(nonatomic, assign)int32_t timestamp;
72 | @property(nonatomic, assign)int32_t msgLocalID;
73 | @property(nonatomic, copy)NSString *content;
74 | @end
75 |
76 | @interface CustomerMessage : NSObject
77 | //本地消息id 不会序列化传到服务器
78 | @property(nonatomic, assign)int32_t msgLocalID;
79 |
80 | @property(nonatomic, assign)int64_t customerAppID;
81 | @property(nonatomic, assign)int64_t customerID;
82 | @property(nonatomic, assign)int64_t storeID;
83 | @property(nonatomic, assign)int64_t sellerID;
84 | @property(nonatomic, assign)int32_t timestamp;
85 | @property(nonatomic, copy)NSString *content;
86 | @end
87 |
88 | @interface RoomMessage : NSObject
89 | @property(nonatomic, assign)int64_t sender;
90 | @property(nonatomic, assign)int64_t receiver;
91 | @property(nonatomic, copy)NSString *content;
92 | @end
93 |
94 | typedef RoomMessage RTMessage;
95 |
96 | @interface MessageInputing : NSObject
97 | @property(nonatomic, assign)int64_t sender;
98 | @property(nonatomic, assign)int64_t receiver;
99 | @end
100 |
101 | @interface AuthenticationToken : NSObject
102 | @property(nonatomic, copy) NSString *token;
103 | @property(nonatomic, assign) int8_t platformID;
104 | @property(nonatomic, copy) NSString *deviceID;
105 | @end
106 |
107 |
108 | @interface VOIPControl : NSObject
109 | @property(nonatomic, assign) int64_t sender;
110 | @property(nonatomic, assign) int64_t receiver;
111 | @property(nonatomic) NSData *content;
112 |
113 | @end
114 |
115 | typedef NSNumber SyncKey;
116 |
117 | @interface GroupSyncKey : NSObject
118 | @property(nonatomic, assign) int64_t groupID;
119 | @property(nonatomic, assign) int64_t syncKey;
120 | @end
121 |
122 |
123 | @interface Message : NSObject
124 | @property(nonatomic, assign)int cmd;
125 | @property(nonatomic, assign)int seq;
126 | @property(nonatomic) NSObject *body;
127 |
128 | -(NSData*)pack;
129 |
130 | -(BOOL)unpack:(NSData*)data;
131 | @end
132 |
--------------------------------------------------------------------------------
/ios/imsdk/imsdk/util.c:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2014-2015, GoBelieve
3 | All rights reserved.
4 |
5 | This source code is licensed under the BSD-style license found in the
6 | LICENSE file in the root directory of this source tree. An additional grant
7 | of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include /* writev */
20 | #include
21 | #include
22 | #include
23 |
24 | int64_t hton64(int64_t val )
25 | {
26 | int64_t high, low;
27 | low = (int64_t)(val & 0x00000000FFFFFFFF);
28 | val >>= 32;
29 | high = (int64_t)(val & 0x00000000FFFFFFFF);
30 | low = htonl( low );
31 | high = htonl( high );
32 |
33 | return (int64_t)low << 32 | high;
34 | }
35 |
36 | int64_t ntoh64(int64_t val )
37 | {
38 | int64_t high, low;
39 | low = (int64_t)(val & 0x00000000FFFFFFFF);
40 | val>>=32;
41 | high = (int64_t)(val & 0x00000000FFFFFFFF);
42 | low = ntohl( low );
43 | high = ntohl( high );
44 |
45 | return (int64_t)low << 32 | high;
46 | }
47 |
48 | void writeInt32(int32_t v, void *p) {
49 | v = htonl(v);
50 | memcpy(p, &v, 4);
51 | }
52 |
53 | int32_t readInt32(const void *p) {
54 | int32_t v;
55 | memcpy(&v, p, 4);
56 | return ntohl(v);
57 | }
58 |
59 | void writeInt64(int64_t v, void *p) {
60 | v = hton64(v);
61 | memcpy(p, &v, 8);
62 | }
63 |
64 | int64_t readInt64(const void *p) {
65 | int64_t v;
66 | memcpy(&v, p, 8);
67 | return ntoh64(v);
68 | }
69 |
70 | void writeInt16(int16_t v, void *p) {
71 | v = htons(v);
72 | memcpy(p, &v, 2);
73 | }
74 |
75 | int16_t readInt16(const void *p) {
76 | int16_t v;
77 | memcpy(&v, p, 2);
78 | return ntohs(v);
79 | }
80 |
81 | int lookupAddr(const char *host, int port, struct sockaddr_in *addr) {
82 | struct addrinfo hints;
83 | struct addrinfo *result, *rp;
84 | int s;
85 |
86 | char buf[32];
87 | snprintf(buf, 32, "%d", port);
88 |
89 | memset(&hints, 0, sizeof(struct addrinfo));
90 | hints.ai_family = AF_INET;
91 | hints.ai_socktype = SOCK_STREAM;
92 | hints.ai_protocol = IPPROTO_TCP;
93 | hints.ai_flags = 0;
94 |
95 | s = getaddrinfo(host, buf, &hints, &result);
96 | if (s != 0) {
97 | return -1;
98 | }
99 | if (result != NULL) {
100 | rp = result;
101 | memcpy(addr, rp->ai_addr, rp->ai_addrlen);
102 | }
103 |
104 | freeaddrinfo(result);
105 | return 0;
106 | }
107 |
108 | int sock_nonblock(int fd, int set) {
109 | int r;
110 |
111 | do
112 | r = ioctl(fd, FIONBIO, &set);
113 | while (r == -1 && errno == EINTR);
114 |
115 | return r;
116 | }
117 |
118 | int write_data(int fd, uint8_t *bytes, int len) {
119 | ssize_t n = 0;
120 |
121 | do {
122 | n = send(fd, bytes, len, 0);
123 | } while(n == -1 && errno == EINTR);
124 | if (n < 0) {
125 | if (errno != EAGAIN && errno != EWOULDBLOCK) {
126 | return -1;
127 | }
128 | return 0;
129 | } else {
130 | return (int)n;
131 | }
132 | }
133 |
134 | size_t fwrite$UNIX2003( const void *a, size_t b, size_t c, FILE *d )
135 | {
136 | return fwrite(a, b, c, d);
137 | }
138 | char* strerror$UNIX2003( int errnum )
139 | {
140 | return strerror(errnum);
141 | }
142 |
143 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
55 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
69 |
70 |
71 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/android/imsdk/src/main/java/com/beetle/im/BytePacket.java:
--------------------------------------------------------------------------------
1 | package com.beetle.im;
2 |
3 | import java.nio.ByteBuffer;
4 | import java.nio.ByteOrder;
5 |
6 | /**
7 | * Created by houxh on 14-7-21.
8 | */
9 | public class BytePacket {
10 | static public void writeInt64(long v, byte[] dst, int pos) {
11 | ByteBuffer b = ByteBuffer.allocate(8);
12 | b.order(ByteOrder.BIG_ENDIAN);
13 | b.putLong(v);
14 | byte[] t = b.array();
15 | System.arraycopy(t, 0, dst, pos, t.length);
16 | }
17 |
18 | static public void writeInt32(int v, byte[] dst, int pos) {
19 | ByteBuffer b = ByteBuffer.allocate(4);
20 | b.order(ByteOrder.BIG_ENDIAN);
21 | b.putInt(v);
22 | byte[] t = b.array();
23 | System.arraycopy(t, 0, dst, pos, t.length);
24 | }
25 |
26 | static public void writeInt16(short v, byte[] dst, int pos) {
27 | ByteBuffer b = ByteBuffer.allocate(2);
28 | b.order(ByteOrder.BIG_ENDIAN);
29 | b.putShort(v);
30 | byte[] t = b.array();
31 | System.arraycopy(t, 0, dst, pos, t.length);
32 | }
33 |
34 | static public long readInt64(byte[] bytes, int pos) {
35 | ByteBuffer b = ByteBuffer.wrap(bytes, pos, 8);
36 | b.order(ByteOrder.BIG_ENDIAN);
37 | return b.getLong();
38 | }
39 |
40 | static public int readInt32(byte[] bytes, int pos) {
41 | ByteBuffer b = ByteBuffer.wrap(bytes, pos, 4);
42 | b.order(ByteOrder.BIG_ENDIAN);
43 | return b.getInt();
44 | }
45 |
46 | static public short readInt16(byte[] bytes, int pos) {
47 | ByteBuffer b = ByteBuffer.wrap(bytes, pos, 2);
48 | b.order(ByteOrder.BIG_ENDIAN);
49 | return b.getShort();
50 | }
51 |
52 | static public int packInetAddress(byte[] bytes) {
53 | ByteBuffer b2 = ByteBuffer.wrap(bytes, 0, 4);
54 | b2.order(ByteOrder.BIG_ENDIAN);
55 | return b2.getInt();
56 | }
57 |
58 | static public byte[] unpackInetAddress(int iaddr) {
59 | ByteBuffer b = ByteBuffer.allocate(4);
60 | b.order(ByteOrder.BIG_ENDIAN);
61 | b.putInt(iaddr);
62 | byte[] t = b.array();
63 | return t;
64 | }
65 |
66 | //little->net
67 | static public int ltonl(int v) {
68 | ByteBuffer b = ByteBuffer.allocate(4);
69 | b.order(ByteOrder.LITTLE_ENDIAN);
70 | b.putInt(v);
71 | byte[] t = b.array();
72 | ByteBuffer b2 = ByteBuffer.wrap(t, 0, 4);
73 | b2.order(ByteOrder.BIG_ENDIAN);
74 | return b2.getInt();
75 | }
76 | //net->little
77 | static public int ntoll(int v) {
78 | ByteBuffer b = ByteBuffer.allocate(4);
79 | b.order(ByteOrder.BIG_ENDIAN);
80 | b.putInt(v);
81 | byte[] t = b.array();
82 | ByteBuffer b2 = ByteBuffer.wrap(t, 0, 4);
83 | b2.order(ByteOrder.LITTLE_ENDIAN);
84 | return b2.getInt();
85 | }
86 |
87 | //little -> native
88 | static public int ltohl(int v) {
89 | ByteBuffer b = ByteBuffer.allocate(4);
90 | b.order(ByteOrder.LITTLE_ENDIAN);
91 | b.putInt(v);
92 | byte[] t = b.array();
93 | ByteBuffer b2 = ByteBuffer.wrap(t, 0, 4);
94 | b2.order(ByteOrder.nativeOrder());
95 | return b2.getInt();
96 | }
97 |
98 | //native -> little
99 | static public int htoll(int v) {
100 | ByteBuffer b = ByteBuffer.allocate(4);
101 | b.order(ByteOrder.nativeOrder());
102 | b.putInt(v);
103 | byte[] t = b.array();
104 | ByteBuffer b2 = ByteBuffer.wrap(t, 0, 4);
105 | b2.order(ByteOrder.LITTLE_ENDIAN);
106 | return b2.getInt();
107 | }
108 |
109 | static public int ntohl(int v) {
110 | ByteBuffer b = ByteBuffer.allocate(4);
111 | b.order(ByteOrder.nativeOrder());
112 | b.putInt(v);
113 | byte[] t = b.array();
114 | ByteBuffer b2 = ByteBuffer.wrap(t, 0, 4);
115 | b2.order(ByteOrder.BIG_ENDIAN);
116 | return b2.getInt();
117 | }
118 |
119 | static public int htonl(int v) {
120 | ByteBuffer b = ByteBuffer.allocate(4);
121 | b.order(ByteOrder.BIG_ENDIAN);
122 | b.putInt(v);
123 | byte[] t = b.array();
124 | ByteBuffer b2 = ByteBuffer.wrap(t, 0, 4);
125 | b2.order(ByteOrder.nativeOrder());
126 | return b2.getInt();
127 | }
128 |
129 | }
130 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/beetle/conference/LoginActivity.java:
--------------------------------------------------------------------------------
1 | package com.beetle.conference;
2 |
3 |
4 | import android.app.ProgressDialog;
5 | import android.content.Intent;
6 | import android.os.AsyncTask;
7 | import android.os.Bundle;
8 | import android.support.v4.app.FragmentActivity;
9 | import android.text.TextUtils;
10 | import android.util.Log;
11 | import android.view.View;
12 | import android.widget.EditText;
13 | import android.widget.Toast;
14 |
15 | import org.json.JSONObject;
16 |
17 | import java.io.BufferedWriter;
18 | import java.io.ByteArrayOutputStream;
19 | import java.io.InputStream;
20 | import java.io.OutputStreamWriter;
21 | import java.net.HttpURLConnection;
22 | import java.net.URL;
23 | import java.nio.charset.StandardCharsets;
24 |
25 | /**
26 | * LoginActivity
27 | * Description: 登录页面,给用户指定消息发送方Id
28 | */
29 | public class LoginActivity extends FragmentActivity {
30 | private final String TAG = "demo";
31 | private final int REQUEST_CONFERENCE = 1;
32 |
33 | @Override
34 | protected void onCreate(Bundle savedInstanceState) {
35 | super.onCreate(savedInstanceState);
36 | setContentView(R.layout.activity_login);
37 | }
38 |
39 | @Override
40 | protected void onDestroy() {
41 | super.onDestroy();
42 | }
43 |
44 |
45 |
46 |
47 | public void enterRoom(View v) {
48 | EditText uidEditText = (EditText)findViewById(R.id.et_username);
49 | EditText conferenceEditText = (EditText)findViewById(R.id.conference_id);
50 |
51 | String uidText = uidEditText.getText().toString();
52 | String confText = conferenceEditText.getText().toString();
53 |
54 | if (TextUtils.isEmpty(uidText) || TextUtils.isEmpty(confText)) {
55 | return;
56 | }
57 |
58 | final long uid = Long.parseLong(uidText);
59 | final long conferenceID = Long.parseLong(confText);
60 |
61 | if (uid == 0 || conferenceID == 0) {
62 | return;
63 | }
64 |
65 | final ProgressDialog dialog = ProgressDialog.show(this, null, "登录中...");
66 |
67 | new AsyncTask() {
68 | @Override
69 | protected String doInBackground(Void... urls) {
70 | return LoginActivity.this.login(uid);
71 | }
72 |
73 | @Override
74 | protected void onPostExecute(String result) {
75 | dialog.dismiss();
76 |
77 | if (TextUtils.isEmpty(result)) {
78 | Toast.makeText(LoginActivity.this, "登陆失败", Toast.LENGTH_SHORT).show();
79 | return;
80 | }
81 |
82 | Log.i(TAG, "uid:" + uid + " channel id:" + conferenceID + " token:" + result);
83 |
84 | Intent intent = new Intent(LoginActivity.this, GroupVOIPActivity.class);
85 | intent.putExtra("current_uid", uid);
86 | intent.putExtra("channel_id", "" + conferenceID);
87 | intent.putExtra("token", result);
88 |
89 | startActivityForResult(intent, REQUEST_CONFERENCE);
90 | }
91 | }.execute();
92 | }
93 |
94 |
95 | private String login(long uid) {
96 | //调用app自身的登陆接口获取im服务必须的access token
97 | String URL = "http://demo.gobelieve.io";
98 | String uri = String.format("%s/auth/token", URL);
99 |
100 | try {
101 | java.net.URL url = new URL(uri);
102 | HttpURLConnection connection = (HttpURLConnection) url.openConnection();
103 | connection.setRequestMethod("POST");
104 | connection.setDoOutput(true);
105 | connection.setDoInput(true);
106 | connection.setUseCaches(false);
107 | connection.setRequestProperty("Content-type", "application/json");
108 | connection.connect();
109 |
110 | JSONObject json = new JSONObject();
111 | json.put("uid", uid);
112 | BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(connection.getOutputStream(), "UTF-8"));
113 | writer.write(json.toString());
114 | writer.close();
115 |
116 | int responseCode = connection.getResponseCode();
117 | if(responseCode != HttpURLConnection.HTTP_OK) {
118 | System.out.println("login failure code is:" + responseCode);
119 | return null;
120 | }
121 |
122 | InputStream inputStream = connection.getInputStream();
123 |
124 | //inputstream -> string
125 | ByteArrayOutputStream result = new ByteArrayOutputStream();
126 | byte[] buffer = new byte[1024];
127 | int length;
128 | while ((length = inputStream.read(buffer)) != -1) {
129 | result.write(buffer, 0, length);
130 | }
131 | String str = result.toString(StandardCharsets.UTF_8.name());
132 |
133 |
134 | JSONObject jsonObject = new JSONObject(str);
135 | String accessToken = jsonObject.getString("token");
136 | return accessToken;
137 | } catch (Exception e) {
138 | e.printStackTrace();
139 | }
140 |
141 | return "";
142 | }
143 |
144 |
145 |
146 | @Override
147 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
148 |
149 | }
150 |
151 |
152 | }
153 |
--------------------------------------------------------------------------------
/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/ios/imsdk/imsdk/IMService.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2014-2015, GoBelieve
3 | All rights reserved.
4 |
5 | This source code is licensed under the BSD-style license found in the
6 | LICENSE file in the root directory of this source tree. An additional grant
7 | of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import
11 | #import "Message.h"
12 | #import "TCPConnection.h"
13 |
14 | @class IMessage;
15 |
16 | @protocol IMPeerMessageHandler
17 | -(BOOL)handleMessage:(IMMessage*)msg uid:(int64_t)uid;
18 | -(BOOL)handleMessageACK:(int)msgLocalID uid:(int64_t)uid;
19 | -(BOOL)handleMessageFailure:(int)msgLocalID uid:(int64_t)uid;
20 | @end
21 |
22 | @protocol IMGroupMessageHandler
23 |
24 | -(BOOL)handleMessage:(IMMessage*)msg;
25 | -(BOOL)handleMessageACK:(int)msgLocalID gid:(int64_t)gid;
26 | -(BOOL)handleMessageFailure:(int)msgLocalID gid:(int64_t)gid;
27 |
28 | -(BOOL)handleGroupNotification:(NSString*)notification;
29 | @end
30 |
31 | @protocol IMCustomerMessageHandler
32 | -(BOOL)handleCustomerSupportMessage:(CustomerMessage*)msg;
33 | -(BOOL)handleMessage:(CustomerMessage*)msg;
34 | -(BOOL)handleMessageACK:(CustomerMessage*)msg;
35 | -(BOOL)handleMessageFailure:(CustomerMessage*)msg;
36 | @end
37 |
38 | //保存消息的同步key
39 | @protocol IMSyncKeyHandler
40 | -(BOOL)saveSyncKey:(int64_t)syncKey;
41 | -(BOOL)saveGroupSyncKey:(int64_t)syncKey gid:(int64_t)gid;
42 | @end
43 |
44 | @protocol PeerMessageObserver
45 | @optional
46 | -(void)onPeerMessage:(IMMessage*)msg;
47 |
48 | //服务器ack
49 | -(void)onPeerMessageACK:(int)msgLocalID uid:(int64_t)uid;
50 |
51 | //消息发送失败
52 | -(void)onPeerMessageFailure:(int)msgLocalID uid:(int64_t)uid;
53 |
54 | //对方正在输入
55 | -(void)onPeerInputing:(int64_t)uid;
56 |
57 | @end
58 |
59 | @protocol GroupMessageObserver
60 | @optional
61 | -(void)onGroupMessage:(IMMessage*)msg;
62 | -(void)onGroupMessageACK:(int)msgLocalID gid:(int64_t)gid;
63 | -(void)onGroupMessageFailure:(int)msgLocalID gid:(int64_t)gid;
64 |
65 | -(void)onGroupNotification:(NSString*)notification;
66 | @end
67 |
68 | @protocol RoomMessageObserver
69 | @optional
70 | -(void)onRoomMessage:(RoomMessage*)rm;
71 | -(void)onRoomMessageACK:(RoomMessage*)rm;
72 | -(void)onRoomMessageFailure:(RoomMessage*)rm;
73 |
74 | @end
75 |
76 | @protocol RTMessageObserver
77 |
78 | @optional
79 | -(void)onRTMessage:(RTMessage*)rt;
80 |
81 | @end
82 |
83 | @protocol SystemMessageObserver
84 | @optional
85 | -(void)onSystemMessage:(NSString*)sm;
86 |
87 | @end
88 |
89 | @protocol CustomerMessageObserver
90 | @optional
91 | -(void)onCustomerMessage:(CustomerMessage*)msg;
92 | -(void)onCustomerSupportMessage:(CustomerMessage*)msg;
93 |
94 | //服务器ack
95 | -(void)onCustomerMessageACK:(CustomerMessage*)msg;
96 | //消息发送失败
97 | -(void)onCustomerMessageFailure:(CustomerMessage*)msg;
98 | @end
99 |
100 | @protocol VOIPObserver
101 |
102 | -(void)onVOIPControl:(VOIPControl*)ctl;
103 |
104 | @end
105 |
106 |
107 | /*消息如何接收
108 | *1.初始化消息的同步key和所有超级群的同步key
109 | *2.上线之后,自动同步所有离线消息
110 | *3.收到同步消息的通知后,同步新消息
111 | */
112 | @interface IMService : TCPConnection
113 | @property(nonatomic, copy) NSString *deviceID;
114 | @property(nonatomic, copy) NSString *token;
115 | @property(nonatomic) int64_t uid;
116 | //客服app需要设置,普通app不需要设置
117 | @property(nonatomic) int64_t appID;
118 |
119 | //离线消息的同步key
120 | @property(nonatomic) int64_t syncKey;
121 |
122 | @property(nonatomic, weak)id peerMessageHandler;
123 | @property(nonatomic, weak)id groupMessageHandler;
124 | @property(nonatomic, weak)id customerMessageHandler;
125 | @property(nonatomic, strong)id syncKeyHandler;
126 |
127 | +(IMService*)instance;
128 |
129 | //超级群消息的同步key
130 | -(void)addSuperGroupSyncKey:(int64_t)syncKey gid:(int64_t)gid;
131 | -(void)removeSuperGroupSyncKey:(int64_t)gid;
132 | -(void)clearSuperGroupSyncKey;
133 |
134 |
135 | -(BOOL)isPeerMessageSending:(int64_t)peer id:(int)msgLocalID;
136 | -(BOOL)isGroupMessageSending:(int64_t)groupID id:(int)msgLocalID;
137 | -(BOOL)isCustomerSupportMessageSending:(int)msgLocalID
138 | customerID:(int64_t)customerID
139 | customerAppID:(int64_t)customerAppID;
140 | -(BOOL)isCustomerMessageSending:(int)msgLocalID storeID:(int64_t)storeID;
141 |
142 | -(BOOL)sendPeerMessage:(IMMessage*)msg;
143 | -(BOOL)sendGroupMessage:(IMMessage*)msg;
144 | -(BOOL)sendRoomMessage:(RoomMessage*)msg;
145 | //顾客->客服
146 | -(BOOL)sendCustomerMessage:(CustomerMessage*)im;
147 | //客服->顾客
148 | -(BOOL)sendCustomerSupportMessage:(CustomerMessage*)im;
149 | -(BOOL)sendRTMessage:(RTMessage*)msg;
150 |
151 | -(void)enterRoom:(int64_t)roomID;
152 | -(void)leaveRoom:(int64_t)roomID;
153 |
154 | //正在输入
155 | -(void)sendInputing:(MessageInputing*)inputing;
156 | //更新未读的消息数目
157 | -(void)sendUnreadCount:(int)unread;
158 |
159 | -(void)addPeerMessageObserver:(id)ob;
160 | -(void)removePeerMessageObserver:(id)ob;
161 |
162 | -(void)addGroupMessageObserver:(id)ob;
163 | -(void)removeGroupMessageObserver:(id)ob;
164 |
165 | -(void)addRoomMessageObserver:(id)ob;
166 | -(void)removeRoomMessageObserver:(id)ob;
167 |
168 | -(void)addSystemMessageObserver:(id)ob;
169 | -(void)removeSystemMessageObserver:(id)ob;
170 |
171 | -(void)addCustomerMessageObserver:(id)ob;
172 | -(void)removeCustomerMessageObserver:(id)ob;
173 |
174 | -(void)addRTMessageObserver:(id)ob;
175 | -(void)removeRTMessageObserver:(id)ob;
176 |
177 | -(void)pushVOIPObserver:(id)ob;
178 | -(void)popVOIPObserver:(id)ob;
179 |
180 | -(BOOL)sendVOIPControl:(VOIPControl*)ctl;
181 |
182 | @end
183 |
184 |
--------------------------------------------------------------------------------
/ios/conference/controller/GroupVOIOPViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ConferenceViewController.m
3 | // Face
4 | //
5 | // Created by houxh on 2016/12/7.
6 | // Copyright © 2016年 beetle. All rights reserved.
7 | //
8 |
9 | #import "GroupVOIPViewController.h"
10 | #import
11 | #import
12 |
13 | #import
14 | #import
15 | #import
16 | #import
17 | #import "AppDelegate.h"
18 |
19 |
20 | static int64_t g_controllerCount = 0;
21 |
22 | @interface GroupVOIPViewController ()
23 | @property(nonatomic, weak) RCTBridge *bridge;
24 | @end
25 |
26 | @implementation GroupVOIPViewController
27 | RCT_EXPORT_MODULE();
28 |
29 |
30 | RCT_EXPORT_METHOD(dismiss) {
31 | [[UIApplication sharedApplication] setIdleTimerDisabled:NO];
32 |
33 | RCTRootView *rootView = (RCTRootView*)self.view;
34 | [rootView.bridge invalidate];
35 | [self dismissViewControllerAnimated:YES completion:nil];
36 | }
37 |
38 |
39 | +(int64_t)controllerCount {
40 | return g_controllerCount;
41 | }
42 |
43 | //http://stackoverflow.com/questions/24595579/how-to-redirect-audio-to-speakers-in-the-apprtc-ios-example
44 | - (void)didSessionRouteChange:(NSNotification *)notification
45 | {
46 | NSDictionary *interuptionDict = notification.userInfo;
47 | NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
48 | NSLog(@"route change:%zd", routeChangeReason);
49 | if (![self isHeadsetPluggedIn] && ![self isLoudSpeaker]) {
50 | NSError* error;
51 | [[AVAudioSession sharedInstance] overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&error];
52 | }
53 | }
54 |
55 | - (BOOL)isHeadsetPluggedIn {
56 | AVAudioSessionRouteDescription *route = [[AVAudioSession sharedInstance] currentRoute];
57 |
58 | BOOL headphonesLocated = NO;
59 | for( AVAudioSessionPortDescription *portDescription in route.outputs )
60 | {
61 | headphonesLocated |= ( [portDescription.portType isEqualToString:AVAudioSessionPortHeadphones] );
62 | }
63 | return headphonesLocated;
64 | }
65 |
66 |
67 | -(BOOL)isLoudSpeaker {
68 | AVAudioSession* session = [AVAudioSession sharedInstance];
69 | AVAudioSessionCategoryOptions options = session.categoryOptions;
70 | BOOL enabled = options & AVAudioSessionCategoryOptionDefaultToSpeaker;
71 | return enabled;
72 | }
73 |
74 |
75 | - (dispatch_queue_t)methodQueue {
76 | return dispatch_get_main_queue();
77 | }
78 |
79 |
80 | -(void)dealloc {
81 | g_controllerCount--;
82 | [[NSNotificationCenter defaultCenter] removeObserver:self];
83 | }
84 |
85 | - (void)viewDidLoad {
86 | [super viewDidLoad];
87 | g_controllerCount++;
88 |
89 | __weak GroupVOIPViewController *wself = self;
90 | RCTBridgeModuleListProvider provider = ^NSArray> *{
91 | return @[wself];
92 | };
93 |
94 | NSLog(@"conference id:%@", self.channelID);
95 |
96 | NSURL *jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios"
97 | fallbackResource:nil];
98 |
99 |
100 | RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:jsCodeLocation
101 | moduleProvider:provider
102 | launchOptions:nil];
103 |
104 |
105 | NSString *name = [NSString stringWithFormat:@"%lld",self.currentUID];
106 | NSDictionary *props = @{@"name":name,
107 | @"room":self.channelID,
108 | @"token":self.token};
109 |
110 |
111 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"GroupCall" initialProperties:props];
112 |
113 | // Set a background color which is in accord with the JavaScript and Android
114 | // parts of the application and causes less perceived visual flicker than the
115 | // default background color.
116 | rootView.backgroundColor = [[UIColor alloc] initWithRed:.07f green:.07f blue:.07f alpha:1];
117 |
118 | self.view = rootView;
119 | self.bridge = bridge;
120 |
121 |
122 | [[UIApplication sharedApplication] setIdleTimerDisabled:YES];
123 |
124 |
125 | if (![self isHeadsetPluggedIn] && ![self isLoudSpeaker]) {
126 | NSError* error;
127 | [[AVAudioSession sharedInstance] overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&error];
128 | }
129 |
130 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didSessionRouteChange:) name:AVAudioSessionRouteChangeNotification object:nil];
131 | }
132 |
133 |
134 | -(int)setLoudspeakerStatus:(BOOL)enable {
135 | AVAudioSession* session = [AVAudioSession sharedInstance];
136 | NSString* category = session.category;
137 | AVAudioSessionCategoryOptions options = session.categoryOptions;
138 | // Respect old category options if category is
139 | // AVAudioSessionCategoryPlayAndRecord. Otherwise reset it since old options
140 | // might not be valid for this category.
141 | if ([category isEqualToString:AVAudioSessionCategoryPlayAndRecord]) {
142 | if (enable) {
143 | options |= AVAudioSessionCategoryOptionDefaultToSpeaker;
144 | } else {
145 | options &= ~AVAudioSessionCategoryOptionDefaultToSpeaker;
146 | }
147 | } else {
148 | options = AVAudioSessionCategoryOptionDefaultToSpeaker;
149 | }
150 |
151 | NSError* error = nil;
152 | [session setCategory:AVAudioSessionCategoryPlayAndRecord
153 | withOptions:options
154 | error:&error];
155 | if (error != nil) {
156 | NSLog(@"set loudspeaker err:%@", error);
157 | return -1;
158 | }
159 |
160 | return 0;
161 | }
162 |
163 |
164 |
165 | - (void)didReceiveMemoryWarning {
166 | [super didReceiveMemoryWarning];
167 | // Dispose of any resources that can be recreated.
168 | }
169 |
170 | @end
171 |
--------------------------------------------------------------------------------
/ios/conference/ViewController.m:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2014-2015, GoBelieve
3 | All rights reserved.
4 |
5 | This source code is licensed under the BSD-style license found in the
6 | LICENSE file in the root directory of this source tree. An additional grant
7 | of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import "ViewController.h"
11 | #import "MBProgressHUD.h"
12 |
13 | #import
14 | #import "GroupVOIPViewController.h"
15 |
16 | @interface ViewController ()
17 | @property (weak, nonatomic) UITextField *myTextField2;
18 | @property (weak, nonatomic) UITextField *conferenceTextField;
19 | @end
20 |
21 | @implementation ViewController
22 |
23 | - (void)viewDidLoad {
24 | [super viewDidLoad];
25 |
26 | if ([self respondsToSelector:@selector(setEdgesForExtendedLayout:)]) {
27 | [self setEdgesForExtendedLayout:UIRectEdgeNone];
28 | }
29 |
30 | UITapGestureRecognizer*tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapAction:)];
31 | [self.view addGestureRecognizer:tapGesture];
32 |
33 | UIImageView *bgImageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
34 | bgImageView.image = [UIImage imageNamed:@"bg"];
35 | [self.view addSubview:bgImageView];
36 |
37 |
38 | float startHeight = 40;
39 | UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(52, startHeight + 4, 180, 37)];
40 | self.myTextField2 = textField;
41 | self.myTextField2.textColor = [UIColor whiteColor];
42 | self.myTextField2.font = [UIFont systemFontOfSize:18];
43 | self.myTextField2.placeholder = @"我的id";
44 | self.myTextField2.keyboardType = UIKeyboardTypeNumberPad;
45 | [self.view addSubview:self.myTextField2];
46 |
47 | startHeight += 48;
48 | textField = [[UITextField alloc] initWithFrame:CGRectMake(52, startHeight + 4, 180, 37)];
49 | self.conferenceTextField = textField;
50 | self.conferenceTextField.textColor = [UIColor whiteColor];
51 | self.conferenceTextField.font = [UIFont systemFontOfSize:18];
52 | self.conferenceTextField.placeholder = @"会议id";
53 | self.conferenceTextField.keyboardType = UIKeyboardTypeNumberPad;
54 | [self.view addSubview:self.conferenceTextField];
55 |
56 | startHeight += 48;
57 | UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
58 | btn.frame = CGRectMake(100, startHeight, 120, 48);
59 | [btn setTitle:@"进入会议室" forState:UIControlStateNormal];
60 | btn.titleLabel.font = [UIFont systemFontOfSize:17];
61 | btn.tintColor = [UIColor blackColor];
62 | btn.backgroundColor = [UIColor redColor];
63 |
64 | [btn addTarget:self action:@selector(enterRoom:) forControlEvents:UIControlEventTouchUpInside];
65 | [self.view addSubview:btn];
66 |
67 |
68 | self.navigationController.navigationBarHidden = YES;
69 | }
70 |
71 |
72 | -(void)tapAction:(id)sender{
73 | [self.myTextField2 resignFirstResponder];
74 | [self.conferenceTextField resignFirstResponder];
75 | }
76 |
77 |
78 | - (void)enterRoom:(id)sender {
79 | [self.myTextField2 resignFirstResponder];
80 | [self.conferenceTextField resignFirstResponder];
81 |
82 | int64_t myUID = [self.myTextField2.text longLongValue];
83 | NSString *conferenceID = self.conferenceTextField.text;
84 | if (myUID == 0 || conferenceID.length == 0) {
85 | return;
86 | }
87 |
88 | MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:NO];
89 | hud.label.text = @"登录中...";
90 |
91 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
92 | NSString *token = [self login:myUID];
93 | NSLog(@"token:%@", token);
94 | dispatch_async(dispatch_get_main_queue(), ^{
95 | [hud hideAnimated:NO];
96 |
97 | if (token.length == 0) {
98 | return;
99 | }
100 |
101 | GroupVOIPViewController *controller = [[GroupVOIPViewController alloc] init];
102 | controller.currentUID = myUID;
103 | controller.channelID = conferenceID;
104 | controller.token = token;
105 | [self presentViewController:controller animated:YES completion:nil];
106 | });
107 | });
108 | }
109 |
110 |
111 | -(NSString*)login:(long long)uid {
112 | //调用app自身的登陆接口获取voip服务必须的access token
113 | //sandbox地址:"http://sandbox.demo.gobelieve.io/auth/token"
114 | NSString *url = @"http://demo.gobelieve.io/auth/token";
115 | NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]
116 | cachePolicy:NSURLRequestUseProtocolCachePolicy
117 | timeoutInterval:60];
118 |
119 |
120 | [urlRequest setHTTPMethod:@"POST"];
121 |
122 | NSDictionary *headers = [NSDictionary dictionaryWithObject:@"application/json" forKey:@"Content-Type"];
123 |
124 | [urlRequest setAllHTTPHeaderFields:headers];
125 |
126 |
127 | NSDictionary *obj = [NSDictionary dictionaryWithObject:[NSNumber numberWithLongLong:uid] forKey:@"uid"];
128 | NSData *postBody = [NSJSONSerialization dataWithJSONObject:obj options:0 error:nil];
129 |
130 | [urlRequest setHTTPBody:postBody];
131 |
132 | NSURLResponse *response = nil;
133 |
134 | NSError *error = nil;
135 |
136 | NSData *data = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:&error];
137 | if (error != nil) {
138 | NSLog(@"error:%@", error);
139 | return nil;
140 | }
141 | NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*)response;
142 | if (httpResp.statusCode != 200) {
143 | return nil;
144 | }
145 | NSDictionary *e = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
146 | return [e objectForKey:@"token"];
147 | }
148 |
149 |
150 |
151 |
152 | @end
153 |
--------------------------------------------------------------------------------
/ios/imsdk/imsdk/AsyncTCP.m:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2014-2015, GoBelieve
3 | All rights reserved.
4 |
5 | This source code is licensed under the BSD-style license found in the
6 | LICENSE file in the root directory of this source tree. An additional grant
7 | of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import "AsyncTCP.h"
11 | #import "util.h"
12 | #include
13 | #include
14 | #include
15 | @interface AsyncTCP()
16 | @property(nonatomic, strong)ConnectCB connect_cb;
17 | @property(nonatomic, strong)ReadCB read_cb;
18 | @property(nonatomic, strong)dispatch_source_t readSource;
19 | @property(nonatomic, strong)dispatch_source_t writeSource;
20 | @property(nonatomic)BOOL writeSourceActive;
21 | @property(nonatomic)BOOL readSourceActive;
22 | @property(nonatomic)int sock;
23 | @property(nonatomic)BOOL connecting;
24 | @property(nonatomic)NSMutableData *data;
25 | @end
26 |
27 | @implementation AsyncTCP
28 |
29 | -(id)init {
30 | self = [super init];
31 | if (self) {
32 | self.data = [NSMutableData data];
33 | self.sock = -1;
34 | }
35 | return self;
36 | }
37 |
38 | -(void)dealloc {
39 | NSLog(@"async tcp dealloc");
40 | self.readSource = nil;
41 | self.writeSource = nil;
42 | self.connect_cb = nil;
43 | self.read_cb = nil;
44 | }
45 |
46 | - (BOOL)synthesizeIPv6:(NSString*)host port:(int)port addr:(struct sockaddr*)addr addrinfo:(struct addrinfo*)info {
47 | int error;
48 | struct addrinfo hints, *res0, *res;
49 | const char *ipv4_str = [host UTF8String];
50 |
51 | memset(&hints, 0, sizeof(hints));
52 | hints.ai_family = PF_UNSPEC;
53 | hints.ai_socktype = SOCK_STREAM;
54 | hints.ai_flags = AI_DEFAULT;
55 | error = getaddrinfo(ipv4_str, "", &hints, &res0);
56 | if (error) {
57 | NSLog(@"%s", gai_strerror(error));
58 | return FALSE;
59 | }
60 |
61 | for (res = res0; res; res = res->ai_next) {
62 | NSLog(@"family:%d socktype;%d protocol:%d", res->ai_family, res->ai_socktype, res->ai_protocol);
63 | }
64 |
65 | BOOL r = YES;
66 | //use first
67 | if (res0) {
68 | if (res0->ai_family == AF_INET6) {
69 | struct sockaddr_in6 *addr6 = ((struct sockaddr_in6*)res0->ai_addr);
70 | addr6->sin6_port = htons(port);
71 |
72 | memcpy(addr, res0->ai_addr, res0->ai_addrlen);
73 | *info = *res0;
74 | } else if (res0->ai_family == AF_INET) {
75 | struct sockaddr_in *addr4 = ((struct sockaddr_in*)res0->ai_addr);
76 | addr4->sin_port = htons(port);
77 |
78 | memcpy(addr, res0->ai_addr, res0->ai_addrlen);
79 | *info = *res0;
80 | } else {
81 | r = NO;
82 | }
83 | }
84 |
85 | freeaddrinfo(res0);
86 | return r;
87 | }
88 |
89 | -(BOOL)connect:(NSString*)host port:(int)port cb:(ConnectCB)cb {
90 | struct sockaddr_in6 addr;
91 | struct addrinfo addrinfo;
92 |
93 | BOOL res = [self synthesizeIPv6:host port:port addr:(struct sockaddr*)&addr addrinfo:&addrinfo];
94 | if (!res) {
95 | NSLog(@"synthesize ipv6 fail");
96 | return NO;
97 | }
98 |
99 | int r;
100 | int sockfd;
101 |
102 | sockfd = socket(addrinfo.ai_family, addrinfo.ai_socktype, addrinfo.ai_protocol);
103 | sock_nonblock(sockfd, 1);
104 |
105 | int value = 1;
106 | setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(value));
107 |
108 | do {
109 | if (addrinfo.ai_family == AF_INET) {
110 | r = connect(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in));
111 | } else {
112 | //ipv6
113 | r = connect(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in6));
114 | }
115 | } while (r == -1 && errno == EINTR);
116 | if (r == -1) {
117 | if (errno != EINPROGRESS) {
118 | close(sockfd);
119 | NSLog(@"connect error:%s", strerror(errno));
120 | return FALSE;
121 | }
122 | }
123 |
124 | dispatch_queue_t queue = dispatch_get_main_queue();
125 | self.writeSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, sockfd, 0, queue);
126 | __weak AsyncTCP *wself = self;
127 | dispatch_source_set_event_handler(self.writeSource, ^{
128 | [wself onWrite];
129 | });
130 |
131 | dispatch_resume(self.writeSource);
132 | self.writeSourceActive = YES;
133 |
134 | self.connecting = YES;
135 | self.connect_cb = cb;
136 | self.sock = sockfd;
137 |
138 | return TRUE;
139 | }
140 |
141 | -(void)onWrite {
142 | if (self.connecting) {
143 | int error;
144 | socklen_t errorsize = sizeof(int);
145 | getsockopt(self.sock, SOL_SOCKET, SO_ERROR, &error, &errorsize);
146 | if (error == EINPROGRESS)
147 | return;
148 | self.connecting = NO;
149 | self.connect_cb(self, error);
150 | return;
151 | }
152 | const char *p = [self.data bytes];
153 | int n = write_data(self.sock, (uint8_t*)p, (int)self.data.length);
154 | if (n < 0) {
155 | NSLog(@"sock write error:%d", errno);
156 | dispatch_suspend(self.writeSource);
157 | self.writeSourceActive = NO;
158 | return;
159 | }
160 | self.data = [NSMutableData dataWithBytes:p+n length:self.data.length - n];
161 | if (self.data.length == 0) {
162 | dispatch_suspend(self.writeSource);
163 | self.writeSourceActive = NO;
164 | }
165 | return;
166 | }
167 |
168 | -(void)close {
169 | __block int count = 0;
170 |
171 | void (^on_cancel)() = ^{
172 | --count;
173 | if (count == 0) {
174 | NSLog(@"async tcp closed");
175 | }
176 | };
177 |
178 | if (self.writeSource) count++;
179 | if (self.readSource) count++;
180 | if (self.writeSource) {
181 | NSLog(@"cancel write source");
182 | if (!self.writeSourceActive) {
183 | dispatch_resume(self.writeSource);
184 | self.writeSourceActive = YES;
185 | }
186 | dispatch_source_set_cancel_handler(self.writeSource, on_cancel);
187 | dispatch_source_cancel(self.writeSource);
188 | }
189 |
190 | if (self.readSource) {
191 | NSLog(@"cancel read source");
192 | if (!self.readSourceActive) {
193 | dispatch_resume(self.readSource);
194 | self.readSourceActive = YES;
195 | }
196 | dispatch_source_set_cancel_handler(self.readSource, on_cancel);
197 | dispatch_source_cancel(self.readSource);
198 | }
199 |
200 | if (self.sock != -1) {
201 | NSLog(@"close socket");
202 | //because event handler and close both be called on main thread
203 | //here can safely close socket
204 | close(self.sock);
205 | self.sock = -1;
206 | }
207 | }
208 |
209 | -(void)write:(NSData*)data {
210 | [self.data appendData:data];
211 | if (!self.writeSourceActive && self.writeSource) {
212 | dispatch_resume(self.writeSource);
213 | self.writeSourceActive = YES;
214 | }
215 | }
216 |
217 | -(void)flush {
218 | if (self.data.length == 0) {
219 | return;
220 | }
221 | const char *p = [self.data bytes];
222 | int n = write_data(self.sock, (uint8_t*)p, (int)self.data.length);
223 | if (n < 0) {
224 | NSLog(@"sock write error:%d", errno);
225 | return;
226 | }
227 | self.data = [NSMutableData dataWithBytes:p+n length:self.data.length - n];
228 | }
229 |
230 | #define BUF_SIZE (64*1024)
231 | -(void)onRead {
232 | while (1) {
233 | ssize_t nread;
234 | char buf[BUF_SIZE];
235 |
236 | do {
237 | nread = read(self.sock, buf, BUF_SIZE);
238 | }while (nread < 0 && errno == EINTR);
239 |
240 | if (nread < 0) {
241 | if (errno == EAGAIN || errno == EWOULDBLOCK) {
242 | return;
243 | } else {
244 | self.read_cb(self, nil, errno);
245 | return;
246 | }
247 | } else if (nread == 0) {
248 | self.read_cb(self, nil, 0);
249 | return;
250 | } else {
251 | NSData *data = [NSData dataWithBytes:buf length:nread];
252 | self.read_cb(self, data, 0);
253 | if (nread < BUF_SIZE) {
254 | return;
255 | }
256 | }
257 | }
258 | }
259 | -(void)startRead:(ReadCB)cb {
260 | dispatch_queue_t queue = dispatch_get_main_queue();
261 | self.readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, self.sock, 0, queue);
262 | __weak AsyncTCP *wself = self;
263 | dispatch_source_set_event_handler(self.readSource, ^{
264 | [wself onRead];
265 | });
266 | dispatch_resume(self.readSource);
267 | self.readSourceActive = YES;
268 | self.read_cb = cb;
269 | }
270 |
271 | @end
272 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/beetle/conference/GroupVOIPActivity.java:
--------------------------------------------------------------------------------
1 | package com.beetle.conference;
2 |
3 | import android.app.Activity;
4 | import android.app.Application;
5 | import android.content.BroadcastReceiver;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.content.IntentFilter;
9 | import android.media.AudioManager;
10 | import android.os.Handler;
11 | import android.os.Bundle;
12 | import android.text.TextUtils;
13 | import android.util.Log;
14 | import android.view.KeyEvent;
15 | import android.view.Window;
16 | import android.view.WindowManager;
17 |
18 | import com.facebook.react.ReactInstanceManager;
19 | import com.facebook.react.ReactNativeHost;
20 | import com.facebook.react.ReactPackage;
21 | import com.facebook.react.ReactRootView;
22 | import com.facebook.react.bridge.NativeModule;
23 | import com.facebook.react.bridge.ReactApplicationContext;
24 | import com.facebook.react.bridge.ReactContext;
25 | import com.facebook.react.bridge.ReactContextBaseJavaModule;
26 | import com.facebook.react.bridge.ReactMethod;
27 | import com.facebook.react.bridge.WritableMap;
28 | import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
29 | import com.facebook.react.modules.core.RCTNativeAppEventEmitter;
30 | import com.facebook.react.shell.MainReactPackage;
31 | import com.facebook.react.uimanager.ViewManager;
32 | import com.oney.WebRTCModule.WebRTCModulePackage;
33 |
34 | import java.util.ArrayList;
35 | import java.util.Collections;
36 | import java.util.List;
37 |
38 | public class GroupVOIPActivity extends Activity implements DefaultHardwareBackBtnHandler {
39 | private final String TAG = "face";
40 |
41 |
42 | public static long activityCount = 0;
43 |
44 | private ReactRootView mReactRootView;
45 | private ReactInstanceManager mReactInstanceManager;
46 | private MusicIntentReceiver headsetReceiver;
47 |
48 | private long currentUID;
49 | private String channelID;
50 | private String token;
51 |
52 | private Handler mainHandler;
53 |
54 | ReactNativeHost host;
55 |
56 | public class GroupVOIPModule extends ReactContextBaseJavaModule {
57 | public GroupVOIPModule(ReactApplicationContext reactContext) {
58 | super(reactContext);
59 | }
60 |
61 | @Override
62 | public String getName() {
63 | return "GroupVOIPActivity";
64 | }
65 |
66 |
67 |
68 | @ReactMethod
69 | public void dismiss() {
70 | Runnable r = new Runnable() {
71 | @Override
72 | public void run() {
73 | GroupVOIPActivity.this.finish();
74 | }
75 | };
76 | mainHandler.post(r);
77 | }
78 |
79 |
80 | }
81 |
82 | class ConferencePackage implements ReactPackage {
83 |
84 |
85 | @Override
86 | public List createViewManagers(ReactApplicationContext reactContext) {
87 | return Collections.emptyList();
88 | }
89 |
90 | @Override
91 | public List createNativeModules(
92 | ReactApplicationContext reactContext) {
93 | List modules = new ArrayList();
94 |
95 | modules.add(new GroupVOIPModule(reactContext));
96 |
97 | return modules;
98 | }
99 | }
100 |
101 | private class ReactNativeHostImpl extends ReactNativeHost {
102 |
103 | public ReactNativeHostImpl(Application app) {
104 | super(app);
105 | }
106 |
107 | @Override
108 | public boolean getUseDeveloperSupport() {
109 | return BuildConfig.DEBUG;
110 | }
111 |
112 | @Override
113 | protected List getPackages() {
114 | List list = new ArrayList<>();
115 | list.add(new MainReactPackage());
116 | list.add(new ConferencePackage());
117 | list.add(new WebRTCModulePackage());
118 | return list;
119 | }
120 | }
121 |
122 | @Override
123 | protected void onCreate(Bundle savedInstanceState) {
124 |
125 | requestWindowFeature(Window.FEATURE_NO_TITLE);
126 |
127 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
128 | WindowManager.LayoutParams.FLAG_FULLSCREEN);
129 |
130 | getWindow().addFlags( WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
131 | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD |
132 | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
133 | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
134 |
135 | super.onCreate(savedInstanceState);
136 | activityCount++;
137 |
138 | Intent intent = getIntent();
139 |
140 | currentUID = intent.getLongExtra("current_uid", 0);
141 | if (currentUID == 0) {
142 | Log.i(TAG, "current uid is 0");
143 | finish();
144 | return;
145 | }
146 |
147 | channelID = intent.getStringExtra("channel_id");
148 | if (TextUtils.isEmpty(channelID)) {
149 | Log.i(TAG, "channel id is empty");
150 | finish();
151 | return;
152 | }
153 | Log.i(TAG, "channel id:" + channelID);
154 |
155 | token = intent.getStringExtra("token");
156 | if (TextUtils.isEmpty(token)) {
157 | Log.i(TAG, "token is empty");
158 | finish();
159 | return;
160 | }
161 |
162 |
163 | mReactRootView = new ReactRootView(this);
164 |
165 | host = new ReactNativeHostImpl(getApplication());
166 | mReactInstanceManager = host.getReactInstanceManager();
167 |
168 | Bundle props = new Bundle();
169 | props.putString("room", channelID);
170 | props.putString("name", ""+currentUID);
171 | props.putString("token", token);
172 |
173 | mReactRootView.startReactApplication(mReactInstanceManager, "GroupCall", props);
174 | setContentView(mReactRootView);
175 |
176 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
177 |
178 | headsetReceiver = new MusicIntentReceiver();
179 | mainHandler = new Handler(getMainLooper());
180 | }
181 |
182 | @Override
183 | public void invokeDefaultOnBackPressed() {
184 | super.onBackPressed();
185 | }
186 |
187 |
188 | @Override
189 | protected void onPause() {
190 | super.onPause();
191 | unregisterReceiver(headsetReceiver);
192 | if (mReactInstanceManager != null) {
193 | mReactInstanceManager.onHostPause();
194 | }
195 | }
196 |
197 | @Override
198 | protected void onResume() {
199 | super.onResume();
200 |
201 | IntentFilter filter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
202 | registerReceiver(headsetReceiver, filter);
203 |
204 | if (mReactInstanceManager != null) {
205 | mReactInstanceManager.onHostResume(this, this);
206 | }
207 | }
208 |
209 | @Override
210 | protected void onDestroy() {
211 | super.onDestroy();
212 | activityCount--;
213 | getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
214 | if (mReactInstanceManager != null) {
215 | mReactInstanceManager.onHostDestroy(this);
216 | }
217 |
218 | }
219 |
220 |
221 | @Override
222 | public void onBackPressed() {
223 | if (mReactInstanceManager != null) {
224 | mReactInstanceManager.onBackPressed();
225 | } else {
226 | super.onBackPressed();
227 | }
228 | }
229 |
230 |
231 | @Override
232 | public boolean onKeyUp(int keyCode, KeyEvent event) {
233 | if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
234 | mReactInstanceManager.showDevOptionsDialog();
235 | return true;
236 | }
237 | return super.onKeyUp(keyCode, event);
238 | }
239 |
240 |
241 | private void sendEvent(ReactContext reactContext, String eventName, WritableMap params) {
242 | reactContext.getJSModule(RCTNativeAppEventEmitter.class).emit(eventName, params);
243 | }
244 |
245 |
246 |
247 | private class MusicIntentReceiver extends BroadcastReceiver {
248 | @Override
249 | public void onReceive(Context context, Intent intent) {
250 | if (intent.getAction().equals(Intent.ACTION_HEADSET_PLUG)) {
251 | AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
252 | int state = intent.getIntExtra("state", -1);
253 | switch (state) {
254 | case 0:
255 | Log.d(TAG, "Headset is unplugged");
256 | audioManager.setSpeakerphoneOn(true);
257 | break;
258 | case 1:
259 | Log.d(TAG, "Headset is plugged");
260 | audioManager.setSpeakerphoneOn(false);
261 | break;
262 | default:
263 | Log.d(TAG, "I have no idea what the headset state is");
264 | }
265 | }
266 | }
267 | }
268 | }
269 |
--------------------------------------------------------------------------------
/ios/imsdk/imsdk/Message.m:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2014-2015, GoBelieve
3 | All rights reserved.
4 |
5 | This source code is licensed under the BSD-style license found in the
6 | LICENSE file in the root directory of this source tree. An additional grant
7 | of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import "Message.h"
11 | #import "util.h"
12 |
13 | #define HEAD_SIZE 8
14 | #define VERSION 1
15 |
16 | @implementation IMMessage
17 |
18 | @end
19 |
20 | @implementation CustomerMessage
21 |
22 | @end
23 |
24 | @implementation RoomMessage
25 |
26 | @end
27 |
28 | @implementation MessageInputing
29 |
30 | @end
31 |
32 | @implementation AuthenticationToken
33 |
34 | @end
35 |
36 |
37 | @implementation GroupSyncKey
38 |
39 | @end
40 |
41 | @implementation VOIPControl
42 |
43 | @end
44 |
45 | @implementation Message
46 | -(NSData*)pack {
47 | char buf[64*1024] = {0};
48 | char *p = buf;
49 |
50 | writeInt32(self.seq, p);
51 | p += 4;
52 | *p++ = (uint8_t)self.cmd;
53 | *p++ = (uint8_t)VERSION;
54 | p += 2;
55 |
56 | if (self.cmd == MSG_HEARTBEAT || self.cmd == MSG_PING) {
57 | return [NSData dataWithBytes:buf length:HEAD_SIZE];
58 | } else if (self.cmd == MSG_AUTH_TOKEN) {
59 | AuthenticationToken *auth = (AuthenticationToken*)self.body;
60 | *p++ = auth.platformID;
61 | const char *t;
62 | t = [auth.token UTF8String];
63 | *p++ = strlen(t);
64 | memcpy(p, t, strlen(t));
65 | p += strlen(t);
66 | t = [auth.deviceID UTF8String];
67 | *p++ = strlen(t);
68 | memcpy(p, t, strlen(t));
69 | p += strlen(t);
70 | return [NSData dataWithBytes:buf length:(p-buf)];
71 | } else if (self.cmd == MSG_IM || self.cmd == MSG_GROUP_IM) {
72 | IMMessage *m = (IMMessage*)self.body;
73 | writeInt64(m.sender, p);
74 | p += 8;
75 | writeInt64(m.receiver, p);
76 | p += 8;
77 | writeInt32(m.timestamp, p);
78 | p += 4;
79 | writeInt32(m.msgLocalID, p);
80 | p += 4;
81 | const char *s = [m.content UTF8String];
82 | size_t l = strlen(s);
83 | if ((l + 36) > 64*1024) {
84 | return nil;
85 | }
86 | memcpy(p, s, l);
87 | return [NSData dataWithBytes:buf length:HEAD_SIZE + 24 +l];
88 | } else if (self.cmd == MSG_CUSTOMER || self.cmd == MSG_CUSTOMER_SUPPORT) {
89 | CustomerMessage *m = (CustomerMessage*)self.body;
90 | writeInt64(m.customerAppID, p);
91 | p += 8;
92 | writeInt64(m.customerID, p);
93 | p += 8;
94 | writeInt64(m.storeID, p);
95 | p += 8;
96 | writeInt64(m.sellerID, p);
97 | p += 8;
98 | writeInt32(m.timestamp, p);
99 | p += 4;
100 | const char *s = [m.content UTF8String];
101 | size_t l = strlen(s);
102 | if ((l + 36) >= 32*1024) {
103 | return nil;
104 | }
105 | memcpy(p, s, l);
106 | return [NSData dataWithBytes:buf length:HEAD_SIZE + 36 + l];
107 | } else if (self.cmd == MSG_ACK) {
108 | writeInt32([(NSNumber*)self.body intValue], p);
109 | return [NSData dataWithBytes:buf length:HEAD_SIZE+4];
110 | } else if (self.cmd == MSG_INPUTING) {
111 | MessageInputing *inputing = (MessageInputing*)self.body;
112 | writeInt64(inputing.sender, p);
113 | p += 8;
114 | writeInt64(inputing.receiver, p);
115 | return [NSData dataWithBytes:buf length:HEAD_SIZE + 16];
116 | } else if (self.cmd == MSG_ENTER_ROOM || self.cmd == MSG_LEAVE_ROOM) {
117 | NSNumber *n = (NSNumber*)self.body;
118 | int64_t roomID = [n longLongValue];
119 | writeInt64(roomID, p);
120 | p += 8;
121 | return [NSData dataWithBytes:buf length:HEAD_SIZE + 8];
122 | } else if (self.cmd == MSG_ROOM_IM || self.cmd == MSG_RT) {
123 | RoomMessage *rm = (RoomMessage*)self.body;
124 | writeInt64(rm.sender, p);
125 | p += 8;
126 | writeInt64(rm.receiver, p);
127 | p += 8;
128 | const char *s = [rm.content UTF8String];
129 | size_t l = strlen(s);
130 | if ((l + 28) > 64*1024) {
131 | return nil;
132 | }
133 | memcpy(p, s, l);
134 | return [NSData dataWithBytes:buf length:HEAD_SIZE + 16 +l];
135 | } else if (self.cmd == MSG_UNREAD_COUNT) {
136 | NSNumber *u = (NSNumber*)self.body;
137 | writeInt32([u intValue], p);
138 | p += 4;
139 | return [NSData dataWithBytes:buf length:HEAD_SIZE + 4];
140 | } else if (self.cmd == MSG_VOIP_CONTROL) {
141 | VOIPControl *ctl = (VOIPControl*)self.body;
142 | writeInt64(ctl.sender, p);
143 | p += 8;
144 | writeInt64(ctl.receiver, p);
145 | p += 8;
146 | if (ctl.content.length > 0) {
147 | [ctl.content getBytes:p length:ctl.content.length];
148 | p += ctl.content.length;
149 | }
150 | return [NSData dataWithBytes:buf length:HEAD_SIZE + 16 + ctl.content.length];
151 | } else if (self.cmd == MSG_SYNC || self.cmd == MSG_SYNC_KEY) {
152 | NSNumber *u = (NSNumber*)self.body;
153 | writeInt64([u longLongValue], p);
154 | p += 8;
155 | return [NSData dataWithBytes:buf length:HEAD_SIZE + 8];
156 | } else if (self.cmd == MSG_SYNC_GROUP || self.cmd == MSG_GROUP_SYNC_KEY) {
157 | GroupSyncKey *s = (GroupSyncKey*)self.body;
158 | writeInt64(s.groupID, p);
159 | p += 8;
160 | writeInt64(s.syncKey, p);
161 | p += 8;
162 | return [NSData dataWithBytes:buf length:HEAD_SIZE + 16];
163 | }
164 | return nil;
165 | }
166 |
167 | -(BOOL)unpack:(NSData*)data {
168 | const char *p = [data bytes];
169 | self.seq = readInt32(p);
170 | p += 4;
171 | self.cmd = *p;
172 | p += 4;
173 | NSLog(@"seq:%d cmd:%d", self.seq, self.cmd);
174 | if (self.cmd == MSG_PONG) {
175 | return YES;
176 | } else if (self.cmd == MSG_AUTH_STATUS) {
177 | int status = readInt32(p);
178 | self.body = [NSNumber numberWithInt:status];
179 | return YES;
180 | } else if (self.cmd == MSG_IM || self.cmd == MSG_GROUP_IM) {
181 | IMMessage *m = [[IMMessage alloc] init];
182 | m.sender = readInt64(p);
183 | p += 8;
184 | m.receiver = readInt64(p);
185 | p += 8;
186 | m.timestamp = readInt32(p);
187 | p += 4;
188 | m.msgLocalID = readInt32(p);
189 | p += 4;
190 | m.content = [[NSString alloc] initWithBytes:p length:data.length-32 encoding:NSUTF8StringEncoding];
191 | self.body = m;
192 | return YES;
193 | } else if (self.cmd == MSG_CUSTOMER || self.cmd == MSG_CUSTOMER_SUPPORT) {
194 | CustomerMessage *m = [[CustomerMessage alloc] init];
195 | m.customerAppID = readInt64(p);
196 | p += 8;
197 | m.customerID = readInt64(p);
198 | p += 8;
199 | m.storeID = readInt64(p);
200 | p += 8;
201 | m.sellerID = readInt64(p);
202 | p += 8;
203 | m.timestamp = readInt32(p);
204 | p += 4;
205 | m.content = [[NSString alloc] initWithBytes:p length:data.length- HEAD_SIZE - 36 encoding:NSUTF8StringEncoding];
206 | self.body = m;
207 | return YES;
208 | } else if (self.cmd == MSG_ACK) {
209 | int seq = readInt32(p);
210 | self.body = [NSNumber numberWithInt:seq];
211 | return YES;
212 | } else if (self.cmd == MSG_INPUTING) {
213 | MessageInputing *inputing = [[MessageInputing alloc] init];
214 | inputing.sender = readInt64(p);
215 | p += 8;
216 | inputing.receiver = readInt64(p);
217 | p += 8;
218 | self.body = inputing;
219 | return YES;
220 | } else if (self.cmd == MSG_GROUP_NOTIFICATION) {
221 | self.body = [[NSString alloc] initWithBytes:p length:data.length-HEAD_SIZE encoding:NSUTF8StringEncoding];
222 | return YES;
223 | } else if (self.cmd == MSG_ROOM_IM || self.cmd == MSG_RT) {
224 | RoomMessage *rm = [[RoomMessage alloc] init];
225 | rm.sender = readInt64(p);
226 | p += 8;
227 | rm.receiver = readInt64(p);
228 | p += 8;
229 | rm.content = [[NSString alloc] initWithBytes:p length:data.length-24 encoding:NSUTF8StringEncoding];
230 | self.body = rm;
231 | return YES;
232 | } else if (self.cmd == MSG_SYSTEM) {
233 | self.body = [[NSString alloc] initWithBytes:p length:data.length-HEAD_SIZE encoding:NSUTF8StringEncoding];
234 | return YES;
235 | } else if (self.cmd == MSG_VOIP_CONTROL) {
236 | VOIPControl *ctl = [[VOIPControl alloc] init];
237 | ctl.sender = readInt64(p);
238 | p += 8;
239 | ctl.receiver = readInt64(p);
240 | p += 8;
241 | ctl.content = [NSData dataWithBytes:p length:data.length - 24];
242 | self.body = ctl;
243 | return YES;
244 | } else if (self.cmd == MSG_SYNC_BEGIN ||
245 | self.cmd == MSG_SYNC_END ||
246 | self.cmd == MSG_SYNC_NOTIFY) {
247 | int64_t k = readInt64(p);
248 | p += 8;
249 | self.body = [NSNumber numberWithLongLong:k];
250 | return YES;
251 | } else if (self.cmd == MSG_SYNC_GROUP_BEGIN ||
252 | self.cmd == MSG_SYNC_GROUP_END ||
253 | self.cmd == MSG_SYNC_GROUP_NOTIFY) {
254 | GroupSyncKey *groupSyncKey = [[GroupSyncKey alloc] init];
255 | groupSyncKey.groupID = readInt64(p);
256 | p += 8;
257 | groupSyncKey.syncKey = readInt64(p);
258 | p += 8;
259 | self.body = groupSyncKey;
260 | return YES;
261 | } else {
262 | self.body = [NSData dataWithBytes:p length:data.length-8];
263 | return YES;
264 | }
265 | }
266 |
267 | @end
268 |
--------------------------------------------------------------------------------
/ios/imsdk/imsdk/TCPConnection.m:
--------------------------------------------------------------------------------
1 | //
2 | // TCPConnection.m
3 | // podcasting
4 | //
5 | // Created by houxh on 15/6/25.
6 | // Copyright (c) 2015年 beetle. All rights reserved.
7 | //
8 |
9 | #import "TCPConnection.h"
10 | #include
11 | #include
12 | #include
13 | #import "AsyncTCP.h"
14 | #import "util.h"
15 | #import "GOReachability.h"
16 |
17 | @interface TCPConnection()
18 |
19 | @property(atomic, copy) NSString *hostIP;
20 | @property(atomic, assign) time_t timestmap;
21 |
22 | @property(nonatomic, assign)BOOL stopped;
23 | @property(nonatomic, assign)BOOL suspended;
24 | @property(nonatomic, assign)BOOL isBackground;
25 |
26 |
27 | @property(nonatomic, strong)dispatch_source_t connectTimer;
28 |
29 | @property(nonatomic, strong)dispatch_source_t heartbeatTimer;
30 | @property(nonatomic)time_t pingTimestamp;
31 |
32 |
33 | @property(nonatomic)int connectFailCount;
34 |
35 | @property(nonatomic)NSMutableArray *connectionObservers;
36 |
37 | @property(nonatomic)GOReachability *reach;
38 | @property(nonatomic)BOOL reachable;
39 | @end
40 |
41 |
42 | @implementation TCPConnection
43 | -(id)init {
44 | self = [super init];
45 | if (self) {
46 | dispatch_queue_t queue = dispatch_get_main_queue();
47 | self.connectTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,queue);
48 | dispatch_source_set_event_handler(self.connectTimer, ^{
49 | [self connect];
50 | });
51 |
52 | self.heartbeatTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,queue);
53 | dispatch_source_set_event_handler(self.heartbeatTimer, ^{
54 | [self ping];
55 | });
56 | self.connectionObservers = [NSMutableArray array];
57 |
58 | self.connectState = STATE_UNCONNECTED;
59 | self.stopped = YES;
60 | self.suspended = YES;
61 | self.reachable = YES;
62 | self.isBackground = NO;
63 | }
64 | return self;
65 | }
66 |
67 | -(void)startRechabilityNotifier {
68 | TCPConnection *wself = self;
69 | self.reach = [GOReachability reachabilityForInternetConnection];
70 |
71 | self.reach.reachableBlock = ^(GOReachability*reach) {
72 | dispatch_async(dispatch_get_main_queue(), ^{
73 | NSLog(@"internet reachable");
74 | wself.reachable = YES;
75 | if (wself != nil && !wself.stopped && !wself.isBackground) {
76 | NSLog(@"reconnect im service");
77 | [wself suspend];
78 | [wself resume];
79 | }
80 | });
81 | };
82 |
83 | self.reach.unreachableBlock = ^(GOReachability*reach) {
84 | dispatch_async(dispatch_get_main_queue(), ^{
85 | NSLog(@"internet unreachable");
86 | wself.reachable = NO;
87 | if (wself != nil && !wself.stopped) {
88 | [wself suspend];
89 | }
90 | });
91 | };
92 |
93 | [self.reach startNotifier];
94 | }
95 |
96 | -(void)enterForeground {
97 | NSLog(@"im service enter foreground");
98 | self.isBackground = NO;
99 | if (!self.stopped && self.reachable) {
100 | [self resume];
101 | }
102 | }
103 |
104 | -(void)enterBackground {
105 | NSLog(@"im service enter background");
106 | self.isBackground = YES;
107 | if (!self.stopped) {
108 | [self suspend];
109 | }
110 | }
111 |
112 | -(void)start {
113 | if (!self.host || !self.port) {
114 | NSLog(@"should init im server host and port");
115 | exit(1);
116 | }
117 | if (!self.stopped) {
118 | return;
119 | }
120 | NSLog(@"start im service");
121 | self.stopped = NO;
122 | if (self.reachable) {
123 | [self resume];
124 | }
125 | }
126 |
127 | -(void)stop {
128 | if (self.stopped) {
129 | return;
130 | }
131 | NSLog(@"stop im service");
132 | self.stopped = YES;
133 |
134 | [self suspend];
135 | }
136 |
137 | -(void)suspend {
138 | if (self.suspended) {
139 | return;
140 | }
141 |
142 | NSLog(@"suspend im service");
143 | self.suspended = YES;
144 |
145 | dispatch_suspend(self.connectTimer);
146 | dispatch_suspend(self.heartbeatTimer);
147 |
148 | [self onClose];
149 | self.connectState = STATE_UNCONNECTED;
150 | [self publishConnectState:STATE_UNCONNECTED];
151 | [self close];
152 | }
153 |
154 | -(void)resume {
155 | if (!self.suspended) {
156 | return;
157 | }
158 | NSLog(@"resume im service");
159 | self.suspended = NO;
160 |
161 | dispatch_time_t w = dispatch_walltime(NULL, 0);
162 | dispatch_source_set_timer(self.connectTimer, w, DISPATCH_TIME_FOREVER, 0);
163 | dispatch_resume(self.connectTimer);
164 |
165 | w = dispatch_walltime(NULL, self.heartbeatHZ);
166 | dispatch_source_set_timer(self.heartbeatTimer, w, self.heartbeatHZ*NSEC_PER_SEC, self.heartbeatHZ*NSEC_PER_SEC/2);
167 | dispatch_resume(self.heartbeatTimer);
168 |
169 | [self refreshHostIP];
170 | }
171 |
172 | //2s后重新连接
173 | -(void)reconnect2S {
174 | self.connectFailCount = 2;
175 | [self close];
176 | [self startConnectTimer];
177 | self.connectState = STATE_UNCONNECTED;
178 | [self publishConnectState:STATE_UNCONNECTED];
179 | }
180 |
181 | -(void)close {
182 | if (self.tcp) {
183 | NSLog(@"im service on close");
184 | [self.tcp flush];
185 | [self.tcp close];
186 | self.tcp = nil;
187 | }
188 | }
189 |
190 | -(void)startConnectTimer {
191 | if (self.stopped || self.suspended || self.isBackground) {
192 | return;
193 | }
194 | //重连
195 | int64_t t = 0;
196 | if (self.connectFailCount > 60) {
197 | t = 60ull*NSEC_PER_SEC;
198 | } else {
199 | t = self.connectFailCount*NSEC_PER_SEC;
200 | }
201 |
202 | dispatch_time_t w = dispatch_walltime(NULL, t);
203 | dispatch_source_set_timer(self.connectTimer, w, DISPATCH_TIME_FOREVER, 0);
204 |
205 | NSLog(@"start connect timer:%lld", t/NSEC_PER_SEC);
206 | }
207 |
208 | -(void)handleClose {
209 | [self onClose];
210 | self.connectState = STATE_UNCONNECTED;
211 | [self publishConnectState:STATE_UNCONNECTED];
212 |
213 | [self close];
214 | [self startConnectTimer];
215 | }
216 |
217 |
218 | -(BOOL)handleData:(NSData*)data {
219 | NSAssert(NO, @"not implmented");
220 | return NO;
221 | }
222 |
223 | -(void)onRead:(NSData*)data error:(int)err {
224 | if (err) {
225 | NSLog(@"tcp read err");
226 | [self handleClose];
227 | return;
228 | } else if (!data) {
229 | NSLog(@"tcp closed");
230 | [self handleClose];
231 | return;
232 | } else {
233 | self.pingTimestamp = 0;
234 | BOOL r = [self handleData:data];
235 | if (!r) {
236 | [self handleClose];
237 | }
238 | }
239 | }
240 |
241 | -(NSString*)IP2String:(struct in_addr)addr {
242 | char buf[64] = {0};
243 | const char *p = inet_ntop(AF_INET, &addr, buf, 64);
244 | if (p) {
245 | return [NSString stringWithUTF8String:p];
246 | }
247 | return nil;
248 |
249 | }
250 |
251 | -(NSString*)resolveIP:(NSString*)host {
252 | struct addrinfo hints;
253 | struct addrinfo *result, *rp;
254 | int s;
255 |
256 | char buf[32];
257 | snprintf(buf, 32, "%d", 0);
258 |
259 | memset(&hints, 0, sizeof(struct addrinfo));
260 | hints.ai_family = AF_INET;
261 | hints.ai_socktype = SOCK_STREAM;
262 | hints.ai_protocol = IPPROTO_TCP;
263 | hints.ai_flags = 0;
264 |
265 | s = getaddrinfo([host UTF8String], buf, &hints, &result);
266 | if (s != 0) {
267 | NSLog(@"get addr info error:%s", gai_strerror(s));
268 | return nil;
269 | }
270 | NSString *ip = nil;
271 | rp = result;
272 | if (rp != NULL) {
273 | struct sockaddr_in *addr = (struct sockaddr_in*)rp->ai_addr;
274 | ip = [self IP2String:addr->sin_addr];
275 | }
276 | freeaddrinfo(result);
277 | return ip;
278 | }
279 |
280 | -(void)refreshHostIP {
281 |
282 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
283 | NSLog(@"refresh host ip...");
284 | NSString *ip = [self resolveIP:self.host];
285 | NSLog(@"host:%@ ip:%@", self.host, ip);
286 | if ([ip length] > 0) {
287 | self.hostIP = ip;
288 | self.timestmap = time(NULL);
289 | }
290 | });
291 | }
292 |
293 | -(void)connect {
294 | if (self.tcp) {
295 | return;
296 | }
297 | if (self.stopped) {
298 | NSLog(@"opps......");
299 | return;
300 | }
301 |
302 | if (self.hostIP.length == 0) {
303 | [self refreshHostIP];
304 | self.connectFailCount = self.connectFailCount + 1;
305 | [self startConnectTimer];
306 | return;
307 | }
308 | time_t now = time(NULL);
309 | if (now - self.timestmap > 5*60) {
310 | [self refreshHostIP];
311 | }
312 |
313 | self.pingTimestamp = 0;
314 | self.connectState = STATE_CONNECTING;
315 | [self publishConnectState:STATE_CONNECTING];
316 | self.tcp = [[AsyncTCP alloc] init];
317 | __weak TCPConnection *wself = self;
318 | BOOL r = [self.tcp connect:self.hostIP port:self.port cb:^(AsyncTCP *tcp, int err) {
319 | if (err) {
320 | NSLog(@"tcp connect err");
321 | wself.connectFailCount = wself.connectFailCount + 1;
322 | [wself close];
323 | self.connectState = STATE_CONNECTFAIL;
324 | [self publishConnectState:STATE_CONNECTFAIL];
325 | [self startConnectTimer];
326 | return;
327 | } else {
328 | NSLog(@"tcp connected");
329 | wself.connectFailCount = 0;
330 | self.connectState = STATE_CONNECTED;
331 | [self publishConnectState:STATE_CONNECTED];
332 | [wself.tcp startRead:^(AsyncTCP *tcp, NSData *data, int err) {
333 | [wself onRead:data error:err];
334 | }];
335 | [self onConnect];
336 | }
337 | }];
338 | if (!r) {
339 | NSLog(@"tcp connect err");
340 | wself.connectFailCount = wself.connectFailCount + 1;
341 | self.connectState = STATE_CONNECTFAIL;
342 | [self publishConnectState:STATE_CONNECTFAIL];
343 |
344 | self.tcp = nil;
345 | [self startConnectTimer];
346 | }
347 | }
348 |
349 | -(void)pong {
350 | self.pingTimestamp = 0;
351 | }
352 |
353 | -(void)sendPing {
354 | NSAssert(NO, @"not implemented");
355 | }
356 |
357 | -(void)ping {
358 | if (self.tcp != nil && self.pingTimestamp == 0) {
359 | NSLog(@"send ping");
360 | [self sendPing];
361 |
362 | self.pingTimestamp = time(NULL);
363 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
364 | time_t now = time(NULL);
365 | if (self.pingTimestamp > 0 && now - self.pingTimestamp >= 3) {
366 | NSLog(@"ping timeout");
367 | [self handleClose];
368 | }
369 | });
370 | }
371 | }
372 |
373 | -(void)onConnect {
374 |
375 | }
376 | -(void)onClose {
377 |
378 | }
379 |
380 | -(void)addConnectionObserver:(id)ob {
381 | [self.connectionObservers addObject:ob];
382 | }
383 | -(void)removeConnectionObserver:(id)ob {
384 | [self.connectionObservers removeObject:ob];
385 | }
386 |
387 |
388 | -(void)publishConnectState:(int)state {
389 | for (id ob in self.connectionObservers) {
390 | if ([ob respondsToSelector:@selector(onConnectState:)]) {
391 | [ob onConnectState:state];
392 | }
393 | }
394 | }
395 |
396 | @end
397 |
--------------------------------------------------------------------------------
/react/RTCPeerConnection.js:
--------------------------------------------------------------------------------
1 | import { NativeModules } from 'react-native';
2 | import { RTCPeerConnection, RTCSessionDescription } from 'react-native-webrtc';
3 |
4 | // XXX At the time of this writing extending RTCPeerConnection using ES6 'class'
5 | // and 'extends' causes a runtime error related to the attempt to define the
6 | // onaddstream property setter. The error mentions that babelHelpers.set is
7 | // undefined which appears to be a thing inside React Native's packager. As a
8 | // workaround, extend using the pre-ES6 way.
9 |
10 | /**
11 | * The RTCPeerConnection provided by react-native-webrtc fires onaddstream
12 | * before it remembers remotedescription (and thus makes it available to API
13 | * clients). Because that appears to be a problem for lib-jitsi-meet which has
14 | * been successfully running on Chrome, Firefox, Temasys, etc. for a very long
15 | * time, attempt to meets its expectations (by extending RTCPPeerConnection).
16 | *
17 | * @class
18 | */
19 | export default function _RTCPeerConnection(...args) {
20 |
21 | /* eslint-disable no-invalid-this */
22 |
23 | RTCPeerConnection.apply(this, args);
24 |
25 | this.onaddstream = (...args) => // eslint-disable-line no-shadow
26 | (this._onaddstreamQueue
27 | ? this._queueOnaddstream
28 | : this._invokeOnaddstream)
29 | .apply(this, args);
30 |
31 | // Shadow RTCPeerConnection's onaddstream but after _RTCPeerConnection has
32 | // assigned to the property in question. Defining the property on
33 | // _RTCPeerConnection's prototype may (or may not, I don't know) work but I
34 | // don't want to try because the following approach appears to work and I
35 | // understand it.
36 | Object.defineProperty(this, 'onaddstream', {
37 | configurable: true,
38 | enumerable: true,
39 | get() {
40 | return this._onaddstream;
41 | },
42 | set(value) {
43 | this._onaddstream = value;
44 | }
45 | });
46 |
47 | this._localStreams = [];
48 |
49 | /* eslint-enable no-invalid-this */
50 | }
51 |
52 | _RTCPeerConnection.prototype = Object.create(RTCPeerConnection.prototype);
53 | _RTCPeerConnection.prototype.constructor = _RTCPeerConnection;
54 |
55 | _RTCPeerConnection.prototype._invokeOnaddstream = function(...args) {
56 | const onaddstream = this._onaddstream;
57 |
58 | return onaddstream && onaddstream.apply(this, args);
59 | };
60 |
61 | _RTCPeerConnection.prototype._invokeQueuedOnaddstream = function(q) {
62 | q && q.forEach(args => {
63 | try {
64 | this._invokeOnaddstream(...args);
65 | } catch (e) {
66 | // TODO Determine whether the combination of the standard
67 | // setRemoteDescription and onaddstream results in a similar
68 | // swallowing of errors.
69 | _LOGE(e);
70 | }
71 | });
72 | };
73 |
74 | _RTCPeerConnection.prototype._queueOnaddstream = function(...args) {
75 | this._onaddstreamQueue.push(Array.from(args));
76 | };
77 |
78 | _RTCPeerConnection.prototype.setRemoteDescription = function(
79 | sessionDescription,
80 | successCallback,
81 | errorCallback) {
82 | // If the deprecated callback-based version is used, translate it to the
83 | // Promise-based version.
84 |
85 | console.log("set remote description...");
86 |
87 | if (typeof successCallback !== 'undefined'
88 | || typeof errorCallback !== 'undefined') {
89 | // XXX Returning a Promise is not necessary. But I don't see why it'd
90 | // hurt (much).
91 | return (
92 | _RTCPeerConnection.prototype.setRemoteDescription.call(
93 | this,
94 | sessionDescription)
95 | .then(successCallback, errorCallback));
96 | }
97 |
98 | return (
99 | _synthesizeIPv6Addresses(sessionDescription)
100 | .catch(reason => {
101 | reason && _LOGE(reason);
102 |
103 | return sessionDescription;
104 | })
105 | .then(value => _setRemoteDescription.bind(this)(value)));
106 |
107 | };
108 |
109 | _RTCPeerConnection.prototype.addStream = function(stream) {
110 | RTCPeerConnection.prototype.addStream.call(this, stream);
111 |
112 | this._localStreams.push(stream);
113 | }
114 |
115 |
116 | _RTCPeerConnection.prototype.removeStream = function(stream) {
117 | RTCPeerConnection.prototype.removeStream.call(this, stream);
118 |
119 | var index = this._localStreams.indexOf(stream);
120 | if (index != -1) {
121 | this._localStreams.splice(index, 1);
122 | }
123 | }
124 |
125 | _RTCPeerConnection.prototype.getLocalStreams = function() {
126 | return this._localStreams;
127 | }
128 |
129 |
130 |
131 | /**
132 | * Logs at error level.
133 | *
134 | * @private
135 | * @returns {void}
136 | */
137 | function _LOGE(...args) {
138 | console && console.error && console.error(...args);
139 | }
140 |
141 | /**
142 | * Adapts react-native-webrtc's {@link RTCPeerConnection#setRemoteDescription}
143 | * implementation which uses the deprecated, callback-based version to the
144 | * Promise-based version.
145 | *
146 | * @param {RTCSessionDescription} sessionDescription - The RTCSessionDescription
147 | * which specifies the configuration of the remote end of the connection.
148 | * @private
149 | * @private
150 | * @returns {Promise}
151 | */
152 | function _setRemoteDescription(sessionDescription) {
153 | return new Promise((resolve, reject) => {
154 |
155 | /* eslint-disable no-invalid-this */
156 |
157 | // Ensure I'm not remembering onaddstream invocations from previous
158 | // setRemoteDescription calls. I shouldn't be but... anyway.
159 | this._onaddstreamQueue = [];
160 |
161 | RTCPeerConnection.prototype.setRemoteDescription.call(
162 | this,
163 | sessionDescription,
164 | (...args) => {
165 | let q;
166 |
167 | try {
168 | resolve(...args);
169 | } finally {
170 | q = this._onaddstreamQueue;
171 | this._onaddstreamQueue = undefined;
172 | }
173 |
174 | this._invokeQueuedOnaddstream(q);
175 | },
176 | (...args) => {
177 | this._onaddstreamQueue = undefined;
178 |
179 | reject(...args);
180 | });
181 |
182 | /* eslint-enable no-invalid-this */
183 | });
184 | }
185 |
186 | /**
187 | * Synthesize IPv6 addresses on iOS in order to support IPv6 NAT64 networks.
188 | *
189 | * @param {RTCSessionDescription} sdp - The RTCSessionDescription which
190 | * specifies the configuration of the remote end of the connection.
191 | * @private
192 | * @returns {Promise}
193 | */
194 | function _synthesizeIPv6Addresses(sdp) {
195 | // The synthesis of IPv6 addresses is implemented on iOS only at the time of
196 | // this writing.
197 | if (!NativeModules.POSIX) {
198 | return Promise.resolve(sdp);
199 | }
200 |
201 | return (
202 | new Promise(resolve => resolve(_synthesizeIPv6Addresses0(sdp)))
203 | .then(({ ips, lines }) =>
204 | Promise.all(Array.from(ips.values()))
205 | .then(() => _synthesizeIPv6Addresses1(sdp, ips, lines))
206 | ));
207 | }
208 |
209 | /* eslint-disable max-depth */
210 |
211 | /**
212 | * Implements the initial phase of the synthesis of IPv6 addresses.
213 | *
214 | * @param {RTCSessionDescription} sessionDescription - The RTCSessionDescription
215 | * for which IPv6 addresses will be synthesized.
216 | * @private
217 | * @returns {{
218 | * ips: Map,
219 | * lines: Array
220 | * }}
221 | */
222 | function _synthesizeIPv6Addresses0(sessionDescription) {
223 | const sdp = sessionDescription.sdp;
224 | let start = 0;
225 | const lines = [];
226 | const ips = new Map();
227 | const getaddrinfo = NativeModules.POSIX.getaddrinfo;
228 |
229 | do {
230 | const end = sdp.indexOf('\r\n', start);
231 | let line;
232 |
233 | if (end === -1) {
234 | line = sdp.substring(start);
235 |
236 | // Break out of the loop at the end of the iteration.
237 | start = undefined;
238 | } else {
239 | line = sdp.substring(start, end);
240 | start = end + 2;
241 | }
242 |
243 | if (line.startsWith('a=candidate:')) {
244 | const candidate = line.split(' ');
245 |
246 | if (candidate.length >= 10 && candidate[6] === 'typ') {
247 | const ip4s = [ candidate[4] ];
248 | let abort = false;
249 |
250 | for (let i = 8; i < candidate.length; ++i) {
251 | if (candidate[i] === 'raddr') {
252 | ip4s.push(candidate[++i]);
253 | break;
254 | }
255 | }
256 |
257 | for (const ip of ip4s) {
258 | if (ip.indexOf(':') === -1) {
259 | ips.has(ip)
260 | || ips.set(ip, new Promise((resolve, reject) => {
261 | const v = ips.get(ip);
262 |
263 | if (v && typeof v === 'string') {
264 | resolve(v);
265 | } else {
266 | getaddrinfo(ip).then(
267 | value => {
268 | if (value.indexOf(':') === -1
269 | || value === ips.get(ip)) {
270 | ips.delete(ip);
271 | } else {
272 | ips.set(ip, value);
273 | }
274 | resolve(value);
275 | },
276 | reject);
277 | }
278 | }));
279 | } else {
280 | abort = true;
281 | break;
282 | }
283 | }
284 | if (abort) {
285 | ips.clear();
286 | break;
287 | }
288 |
289 | line = candidate;
290 | }
291 | }
292 |
293 | lines.push(line);
294 | } while (start);
295 |
296 | return {
297 | ips,
298 | lines
299 | };
300 | }
301 |
302 | /* eslint-enable max-depth */
303 |
304 | /**
305 | * Implements the initial phase of the synthesis of IPv6 addresses.
306 | *
307 | * @param {RTCSessionDescription} sessionDescription - The RTCSessionDescription
308 | * for which IPv6 addresses are being synthesized.
309 | * @param {Map} ips - A Map of IPv4 addresses found in the specified
310 | * sessionDescription to synthesized IPv6 addresses.
311 | * @param {Array} lines - The lines of the specified sessionDescription.
312 | * @private
313 | * @returns {RTCSessionDescription} A RTCSessionDescription that represents the
314 | * result of the synthesis of IPv6 addresses.
315 | */
316 | function _synthesizeIPv6Addresses1(sessionDescription, ips, lines) {
317 | if (ips.size === 0) {
318 | return sessionDescription;
319 | }
320 |
321 | for (let l = 0; l < lines.length; ++l) {
322 | const candidate = lines[l];
323 |
324 | if (typeof candidate !== 'string') {
325 | let ip4 = candidate[4];
326 | let ip6 = ips.get(ip4);
327 |
328 | ip6 && (candidate[4] = ip6);
329 |
330 | for (let i = 8; i < candidate.length; ++i) {
331 | if (candidate[i] === 'raddr') {
332 | ip4 = candidate[++i];
333 | (ip6 = ips.get(ip4)) && (candidate[i] = ip6);
334 | break;
335 | }
336 | }
337 |
338 | lines[l] = candidate.join(' ');
339 | }
340 | }
341 |
342 | return new RTCSessionDescription({
343 | sdp: lines.join('\r\n'),
344 | type: sessionDescription.type
345 | });
346 | }
347 |
--------------------------------------------------------------------------------
/react/adapter.js:
--------------------------------------------------------------------------------
1 |
2 | var webrtcUtils = {
3 | log: function() {
4 | // suppress console.log output when being included as a module.
5 | //if (typeof module !== 'undefined' ||
6 | // typeof require === 'function' && typeof define === 'function') {
7 | // return;
8 | //}
9 | console.log.apply(console, arguments);
10 | },
11 | extractVersion: function(uastring, expr, pos) {
12 | var match = uastring.match(expr);
13 | return match && match.length >= pos && parseInt(match[pos], 10);
14 | }
15 | };
16 |
17 | {
18 | // The RTCPeerConnection object.
19 | window.RTCPeerConnection = function(pcConfig, pcConstraints) {
20 | // Translate iceTransportPolicy to iceTransports,
21 | // see https://code.google.com/p/webrtc/issues/detail?id=4869
22 | if (pcConfig && pcConfig.iceTransportPolicy) {
23 | pcConfig.iceTransports = pcConfig.iceTransportPolicy;
24 | }
25 |
26 | var pc = new webkitRTCPeerConnection(pcConfig, pcConstraints); // jscs:ignore requireCapitalizedConstructors
27 | var origGetStats = pc.getStats.bind(pc);
28 | pc.getStats = function(selector, successCallback, errorCallback) { // jshint ignore: line
29 | var self = this;
30 | var args = arguments;
31 |
32 | // If selector is a function then we are in the old style stats so just
33 | // pass back the original getStats format to avoid breaking old users.
34 | if (arguments.length > 0 && typeof selector === 'function') {
35 | return origGetStats(selector, successCallback);
36 | }
37 |
38 | var fixChromeStats = function(response) {
39 | var standardReport = {};
40 | var reports = response.result();
41 | reports.forEach(function(report) {
42 | var standardStats = {
43 | id: report.id,
44 | timestamp: report.timestamp,
45 | type: report.type
46 | };
47 | report.names().forEach(function(name) {
48 | standardStats[name] = report.stat(name);
49 | });
50 | standardReport[standardStats.id] = standardStats;
51 | });
52 |
53 | return standardReport;
54 | };
55 |
56 | if (arguments.length >= 2) {
57 | var successCallbackWrapper = function(response) {
58 | args[1](fixChromeStats(response));
59 | };
60 |
61 | return origGetStats.apply(this, [successCallbackWrapper, arguments[0]]);
62 | }
63 |
64 | // promise-support
65 | return new Promise(function(resolve, reject) {
66 | if (args.length === 1 && selector === null) {
67 | origGetStats.apply(self, [
68 | function(response) {
69 | resolve.apply(null, [fixChromeStats(response)]);
70 | }, reject]);
71 | } else {
72 | origGetStats.apply(self, [resolve, reject]);
73 | }
74 | });
75 | };
76 |
77 | return pc;
78 | };
79 |
80 | // wrap static methods. Currently just generateCertificate.
81 | if (webkitRTCPeerConnection.generateCertificate) {
82 | Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
83 | get: function() {
84 | if (arguments.length) {
85 | return webkitRTCPeerConnection.generateCertificate.apply(null,
86 | arguments);
87 | } else {
88 | return webkitRTCPeerConnection.generateCertificate;
89 | }
90 | }
91 | });
92 | }
93 |
94 | // add promise support
95 | ['createOffer', 'createAnswer'].forEach(function(method) {
96 | var nativeMethod = webkitRTCPeerConnection.prototype[method];
97 | webkitRTCPeerConnection.prototype[method] = function() {
98 | var self = this;
99 | if (arguments.length < 1 || (arguments.length === 1 &&
100 | typeof(arguments[0]) === 'object')) {
101 | var opts = arguments.length === 1 ? arguments[0] : undefined;
102 | return new Promise(function(resolve, reject) {
103 | nativeMethod.apply(self, [resolve, reject, opts]);
104 | });
105 | } else {
106 | return nativeMethod.apply(this, arguments);
107 | }
108 | };
109 | });
110 |
111 | //'setRemoteDescription',
112 | ['setLocalDescription',
113 | 'addIceCandidate'].forEach(function(method) {
114 | var nativeMethod = webkitRTCPeerConnection.prototype[method];
115 | webkitRTCPeerConnection.prototype[method] = function() {
116 | var args = arguments;
117 | var self = this;
118 | return new Promise(function(resolve, reject) {
119 | nativeMethod.apply(self, [args[0],
120 | function() {
121 | resolve();
122 | if (args.length >= 2) {
123 | args[1].apply(null, []);
124 | }
125 | },
126 | function(err) {
127 | reject(err);
128 | if (args.length >= 3) {
129 | args[2].apply(null, [err]);
130 | }
131 | }]
132 | );
133 | });
134 | };
135 | });
136 |
137 | // getUserMedia constraints shim.
138 | var constraintsToChrome = function(c) {
139 | if (typeof c !== 'object' || c.mandatory || c.optional) {
140 | return c;
141 | }
142 | var cc = {};
143 | Object.keys(c).forEach(function(key) {
144 | if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
145 | return;
146 | }
147 | var r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]};
148 | if (r.exact !== undefined && typeof r.exact === 'number') {
149 | r.min = r.max = r.exact;
150 | }
151 | var oldname = function(prefix, name) {
152 | if (prefix) {
153 | return prefix + name.charAt(0).toUpperCase() + name.slice(1);
154 | }
155 | return (name === 'deviceId') ? 'sourceId' : name;
156 | };
157 | if (r.ideal !== undefined) {
158 | cc.optional = cc.optional || [];
159 | var oc = {};
160 | if (typeof r.ideal === 'number') {
161 | oc[oldname('min', key)] = r.ideal;
162 | cc.optional.push(oc);
163 | oc = {};
164 | oc[oldname('max', key)] = r.ideal;
165 | cc.optional.push(oc);
166 | } else {
167 | oc[oldname('', key)] = r.ideal;
168 | cc.optional.push(oc);
169 | }
170 | }
171 | if (r.exact !== undefined && typeof r.exact !== 'number') {
172 | cc.mandatory = cc.mandatory || {};
173 | cc.mandatory[oldname('', key)] = r.exact;
174 | } else {
175 | ['min', 'max'].forEach(function(mix) {
176 | if (r[mix] !== undefined) {
177 | cc.mandatory = cc.mandatory || {};
178 | cc.mandatory[oldname(mix, key)] = r[mix];
179 | }
180 | });
181 | }
182 | });
183 | if (c.advanced) {
184 | cc.optional = (cc.optional || []).concat(c.advanced);
185 | }
186 | return cc;
187 | };
188 |
189 | getUserMedia = function(constraints, onSuccess, onError) {
190 | if (constraints.audio) {
191 | constraints.audio = constraintsToChrome(constraints.audio);
192 | }
193 | if (constraints.video) {
194 | constraints.video = constraintsToChrome(constraints.video);
195 | }
196 | webrtcUtils.log('chrome: ' + JSON.stringify(constraints));
197 | return navigator.webkitGetUserMedia(constraints, onSuccess, onError);
198 | };
199 | navigator.getUserMedia = getUserMedia;
200 |
201 | if (!navigator.mediaDevices) {
202 | navigator.mediaDevices = {getUserMedia: requestUserMedia,
203 | enumerateDevices: function() {
204 | return new Promise(function(resolve) {
205 | var kinds = {audio: 'audioinput', video: 'videoinput'};
206 | return MediaStreamTrack.getSources(function(devices) {
207 | resolve(devices.map(function(device) {
208 | return {label: device.label,
209 | kind: kinds[device.kind],
210 | deviceId: device.id,
211 | groupId: ''};
212 | }));
213 | });
214 | });
215 | }};
216 | }
217 |
218 | // A shim for getUserMedia method on the mediaDevices object.
219 | // TODO(KaptenJansson) remove once implemented in Chrome stable.
220 | if (!navigator.mediaDevices.getUserMedia) {
221 | navigator.mediaDevices.getUserMedia = requestUserMedia;
222 | } else {
223 | // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia
224 | // function which returns a Promise, it does not accept spec-style
225 | // constraints.
226 | var origGetUserMedia = navigator.mediaDevices.getUserMedia.
227 | bind(navigator.mediaDevices);
228 | navigator.mediaDevices.getUserMedia = function(c) {
229 | webrtcUtils.log('spec: ' + JSON.stringify(c)); // whitespace for alignment
230 | c.audio = constraintsToChrome(c.audio);
231 | c.video = constraintsToChrome(c.video);
232 | webrtcUtils.log('chrome: ' + JSON.stringify(c));
233 | return origGetUserMedia(c);
234 | };
235 | }
236 |
237 | // Dummy devicechange event methods.
238 | // TODO(KaptenJansson) remove once implemented in Chrome stable.
239 | if (typeof navigator.mediaDevices.addEventListener === 'undefined') {
240 | navigator.mediaDevices.addEventListener = function() {
241 | webrtcUtils.log('Dummy mediaDevices.addEventListener called.');
242 | };
243 | }
244 | if (typeof navigator.mediaDevices.removeEventListener === 'undefined') {
245 | navigator.mediaDevices.removeEventListener = function() {
246 | webrtcUtils.log('Dummy mediaDevices.removeEventListener called.');
247 | };
248 | }
249 |
250 | // Attach a media stream to an element.
251 | attachMediaStream = function(element, stream) {
252 | if (webrtcDetectedVersion >= 43) {
253 | element.srcObject = stream;
254 | } else if (typeof element.src !== 'undefined') {
255 | element.src = URL.createObjectURL(stream);
256 | } else {
257 | webrtcUtils.log('Error attaching stream to element.');
258 | }
259 | };
260 | reattachMediaStream = function(to, from) {
261 | if (webrtcDetectedVersion >= 43) {
262 | to.srcObject = from.srcObject;
263 | } else {
264 | to.src = from.src;
265 | }
266 | };
267 |
268 | }
269 |
270 | // Returns the result of getUserMedia as a Promise.
271 | function requestUserMedia(constraints) {
272 | return new Promise(function(resolve, reject) {
273 | getUserMedia(constraints, resolve, reject);
274 | });
275 | }
276 |
277 |
--------------------------------------------------------------------------------
/react/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'env': {
3 | 'browser': true,
4 | 'commonjs': true,
5 | 'es6': true
6 | },
7 | 'parserOptions': {
8 | 'ecmaFeatures': {
9 | 'experimentalObjectRestSpread': true,
10 | 'jsx': true
11 | },
12 | 'sourceType': 'module'
13 | },
14 | 'plugins': [
15 | 'jsdoc',
16 | 'react',
17 | 'react-native'
18 | ],
19 | 'rules': {
20 | // Possible Errors group
21 | 'no-cond-assign': 2,
22 | 'no-console': 0,
23 | 'no-constant-condition': 2,
24 | 'no-control-regex': 2,
25 | 'no-debugger': 2,
26 | 'no-dupe-args': 2,
27 | 'no-dupe-keys': 2,
28 | 'no-duplicate-case': 2,
29 | 'no-empty': 2,
30 | 'no-empty-character-class': 2,
31 | 'no-ex-assign': 2,
32 | 'no-extra-boolean-cast': 2,
33 | 'no-extra-parens': [
34 | 'error',
35 | 'all',
36 | { 'nestedBinaryExpressions': false }
37 | ],
38 | 'no-extra-semi': 2,
39 | 'no-func-assign': 2,
40 | 'no-inner-declarations': 2,
41 | 'no-invalid-regexp': 2,
42 | 'no-irregular-whitespace': 2,
43 | 'no-negated-in-lhs': 2,
44 | 'no-obj-calls': 2,
45 | 'no-prototype-builtins': 0,
46 | 'no-regex-spaces': 2,
47 | 'no-sparse-arrays': 2,
48 | 'no-unexpected-multiline': 2,
49 | 'no-unreachable': 2,
50 | 'no-unsafe-finally': 2,
51 | 'use-isnan': 2,
52 |
53 | // Currently, we are using both valid-jsdoc and 'jsdoc' plugin. In the
54 | // future we might stick to one as soon as it has all the features.
55 | 'valid-jsdoc': [
56 | 'error',
57 | {
58 | 'matchDescription': '.+',
59 | 'prefer': {
60 | 'arg': 'param',
61 | 'argument': 'param',
62 | 'return': 'returns'
63 | },
64 | 'preferType': {
65 | 'array': 'Array',
66 | 'Boolean': 'boolean',
67 | 'function': 'Function',
68 | 'Number': 'number',
69 | 'object': 'Object',
70 | 'String': 'string'
71 | },
72 | 'requireParamDescription': true,
73 | 'requireReturn': true,
74 | 'requireReturnDescription': false,
75 | 'requireReturnType': true
76 | }
77 | ],
78 | 'valid-typeof': 2,
79 |
80 | // Best Practices group
81 | 'accessor-pairs': 0,
82 | 'array-callback-return': 2,
83 | 'block-scoped-var': 0,
84 | 'complexity': 0,
85 | 'consistent-return': 0,
86 | 'curly': 2,
87 | 'default-case': 0,
88 | 'dot-location': [ 'error', 'property' ],
89 | 'dot-notation': 2,
90 | 'eqeqeq': 2,
91 | 'guard-for-in': 2,
92 | 'no-alert': 2,
93 | 'no-caller': 2,
94 | 'no-case-declarations': 2,
95 | 'no-div-regex': 0,
96 | 'no-else-return': 2,
97 | 'no-empty-function': 2,
98 | 'no-empty-pattern': 2,
99 | 'no-eq-null': 2,
100 | 'no-eval': 2,
101 | 'no-extend-native': 2,
102 | 'no-extra-bind': 2,
103 | 'no-extra-label': 2,
104 | 'no-fallthrough': 2,
105 | 'no-floating-decimal': 2,
106 | 'no-implicit-coercion': 2,
107 | 'no-implicit-globals': 2,
108 | 'no-implied-eval': 2,
109 | 'no-invalid-this': 2,
110 | 'no-iterator': 2,
111 | 'no-labels': 2,
112 | 'no-lone-blocks': 2,
113 | 'no-loop-func': 2,
114 | 'no-magic-numbers': 0,
115 | 'no-multi-spaces': 2,
116 | 'no-multi-str': 2,
117 | 'no-native-reassign': 2,
118 | 'no-new': 2,
119 | 'no-new-func': 2,
120 | 'no-new-wrappers': 2,
121 | 'no-octal': 2,
122 | 'no-octal-escape': 2,
123 | 'no-param-reassign': 2,
124 | 'no-proto': 2,
125 | 'no-redeclare': 2,
126 | 'no-return-assign': 2,
127 | 'no-script-url': 2,
128 | 'no-self-assign': 2,
129 | 'no-self-compare': 2,
130 | 'no-sequences': 2,
131 | 'no-throw-literal': 2,
132 | 'no-unmodified-loop-condition': 2,
133 | 'no-unused-expressions': [
134 | 'error',
135 | {
136 | 'allowShortCircuit': true,
137 | 'allowTernary': true
138 | }
139 | ],
140 | 'no-unused-labels': 2,
141 | 'no-useless-call': 2,
142 | 'no-useless-concat': 2,
143 | 'no-useless-escape': 2,
144 | 'no-void': 2,
145 | 'no-warning-comments': 0,
146 | 'no-with': 2,
147 | 'radix': 2,
148 | 'vars-on-top': 2,
149 | 'wrap-iife': [ 'error', 'inside' ],
150 | 'yoda': 2,
151 |
152 | // Strict Mode group
153 | 'strict': 2,
154 |
155 | // Variables group
156 | 'init-declarations': 0,
157 | 'no-catch-shadow': 2,
158 | 'no-delete-var': 2,
159 | 'no-label-var': 2,
160 | 'no-restricted-globals': 0,
161 | 'no-shadow': 2,
162 | 'no-shadow-restricted-names': 2,
163 | 'no-undef': 2,
164 | 'no-undef-init': 2,
165 | 'no-undefined': 0,
166 | 'no-unused-vars': 2,
167 | 'no-use-before-define': [ 'error', { 'functions': false } ],
168 |
169 | // Stylistic issues group
170 | 'array-bracket-spacing': [
171 | 'error',
172 | 'always',
173 | { 'objectsInArrays': true }
174 | ],
175 | 'block-spacing': [ 'error', 'always' ],
176 | 'brace-style': 2,
177 | 'camelcase': 2,
178 | 'comma-dangle': 2,
179 | 'comma-spacing': 2,
180 | 'comma-style': 2,
181 | 'computed-property-spacing': 2,
182 | 'consistent-this': [ 'error', 'self' ],
183 | 'eol-last': 2,
184 | 'func-names': 0,
185 | 'func-style': 0,
186 | 'id-blacklist': 0,
187 | 'id-length': 0,
188 | 'id-match': 0,
189 | 'indent': [ 'error', 4, { 'SwitchCase': 0 } ],
190 | 'jsx-quotes': [ 'error', 'prefer-single' ],
191 | 'key-spacing': 2,
192 | 'keyword-spacing': 2,
193 | 'linebreak-style': [ 'error', 'unix' ],
194 | 'lines-around-comment': [
195 | 'error',
196 | {
197 | 'allowBlockStart': true,
198 | 'allowObjectStart': true,
199 | 'beforeBlockComment': true,
200 | 'beforeLineComment': true
201 | }
202 | ],
203 | 'max-depth': 2,
204 | 'max-len': [ 'error', 80 ],
205 | 'max-lines': 0,
206 | 'max-nested-callbacks': 2,
207 | 'max-params': 2,
208 | 'max-statements': 0,
209 | 'max-statements-per-line': 2,
210 | 'multiline-ternary': 0,
211 | 'new-cap': 2,
212 | 'new-parens': 2,
213 | 'newline-after-var': 2,
214 | 'newline-before-return': 2,
215 | 'newline-per-chained-call': 2,
216 | 'no-array-constructor': 2,
217 | 'no-bitwise': 2,
218 | 'no-continue': 2,
219 | 'no-inline-comments': 0,
220 | 'no-lonely-if': 2,
221 | 'no-mixed-operators': 2,
222 | 'no-mixed-spaces-and-tabs': 2,
223 | 'no-multiple-empty-lines': 2,
224 | 'no-negated-condition': 2,
225 | 'no-nested-ternary': 0,
226 | 'no-new-object': 2,
227 | 'no-plusplus': 0,
228 | 'no-restricted-syntax': 0,
229 | 'no-spaced-func': 2,
230 | 'no-tabs': 2,
231 | 'no-ternary': 0,
232 | 'no-trailing-spaces': 2,
233 | 'no-underscore-dangle': 0,
234 | 'no-unneeded-ternary': 2,
235 | 'no-whitespace-before-property': 2,
236 | 'object-curly-newline': 0,
237 | 'object-curly-spacing': [ 'error', 'always' ],
238 | 'object-property-newline': 2,
239 | 'one-var': 0,
240 | 'one-var-declaration-per-line': 0,
241 | 'operator-assignment': 0,
242 | 'operator-linebreak': [ 'error', 'before' ],
243 | 'padded-blocks': 0,
244 | 'quote-props': 0,
245 | 'quotes': [ 'error', 'single' ],
246 | 'require-jsdoc': [
247 | 'error',
248 | {
249 | 'require': {
250 | 'ClassDeclaration': true,
251 | 'FunctionDeclaration': true,
252 | 'MethodDefinition': true
253 | }
254 | }
255 | ],
256 | 'semi': [ 'error', 'always' ],
257 | 'semi-spacing': 2,
258 | 'sort-vars': 2,
259 | 'space-before-blocks': 2,
260 | 'space-before-function-paren': [ 'error', 'never' ],
261 | 'space-in-parens': [ 'error', 'never' ],
262 | 'space-infix-ops': 2,
263 | 'space-unary-ops': 2,
264 | 'spaced-comment': 2,
265 | 'unicode-bom': 0,
266 | 'wrap-regex': 0,
267 |
268 | // ES6 group rules
269 | 'arrow-body-style': [
270 | 'error',
271 | 'as-needed',
272 | { requireReturnForObjectLiteral: true }
273 | ],
274 | 'arrow-parens': [ 'error', 'as-needed' ],
275 | 'arrow-spacing': 2,
276 | 'constructor-super': 2,
277 | 'generator-star-spacing': 2,
278 | 'no-class-assign': 2,
279 | 'no-confusing-arrow': 2,
280 | 'no-const-assign': 2,
281 | 'no-dupe-class-members': 2,
282 | 'no-duplicate-imports': 2,
283 | 'no-new-symbol': 2,
284 | 'no-restricted-imports': 0,
285 | 'no-this-before-super': 2,
286 | 'no-useless-computed-key': 2,
287 | 'no-useless-constructor': 2,
288 | 'no-useless-rename': 2,
289 | 'no-var': 2,
290 | 'object-shorthand': [
291 | 'error',
292 | 'always',
293 | { 'avoidQuotes': true }
294 | ],
295 | 'prefer-arrow-callback': [ 'error', { 'allowNamedFunctions': true } ],
296 | 'prefer-const': 2,
297 | 'prefer-reflect': 0,
298 | 'prefer-rest-params': 2,
299 | 'prefer-spread': 2,
300 | 'prefer-template': 2,
301 | 'require-yield': 2,
302 | 'rest-spread-spacing': 2,
303 | 'sort-imports': 0,
304 | 'template-curly-spacing': 2,
305 | 'yield-star-spacing': 2,
306 |
307 | // JsDoc plugin rules group. The following rules are in addition to
308 | // valid-jsdoc rule.
309 | 'jsdoc/check-param-names': 0,
310 | 'jsdoc/check-tag-names': 2,
311 | 'jsdoc/check-types': 0,
312 | 'jsdoc/newline-after-description': 2,
313 |
314 | // XXX Because the following plugin is not very smart about words which
315 | // legitimately begin with uppercase characters mid-sentence, set it to
316 | // warn only.
317 | 'jsdoc/require-description-complete-sentence': 1,
318 | 'jsdoc/require-hyphen-before-param-description': 2,
319 |
320 | // The following 5 rules are covered by valid-jsdoc, so disable them.
321 | 'jsdoc/require-param': 0,
322 | 'jsdoc/require-param-description': 0,
323 | 'jsdoc/require-param-type': 0,
324 | 'jsdoc/require-returns-description': 0,
325 | 'jsdoc/require-returns-type': 0,
326 |
327 | // React plugin rules group
328 | 'react/display-name': 0,
329 | 'react/forbid-prop-types': 0,
330 | 'react/no-danger': 2,
331 | 'react/no-deprecated': 2,
332 | 'react/no-did-mount-set-state': 2,
333 | 'react/no-did-update-set-state': 2,
334 | 'react/no-direct-mutation-state': 2,
335 | 'react/no-find-dom-node': 2,
336 | 'react/no-is-mounted': 2,
337 | 'react/no-multi-comp': 2,
338 | 'react/no-render-return-value': 2,
339 | 'react/no-set-state': 0,
340 | 'react/no-string-refs': 2,
341 | 'react/no-unknown-property': 2,
342 | 'react/prefer-es6-class': 2,
343 | 'react/prefer-stateless-function': 0,
344 | 'react/prop-types': 2,
345 | 'react/react-in-jsx-scope': 2,
346 | 'react/require-extension': 0,
347 | 'react/require-optimization': 0,
348 | 'react/require-render-return': 2,
349 | 'react/self-closing-comp': 2,
350 | 'react/sort-comp': 0,
351 | 'react/sort-prop-types': 2,
352 |
353 | // React plugin JSX-specific rule group
354 | 'react/jsx-boolean-value': [ 'error', 'always' ],
355 | 'react/jsx-closing-bracket-location': [
356 | 'error',
357 | 'after-props'
358 | ],
359 | 'react/jsx-curly-spacing': [
360 | 'error',
361 | 'always',
362 | {
363 | 'spacing': {
364 | 'objectLiterals': 'never'
365 | }
366 | }
367 | ],
368 | 'react/jsx-equals-spacing': [ 'error', 'always' ],
369 | 'react/jsx-filename-extension': 0,
370 | 'react/jsx-first-prop-new-line': [ 'error', 'multiline' ],
371 | 'react/jsx-handler-names': [
372 | 'error',
373 | {
374 | 'eventHandlerPrefix': '_on',
375 | 'eventHandlerPropPrefix': 'on'
376 | }
377 | ],
378 | 'react/jsx-indent': 2,
379 | 'react/jsx-indent-props': 2,
380 | 'react/jsx-key': 2,
381 | 'react/jsx-max-props-per-line': 2,
382 | 'react/jsx-no-bind': 2,
383 | 'react/jsx-no-comment-textnodes': 2,
384 | 'react/jsx-no-duplicate-props': 2,
385 | 'react/jsx-no-literals': 0,
386 | 'react/jsx-no-target-blank': 2,
387 | 'react/jsx-no-undef': 2,
388 | 'react/jsx-pascal-case': 2,
389 | 'react/jsx-sort-props': 2,
390 | 'react/jsx-space-before-closing': 2,
391 | 'react/jsx-uses-react': 2,
392 | 'react/jsx-uses-vars': 2,
393 | 'react/jsx-wrap-multilines': 2,
394 |
395 | // React-Mative plugin rules group
396 | 'react-native/no-color-literals': 2,
397 | 'react-native/no-inline-styles': 2,
398 | 'react-native/no-unused-styles': 2,
399 | 'react-native/split-platform-components': 2
400 | }
401 | };
402 |
--------------------------------------------------------------------------------
/ios/imsdk/imsdk/GOReachability.m:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2014-2015, GoBelieve
3 | All rights reserved.
4 |
5 | This source code is licensed under the BSD-style license found in the
6 | LICENSE file in the root directory of this source tree. An additional grant
7 | of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | #import "GOReachability.h"
11 |
12 | #import
13 | #import
14 | #import
15 | #import
16 | #import
17 | #import
18 |
19 | @interface GOReachability ()
20 |
21 | @property (nonatomic, assign) SCNetworkReachabilityRef reachabilityRef;
22 |
23 |
24 | #if NEEDS_DISPATCH_RETAIN_RELEASE
25 | @property (nonatomic, assign) dispatch_queue_t reachabilitySerialQueue;
26 | #else
27 | @property (nonatomic, strong) dispatch_queue_t reachabilitySerialQueue;
28 | #endif
29 |
30 |
31 | @property (nonatomic, strong) id reachabilityObject;
32 |
33 | -(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags;
34 | -(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags;
35 |
36 | @end
37 |
38 | static NSString *reachabilityFlags(SCNetworkReachabilityFlags flags)
39 | {
40 | return [NSString stringWithFormat:@"%c%c %c%c%c%c%c%c%c",
41 | #if TARGET_OS_IPHONE
42 | (flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-',
43 | #else
44 | 'X',
45 | #endif
46 | (flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-',
47 | (flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-',
48 | (flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-',
49 | (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
50 | (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-',
51 | (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-',
52 | (flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-',
53 | (flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-'];
54 | }
55 |
56 | // Start listening for reachability notifications on the current run loop
57 | static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
58 | {
59 | #pragma unused (target)
60 | #if __has_feature(objc_arc)
61 | GOReachability *reachability = ((__bridge GOReachability*)info);
62 | #else
63 | GOReachability *reachability = ((Reachability*)info);
64 | #endif
65 |
66 | // We probably don't need an autoreleasepool here, as GCD docs state each queue has its own autorelease pool,
67 | // but what the heck eh?
68 | @autoreleasepool
69 | {
70 | [reachability reachabilityChanged:flags];
71 | }
72 | }
73 |
74 |
75 | @implementation GOReachability
76 |
77 | @synthesize reachabilityRef;
78 | @synthesize reachabilitySerialQueue;
79 |
80 | @synthesize reachableOnWWAN;
81 |
82 | @synthesize reachableBlock;
83 | @synthesize unreachableBlock;
84 |
85 | @synthesize reachabilityObject;
86 |
87 | #pragma mark - Class Constructor Methods
88 |
89 | +(GOReachability*)reachabilityWithHostName:(NSString*)hostname
90 | {
91 | return [GOReachability reachabilityWithHostname:hostname];
92 | }
93 |
94 | +(GOReachability*)reachabilityWithHostname:(NSString*)hostname
95 | {
96 | SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithName(NULL, [hostname UTF8String]);
97 | if (ref)
98 | {
99 | id reachability = [[self alloc] initWithReachabilityRef:ref];
100 |
101 | #if __has_feature(objc_arc)
102 | return reachability;
103 | #else
104 | return [reachability autorelease];
105 | #endif
106 |
107 | }
108 |
109 | return nil;
110 | }
111 |
112 | +(GOReachability *)reachabilityWithAddress:(const struct sockaddr_in *)hostAddress
113 | {
114 | SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress);
115 | if (ref)
116 | {
117 | id reachability = [[self alloc] initWithReachabilityRef:ref];
118 |
119 | #if __has_feature(objc_arc)
120 | return reachability;
121 | #else
122 | return [reachability autorelease];
123 | #endif
124 | }
125 |
126 | return nil;
127 | }
128 |
129 | +(GOReachability *)reachabilityForInternetConnection
130 | {
131 | struct sockaddr_in zeroAddress;
132 | bzero(&zeroAddress, sizeof(zeroAddress));
133 | zeroAddress.sin_len = sizeof(zeroAddress);
134 | zeroAddress.sin_family = AF_INET;
135 |
136 | return [self reachabilityWithAddress:&zeroAddress];
137 | }
138 |
139 | +(GOReachability*)reachabilityForLocalWiFi
140 | {
141 | struct sockaddr_in localWifiAddress;
142 | bzero(&localWifiAddress, sizeof(localWifiAddress));
143 | localWifiAddress.sin_len = sizeof(localWifiAddress);
144 | localWifiAddress.sin_family = AF_INET;
145 | // IN_LINKLOCALNETNUM is defined in as 169.254.0.0
146 | localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);
147 |
148 | return [self reachabilityWithAddress:&localWifiAddress];
149 | }
150 |
151 |
152 | // Initialization methods
153 |
154 | -(GOReachability *)initWithReachabilityRef:(SCNetworkReachabilityRef)ref
155 | {
156 | self = [super init];
157 | if (self != nil)
158 | {
159 | self.reachableOnWWAN = YES;
160 | self.reachabilityRef = ref;
161 | }
162 |
163 | return self;
164 | }
165 |
166 | -(void)dealloc
167 | {
168 | [self stopNotifier];
169 |
170 | if(self.reachabilityRef)
171 | {
172 | CFRelease(self.reachabilityRef);
173 | self.reachabilityRef = nil;
174 | }
175 |
176 | self.reachableBlock = nil;
177 | self.unreachableBlock = nil;
178 |
179 | #if !(__has_feature(objc_arc))
180 | [super dealloc];
181 | #endif
182 |
183 |
184 | }
185 |
186 | #pragma mark - Notifier Methods
187 |
188 | // Notifier
189 | // NOTE: This uses GCD to trigger the blocks - they *WILL NOT* be called on THE MAIN THREAD
190 | // - In other words DO NOT DO ANY UI UPDATES IN THE BLOCKS.
191 | // INSTEAD USE dispatch_async(dispatch_get_main_queue(), ^{UISTUFF}) (or dispatch_sync if you want)
192 |
193 | -(BOOL)startNotifier
194 | {
195 | SCNetworkReachabilityContext context = { 0, NULL, NULL, NULL, NULL };
196 |
197 | // this should do a retain on ourself, so as long as we're in notifier mode we shouldn't disappear out from under ourselves
198 | // woah
199 | self.reachabilityObject = self;
200 |
201 |
202 |
203 | // First, we need to create a serial queue.
204 | // We allocate this once for the lifetime of the notifier.
205 | self.reachabilitySerialQueue = dispatch_queue_create("com.tonymillion.reachability", NULL);
206 | if(self.reachabilitySerialQueue == nil)
207 | {
208 | return NO;
209 | }
210 |
211 | #if __has_feature(objc_arc)
212 | context.info = (__bridge void *)self;
213 | #else
214 | context.info = (void *)self;
215 | #endif
216 |
217 | if (!SCNetworkReachabilitySetCallback(self.reachabilityRef, TMReachabilityCallback, &context))
218 | {
219 | #ifdef DEBUG
220 | NSLog(@"SCNetworkReachabilitySetCallback() failed: %s", SCErrorString(SCError()));
221 | #endif
222 |
223 | // Clear out the dispatch queue
224 | if(self.reachabilitySerialQueue)
225 | {
226 | #if NEEDS_DISPATCH_RETAIN_RELEASE
227 | dispatch_release(self.reachabilitySerialQueue);
228 | #endif
229 | self.reachabilitySerialQueue = nil;
230 | }
231 |
232 | self.reachabilityObject = nil;
233 |
234 | return NO;
235 | }
236 |
237 | // Set it as our reachability queue, which will retain the queue
238 | if(!SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, self.reachabilitySerialQueue))
239 | {
240 | #ifdef DEBUG
241 | NSLog(@"SCNetworkReachabilitySetDispatchQueue() failed: %s", SCErrorString(SCError()));
242 | #endif
243 |
244 | // UH OH - FAILURE!
245 |
246 | // First stop, any callbacks!
247 | SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL);
248 |
249 | // Then clear out the dispatch queue.
250 | if(self.reachabilitySerialQueue)
251 | {
252 | #if NEEDS_DISPATCH_RETAIN_RELEASE
253 | dispatch_release(self.reachabilitySerialQueue);
254 | #endif
255 | self.reachabilitySerialQueue = nil;
256 | }
257 |
258 | self.reachabilityObject = nil;
259 |
260 | return NO;
261 | }
262 |
263 | return YES;
264 | }
265 |
266 | -(void)stopNotifier
267 | {
268 | // First stop, any callbacks!
269 | SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL);
270 |
271 | // Unregister target from the GCD serial dispatch queue.
272 | SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, NULL);
273 |
274 | if(self.reachabilitySerialQueue)
275 | {
276 | #if NEEDS_DISPATCH_RETAIN_RELEASE
277 | dispatch_release(self.reachabilitySerialQueue);
278 | #endif
279 | self.reachabilitySerialQueue = nil;
280 | }
281 |
282 | self.reachabilityObject = nil;
283 | }
284 |
285 | #pragma mark - reachability tests
286 |
287 | // This is for the case where you flick the airplane mode;
288 | // you end up getting something like this:
289 | //Reachability: WR ct-----
290 | //Reachability: -- -------
291 | //Reachability: WR ct-----
292 | //Reachability: -- -------
293 | // We treat this as 4 UNREACHABLE triggers - really apple should do better than this
294 |
295 | #define testcase (kSCNetworkReachabilityFlagsConnectionRequired | kSCNetworkReachabilityFlagsTransientConnection)
296 |
297 | -(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags
298 | {
299 | BOOL connectionUP = YES;
300 |
301 | if(!(flags & kSCNetworkReachabilityFlagsReachable))
302 | connectionUP = NO;
303 |
304 | if( (flags & testcase) == testcase )
305 | connectionUP = NO;
306 |
307 | #if TARGET_OS_IPHONE
308 | if(flags & kSCNetworkReachabilityFlagsIsWWAN)
309 | {
310 | // We're on 3G.
311 | if(!self.reachableOnWWAN)
312 | {
313 | // We don't want to connect when on 3G.
314 | connectionUP = NO;
315 | }
316 | }
317 | #endif
318 |
319 | return connectionUP;
320 | }
321 |
322 | -(BOOL)isReachable
323 | {
324 | SCNetworkReachabilityFlags flags;
325 |
326 | if(!SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
327 | return NO;
328 |
329 | return [self isReachableWithFlags:flags];
330 | }
331 |
332 | -(BOOL)isReachableViaWWAN
333 | {
334 | #if TARGET_OS_IPHONE
335 |
336 | SCNetworkReachabilityFlags flags = 0;
337 |
338 | if(SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
339 | {
340 | // Check we're REACHABLE
341 | if(flags & kSCNetworkReachabilityFlagsReachable)
342 | {
343 | // Now, check we're on WWAN
344 | if(flags & kSCNetworkReachabilityFlagsIsWWAN)
345 | {
346 | return YES;
347 | }
348 | }
349 | }
350 | #endif
351 |
352 | return NO;
353 | }
354 |
355 | -(BOOL)isReachableViaWiFi
356 | {
357 | SCNetworkReachabilityFlags flags = 0;
358 |
359 | if(SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
360 | {
361 | // Check we're reachable
362 | if((flags & kSCNetworkReachabilityFlagsReachable))
363 | {
364 | #if TARGET_OS_IPHONE
365 | // Check we're NOT on WWAN
366 | if((flags & kSCNetworkReachabilityFlagsIsWWAN))
367 | {
368 | return NO;
369 | }
370 | #endif
371 | return YES;
372 | }
373 | }
374 |
375 | return NO;
376 | }
377 |
378 |
379 | // WWAN may be available, but not active until a connection has been established.
380 | // WiFi may require a connection for VPN on Demand.
381 | -(BOOL)isConnectionRequired
382 | {
383 | return [self connectionRequired];
384 | }
385 |
386 | -(BOOL)connectionRequired
387 | {
388 | SCNetworkReachabilityFlags flags;
389 |
390 | if(SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
391 | {
392 | return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
393 | }
394 |
395 | return NO;
396 | }
397 |
398 | // Dynamic, on demand connection?
399 | -(BOOL)isConnectionOnDemand
400 | {
401 | SCNetworkReachabilityFlags flags;
402 |
403 | if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
404 | {
405 | return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) &&
406 | (flags & (kSCNetworkReachabilityFlagsConnectionOnTraffic | kSCNetworkReachabilityFlagsConnectionOnDemand)));
407 | }
408 |
409 | return NO;
410 | }
411 |
412 | // Is user intervention required?
413 | -(BOOL)isInterventionRequired
414 | {
415 | SCNetworkReachabilityFlags flags;
416 |
417 | if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
418 | {
419 | return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) &&
420 | (flags & kSCNetworkReachabilityFlagsInterventionRequired));
421 | }
422 |
423 | return NO;
424 | }
425 |
426 |
427 | #pragma mark - reachability status stuff
428 |
429 | -(NetworkStatus)currentReachabilityStatus
430 | {
431 | if([self isReachable])
432 | {
433 | if([self isReachableViaWiFi])
434 | return ReachableViaWiFi;
435 |
436 | #if TARGET_OS_IPHONE
437 | return ReachableViaWWAN;
438 | #endif
439 | }
440 |
441 | return NotReachable;
442 | }
443 |
444 | -(SCNetworkReachabilityFlags)reachabilityFlags
445 | {
446 | SCNetworkReachabilityFlags flags = 0;
447 |
448 | if(SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
449 | {
450 | return flags;
451 | }
452 |
453 | return 0;
454 | }
455 |
456 | -(NSString*)currentReachabilityString
457 | {
458 | NetworkStatus temp = [self currentReachabilityStatus];
459 |
460 | if(temp == reachableOnWWAN)
461 | {
462 | // Updated for the fact that we have CDMA phones now!
463 | return NSLocalizedString(@"Cellular", @"");
464 | }
465 | if (temp == ReachableViaWiFi)
466 | {
467 | return NSLocalizedString(@"WiFi", @"");
468 | }
469 |
470 | return NSLocalizedString(@"No Connection", @"");
471 | }
472 |
473 | -(NSString*)currentReachabilityFlags
474 | {
475 | return reachabilityFlags([self reachabilityFlags]);
476 | }
477 |
478 | #pragma mark - Callback function calls this method
479 |
480 | -(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags
481 | {
482 | if([self isReachableWithFlags:flags])
483 | {
484 | if(self.reachableBlock)
485 | {
486 | self.reachableBlock(self);
487 | }
488 | }
489 | else
490 | {
491 | if(self.unreachableBlock)
492 | {
493 | self.unreachableBlock(self);
494 | }
495 | }
496 | }
497 |
498 | #pragma mark - Debug Description
499 |
500 | - (NSString *) description
501 | {
502 | NSString *description = [NSString stringWithFormat:@"<%@: %#x>",
503 | NSStringFromClass([self class]), (unsigned int) self];
504 | return description;
505 | }
506 |
507 | @end
508 |
--------------------------------------------------------------------------------