├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── Group-Voice-Call ├── OpenVoiceCall-Android │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── README.zh.md │ ├── app │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── libs │ │ │ └── PLACEHOLDER │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── androidTest │ │ │ └── java │ │ │ │ └── io │ │ │ │ └── agora │ │ │ │ └── openacall │ │ │ │ └── ui │ │ │ │ └── BasicTests.java │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── assets │ │ │ └── logback.xml │ │ │ ├── java │ │ │ └── io │ │ │ │ └── agora │ │ │ │ ├── openacall │ │ │ │ ├── AGApplication.java │ │ │ │ ├── model │ │ │ │ │ ├── AGEventHandler.java │ │ │ │ │ ├── ConstantApp.java │ │ │ │ │ ├── CurrentUserSettings.java │ │ │ │ │ ├── EngineConfig.java │ │ │ │ │ ├── MyEngineEventHandler.java │ │ │ │ │ └── WorkerThread.java │ │ │ │ └── ui │ │ │ │ │ ├── BaseActivity.java │ │ │ │ │ ├── ChatActivity.java │ │ │ │ │ └── MainActivity.java │ │ │ │ └── propeller │ │ │ │ ├── Constant.java │ │ │ │ └── ui │ │ │ │ ├── AGLinearLayout.java │ │ │ │ └── ViewUtil.java │ │ │ ├── jniLibs │ │ │ ├── armeabi-v7a │ │ │ │ └── PLACEHOLDER │ │ │ └── x86 │ │ │ │ └── PLACEHOLDER │ │ │ └── res │ │ │ ├── drawable-xxxhdpi │ │ │ ├── btn_endcall.png │ │ │ ├── btn_mute.png │ │ │ ├── btn_speaker.png │ │ │ └── ic_launcher.png │ │ │ ├── layout │ │ │ ├── activity_chat.xml │ │ │ └── activity_main.xml │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── ids.xml │ │ │ ├── strings.xml │ │ │ ├── strings_config.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── OpenVoiceCall-iOS-Objective-C │ ├── .gitignore │ ├── LICENSE │ ├── OpenVoiceCall-iOS-Objective-C.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── OpenVoiceCall-iOS-Objective-C │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ ├── call.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── calling@2x.png │ │ │ │ └── calling@3x.png │ │ │ ├── earphone.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── earphone@2x.png │ │ │ │ └── earphone@3x.png │ │ │ ├── hungup.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── hungup@2x.png │ │ │ │ └── hungup@3x.png │ │ │ ├── mute.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── mute@2x.png │ │ │ │ └── mute@3x.png │ │ │ ├── speaker.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── speaker@2x.png │ │ │ │ └── speaker@3x.png │ │ │ └── un-mute.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── un-mute@2x.png │ │ │ │ └── un-mute@3x.png │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── ChannelNameCheck.h │ │ ├── ChannelNameCheck.m │ │ ├── ChatButton.h │ │ ├── ChatButton.m │ │ ├── Define.h │ │ ├── Info.plist │ │ ├── InfoCell.h │ │ ├── InfoCell.m │ │ ├── InfoModel.h │ │ ├── InfoModel.m │ │ ├── KeyCenter.h │ │ ├── KeyCenter.m │ │ ├── MainViewController.h │ │ ├── MainViewController.m │ │ ├── OpenVoiceCall.pch │ │ ├── RoomViewController.h │ │ ├── RoomViewController.m │ │ └── main.m │ ├── README.md │ └── README.zh.md └── OpenVoiceCall-iOS │ ├── .gitignore │ ├── LICENSE │ ├── OpenVoiceCall.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── OpenVoiceCall.xcscheme │ ├── OpenVoiceCall │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── btn_endcall.imageset │ │ │ ├── Contents.json │ │ │ └── btn_endcall.pdf │ │ ├── btn_mute.imageset │ │ │ ├── Contents.json │ │ │ └── btn_mute.pdf │ │ ├── btn_mute_blue.imageset │ │ │ ├── Contents.json │ │ │ └── btn_mute_blue.pdf │ │ ├── btn_speaker.imageset │ │ │ ├── Contents.json │ │ │ └── btn_speaker.pdf │ │ └── btn_speaker_blue.imageset │ │ │ ├── Contents.json │ │ │ └── btn_speaker_blue.pdf │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ ├── KeyCenter.swift │ ├── LogCell.swift │ ├── MainViewController.swift │ ├── MediaCharacter.swift │ └── RoomViewController.swift │ ├── OpenVoiceCallUITests │ ├── Info.plist │ └── OpenVoiceCallUITests.swift │ ├── README.md │ └── README.zh.md ├── LICENSE.md ├── One-to-One-Voice ├── Agora-Android-Voice-Tutorial-1to1 │ ├── .gitignore │ ├── LICENSE.md │ ├── README.md │ ├── README.zh.md │ ├── app │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── libs │ │ │ └── PLACEHOLDER │ │ ├── proguard-rules.pro │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── io │ │ │ │ └── agora │ │ │ │ └── tutorials1v1acall │ │ │ │ └── VoiceChatViewActivity.java │ │ │ ├── jniLibs │ │ │ ├── arm64-v8a │ │ │ │ └── PLACEHOLDER │ │ │ ├── armeabi-v7a │ │ │ │ └── PLACEHOLDER │ │ │ └── x86 │ │ │ │ └── PLACEHOLDER │ │ │ └── res │ │ │ ├── drawable-xxxhdpi │ │ │ ├── btn_end_call.png │ │ │ ├── btn_mute.png │ │ │ ├── btn_speaker.png │ │ │ └── ic_launcher.png │ │ │ ├── layout │ │ │ └── activity_voice_chat_view.xml │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle └── Agora-iOS-Voice-Tutorial-Swift-1to1 │ ├── .gitignore │ ├── Agora-iOS-Voice-Tutorial-Tests │ ├── Agora_iOS_Voice_Tutorial_Tests.swift │ └── Info.plist │ ├── Agora-iOS-Voice-Tutorial.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ ├── Agora-iOS-Voice-Tutorial-Tests.xcscheme │ │ └── Agora-iOS-Voice-Tutorial.xcscheme │ ├── Agora-iOS-Voice-Tutorial │ ├── AppDelegate.swift │ ├── AppID.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── btn_endcall.imageset │ │ │ ├── Contents.json │ │ │ └── btn_endcall.pdf │ │ ├── btn_mute.imageset │ │ │ ├── Contents.json │ │ │ └── btn_mute.pdf │ │ ├── btn_mute_blue.imageset │ │ │ ├── Contents.json │ │ │ └── btn_mute_blue.pdf │ │ ├── btn_speaker.imageset │ │ │ ├── Contents.json │ │ │ └── btn_speaker.pdf │ │ └── btn_speaker_blue.imageset │ │ │ ├── Contents.json │ │ │ └── btn_speaker_blue.pdf │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ └── VoiceChatViewController.swift │ ├── LICENSE │ ├── README.md │ └── README.zh.md ├── README.md └── README.zh.md /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.framework 2 | *.a 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | build/ 15 | 16 | # Local configuration file (sdk path, etc) 17 | local.properties 18 | 19 | # Eclipse project files 20 | .classpath 21 | .project 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Intellij project files 27 | *.iml 28 | *.ipr 29 | *.iws 30 | .idea/ 31 | 32 | # Mac OS X 33 | .DS_Store 34 | 35 | # Android Studio 36 | .gradle 37 | /local.properties 38 | /.idea/workspace.xml 39 | obj/ 40 | 41 | .externalNativeBuild 42 | 43 | # cscope or ctags files 44 | cscope.in.out 45 | cscope.out 46 | cscope.po.out 47 | tags 48 | 49 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2019 Agora Lab, Inc (http://www.agora.io/) 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | 10 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/README.md: -------------------------------------------------------------------------------- 1 | # Open Voice Call for Android 2 | 3 | *其他语言版本: [简体中文](README.zh.md)* 4 | 5 | The Open Voice Call for Android Sample App is an open-source demo that will help you get voice chat integrated directly into your Android applications using the Agora Voice SDK. 6 | 7 | With this sample app, you can: 8 | 9 | - Join / leave channel 10 | - Mute / unmute audio 11 | - Switch speaker 12 | 13 | A tutorial demo can be found here: [Agora-Android-Voice-Tutorial-1to1](https://github.com/AgoraIO/Basic-Audio-Call/tree/master/One-to-One-Voice/Agora-Android-Voice-Tutorial-1to1) 14 | 15 | ## Running the App 16 | **First**, create a developer account at [Agora.io](https://dashboard.agora.io/signin/), and obtain an App ID. Update "app/src/main/res/values/strings_config.xml" with your App ID. 17 | 18 | ``` 19 | <#YOUR APP ID#> 20 | ``` 21 | 22 | **Next**, integrate the Agora Voice SDK and there are two ways to integrate: 23 | 24 | - The recommended way to integrate: 25 | 26 | Add the address which can integrate the Agora Voice SDK automatically through JCenter in the property of the dependence of the "app/build.gradle": 27 | ``` 28 | implementation 'io.agora.rtc:voice-sdk:3.0.0' 29 | ``` 30 | (This sample program has added this address and do not need to add again. Adding the link address is the most important step if you want to integrate the Agora Voice SDK in your own application.) 31 | 32 | - Alternative way to integrate: 33 | 34 | First, download the **Agora Voice SDK** from [Agora.io SDK](https://www.agora.io/en/download/). Unzip the downloaded SDK package and copy ***.jar** under **libs** to **app/libs**, **arm64-v8a**/**x86**/**armeabi-v7a** under **libs** to **app/src/main/jniLibs**. 35 | 36 | Then, add the fllowing code in the property of the dependence of the "app/build.gradle": 37 | 38 | ``` 39 | compile fileTree(dir: 'libs', include: ['*.jar']) 40 | ``` 41 | 42 | **Finally**, open project with Android Studio, connect your Android device, build and run. 43 | 44 | Or use `Gradle` to build and run. 45 | 46 | ## Developer Environment Requirements 47 | - Android Studio 3.0 or above 48 | - Real devices (Nexus 5X or other devices) 49 | - Some simulators are function missing or have performance issue, so real device is the best choice 50 | 51 | ## Contact Us 52 | - For potential issues, take a look at our [FAQ](https://docs.agora.io/en/faq) first 53 | - Dive into [Agora SDK Samples](https://github.com/AgoraIO) to see more tutorials 54 | - Take a look at [Agora Use Case](https://github.com/AgoraIO-usecase) for more complicated real use case 55 | - Repositories managed by developer communities can be found at [Agora Community](https://github.com/AgoraIO-Community) 56 | - You can find full API documentation at [Document Center](https://docs.agora.io/en/) 57 | - If you encounter problems during integration, you can ask question in [Stack Overflow](https://stackoverflow.com/questions/tagged/agora.io) 58 | - You can file bugs about this sample at [issue](https://github.com/AgoraIO/Basic-Audio-Call/issues) 59 | 60 | ## License 61 | The MIT License (MIT). 62 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/README.zh.md: -------------------------------------------------------------------------------- 1 | # Open Voice Call for Android 2 | 3 | *Read this in other languages: [English](README.md)* 4 | 5 | 这个开源示例项目演示了如何快速集成 Agora 音频 SDK,实现多人音频通话。 6 | 7 | 在这个示例项目中包含了以下功能: 8 | 9 | - 加入通话和离开通话; 10 | - 静音和解除静音; 11 | - 切换扬声器和听筒; 12 | 13 | 你也可以在这里查看入门版的示例项目:[Agora-Android-Voice-Tutorial-1to1](https://github.com/AgoraIO/Basic-Audio-Call/tree/master/One-to-One-Voice/Agora-Android-Voice-Tutorial-1to1) 14 | 15 | ## 运行示例程序 16 | **首先**在 [Agora.io 注册](https://dashboard.agora.io/cn/signup/) 注册账号,并创建自己的测试项目,获取到 AppID。将 AppID 填写进 "app/src/main/res/values/strings_config.xml" 17 | 18 | ``` 19 | <#YOUR APP ID#> 20 | ``` 21 | 22 | **然后**是集成 Agora 音频 SDK,集成方式有以下两种: 23 | 24 | - 首选集成方式: 25 | 26 | 在项目对应的模块的 "app/build.gradle" 文件的依赖属性中加入通过 JCenter 自动集成 Agora 音频 SDK 的地址: 27 | 28 | ``` 29 | implementation 'io.agora.rtc:voice-sdk:2.4.0' 30 | ``` 31 | 32 | (该示例程序已添加此链接地址,无需再添加,如果要在自己的应用中集成 Agora 音频 SDK,添加链接地址是最重要的一步。) 33 | 34 | - 次选集成方式: 35 | 36 | 第一步: 在 [Agora.io SDK](https://www.agora.io/cn/download/) 下载 **语音通话 + 直播 SDK**,解压后将其中的 **libs** 文件夹下的 ***.jar** 复制到本项目的 **app/libs** 下,其中的 **libs** 文件夹下的 **arm64-v8a**/**x86**/**armeabi-v7a** 复制到本项目的 **app/src/main/jniLibs** 下。 37 | 38 | 第二步: 在本项目的 "app/build.gradle" 文件依赖属性中添加如下依赖关系: 39 | 40 | ``` 41 | compile fileTree(dir: 'libs', include: ['*.jar']) 42 | ``` 43 | 44 | **最后**用 Android Studio 打开该项目,连上设备,编译并运行。 45 | 46 | 也可以使用 `Gradle` 直接编译运行。 47 | 48 | ## 运行环境 49 | - Android Studio 3.0 + 50 | - 真实 Android 设备 (Nexus 5X 或者其它设备) 51 | - 部分模拟器会存在功能缺失或者性能问题,所以推荐使用真机 52 | 53 | ## 联系我们 54 | - 如果你遇到了困难,可以先参阅 [常见问题](https://docs.agora.io/cn/faq) 55 | - 如果你想了解更多官方示例,可以参考 [官方SDK示例](https://github.com/AgoraIO) 56 | - 如果你想了解声网SDK在复杂场景下的应用,可以参考 [官方场景案例](https://github.com/AgoraIO-usecase) 57 | - 如果你想了解声网的一些社区开发者维护的项目,可以查看 [社区](https://github.com/AgoraIO-Community) 58 | - 完整的 API 文档见 [文档中心](https://docs.agora.io/cn/) 59 | - 若遇到问题需要开发者帮助,你可以到 [开发者社区](https://rtcdeveloper.com/) 提问 60 | - 如果需要售后技术支持, 你可以在 [Agora Dashboard](https://dashboard.agora.io) 提交工单 61 | - 如果发现了示例代码的 bug,欢迎提交 [issue](https://github.com/AgoraIO/Basic-Audio-Call/issues) 62 | 63 | ## 代码许可 64 | The MIT License (MIT). 65 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | repositories { 4 | jcenter() 5 | } 6 | 7 | android { 8 | compileSdkVersion 26 9 | 10 | defaultConfig { 11 | applicationId "io.agora.openacall" 12 | minSdkVersion 16 // ICE_CREAM_SANDWICH 13 | targetSdkVersion 26 14 | versionCode 19 15 | versionName "x.y.z" 16 | } 17 | 18 | compileOptions { 19 | sourceCompatibility JavaVersion.VERSION_1_7 20 | targetCompatibility JavaVersion.VERSION_1_7 21 | } 22 | 23 | buildTypes { 24 | release { 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 27 | } 28 | } 29 | } 30 | 31 | dependencies { 32 | implementation fileTree(dir: 'libs', include: ['*.jar']) 33 | 34 | implementation 'com.android.support:appcompat-v7:26.1.0' 35 | implementation 'com.android.support:support-v4:26.1.0' 36 | implementation 'com.android.support:design:26.1.0' 37 | implementation 'org.slf4j:slf4j-api:1.7.21' 38 | implementation 'com.github.tony19:logback-android-core:1.1.1-4' 39 | implementation('com.github.tony19:logback-android-classic:1.1.1-4') { 40 | // workaround issue #73 41 | exclude group: 'com.google.android', module: 'android' 42 | } 43 | 44 | androidTestImplementation 'com.jayway.android.robotium:robotium-solo:5.6.3' 45 | } 46 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/libs/PLACEHOLDER: -------------------------------------------------------------------------------- 1 | agora-rtc-sdk.jar 2 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/guohai/Dev/android-sdk-macosx/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/src/androidTest/java/io/agora/openacall/ui/BasicTests.java: -------------------------------------------------------------------------------- 1 | package io.agora.openacall.ui; 2 | 3 | import android.test.ActivityInstrumentationTestCase2; 4 | 5 | import com.robotium.solo.Condition; 6 | import com.robotium.solo.Solo; 7 | 8 | import io.agora.openacall.BuildConfig; 9 | import io.agora.openacall.R; 10 | 11 | public class BasicTests extends ActivityInstrumentationTestCase2 { 12 | 13 | private Solo solo; 14 | 15 | public BasicTests() { 16 | super(MainActivity.class); 17 | } 18 | 19 | @Override 20 | public void setUp() throws Exception { 21 | solo = new Solo(getInstrumentation(), getActivity()); 22 | } 23 | 24 | @Override 25 | public void tearDown() throws Exception { 26 | solo.finishOpenedActivities(); 27 | } 28 | 29 | public String getString(int resId) { 30 | return solo.getString(resId); 31 | } 32 | 33 | public void testJoinChannel() throws Exception { 34 | String AUTO_TEST_CHANNEL_NAME = "for_auto_test_" + BuildConfig.VERSION_NAME + BuildConfig.VERSION_CODE; 35 | 36 | solo.unlockScreen(); 37 | 38 | solo.assertCurrentActivity("Expected MainActivity activity", "MainActivity"); 39 | solo.clearEditText(0); 40 | solo.enterText(0, AUTO_TEST_CHANNEL_NAME); 41 | solo.waitForText(AUTO_TEST_CHANNEL_NAME, 1, 2000L); 42 | 43 | solo.clickOnView(solo.getView(R.id.button_join)); 44 | 45 | String targetActivity = ChatActivity.class.getSimpleName(); 46 | 47 | solo.waitForLogMessage("onJoinChannelSuccess " + AUTO_TEST_CHANNEL_NAME, JOIN_CHANNEL_SUCCESS_THRESHOLD + 1000); 48 | 49 | solo.assertCurrentActivity("Expected " + targetActivity + " activity", targetActivity); 50 | 51 | long firstRemoteAudioTs = System.currentTimeMillis(); 52 | solo.waitForLogMessage("volume: ", FIRST_REMOTE_AUDIO_RECEIVED_THRESHOLD + 500); 53 | 54 | assertTrue("first remote audio frame not received", System.currentTimeMillis() - firstRemoteAudioTs <= FIRST_REMOTE_AUDIO_RECEIVED_THRESHOLD); 55 | 56 | solo.waitForCondition(new Condition() { // stay at the channel for some time 57 | @Override 58 | public boolean isSatisfied() { 59 | return false; 60 | } 61 | }, FIRST_REMOTE_AUDIO_RECEIVED_THRESHOLD); 62 | } 63 | 64 | private static final int FIRST_REMOTE_AUDIO_RECEIVED_THRESHOLD = 50 * 1000; 65 | private static final int JOIN_CHANNEL_SUCCESS_THRESHOLD = 5000; 66 | } 67 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/src/main/assets/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 12 | 13 | 16 | 17 | ${LOG_DIR}/${FILE_PREFIX}${FILE_POSTFIX} 18 | 19 | 20 | 21 | 22 | ${LOG_DIR}/${FILE_PREFIX}.%i${FILE_POSTFIX} 23 | 24 | 1 25 | 7 26 | 27 | 28 | 1MB 29 | 30 | 31 | 32 | 33 | %d{yyyy-MM-dd HH:mm:ss.SSSZ} [%thread] %-5level %logger{0} %msg%n 34 | 35 | 36 | 37 | 38 | 39 | 40 | [%thread] %-5level %logger{0} %msg%n 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/src/main/java/io/agora/openacall/AGApplication.java: -------------------------------------------------------------------------------- 1 | package io.agora.openacall; 2 | 3 | import android.app.Application; 4 | import io.agora.openacall.model.CurrentUserSettings; 5 | import io.agora.openacall.model.WorkerThread; 6 | 7 | public class AGApplication extends Application { 8 | 9 | private WorkerThread mWorkerThread; 10 | 11 | public synchronized void initWorkerThread() { 12 | if (mWorkerThread == null) { 13 | mWorkerThread = new WorkerThread(getApplicationContext()); 14 | mWorkerThread.start(); 15 | 16 | mWorkerThread.waitForReady(); 17 | } 18 | } 19 | 20 | public synchronized WorkerThread getWorkerThread() { 21 | return mWorkerThread; 22 | } 23 | 24 | public synchronized void deInitWorkerThread() { 25 | mWorkerThread.exit(); 26 | try { 27 | mWorkerThread.join(); 28 | } catch (InterruptedException e) { 29 | e.printStackTrace(); 30 | } 31 | mWorkerThread = null; 32 | } 33 | 34 | public static final CurrentUserSettings mAudioSettings = new CurrentUserSettings(); 35 | } 36 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/src/main/java/io/agora/openacall/model/AGEventHandler.java: -------------------------------------------------------------------------------- 1 | package io.agora.openacall.model; 2 | 3 | public interface AGEventHandler { 4 | void onJoinChannelSuccess(String channel, int uid, int elapsed); 5 | 6 | void onUserOffline(int uid, int reason); 7 | 8 | void onExtraCallback(int type, Object... data); 9 | 10 | int EVENT_TYPE_ON_USER_AUDIO_MUTED = 7; 11 | 12 | int EVENT_TYPE_ON_SPEAKER_STATS = 8; 13 | 14 | int EVENT_TYPE_ON_AGORA_MEDIA_ERROR = 9; 15 | 16 | int EVENT_TYPE_ON_AUDIO_QUALITY = 10; 17 | 18 | int EVENT_TYPE_ON_APP_ERROR = 13; 19 | 20 | int EVENT_TYPE_ON_AUDIO_ROUTE_CHANGED = 18; 21 | } 22 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/src/main/java/io/agora/openacall/model/ConstantApp.java: -------------------------------------------------------------------------------- 1 | package io.agora.openacall.model; 2 | 3 | public class ConstantApp { 4 | public static final String APP_BUILD_DATE = "today"; 5 | 6 | public static final int BASE_VALUE_PERMISSION = 0X0001; 7 | public static final int PERMISSION_REQ_ID_RECORD_AUDIO = BASE_VALUE_PERMISSION + 1; 8 | public static final int PERMISSION_REQ_ID_WRITE_EXTERNAL_STORAGE = BASE_VALUE_PERMISSION + 3; 9 | 10 | public static class PrefManager { 11 | public static final String PREF_PROPERTY_UID = "pOCXx_uid"; 12 | } 13 | 14 | public static final String ACTION_KEY_CHANNEL_NAME = "ecHANEL"; 15 | 16 | public static class AppError { 17 | public static final int NO_NETWORK_CONNECTION = 3; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/src/main/java/io/agora/openacall/model/CurrentUserSettings.java: -------------------------------------------------------------------------------- 1 | package io.agora.openacall.model; 2 | 3 | public class CurrentUserSettings { 4 | public String mChannelName; 5 | 6 | public CurrentUserSettings() { 7 | reset(); 8 | } 9 | 10 | public void reset() { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/src/main/java/io/agora/openacall/model/EngineConfig.java: -------------------------------------------------------------------------------- 1 | package io.agora.openacall.model; 2 | 3 | public class EngineConfig { 4 | public int mUid; 5 | 6 | public String mChannel; 7 | 8 | public void reset() { 9 | mChannel = null; 10 | } 11 | 12 | EngineConfig() { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/src/main/java/io/agora/openacall/model/WorkerThread.java: -------------------------------------------------------------------------------- 1 | package io.agora.openacall.model; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.os.Environment; 6 | import android.os.Handler; 7 | import android.os.Looper; 8 | import android.os.Message; 9 | import android.preference.PreferenceManager; 10 | import android.provider.Settings; 11 | import android.text.TextUtils; 12 | import android.util.Log; 13 | 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | import java.io.File; 18 | 19 | import io.agora.openacall.R; 20 | import io.agora.rtc.Constants; 21 | import io.agora.rtc.RtcEngine; 22 | 23 | public class WorkerThread extends Thread { 24 | private final static Logger log = LoggerFactory.getLogger(WorkerThread.class); 25 | 26 | private final Context mContext; 27 | 28 | private static final int ACTION_WORKER_THREAD_QUIT = 0X1010; // quit this thread 29 | 30 | private static final int ACTION_WORKER_JOIN_CHANNEL = 0X2010; 31 | 32 | private static final int ACTION_WORKER_LEAVE_CHANNEL = 0X2011; 33 | 34 | private static final class WorkerThreadHandler extends Handler { 35 | 36 | private WorkerThread mWorkerThread; 37 | 38 | WorkerThreadHandler(WorkerThread thread) { 39 | this.mWorkerThread = thread; 40 | } 41 | 42 | public void release() { 43 | mWorkerThread = null; 44 | } 45 | 46 | @Override 47 | public void handleMessage(Message msg) { 48 | if (this.mWorkerThread == null) { 49 | log.warn("handler is already released! " + msg.what); 50 | return; 51 | } 52 | 53 | switch (msg.what) { 54 | case ACTION_WORKER_THREAD_QUIT: 55 | mWorkerThread.exit(); 56 | break; 57 | case ACTION_WORKER_JOIN_CHANNEL: 58 | String[] data = (String[]) msg.obj; 59 | mWorkerThread.joinChannel(data[0], msg.arg1); 60 | break; 61 | case ACTION_WORKER_LEAVE_CHANNEL: 62 | String channel = (String) msg.obj; 63 | mWorkerThread.leaveChannel(channel); 64 | break; 65 | } 66 | } 67 | } 68 | 69 | private WorkerThreadHandler mWorkerHandler; 70 | 71 | private boolean mReady; 72 | 73 | public final void waitForReady() { 74 | while (!mReady) { 75 | try { 76 | Thread.sleep(20); 77 | } catch (InterruptedException e) { 78 | e.printStackTrace(); 79 | } 80 | log.debug("wait for " + WorkerThread.class.getSimpleName()); 81 | } 82 | } 83 | 84 | @Override 85 | public void run() { 86 | log.trace("start to run"); 87 | Looper.prepare(); 88 | 89 | mWorkerHandler = new WorkerThreadHandler(this); 90 | 91 | ensureRtcEngineReadyLock(); 92 | 93 | mReady = true; 94 | 95 | // enter thread looper 96 | Looper.loop(); 97 | } 98 | 99 | private RtcEngine mRtcEngine; 100 | 101 | public final void joinChannel(final String channel, int uid) { 102 | if (Thread.currentThread() != this) { 103 | log.warn("joinChannel() - worker thread asynchronously " + channel + " " + uid); 104 | Message envelop = new Message(); 105 | envelop.what = ACTION_WORKER_JOIN_CHANNEL; 106 | envelop.obj = new String[]{channel}; 107 | envelop.arg1 = uid; 108 | mWorkerHandler.sendMessage(envelop); 109 | return; 110 | } 111 | 112 | ensureRtcEngineReadyLock(); 113 | mRtcEngine.joinChannel(null, channel, "OpenVCall", uid); 114 | 115 | mEngineConfig.mChannel = channel; 116 | 117 | log.debug("joinChannel " + channel + " " + uid); 118 | } 119 | 120 | public final void leaveChannel(String channel) { 121 | if (Thread.currentThread() != this) { 122 | log.warn("leaveChannel() - worker thread asynchronously " + channel); 123 | Message envelop = new Message(); 124 | envelop.what = ACTION_WORKER_LEAVE_CHANNEL; 125 | envelop.obj = channel; 126 | mWorkerHandler.sendMessage(envelop); 127 | return; 128 | } 129 | 130 | if (mRtcEngine != null) { 131 | mRtcEngine.leaveChannel(); 132 | } 133 | 134 | mEngineConfig.reset(); 135 | log.debug("leaveChannel " + channel); 136 | } 137 | 138 | private EngineConfig mEngineConfig; 139 | 140 | public final EngineConfig getEngineConfig() { 141 | return mEngineConfig; 142 | } 143 | 144 | private final MyEngineEventHandler mEngineEventHandler; 145 | 146 | public static String getDeviceID(Context context) { 147 | // XXX according to the API docs, this value may change after factory reset 148 | // use Android id as device id 149 | return Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); 150 | } 151 | 152 | private RtcEngine ensureRtcEngineReadyLock() { 153 | if (mRtcEngine == null) { 154 | String appId = mContext.getString(R.string.private_app_id); 155 | if (TextUtils.isEmpty(appId)) { 156 | throw new RuntimeException("NEED TO use your App ID, get your own ID at https://dashboard.agora.io/"); 157 | } 158 | try { 159 | // Creates an RtcEngine instance 160 | mRtcEngine = RtcEngine.create(mContext, appId, mEngineEventHandler.mRtcEventHandler); 161 | } catch (Exception e) { 162 | log.error(Log.getStackTraceString(e)); 163 | throw new RuntimeException("NEED TO check rtc sdk init fatal error\n" + Log.getStackTraceString(e)); 164 | } 165 | 166 | /* 167 | Sets the channel profile of the Agora RtcEngine. 168 | The Agora RtcEngine differentiates channel profiles and applies different optimization 169 | algorithms accordingly. For example, it prioritizes smoothness and low latency for a 170 | video call, and prioritizes video quality for a video broadcast. 171 | */ 172 | mRtcEngine.setChannelProfile(Constants.CHANNEL_PROFILE_COMMUNICATION); 173 | 174 | /* 175 | Enables the onAudioVolumeIndication callback at a set time interval to report on which 176 | users are speaking and the speakers' volume. 177 | Once this method is enabled, the SDK returns the volume indication in the 178 | onAudioVolumeIndication callback at the set time interval, regardless of whether any user 179 | is speaking in the channel. 180 | */ 181 | mRtcEngine.enableAudioVolumeIndication(200, 3, false); // 200 ms 182 | mRtcEngine.setLogFile(Environment.getExternalStorageDirectory() 183 | + File.separator + mContext.getPackageName() + "/log/agora-rtc.log"); 184 | } 185 | return mRtcEngine; 186 | } 187 | 188 | public MyEngineEventHandler eventHandler() { 189 | return mEngineEventHandler; 190 | } 191 | 192 | public RtcEngine getRtcEngine() { 193 | return mRtcEngine; 194 | } 195 | 196 | /** 197 | * call this method to exit 198 | * should ONLY call this method when this thread is running 199 | */ 200 | public final void exit() { 201 | if (Thread.currentThread() != this) { 202 | log.warn("exit() - exit app thread asynchronously"); 203 | mWorkerHandler.sendEmptyMessage(ACTION_WORKER_THREAD_QUIT); 204 | return; 205 | } 206 | 207 | mReady = false; 208 | 209 | // TODO should remove all pending(read) messages 210 | 211 | log.debug("exit() > start"); 212 | 213 | // exit thread looper 214 | Looper.myLooper().quit(); 215 | 216 | mWorkerHandler.release(); 217 | 218 | log.debug("exit() > end"); 219 | } 220 | 221 | public WorkerThread(Context context) { 222 | this.mContext = context; 223 | 224 | this.mEngineConfig = new EngineConfig(); 225 | SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context); 226 | this.mEngineConfig.mUid = pref.getInt(ConstantApp.PrefManager.PREF_PROPERTY_UID, 0); 227 | 228 | this.mEngineEventHandler = new MyEngineEventHandler(mContext, this.mEngineConfig); 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/src/main/java/io/agora/openacall/ui/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package io.agora.openacall.ui; 2 | 3 | import android.Manifest; 4 | import android.content.pm.PackageManager; 5 | import android.os.Build; 6 | import android.os.Bundle; 7 | import android.os.Handler; 8 | import android.support.annotation.NonNull; 9 | import android.support.v4.app.ActivityCompat; 10 | import android.support.v4.content.ContextCompat; 11 | import android.support.v4.view.ViewConfigurationCompat; 12 | import android.support.v7.app.AppCompatActivity; 13 | import android.util.DisplayMetrics; 14 | import android.view.*; 15 | import android.widget.Toast; 16 | import io.agora.openacall.AGApplication; 17 | import io.agora.openacall.BuildConfig; 18 | import io.agora.openacall.model.*; 19 | import io.agora.propeller.Constant; 20 | import io.agora.rtc.RtcEngine; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | import java.util.Arrays; 25 | 26 | public abstract class BaseActivity extends AppCompatActivity { 27 | private final static Logger log = LoggerFactory.getLogger(BaseActivity.class); 28 | 29 | @Override 30 | protected void onCreate(Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | 33 | final View layout = findViewById(Window.ID_ANDROID_CONTENT); 34 | ViewTreeObserver vto = layout.getViewTreeObserver(); 35 | vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 36 | @Override 37 | public void onGlobalLayout() { 38 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 39 | layout.getViewTreeObserver().removeOnGlobalLayoutListener(this); 40 | } else { 41 | layout.getViewTreeObserver().removeGlobalOnLayoutListener(this); 42 | } 43 | initUIandEvent(); 44 | } 45 | }); 46 | } 47 | 48 | protected abstract void initUIandEvent(); 49 | 50 | protected abstract void deInitUIandEvent(); 51 | 52 | @Override 53 | protected void onPostCreate(Bundle savedInstanceState) { 54 | super.onPostCreate(savedInstanceState); 55 | 56 | new Handler().postDelayed(new Runnable() { 57 | @Override 58 | public void run() { 59 | if (isFinishing()) { 60 | return; 61 | } 62 | 63 | boolean checkPermissionResult = checkSelfPermissions(); 64 | 65 | if ((Build.VERSION.SDK_INT < Build.VERSION_CODES.M)) { 66 | // so far we do not use OnRequestPermissionsResultCallback 67 | } 68 | } 69 | }, 500); 70 | } 71 | 72 | private boolean checkSelfPermissions() { 73 | return checkSelfPermission(Manifest.permission.RECORD_AUDIO, ConstantApp.PERMISSION_REQ_ID_RECORD_AUDIO) && 74 | checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, ConstantApp.PERMISSION_REQ_ID_WRITE_EXTERNAL_STORAGE); 75 | } 76 | 77 | @Override 78 | protected void onDestroy() { 79 | deInitUIandEvent(); 80 | super.onDestroy(); 81 | } 82 | 83 | public boolean checkSelfPermission(String permission, int requestCode) { 84 | log.debug("checkSelfPermission " + permission + " " + requestCode); 85 | if (ContextCompat.checkSelfPermission(this, 86 | permission) 87 | != PackageManager.PERMISSION_GRANTED) { 88 | 89 | ActivityCompat.requestPermissions(this, 90 | new String[]{permission}, 91 | requestCode); 92 | return false; 93 | } 94 | 95 | if (Manifest.permission.RECORD_AUDIO.equals(permission)) { 96 | ((AGApplication) getApplication()).initWorkerThread(); 97 | } 98 | return true; 99 | } 100 | 101 | protected RtcEngine rtcEngine() { 102 | return ((AGApplication) getApplication()).getWorkerThread().getRtcEngine(); 103 | } 104 | 105 | protected final WorkerThread worker() { 106 | return ((AGApplication) getApplication()).getWorkerThread(); 107 | } 108 | 109 | protected final EngineConfig config() { 110 | return ((AGApplication) getApplication()).getWorkerThread().getEngineConfig(); 111 | } 112 | 113 | protected final MyEngineEventHandler event() { 114 | return ((AGApplication) getApplication()).getWorkerThread().eventHandler(); 115 | } 116 | 117 | public final void showLongToast(final String msg) { 118 | this.runOnUiThread(new Runnable() { 119 | @Override 120 | public void run() { 121 | Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG).show(); 122 | } 123 | }); 124 | } 125 | 126 | @Override 127 | public void onRequestPermissionsResult(int requestCode, 128 | @NonNull String permissions[], @NonNull int[] grantResults) { 129 | log.debug("onRequestPermissionsResult " + requestCode + " " + Arrays.toString(permissions) + " " + Arrays.toString(grantResults)); 130 | switch (requestCode) { 131 | case ConstantApp.PERMISSION_REQ_ID_RECORD_AUDIO: { 132 | if (grantResults.length > 0 133 | && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 134 | checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, ConstantApp.PERMISSION_REQ_ID_WRITE_EXTERNAL_STORAGE); 135 | ((AGApplication) getApplication()).initWorkerThread(); 136 | } else { 137 | finish(); 138 | } 139 | break; 140 | } 141 | case ConstantApp.PERMISSION_REQ_ID_WRITE_EXTERNAL_STORAGE: { 142 | if (grantResults.length > 0 143 | && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 144 | } else { 145 | finish(); 146 | } 147 | break; 148 | } 149 | } 150 | } 151 | 152 | protected CurrentUserSettings vSettings() { 153 | return AGApplication.mAudioSettings; 154 | } 155 | 156 | protected int virtualKeyHeight() { 157 | boolean hasPermanentMenuKey = ViewConfigurationCompat.hasPermanentMenuKey(ViewConfiguration.get(getApplication())); 158 | 159 | DisplayMetrics metrics = new DisplayMetrics(); 160 | Display display = getWindowManager().getDefaultDisplay(); 161 | 162 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 163 | display.getRealMetrics(metrics); 164 | } else { 165 | display.getMetrics(metrics); 166 | } 167 | 168 | int fullHeight = metrics.heightPixels; 169 | 170 | display.getMetrics(metrics); 171 | 172 | return fullHeight - metrics.heightPixels; 173 | } 174 | 175 | protected void initVersionInfo() { 176 | String version = "V " + BuildConfig.VERSION_NAME + "(Build: " + BuildConfig.VERSION_CODE 177 | + ", " + ConstantApp.APP_BUILD_DATE + ", SDK: " + Constant.MEDIA_SDK_VERSION + ")"; 178 | // TextView textVersion = (TextView) findViewById(R.id.app_version); 179 | // textVersion.setText(version); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/src/main/java/io/agora/openacall/ui/MainActivity.java: -------------------------------------------------------------------------------- 1 | package io.agora.openacall.ui; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.text.Editable; 6 | import android.text.TextUtils; 7 | import android.text.TextWatcher; 8 | import android.view.Menu; 9 | import android.view.MenuItem; 10 | import android.view.View; 11 | import android.widget.EditText; 12 | import io.agora.openacall.R; 13 | import io.agora.openacall.model.ConstantApp; 14 | 15 | public class MainActivity extends BaseActivity { 16 | @Override 17 | protected void onCreate(Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | setContentView(R.layout.activity_main); 20 | } 21 | 22 | @Override 23 | protected void initUIandEvent() { 24 | EditText v_channel = (EditText) findViewById(R.id.channel_name); 25 | v_channel.addTextChangedListener(new TextWatcher() { 26 | @Override 27 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 28 | 29 | } 30 | 31 | @Override 32 | public void onTextChanged(CharSequence s, int start, int before, int count) { 33 | 34 | } 35 | 36 | @Override 37 | public void afterTextChanged(Editable s) { 38 | boolean isEmpty = TextUtils.isEmpty(s.toString()); 39 | findViewById(R.id.button_join).setEnabled(!isEmpty); 40 | } 41 | }); 42 | 43 | String lastChannelName = vSettings().mChannelName; 44 | if (!TextUtils.isEmpty(lastChannelName)) { 45 | v_channel.setText(lastChannelName); 46 | v_channel.setSelection(lastChannelName.length()); 47 | } 48 | } 49 | 50 | @Override 51 | protected void deInitUIandEvent() { 52 | } 53 | 54 | @Override 55 | public boolean onCreateOptionsMenu(final Menu menu) { 56 | return super.onCreateOptionsMenu(menu); 57 | } 58 | 59 | @Override 60 | public boolean onOptionsItemSelected(MenuItem item) { 61 | return super.onOptionsItemSelected(item); 62 | } 63 | 64 | public void onClickJoin(View view) { 65 | forwardToRoom(); 66 | } 67 | 68 | public void forwardToRoom() { 69 | EditText v_channel = (EditText) findViewById(R.id.channel_name); 70 | String channel = v_channel.getText().toString(); 71 | vSettings().mChannelName = channel; 72 | 73 | Intent i = new Intent(MainActivity.this, ChatActivity.class); 74 | i.putExtra(ConstantApp.ACTION_KEY_CHANNEL_NAME, channel); 75 | 76 | startActivity(i); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/src/main/java/io/agora/propeller/Constant.java: -------------------------------------------------------------------------------- 1 | package io.agora.propeller; 2 | 3 | import io.agora.rtc.RtcEngine; 4 | 5 | public class Constant { 6 | 7 | public static final String MEDIA_SDK_VERSION; 8 | 9 | static { 10 | String sdk = "undefined"; 11 | try { 12 | sdk = RtcEngine.getSdkVersion(); 13 | } catch (Throwable e) { 14 | } 15 | MEDIA_SDK_VERSION = sdk; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/src/main/java/io/agora/propeller/ui/AGLinearLayout.java: -------------------------------------------------------------------------------- 1 | package io.agora.propeller.ui; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.NonNull; 5 | import android.util.AttributeSet; 6 | import android.view.KeyEvent; 7 | import android.view.MotionEvent; 8 | import android.widget.LinearLayout; 9 | 10 | public class AGLinearLayout extends LinearLayout { 11 | public AGLinearLayout(Context context) { 12 | super(context); 13 | } 14 | 15 | public AGLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { 16 | super(context, attrs, defStyleAttr); 17 | } 18 | 19 | public AGLinearLayout(Context context, AttributeSet attrs) { 20 | super(context, attrs); 21 | } 22 | 23 | @Override 24 | public boolean dispatchTouchEvent(@NonNull MotionEvent event) { 25 | return ViewUtil.checkDoubleTouchEvent(event, this) || super.dispatchTouchEvent(event); 26 | } 27 | 28 | @Override 29 | public boolean dispatchKeyEvent(@NonNull KeyEvent event) { 30 | return ViewUtil.checkDoubleKeyEvent(event, this) || super.dispatchKeyEvent(event); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/src/main/java/io/agora/propeller/ui/ViewUtil.java: -------------------------------------------------------------------------------- 1 | package io.agora.propeller.ui; 2 | 3 | import android.graphics.drawable.Drawable; 4 | import android.os.Build; 5 | import android.os.SystemClock; 6 | import android.view.KeyEvent; 7 | import android.view.MotionEvent; 8 | import android.view.View; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | public class ViewUtil { 13 | protected static final boolean DEBUG_ENABLED = false; 14 | 15 | private final static Logger log = LoggerFactory.getLogger(ViewUtil.class); 16 | 17 | private static final int DEFAULT_TOUCH_TIMESTAMP = -1; // first time 18 | 19 | private static final int TOUCH_COOL_DOWN_TIME = 500; // ms 20 | 21 | private static long mLastTouchTime = DEFAULT_TOUCH_TIMESTAMP; 22 | 23 | /* package */ 24 | static final boolean checkDoubleTouchEvent(MotionEvent event, View view) { 25 | if (DEBUG_ENABLED) { 26 | log.debug("dispatchTouchEvent " + mLastTouchTime + " " + event); 27 | } 28 | 29 | if (event.getAction() == MotionEvent.ACTION_DOWN) { // only check touch down event 30 | if (mLastTouchTime == DEFAULT_TOUCH_TIMESTAMP || (SystemClock.elapsedRealtime() - mLastTouchTime) >= TOUCH_COOL_DOWN_TIME) { 31 | mLastTouchTime = SystemClock.elapsedRealtime(); 32 | } else { 33 | log.warn("too many touch events " + view + " " + MotionEvent.ACTION_DOWN); 34 | return true; 35 | } 36 | } 37 | return false; 38 | } 39 | 40 | /* package */ 41 | static final boolean checkDoubleKeyEvent(KeyEvent event, View view) { 42 | if (DEBUG_ENABLED) { 43 | log.debug("dispatchKeyEvent " + mLastTouchTime + " " + event); 44 | } 45 | 46 | if (event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_ENTER) { 47 | if (mLastTouchTime != DEFAULT_TOUCH_TIMESTAMP && (SystemClock.elapsedRealtime() - mLastTouchTime) < TOUCH_COOL_DOWN_TIME) { 48 | log.warn("too many key events " + view + " " + KeyEvent.ACTION_DOWN); 49 | return true; 50 | } 51 | mLastTouchTime = SystemClock.elapsedRealtime(); 52 | } 53 | 54 | return false; 55 | } 56 | 57 | public static void setBackground(View view, Drawable drawable) { 58 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 59 | view.setBackground(drawable); 60 | } else { 61 | view.setBackgroundDrawable(drawable); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/src/main/jniLibs/armeabi-v7a/PLACEHOLDER: -------------------------------------------------------------------------------- 1 | libagora-rtc-sdk-jni.so 2 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/src/main/jniLibs/x86/PLACEHOLDER: -------------------------------------------------------------------------------- 1 | libagora-rtc-sdk-jni.so 2 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/src/main/res/drawable-xxxhdpi/btn_endcall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO/Basic-Audio-Call/da10f89abf4677cdb52b9236d656bb954d791471/Group-Voice-Call/OpenVoiceCall-Android/app/src/main/res/drawable-xxxhdpi/btn_endcall.png -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/src/main/res/drawable-xxxhdpi/btn_mute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO/Basic-Audio-Call/da10f89abf4677cdb52b9236d656bb954d791471/Group-Voice-Call/OpenVoiceCall-Android/app/src/main/res/drawable-xxxhdpi/btn_mute.png -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/src/main/res/drawable-xxxhdpi/btn_speaker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO/Basic-Audio-Call/da10f89abf4677cdb52b9236d656bb954d791471/Group-Voice-Call/OpenVoiceCall-Android/app/src/main/res/drawable-xxxhdpi/btn_speaker.png -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/src/main/res/drawable-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO/Basic-Audio-Call/da10f89abf4677cdb52b9236d656bb954d791471/Group-Voice-Call/OpenVoiceCall-Android/app/src/main/res/drawable-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/src/main/res/layout/activity_chat.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 13 | 23 | 24 | 32 | 33 | 41 | 42 | 48 | 49 | 54 | 55 | 62 | 63 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /Group-Voice-Call/OpenVoiceCall-Android/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 17 | 18 | 25 | 26 | 37 | 38 | 40 | 51 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /One-to-One-Voice/Agora-iOS-Voice-Tutorial-Swift-1to1/Agora-iOS-Voice-Tutorial/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 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | NSMicrophoneUsageDescription 24 | 25 | UIBackgroundModes 26 | 27 | audio 28 | 29 | UILaunchStoryboardName 30 | LaunchScreen 31 | UIMainStoryboardFile 32 | Main 33 | UIRequiredDeviceCapabilities 34 | 35 | armv7 36 | 37 | UISupportedInterfaceOrientations 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationLandscapeLeft 41 | UIInterfaceOrientationLandscapeRight 42 | 43 | UISupportedInterfaceOrientations~ipad 44 | 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationPortraitUpsideDown 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /One-to-One-Voice/Agora-iOS-Voice-Tutorial-Swift-1to1/Agora-iOS-Voice-Tutorial/VoiceChatViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VoiceChatViewController.swift 3 | // Agora-iOS-Voice-Tutorial 4 | // 5 | // Created by GongYuhua on 2017/4/10. 6 | // Copyright © 2017年 Agora. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AgoraRtcKit 11 | 12 | class VoiceChatViewController: UIViewController { 13 | 14 | @IBOutlet weak var controlButtonsView: UIView! 15 | 16 | var agoraKit: AgoraRtcEngineKit! 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | 21 | initializeAgoraEngine() 22 | joinChannel() 23 | } 24 | 25 | func initializeAgoraEngine() { 26 | // Initializes the Agora engine with your app ID. 27 | agoraKit = AgoraRtcEngineKit.sharedEngine(withAppId: AppID, delegate: nil) 28 | } 29 | 30 | func joinChannel() { 31 | // Allows a user to join a channel. 32 | agoraKit.joinChannel(byToken: Token, channelId: "demoChannel", info:nil, uid:0) {[unowned self] (sid, uid, elapsed) -> Void in 33 | // Joined channel "demoChannel" 34 | self.agoraKit.setEnableSpeakerphone(true) 35 | UIApplication.shared.isIdleTimerDisabled = true 36 | } 37 | } 38 | 39 | @IBAction func didClickHangUpButton(_ sender: UIButton) { 40 | leaveChannel() 41 | } 42 | 43 | func leaveChannel() { 44 | agoraKit.leaveChannel(nil) 45 | hideControlButtons() 46 | 47 | UIApplication.shared.isIdleTimerDisabled = false 48 | } 49 | 50 | func hideControlButtons() { 51 | controlButtonsView.isHidden = true 52 | } 53 | 54 | @IBAction func didClickMuteButton(_ sender: UIButton) { 55 | sender.isSelected = !sender.isSelected 56 | // Stops/Resumes sending the local audio stream. 57 | agoraKit.muteLocalAudioStream(sender.isSelected) 58 | } 59 | 60 | @IBAction func didClickSwitchSpeakerButton(_ sender: UIButton) { 61 | sender.isSelected = !sender.isSelected 62 | // Enables/Disables the audio playback route to the speakerphone. 63 | // 64 | // This method sets whether the audio is routed to the speakerphone or earpiece. After calling this method, the SDK returns the onAudioRouteChanged callback to indicate the changes. 65 | agoraKit.setEnableSpeakerphone(sender.isSelected) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /One-to-One-Voice/Agora-iOS-Voice-Tutorial-Swift-1to1/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Agora.io 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /One-to-One-Voice/Agora-iOS-Voice-Tutorial-Swift-1to1/README.md: -------------------------------------------------------------------------------- 1 | # Agora iOS Voice Tutorial for Swift - 1to1 2 | 3 | *其他语言版本: [简体中文](README.zh.md)* 4 | 5 | The Agora iOS Voice Tutorial for Swift 1to1 Sample App is an open-source demo that will help you get voice chat integrated directly into your iOS applications using the Agora Voice SDK. 6 | 7 | With this sample app, you can: 8 | 9 | - Join / leave channel 10 | - Mute / unmute audio 11 | - Switch speaker 12 | 13 | ## Prerequisites 14 | - Xcode 10.0+ 15 | - Physical iOS device (iPhone or iPad) 16 | - iOS simulator is NOT supported 17 | 18 | ## Quick Start 19 | 20 | This section shows you how to prepare, build, and run the sample application. 21 | 22 | ### Obtain an App Id 23 | 24 | To build and run the sample application, get an App Id: 25 | 26 | 1. Create a developer account at [agora.io](https://dashboard.agora.io/signin/). Once you finish the signup process, you will be redirected to the Dashboard. 27 | 2. Navigate in the Dashboard tree on the left to **Projects** > **Project List**. 28 | 3. Save the **App Id** from the Dashboard for later use. 29 | 4. Generate a temp **Access Token** (valid for 24 hours) from dashboard page with given channel name, save for later use. 30 | 31 | 5. Open `Agora iOS Voice Tutorial.xcodeproj` and edit the `AppID.swift` file. Update `<#Your App Id#>` with your App Id, and assign the token variable with the temp Access Token generated from dashboard. 32 | 33 | ``` Swift 34 | let AppID: String = <#Your App Id#> 35 | // assign Token to nil if you have not enabled app certificate 36 | let Token: String? = <#Temp Token#> 37 | ``` 38 | 39 | ### Integrate the Agora Audio SDK 40 | 41 | 1. Download the [Agora Voice SDK](https://www.agora.io/en/download/). Unzip the downloaded SDK package and copy the following files from the SDK `libs` folder into the sample application `Agora iOS Voice Tutorial` folder. 42 | 43 | - `AograRtcKit.framework` 44 | 45 | 2. Connect your iPhone or iPad device and run the project. Ensure a valid provisioning profile is applied or your project will not run. 46 | 47 | ## Contact Us 48 | 49 | - For potential issues, take a look at our [FAQ](https://docs.agora.io/en/faq) first 50 | - Dive into [Agora SDK Samples](https://github.com/AgoraIO) to see more tutorials 51 | - Take a look at [Agora Use Case](https://github.com/AgoraIO-usecase) for more complicated real use case 52 | - Repositories managed by developer communities can be found at [Agora Community](https://github.com/AgoraIO-Community) 53 | - You can find full API documentation at [Document Center](https://docs.agora.io/en/) 54 | - If you encounter problems during integration, you can ask question in [Stack Overflow](https://stackoverflow.com/questions/tagged/agora.io) 55 | - You can file bugs about this sample at [issue](https://github.com/AgoraIO/Basic-Audio-Call/issues) 56 | 57 | ## License 58 | 59 | The MIT License (MIT). 60 | -------------------------------------------------------------------------------- /One-to-One-Voice/Agora-iOS-Voice-Tutorial-Swift-1to1/README.zh.md: -------------------------------------------------------------------------------- 1 | # Agora iOS Voice Tutorial for Swift - 1to1 2 | 3 | *Read this in other languages: [English](README.md)* 4 | 5 | 这个开源示例项目演示了如何快速集成Agora音频SDK,实现1对1音频通话。 6 | 7 | 在这个示例项目中包含了以下功能: 8 | 9 | - 加入通话和离开通话; 10 | - 静音和解除静音; 11 | - 切换扬声器和听筒; 12 | 13 | ## 环境准备 14 | 15 | - XCode 10.0 + 16 | - iOS 真机设备 17 | - 不支持模拟器 18 | 19 | ## 运行示例程序 20 | 21 | 这个段落主要讲解了如何编译和运行实例程序。 22 | 23 | ### 创建Agora账号并获取AppId 24 | 25 | 在编译和启动实例程序前,您需要首先获取一个可用的App Id: 26 | 27 | 1. 在 [agora.io](https://dashboard.agora.io/signin/) 创建一个开发者账号 28 | 2. 前往后台页面,点击左部导航栏的 **项目 > 项目列表** 菜单 29 | 3. 复制后台的 **App Id** 并备注,稍后启动应用时会用到它 30 | 4. 在项目页面生成临时 **Access Token** (24小时内有效)并备注,注意生成的Token只能适用于对应的频道名。 31 | 32 | 5. 将 AppID 和 Token 填写进 AppID.swift 33 | 34 | ``` 35 | let AppID: String = <#Your App Id#> 36 | // 如果你没有打开Token功能,token可以直接给nil 37 | let Token: String? = <#Temp Token#> 38 | ``` 39 | 40 | ### 集成 Agora 音频 SDK 41 | 42 | 1. 在 [Agora.io SDK](https://www.agora.io/cn/blog/download/) 下载 **语音通话 + 直播 SDK**,解压后将其中**libs**文件夹中的下列文件拷贝到本项目的 Agora iOS Voice Tutorial 文件夹下。 43 | 44 | - AgoraRtcKit.framework 45 | 46 | 2. 最后使用 Xcode 打开 Agora iOS Voice Tutorial.xcodeproj,连接 iPhone/iPad 测试设备,设置有效的开发者签名后即可运行。 47 | 48 | ## 联系我们 49 | 50 | - 如果你遇到了困难,可以先参阅 [常见问题](https://docs.agora.io/cn/faq) 51 | - 如果你想了解更多官方示例,可以参考 [官方SDK示例](https://github.com/AgoraIO) 52 | - 如果你想了解声网SDK在复杂场景下的应用,可以参考 [官方场景案例](https://github.com/AgoraIO-usecase) 53 | - 如果你想了解声网的一些社区开发者维护的项目,可以查看 [社区](https://github.com/AgoraIO-Community) 54 | - 完整的 API 文档见 [文档中心](https://docs.agora.io/cn/) 55 | - 若遇到问题需要开发者帮助,你可以到 [开发者社区](https://rtcdeveloper.com/) 提问 56 | - 如果需要售后技术支持, 你可以在 [Agora Dashboard](https://dashboard.agora.io) 提交工单 57 | - 如果发现了示例代码的 bug,欢迎提交 [issue](https://github.com/AgoraIO/Basic-Audio-Call/issues) 58 | 59 | ## 代码许可 60 | 61 | The MIT License (MIT). 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Basic Audio Call 2 | 3 | _Other Languages: [简体中文](README.zh.md)_ 4 | 5 | Agora provides a set of sample applications demonstrating how to integrate the Agora Audio SDK for: 6 | 7 | - [1-to-1 voice calls](#1-to-1-voice-calls) 8 | - [Group vocie calls](#group-voice-calls) 9 | 10 | ## Platforms Supported 11 | 12 | ### 1-to-1 Voice Calls 13 | 14 | Sample applications for 1-to-1 voice calls are provided for Android, iOS. 15 | 16 | The 1-to-1 voice call sample applications, demonstrate how to: 17 | 18 | - Join / leave a channel 19 | - Mute / unmute audio 20 | - Switch speaker 21 | 22 | 23 | Project Folder Name|Platform|Description 24 | ---|---|--- 25 | [Agora Android Voice Tutorial - 1 to 1](./One-to-One-Voice/Agora-Android-Voice-Tutorial-1to1)|Android|Agora Android Voice Tutorial 1 to 1 26 | [Agora iOS Voice Tutorial for Swift - 1 to 1](./One-to-One-Voice/Agora-iOS-Voice-Tutorial-Swift-1to1)|iOS|Agora iOS Voice Tutorial 1 to 1 using Swift 27 | 28 | 29 | 30 | ### Group Voice Calls 31 | 32 | Sample applications for group voice calls are provided for Android, iOS. 33 | 34 | 35 | The group voice call sample applications, demonstrate how to: 36 | 37 | - Join / leave a channel 38 | - Mute / unmute audio 39 | - Switch speaker 40 | 41 | Project Folder Name|Platform|Description 42 | ---|---|--- 43 | [Open Voice Call for Android](./Group-Voice-Call/OpenVoiceCall-Android)|Android|Open Voice Call for Android 44 | [Open Voice Call iOS for Objective C](./Group-Voice-Call/OpenVoiceCall-iOS-Objective-C)|iOS|Open Voice Call for iOS using Objective C 45 | [Open Voice Call iOS for Swift](./Group-Voice-Call/OpenVoiceCall-iOS)|iOS|Open Voice Call for iOS using Swift 46 | 47 | ## Contact Us 48 | 49 | - For potential issues, take a look at our [FAQ](https://docs.agora.io/en/faq) first 50 | - Dive into [Agora SDK Samples](https://github.com/AgoraIO) to see more tutorials 51 | - Take a look at [Agora Use Case](https://github.com/AgoraIO-usecase) for more complicated real use case 52 | - Repositories managed by developer communities can be found at [Agora Community](https://github.com/AgoraIO-Community) 53 | - You can find full API documentation at [Document Center](https://docs.agora.io/en/) 54 | - If you encounter problems during integration, you can ask question in [Stack Overflow](https://stackoverflow.com/questions/tagged/agora.io) 55 | - You can file bugs about this sample at [issue](https://github.com/AgoraIO/Basic-Audio-Call/issues) 56 | 57 | 58 | ## License 59 | 60 | All sample applications are licensed under the MIT License (MIT). [View the license](LICENSE.md). 61 | -------------------------------------------------------------------------------- /README.zh.md: -------------------------------------------------------------------------------- 1 | # 音频通话 2 | 3 | _其他语言:[English](README.md)_ 4 | 5 | Agora 提供一系列的示例应用,展示如何集成 Agora 音频 SDK。 6 | 7 | - [一对一音频通话](#一对一音频通话) 8 | - [多人音频通话](#多人音频通话) 9 | 10 | ## 支持的平台 11 | 12 | ### 一对一音频通话 13 | 14 | 一对一音频通话的示例应用包含 Android, iOS平台。 15 | 16 | 一对一音频通话的示例应用,展示了: 17 | 18 | - 加入通话和离开通话 19 | - 静音和解除静音 20 | - 外放和听筒切换 21 | 22 | 项目文件夹名称|平台|描述 23 | ---|---|--- 24 | [Agora Android Voice Tutorial - 1 to 1](./One-to-One-Voice/Agora-Android-Voice-Tutorial-1to1)|Android|Agora Android 一对一音频通话指南 25 | [Agora iOS Voice Tutorial for Swift - 1 to 1](./One-to-One-Voice/Agora-iOS-Voice-Tutorial-Swift-1to1)|iOS|Agora iOS 一对一音频通话指南(Swift) 26 | 27 | ### 多人音频通话 28 | 29 | 多人音频通话的示例应用包含 Web, Android, iOS, MacOS, Windows 以及 Linux 平台。 30 | 31 | 32 | 多人音频通话的示例应用,展示了: 33 | 34 | - 加入通话和离开通话 35 | - 静音和解除静音 36 | - 外放和听筒切换 37 | 38 | 项目文件夹名称|平台|描述 39 | ---|---|--- 40 | [Open Voice Call for Android](./Group-Voice-Call/OpenVoiceCall-Android)|Android|Android 多人音频通话指南 41 | [Open Voice Call iOS for Objective C](./Group-Voice-Call/OpenVoiceCall-iOS-Objective-C)|iOS|iOS 多人音频通话指南(Objective C) 42 | [Open Voice Call iOS for Swift](./Group-Voice-Call/OpenVoiceCall-iOS)|iOS|iOS 多人音频通话指南(Swift) 43 | 44 | 45 | ## 联系我们 46 | 47 | - 如果你遇到了困难,可以先参阅 [常见问题](https://docs.agora.io/cn/faq) 48 | - 如果你想了解更多官方示例,可以参考 [官方SDK示例](https://github.com/AgoraIO) 49 | - 如果你想了解声网SDK在复杂场景下的应用,可以参考 [官方场景案例](https://github.com/AgoraIO-usecase) 50 | - 如果你想了解声网的一些社区开发者维护的项目,可以查看 [社区](https://github.com/AgoraIO-Community) 51 | - 完整的 API 文档见 [文档中心](https://docs.agora.io/cn/) 52 | - 若遇到问题需要开发者帮助,你可以到 [开发者社区](https://rtcdeveloper.com/) 提问 53 | - 如果需要售后技术支持, 你可以在 [Agora Dashboard](https://dashboard.agora.io) 提交工单 54 | - 如果发现了示例代码的 bug,欢迎提交 [issue](https://github.com/AgoraIO/Basic-Audio-Call/issues) 55 | 56 | ## 代码许可 57 | 58 | MIT许可证 [查看许可](LICENSE.md)。 59 | --------------------------------------------------------------------------------