├── .github └── workflows │ ├── build.yml │ └── publish.yml ├── .gitignore ├── .release-it.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── android ├── build.gradle ├── consumer-rules.pro ├── gradle.properties ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── io │ └── agora │ └── rtc │ ├── base │ ├── .gitignore │ ├── Annotations.java │ ├── BeanCovertor.kt │ ├── Callback.kt │ ├── EnumCovertor.kt │ ├── Extensions.kt │ ├── MediaObserver.kt │ ├── RtcChannel.kt │ ├── RtcChannelEvent.kt │ ├── RtcEngine.kt │ ├── RtcEngineEvent.kt │ ├── RtcEnginePlugin.kt │ ├── RtcEngineRegistry.kt │ ├── RtcSurfaceView.kt │ └── RtcTextureView.kt │ └── uni │ ├── AgoraRtcChannelModule.kt │ ├── AgoraRtcEngineModule.kt │ ├── AgoraRtcSurfaceView.kt │ ├── AgoraRtcTextureView.kt │ └── UniCallback.kt ├── example ├── .gitignore ├── App.vue ├── common │ └── agora.config.js ├── components │ ├── Agora-RTC-JS │ │ ├── RtcSurfaceView.nvue │ │ ├── RtcTextureView.nvue │ │ ├── common │ │ │ ├── Classes.js │ │ │ ├── Classes.js.map │ │ │ ├── Enums.js │ │ │ ├── Enums.js.map │ │ │ ├── RtcChannel.native.js │ │ │ ├── RtcChannel.native.js.map │ │ │ ├── RtcEngine.native.js │ │ │ ├── RtcEngine.native.js.map │ │ │ ├── RtcEvents.js │ │ │ └── RtcEvents.js.map │ │ ├── index.js │ │ └── index.js.map │ ├── uni-icons │ │ ├── icons.js │ │ ├── uni-icons.vue │ │ └── uni.ttf │ └── uni-indexed-list │ │ ├── uni-indexed-list-item.vue │ │ └── uni-indexed-list.vue ├── js_sdk │ └── wa-permission │ │ └── permission.js ├── main.js ├── manifest.json ├── pages.json ├── pages │ ├── Advanced │ │ └── MultiChannel.nvue │ ├── Basic │ │ ├── JoinChannelAudio.nvue │ │ ├── JoinChannelVideo.nvue │ │ └── StringUid.nvue │ └── index │ │ └── index.vue ├── static │ └── logo.png └── uni.scss ├── install.sh ├── ios ├── AgoraRtcUniPlugin.podspec ├── AgoraRtcUniPlugin.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ └── AgoraRtcUniPlugin.xcscheme ├── AgoraRtcUniPlugin │ ├── Base │ │ ├── .gitignore │ │ ├── AgoraRtcEngineKit.h │ │ ├── BeanCovertor.swift │ │ ├── Callback.swift │ │ ├── Extensions.swift │ │ ├── MediaObserver.swift │ │ ├── RtcChannel.swift │ │ ├── RtcChannelEvent.swift │ │ ├── RtcEngine.swift │ │ ├── RtcEngineEvent.swift │ │ ├── RtcEnginePlugin.h │ │ ├── RtcEnginePluginRegistrant.swift │ │ ├── RtcEngineRegistry.swift │ │ └── RtcSurfaceView.swift │ ├── Info.plist │ └── Uni │ │ ├── AgoraRtcChannelModule.h │ │ ├── AgoraRtcChannelModule.m │ │ ├── AgoraRtcChannelModule.swift │ │ ├── AgoraRtcEngineModule.h │ │ ├── AgoraRtcEngineModule.m │ │ ├── AgoraRtcEngineModule.swift │ │ ├── AgoraRtcSurfaceView.swift │ │ ├── AgoraRtcUniPlugin-bridging-header.h │ │ └── UniCallback.swift └── libs │ └── .gitkeep ├── package.json ├── package └── Agora-RTC │ ├── android │ └── .gitkeep │ ├── ios │ └── .gitkeep │ └── package.json ├── src ├── components │ └── Agora-RTC-JS │ │ ├── RtcSurfaceView.nvue │ │ ├── RtcTextureView.nvue │ │ ├── common │ │ ├── Classes.ts │ │ ├── Enums.ts │ │ ├── RtcChannel.native.ts │ │ ├── RtcEngine.native.ts │ │ └── RtcEvents.ts │ │ └── index.ts └── package.json ├── to-framework.sh ├── tsconfig.json └── yarn.lock /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build Plugin 2 | 3 | concurrency: 4 | group: ${{ github.workflow }}-${{ github.ref }} 5 | cancel-in-progress: true 6 | 7 | on: 8 | workflow_dispatch: 9 | push: 10 | branches: 11 | - master 12 | - main 13 | tags: 14 | - v*.*.** 15 | pull_request: 16 | branches: 17 | - master 18 | - main 19 | - release/* 20 | 21 | jobs: 22 | build-android: 23 | runs-on: ubuntu-latest 24 | env: 25 | SDK_ROOT: Android-SDK@3.6.18.81676_20230117 26 | steps: 27 | - name: Download unzip 28 | run: | 29 | wget https://download.agora.io/sdk/release/uni-app-sdk-android.zip 30 | unzip uni-app-sdk-android.zip 31 | 32 | - uses: actions/checkout@v3 33 | with: 34 | path: Agora-Uniapp-SDK 35 | 36 | - uses: actions/setup-java@v3 37 | with: 38 | distribution: 'adopt' 39 | java-version: '11' 40 | 41 | - name: Modify settings.gradle 42 | run: | 43 | echo "include ':uniplugin_agora_rtc'" >> settings.gradle 44 | echo "project(':uniplugin_agora_rtc').projectDir = new File('../../Agora-Uniapp-SDK', 'android')" >> settings.gradle 45 | working-directory: ${{ env.SDK_ROOT }}/UniPlugin-Hello-AS 46 | 47 | - name: Gradle build 48 | run: | 49 | sh gradlew :uniplugin_agora_rtc:assembleRelease 50 | working-directory: ${{ env.SDK_ROOT }}/UniPlugin-Hello-AS 51 | 52 | - name: Archive 53 | run: | 54 | cp -rp android/build/outputs/aar/*.aar package/Agora-RTC/android/ 55 | working-directory: Agora-Uniapp-SDK 56 | 57 | - uses: actions/upload-artifact@v4 58 | if: ${{ github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/') }} 59 | with: 60 | name: Agora-RTC 61 | path: | 62 | Agora-Uniapp-SDK/package/Agora-RTC/*/*.aar 63 | 64 | build-ios: 65 | runs-on: macos-latest 66 | env: 67 | SDK_ROOT: SDK 68 | steps: 69 | - name: Download unzip 70 | run: | 71 | wget https://download.agora.io/sdk/release/uni-app-sdk-ios.zip 72 | unzip uni-app-sdk-ios.zip 73 | 74 | - uses: actions/checkout@v3 75 | with: 76 | path: Agora-Uniapp-SDK 77 | 78 | - name: Modify Podfile 79 | run: | 80 | pod init 81 | sed "s/# Pods for .*/pod 'AgoraRtcUniPlugin', :path => '\.\.\/\.\.\/Agora-Uniapp-SDK\/ios'/g" Podfile > tmp 82 | mv tmp Podfile 83 | working-directory: ${{ env.SDK_ROOT }}/HBuilder-uniPluginDemo 84 | 85 | - name: Xcodebuild build 86 | run: | 87 | pod install 88 | xcodebuild -workspace ./HBuilder-uniPlugin.xcworkspace -scheme AgoraRtcUniPlugin -sdk iphoneos -configuration "Release" build 89 | working-directory: ${{ env.SDK_ROOT }}/HBuilder-uniPluginDemo 90 | 91 | - name: Archive 92 | run: | 93 | cp -rp ../${{ env.SDK_ROOT }}/HBuilder-uniPluginDemo/Pods/AgoraRtcEngine_Special_iOS/*.xcframework package/Agora-RTC/ios/ 94 | cp -rp ~/Library/Developer/Xcode/DerivedData/*/Build/Products/Release-iphoneos/AgoraRtcUniPlugin/AgoraRtcUniPlugin.framework package/Agora-RTC/ios/ 95 | working-directory: Agora-Uniapp-SDK 96 | 97 | - uses: actions/upload-artifact@v4 98 | if: ${{ github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/') }} 99 | with: 100 | name: Agora-RTC 101 | path: | 102 | Agora-Uniapp-SDK/package/Agora-RTC/*/*.framework 103 | Agora-Uniapp-SDK/package/Agora-RTC/*/*.xcframework 104 | !Agora-Uniapp-SDK/package/Agora-RTC/*/*Extension.xcframework 105 | 106 | build-ts: 107 | runs-on: ubuntu-latest 108 | steps: 109 | - uses: actions/checkout@v3 110 | 111 | - uses: actions/setup-node@v3 112 | with: 113 | node-version: '16' 114 | cache: 'yarn' 115 | - run: yarn 116 | 117 | - uses: actions/upload-artifact@v4 118 | if: ${{ github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/') }} 119 | with: 120 | name: Agora-RTC 121 | path: | 122 | package/Agora-RTC/package.json 123 | 124 | - uses: actions/upload-artifact@v4 125 | if: ${{ github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/') }} 126 | with: 127 | name: Agora-RTC-JS 128 | path: | 129 | lib/module/ 130 | 131 | release: 132 | if: startsWith(github.ref, 'refs/tags/') 133 | runs-on: ubuntu-latest 134 | needs: [build-android, build-ios, build-ts] 135 | steps: 136 | - uses: actions/download-artifact@v3 137 | 138 | - run: | 139 | zip -r Agora-RTC.zip Agora-RTC 140 | zip -r Agora-RTC-JS.zip Agora-RTC-JS 141 | 142 | - name: Release 143 | uses: softprops/action-gh-release@v1 144 | with: 145 | files: | 146 | Agora-RTC.zip 147 | Agora-RTC-JS.zip 148 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | increment: 7 | description: 'Increment "major", "minor", "patch", or "pre*" version; or specify version [default: "patch"]' 8 | required: true 9 | default: 'patch' 10 | type: string 11 | dry-run: 12 | description: 'Do not touch or write anything, but show the commands' 13 | default: true 14 | type: boolean 15 | 16 | jobs: 17 | release-it: 18 | runs-on: ubuntu-latest 19 | env: 20 | GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} 21 | steps: 22 | - uses: actions/checkout@v3 23 | with: 24 | fetch-depth: 0 25 | token: ${{ secrets.GH_TOKEN }} 26 | 27 | - name: Setup 28 | uses: actions/setup-node@v3 29 | with: 30 | node-version: 16 31 | 32 | - run: | 33 | yarn 34 | 35 | - name: Dry Run Release 36 | if: ${{ inputs.dry-run }} 37 | run: | 38 | yarn release ${{ inputs.increment }} -d --ci 39 | 40 | - name: Release 41 | if: ${{ !inputs.dry-run }} 42 | run: | 43 | git config --global user.email "${{ secrets.GIT_EMAIL }}" 44 | git config --global user.name "${{ secrets.GIT_USERNAME }}" 45 | yarn release ${{ inputs.increment }} --ci --plugins.@release-it/bumper.out=src/package.json --plugins.@release-it/bumper.out=package/Agora-RTC/package.json 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # XDE 6 | .expo/ 7 | 8 | # VSCode 9 | .vscode/ 10 | jsconfig.json 11 | 12 | # Xcode 13 | # 14 | build/ 15 | *.pbxuser 16 | !default.pbxuser 17 | *.mode1v3 18 | !default.mode1v3 19 | *.mode2v3 20 | !default.mode2v3 21 | *.perspectivev3 22 | !default.perspectivev3 23 | xcuserdata 24 | *.xccheckout 25 | *.moved-aside 26 | DerivedData 27 | *.hmap 28 | *.ipa 29 | *.xcuserstate 30 | project.xcworkspace 31 | 32 | # Android/IJ 33 | # 34 | .idea 35 | .gradle 36 | local.properties 37 | android.iml 38 | 39 | # Cocoapods 40 | # 41 | example/ios/Pods 42 | example/ios/Podfile.lock 43 | 44 | # node.js 45 | # 46 | node_modules/ 47 | npm-debug.log 48 | yarn-debug.log 49 | yarn-error.log 50 | package-lock.json 51 | yarn.lock 52 | 53 | # BUCK 54 | buck-out/ 55 | \.buckd/ 56 | android/app/libs 57 | android/keystores/debug.keystore 58 | docs/api/ 59 | gitpull.sh 60 | gitpush.sh 61 | 62 | # Expo 63 | .expo/* 64 | 65 | # generated by bob 66 | lib/ 67 | libs/ 68 | package/ 69 | *.zip 70 | -------------------------------------------------------------------------------- /.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "git": { 3 | "commitMessage": "chore: release ${version}", 4 | "tagName": "v${version}" 5 | }, 6 | "npm": false, 7 | "github": { 8 | "release": true 9 | }, 10 | "plugins": { 11 | "@release-it/conventional-changelog": { 12 | "preset": "angular", 13 | "infile": "CHANGELOG.md" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [3.7.2](https://github.com/AgoraIO-Community/Agora-Uniapp-SDK/compare/v3.4.6...v3.7.2) (2023-06-07) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * `enableFaceDetection` not working ([16051e2](https://github.com/AgoraIO-Community/Agora-Uniapp-SDK/commit/16051e22985f7aa011468d873019055579300a51)) 7 | * CSD-56669 ([3fedaa1](https://github.com/AgoraIO-Community/Agora-Uniapp-SDK/commit/3fedaa1a070b053ce05db0931ade6e8997985ad9)) 8 | 9 | 10 | ### Features 11 | 12 | * support vue3 ([3c2a546](https://github.com/AgoraIO-Community/Agora-Uniapp-SDK/commit/3c2a54666cc06f1eca8bae3a1d8dda122a75896a)) 13 | * update native to 3.7.2.4 ([dbf24b7](https://github.com/AgoraIO-Community/Agora-Uniapp-SDK/commit/dbf24b71836215a67cfd0fd26404be8693b22af5)) 14 | 15 | ## [3.7.1](https://github.com/AgoraIO-Community/Agora-Uniapp-SDK/compare/v3.4.6...v3.7.1) (2023-02-10) 16 | 17 | 18 | ### Features 19 | 20 | * support native 3.7.1 ([439e5dd](https://github.com/AgoraIO-Community/Agora-Uniapp-SDK/commit/439e5dd5c69462889b9aea415c7798bdda85bb7e)) 21 | 22 | # [3.7.0-rc.3](https://github.com/AgoraIO-Community/Agora-Uniapp-SDK/compare/v3.7.0...v3.7.0-rc.3) (2022-11-08) 23 | 24 | 25 | ### Bug Fixes 26 | 27 | * ios compile error ([fd9d6dd](https://github.com/AgoraIO-Community/Agora-Uniapp-SDK/commit/fd9d6dd25961ea59550fcd9930370121ebe72412)) 28 | 29 | 30 | ### Features 31 | 32 | * support 3.7.0.3 ([b08a65e](https://github.com/AgoraIO-Community/Agora-Uniapp-SDK/commit/b08a65e791cbe18fc62903378d8be3359803f6dd)) 33 | 34 | # [3.7.0](https://github.com/AgoraIO-Community/Agora-Uniapp-SDK/compare/v3.4.6...v3.7.0) (2022-05-12) 35 | 36 | 37 | ### Features 38 | 39 | * support 3.7.0 ([badaf9c](https://github.com/AgoraIO-Community/Agora-Uniapp-SDK/commit/badaf9cb62b059a0c237cf93a5cb2f57d9ff314b)) 40 | 41 | ## [3.4.6](https://github.com/AgoraIO-Community/Agora-Uniapp-SDK/compare/v3.4.5...v3.4.6) (2021-07-23) 42 | 43 | 44 | ### Features 45 | 46 | * support 3.4.6 ([3ddf83f](https://github.com/AgoraIO-Community/Agora-Uniapp-SDK/commit/3ddf83f2102a21f93cca4373070d90151af6733c)) 47 | 48 | ## [3.4.5](https://github.com/AgoraIO-Community/Agora-Uniapp-SDK/compare/v3.2.0...v3.4.5) (2021-06-30) 49 | 50 | 51 | ### Features 52 | 53 | * support native 3.4.5 ([9afd6ac](https://github.com/AgoraIO-Community/Agora-Uniapp-SDK/commit/9afd6ac3a2d6c52edb270660db5245b5035e626b)) 54 | 55 | # 3.2.0 (2021-06-30) 56 | 57 | 58 | ### Bug Fixes 59 | 60 | * appType error ([e139ebd](https://github.com/AgoraIO-Community/Agora-Uniapp-SDK/commit/e139ebd062861b8b721195f3343d5882b249f86c)) 61 | * remove uni appid ([0882fa6](https://github.com/AgoraIO-Community/Agora-Uniapp-SDK/commit/0882fa64a5a1407d65fff857fa928acb790c2987)) 62 | 63 | 64 | ### Features 65 | 66 | * add API example ([38af25f](https://github.com/AgoraIO-Community/Agora-Uniapp-SDK/commit/38af25f4179ad6b384057f65af2f6bffc7bd8d78)) 67 | * support native 3.2.0 SDK ([16a98e4](https://github.com/AgoraIO-Community/Agora-Uniapp-SDK/commit/16a98e40be3b9b0cfe0d2275d9ec6468daab3a79)) 68 | 69 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 agora 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Agora-Uniapp-SDK 2 | 3 | 此 SDK 基于 uni-app 以及 Agora Android 和 iOS 的视频 SDK 实现。 4 | 5 | ## 发版说明 6 | [变更日志](CHANGELOG.md) 7 | 8 | ## 集成文档(云打包) 9 | 10 | 需要同时引用以下两个插件,JS 插件主要是为了做代码提示,且包含一些JS的逻辑,便于开发者使用 Native 插件 11 | 12 | [Native 插件](https://ext.dcloud.net.cn/plugin?id=3720) 13 | 14 | [JS 插件](https://ext.dcloud.net.cn/plugin?id=3741) 15 | 16 | ## 集成文档(离线打包) 17 | 18 | ### 克隆或下载本工程,并进入工程目录 19 | 20 | ```shell 21 | git clone https://github.com/AgoraIO-Community/Agora-Uniapp-SDK.git 22 | cd Agora-Uniapp-SDK 23 | ``` 24 | 25 | ### 安装依赖项并编译 JavaScript 脚本 26 | 27 | ```shell 28 | yarn 29 | ``` 30 | 31 | 随后拷贝 [lib/commonjs](lib/commonjs) 中生成的源代码到你的工程 32 | 33 | **如果你的 uni-app 项目支持 TypeScript, 则直接拷贝 [src](https://github.com/AgoraIO-Community/Agora-Uniapp-SDK/tree/master/src) 内的源代码到你的工程即可** 34 | 35 | ### 执行 **install.sh** 脚本以下载 Agora iOS SDK 36 | 37 | ```shell 38 | sh ./install.sh 39 | ``` 40 | 41 | 并确认 **ios/libs** 目录中包含 **.framework** 文件 42 | 43 | ### 将 Android 和 iOS 工程分别放到 uni-app 离线 SDK 对应的目录中 44 | 45 | * Android:**UniPlugin-Hello-AS** 46 | * iOS:**HBuilder-uniPluginDemo** 47 | 48 | ### 将 Android 和 iOS 工程分别引入 uni-app 离线 SDK 工程 49 | 50 | #### Android 51 | 52 | * 在 **settings.gradle** 中添加 53 | ``` 54 | include ':uniplugin_agora_rtc' 55 | project(':uniplugin_agora_rtc').projectDir = new File(rootProject.projectDir, 'android') 56 | ``` 57 | 58 | * 在 **app/build.gradle** 中添加 `implementation project(':uniplugin_agora_rtc')` 59 | 60 | #### iOS 61 | 62 | 在 Xcode 中右键 **HBuilder-uniPlugin** 工程,并点击 **Add Files to "HBuilder-uniPlugin"**, 选中 **AgoraRtcUniPlugin.xcodeproj** 并添加 63 | 64 | 在 Xcode 中点击 **HBuilder-uniPlugin** 工程,并点击 **HBuilder** Target,选中 **Build Phases** 65 | 66 | * 在 **Dependencies** 中添加 **AgoraRtcUniPlugin** 67 | * 在 **Link Binary With Libraries** 中添加 **AgoraRtcUniPlugin.framework** 68 | * 在 **Embed Frameworks** 中添加 **AgoraCore.framework** **AgoraRtcKit.framework** **Agorafdkaac.framework** **Agoraffmpeg.framework** **AgoraSoundTouch.framework** (需要通过 **Add Other...** 选择 **ios/libs** 目录中的 **.framework** 文件添加) 69 | 70 | ### 配置插件信息 71 | 72 | #### Android 73 | 74 | * 在 **app/src/main/assets/dcloud_uniplugins.json** 中添加 75 | ``` 76 | { 77 | "nativePlugins": [ 78 | ... 79 | { 80 | "plugins": [ 81 | { 82 | "type": "module", 83 | "name": "Agora-RTC-EngineModule", 84 | "class": "io.agora.rtc.uni.AgoraRtcEngineModule" 85 | }, 86 | { 87 | "type": "module", 88 | "name": "Agora-RTC-ChannelModule", 89 | "class": "io.agora.rtc.uni.AgoraRtcChannelModule" 90 | }, 91 | { 92 | "type": "component", 93 | "name": "Agora-RTC-SurfaceView", 94 | "class": "io.agora.rtc.uni.AgoraRtcSurfaceView" 95 | }, 96 | { 97 | "type": "component", 98 | "name": "Agora-RTC-TextureView", 99 | "class": "io.agora.rtc.uni.AgoraRtcTextureView" 100 | } 101 | ] 102 | }, 103 | ... 104 | ] 105 | } 106 | ``` 107 | 108 | #### iOS 109 | 110 | * 在 **HBuilder-Hello/HBuilder-uniPlugin-Info.plist** 中添加 111 | ``` 112 | ... 113 | dcloud_uniplugins 114 | 115 | 116 | plugins 117 | 118 | 119 | class 120 | AgoraRtcEngineModule 121 | name 122 | Agora-RTC-EngineModule 123 | type 124 | module 125 | 126 | 127 | class 128 | AgoraRtcChannelModule 129 | name 130 | Agora-RTC-ChannelModule 131 | type 132 | module 133 | 134 | 135 | class 136 | AgoraRtcSurfaceView 137 | name 138 | Agora-RTC-SurfaceView 139 | type 140 | component 141 | 142 | 143 | class 144 | AgoraRtcTextureView 145 | name 146 | Agora-RTC-TextureView 147 | type 148 | component 149 | 150 | 151 | 152 | ... 153 | 154 | ... 155 | ``` 156 | 157 | ## 如何使用 158 | 159 | ```javascript 160 | // 指向插件JS源码在你的工程中的相对路径,比如 161 | import RtcEngine from '../../components/Agora-RTC-JS/index'; 162 | RtcEngine.create('你的AppID').then((engine) => { 163 | console.log('init success'); 164 | }); 165 | ``` 166 | 167 | **插件绝大部分 API 都使用 Promise 包装,为保证调用时序,请使用 await 关键字** 168 | 169 | ## 常见错误 170 | 171 | ### building for ios simulator, but the embedded framework 'xxx.framework' was built for ios + ios simulator. 172 | 173 | 在 Xcode 的 **Build Settings** 中搜索 **Validate Workspace** 并设置为 No 174 | 175 | ### AppKey问题 176 | 177 | 请参考 [官网文档](https://nativesupport.dcloud.net.cn/AppDocs/README?id=appkey) 178 | 179 | ## API文档 180 | 181 | * [uni-app API](https://docs.agora.io/cn/Interactive%20Broadcast/API%20Reference/react_native/index.html) 182 | * [Android API](https://docs.agora.io/cn/Interactive%20Broadcast/API%20Reference/java/index.html) 183 | * [iOS API](https://docs.agora.io/cn/Interactive%20Broadcast/API%20Reference/oc/docs/headers/Agora-Objective-C-API-Overview.html) 184 | 185 | ## 资源 186 | 187 | * 完整的 [API Doc](https://docs.agora.io/cn/) 在开发者中心 188 | * [反馈问题](https://github.com/AgoraIO-Community/Agora-Uniapp-SDK/issues) 189 | * [uni-app 原生插件](https://nativesupport.dcloud.net.cn/NativePlugin/README) 190 | 191 | ## 开源许可 192 | 193 | MIT 194 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | // Buildscript is evaluated before everything else so we can't use getExtOrDefault 3 | def kotlin_version = rootProject.ext.has('kotlinVersion') ? rootProject.ext.get('kotlinVersion') : project.properties['Agora_kotlinVersion'] 4 | 5 | repositories { 6 | google() 7 | mavenCentral() 8 | } 9 | 10 | dependencies { 11 | // noinspection DifferentKotlinGradleVersion 12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 13 | } 14 | } 15 | 16 | rootProject.allprojects { 17 | repositories { 18 | mavenCentral() 19 | google() 20 | maven { url 'https://www.jitpack.io' } 21 | } 22 | } 23 | 24 | apply plugin: 'com.android.library' 25 | apply plugin: 'kotlin-android' 26 | apply plugin: 'kotlin-android-extensions' 27 | 28 | def getExtOrDefault(name) { 29 | return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['Agora_' + name] 30 | } 31 | 32 | def getExtOrIntegerDefault(name) { 33 | return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties['Agora_' + name]).toInteger() 34 | } 35 | 36 | android { 37 | compileSdkVersion getExtOrIntegerDefault('compileSdkVersion') 38 | 39 | defaultConfig { 40 | minSdkVersion getExtOrIntegerDefault('minSdkVersion') 41 | targetSdkVersion getExtOrIntegerDefault('targetSdkVersion') 42 | consumerProguardFiles 'consumer-rules.pro' 43 | } 44 | buildTypes { 45 | release { 46 | minifyEnabled false 47 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 48 | } 49 | } 50 | compileOptions { 51 | sourceCompatibility JavaVersion.VERSION_1_8 52 | targetCompatibility JavaVersion.VERSION_1_8 53 | } 54 | kotlinOptions { 55 | jvmTarget = '1.8' 56 | } 57 | } 58 | 59 | def kotlin_version = getExtOrDefault('kotlinVersion') 60 | 61 | dependencies { 62 | compileOnly fileTree(dir: 'libs', include: ['*.jar']) 63 | compileOnly fileTree(dir: new File(rootProject.rootDir, 'app/libs'), include: ['uniapp-v8-release.aar']) 64 | 65 | compileOnly 'com.android.support:recyclerview-v7:28.0.0' 66 | compileOnly 'com.android.support:support-v4:28.0.0' 67 | compileOnly 'com.android.support:appcompat-v7:28.0.0' 68 | compileOnly 'com.alibaba:fastjson:1.1.46.android' 69 | 70 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 71 | api 'io.agora.rtc:agora-special-full:3.7.3.4' 72 | api 'io.agora.rtc:full-screen-sharing:3.7.3.4' 73 | } 74 | -------------------------------------------------------------------------------- /android/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -keepattributes *Annotation* 2 | -keep class kotlin.** { *; } 3 | -keep class org.jetbrains.** { *; } 4 | 5 | -keep class io.agora.**{*;} 6 | 7 | -keep public class * extends io.dcloud.feature.uniapp.common.UniModule{*;} 8 | -keep public class * extends io.dcloud.feature.uniapp.ui.component.UniComponent{*;} 9 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | Agora_kotlinVersion=1.3.50 2 | Agora_minSdkVersion=21 3 | Agora_targetSdkVersion=31 4 | Agora_compileSdkVersion=31 5 | Agora_ndkversion=21.4.7075529 6 | -------------------------------------------------------------------------------- /android/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /android/src/main/java/io/agora/rtc/base/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | -------------------------------------------------------------------------------- /android/src/main/java/io/agora/rtc/base/BeanCovertor.kt: -------------------------------------------------------------------------------- 1 | package io.agora.rtc.base 2 | 3 | import io.agora.rtc.AgoraMediaRecorder 4 | import io.agora.rtc.RtcEngineConfig 5 | import io.agora.rtc.ScreenCaptureParameters 6 | import io.agora.rtc.audio.AgoraRhythmPlayerConfig 7 | import io.agora.rtc.audio.AudioRecordingConfiguration 8 | import io.agora.rtc.audio.SpatialAudioParams 9 | import io.agora.rtc.internal.EncryptionConfig 10 | import io.agora.rtc.internal.LastmileProbeConfig 11 | import io.agora.rtc.live.LiveInjectStreamConfig 12 | import io.agora.rtc.live.LiveTranscoding 13 | import io.agora.rtc.live.LiveTranscoding.TranscodingUser 14 | import io.agora.rtc.models.* 15 | import io.agora.rtc.proxy.LocalAccessPointConfiguration 16 | import io.agora.rtc.video.* 17 | 18 | fun mapToVideoDimensions(map: Map<*, *>): VideoEncoderConfiguration.VideoDimensions { 19 | return VideoEncoderConfiguration.VideoDimensions().apply { 20 | (map["width"] as? Number)?.let { width = it.toInt() } 21 | (map["height"] as? Number)?.let { height = it.toInt() } 22 | } 23 | } 24 | 25 | fun mapToVideoEncoderConfiguration(map: Map<*, *>): VideoEncoderConfiguration { 26 | return VideoEncoderConfiguration().apply { 27 | (map["dimensions"] as? Map<*, *>)?.let { dimensions = mapToVideoDimensions(it) } 28 | (map["frameRate"] as? Number)?.let { frameRate = it.toInt() } 29 | (map["minFrameRate"] as? Number)?.let { minFrameRate = it.toInt() } 30 | (map["bitrate"] as? Number)?.let { bitrate = it.toInt() } 31 | (map["minBitrate"] as? Number)?.let { minBitrate = it.toInt() } 32 | (map["orientationMode"] as? Number)?.let { orientationMode = intToOrientationMode(it.toInt()) } 33 | (map["degradationPrefer"] as? Number)?.let { 34 | degradationPrefer = intToDegradationPreference(it.toInt()) 35 | } 36 | (map["mirrorMode"] as? Number)?.let { mirrorMode = it.toInt() } 37 | } 38 | } 39 | 40 | fun mapToBeautyOptions(map: Map<*, *>): BeautyOptions { 41 | return BeautyOptions().apply { 42 | (map["lighteningContrastLevel"] as? Number)?.let { lighteningContrastLevel = it.toInt() } 43 | (map["lighteningLevel"] as? Number)?.let { lighteningLevel = it.toFloat() } 44 | (map["smoothnessLevel"] as? Number)?.let { smoothnessLevel = it.toFloat() } 45 | (map["rednessLevel"] as? Number)?.let { rednessLevel = it.toFloat() } 46 | (map["sharpnessLevel"] as? Number)?.let { sharpnessLevel = it.toFloat() } 47 | } 48 | } 49 | 50 | fun mapToAgoraImage(map: Map<*, *>): AgoraImage { 51 | return AgoraImage().apply { 52 | (map["url"] as? String)?.let { url = it } 53 | (map["x"] as? Number)?.let { x = it.toInt() } 54 | (map["y"] as? Number)?.let { y = it.toInt() } 55 | (map["width"] as? Number)?.let { width = it.toInt() } 56 | (map["height"] as? Number)?.let { height = it.toInt() } 57 | (map["zOrder"] as? Number)?.let { zOrder = it.toInt() } 58 | (map["alpha"] as? Number)?.let { alpha = it.toDouble() } 59 | } 60 | } 61 | 62 | fun mapToTranscodingUser(map: Map<*, *>): TranscodingUser { 63 | return TranscodingUser().apply { 64 | (map["uid"] as? Number)?.let { uid = it.toNativeUInt() } 65 | (map["x"] as? Number)?.let { x = it.toInt() } 66 | (map["y"] as? Number)?.let { y = it.toInt() } 67 | (map["width"] as? Number)?.let { width = it.toInt() } 68 | (map["height"] as? Number)?.let { height = it.toInt() } 69 | (map["zOrder"] as? Number)?.let { zOrder = it.toInt() } 70 | (map["alpha"] as? Number)?.let { alpha = it.toFloat() } 71 | (map["audioChannel"] as? Number)?.let { audioChannel = it.toInt() } 72 | } 73 | } 74 | 75 | fun mapToColor(map: Map<*, *>): Int { 76 | return ((map["red"] as Number).toInt() shl 16) + ((map["green"] as Number).toInt() shl 8) + (map["blue"] as Number).toInt() 77 | } 78 | 79 | fun mapToLiveTranscoding(map: Map<*, *>): LiveTranscoding { 80 | return LiveTranscoding().apply { 81 | (map["width"] as? Number)?.let { width = it.toInt() } 82 | (map["height"] as? Number)?.let { height = it.toInt() } 83 | (map["videoBitrate"] as? Number)?.let { videoBitrate = it.toInt() } 84 | (map["videoFramerate"] as? Number)?.let { videoFramerate = it.toInt() } 85 | (map["lowLatency"] as? Boolean)?.let { lowLatency = it } 86 | (map["videoGop"] as? Number)?.let { videoGop = it.toInt() } 87 | (map["watermark"] as? Map<*, *>)?.let { watermark = mapToAgoraImage(it) } 88 | (map["watermarkList"] as? List<*>)?.let { list -> 89 | list.forEach { item -> 90 | (item as? Map<*, *>)?.let { 91 | addWatermark(mapToAgoraImage(it)) 92 | } 93 | } 94 | } 95 | (map["backgroundImage"] as? Map<*, *>)?.let { backgroundImage = mapToAgoraImage(it) } 96 | (map["backgroundImageList"] as? List<*>)?.let { list -> 97 | list.forEach { item -> 98 | (item as? Map<*, *>)?.let { 99 | addBackgroundImage(mapToAgoraImage(it)) 100 | } 101 | } 102 | } 103 | (map["audioSampleRate"] as? Number)?.let { 104 | audioSampleRate = intToLiveTranscodingAudioSampleRate(it.toInt()) 105 | } 106 | (map["audioBitrate"] as? Number)?.let { audioBitrate = it.toInt() } 107 | (map["audioChannels"] as? Number)?.let { audioChannels = it.toInt() } 108 | (map["audioCodecProfile"] as? Number)?.let { 109 | audioCodecProfile = intToAudioCodecProfile(it.toInt()) 110 | } 111 | (map["videoCodecProfile"] as? Number)?.let { 112 | videoCodecProfile = intToVideoCodecProfile(it.toInt()) 113 | } 114 | (map["videoCodecType"] as? Number)?.let { 115 | videoCodecType = intToVideoCodecType(it.toInt()) 116 | } 117 | (map["backgroundColor"] as? Map<*, *>)?.let { backgroundColor = mapToColor(it) } 118 | (map["userConfigExtraInfo"] as? String)?.let { userConfigExtraInfo = it } 119 | (map["transcodingUsers"] as? List<*>)?.let { list -> 120 | list.forEach { item -> 121 | (item as? Map<*, *>)?.let { 122 | addUser(mapToTranscodingUser(it)) 123 | } 124 | } 125 | } 126 | (map["advancedFeatures"] as? Map<*, *>)?.let { 127 | it.forEach { item -> 128 | setAdvancedFeatures(item.key as String, item.value as Boolean) 129 | } 130 | } 131 | } 132 | } 133 | 134 | fun mapToChannelMediaInfo(map: Map<*, *>): ChannelMediaInfo { 135 | return ChannelMediaInfo( 136 | map["channelName"] as? String, 137 | map["token"] as? String, 138 | (map["uid"] as Number).toNativeUInt() 139 | ) 140 | } 141 | 142 | fun mapToChannelMediaRelayConfiguration(map: Map<*, *>): ChannelMediaRelayConfiguration { 143 | return ChannelMediaRelayConfiguration().apply { 144 | (map["srcInfo"] as? Map<*, *>)?.let { setSrcChannelInfo(mapToChannelMediaInfo(it)) } 145 | (map["destInfos"] as? List<*>)?.let { list -> 146 | list.forEach { item -> 147 | (item as? Map<*, *>)?.let { 148 | val info = mapToChannelMediaInfo(it) 149 | setDestChannelInfo(info.channelName, info) 150 | } 151 | } 152 | } 153 | } 154 | } 155 | 156 | fun mapToLastmileProbeConfig(map: Map<*, *>): LastmileProbeConfig { 157 | return LastmileProbeConfig().apply { 158 | (map["probeUplink"] as? Boolean)?.let { probeUplink = it } 159 | (map["probeDownlink"] as? Boolean)?.let { probeDownlink = it } 160 | (map["expectedUplinkBitrate"] as? Number)?.let { expectedUplinkBitrate = it.toInt() } 161 | (map["expectedDownlinkBitrate"] as? Number)?.let { expectedDownlinkBitrate = it.toInt() } 162 | } 163 | } 164 | 165 | fun mapToRectangle(map: Map<*, *>): WatermarkOptions.Rectangle { 166 | return WatermarkOptions.Rectangle().apply { 167 | (map["x"] as? Number)?.let { x = it.toInt() } 168 | (map["y"] as? Number)?.let { y = it.toInt() } 169 | (map["width"] as? Number)?.let { width = it.toInt() } 170 | (map["height"] as? Number)?.let { height = it.toInt() } 171 | } 172 | } 173 | 174 | fun mapToWatermarkOptions(map: Map<*, *>): WatermarkOptions { 175 | return WatermarkOptions().apply { 176 | (map["visibleInPreview"] as? Boolean)?.let { visibleInPreview = it } 177 | (map["positionInLandscapeMode"] as? Map<*, *>)?.let { 178 | positionInLandscapeMode = mapToRectangle(it) 179 | } 180 | (map["positionInPortraitMode"] as? Map<*, *>)?.let { 181 | positionInPortraitMode = mapToRectangle(it) 182 | } 183 | } 184 | } 185 | 186 | fun mapToLiveInjectStreamConfig(map: Map<*, *>): LiveInjectStreamConfig { 187 | return LiveInjectStreamConfig().apply { 188 | (map["width"] as? Number)?.let { width = it.toInt() } 189 | (map["height"] as? Number)?.let { height = it.toInt() } 190 | (map["videoGop"] as? Number)?.let { videoGop = it.toInt() } 191 | (map["videoFramerate"] as? Number)?.let { videoFramerate = it.toInt() } 192 | (map["videoBitrate"] as? Number)?.let { videoBitrate = it.toInt() } 193 | (map["audioSampleRate"] as? Number)?.let { 194 | audioSampleRate = intToLiveInjectStreamConfigAudioSampleRate(it.toInt()) 195 | } 196 | (map["audioBitrate"] as? Number)?.let { audioBitrate = it.toInt() } 197 | (map["audioChannels"] as? Number)?.let { audioChannels = it.toInt() } 198 | } 199 | } 200 | 201 | fun mapToRhythmPlayerConfig(map: Map<*, *>): AgoraRhythmPlayerConfig { 202 | return AgoraRhythmPlayerConfig().apply { 203 | (map["beatsPerMeasure"] as? Number)?.let { beatsPerMeasure = it.toInt() } 204 | (map["beatsPerMinute"] as? Number)?.let { beatsPerMinute = it.toInt() } 205 | (map["publish"] as? Boolean)?.let { publish = it } 206 | } 207 | } 208 | 209 | fun mapToCameraCapturerConfiguration(map: Map<*, *>): CameraCapturerConfiguration { 210 | return CameraCapturerConfiguration( 211 | intToCapturerOutputPreference((map["preference"] as Number).toInt()), 212 | intToCameraDirection((map["cameraDirection"] as Number).toInt()) 213 | ).apply { 214 | dimensions = CameraCapturerConfiguration.CaptureDimensions() 215 | (map["captureWidth"] as? Number)?.toInt()?.let { dimensions.width = it } 216 | (map["captureHeight"] as? Number)?.toInt()?.let { dimensions.height = it } 217 | } 218 | } 219 | 220 | fun mapToChannelMediaOptions(map: Map<*, *>): ChannelMediaOptions { 221 | return ChannelMediaOptions().apply { 222 | (map["autoSubscribeAudio"] as? Boolean)?.let { autoSubscribeAudio = it } 223 | (map["autoSubscribeVideo"] as? Boolean)?.let { autoSubscribeVideo = it } 224 | (map["publishLocalAudio"] as? Boolean)?.let { publishLocalAudio = it } 225 | (map["publishLocalVideo"] as? Boolean)?.let { publishLocalVideo = it } 226 | } 227 | } 228 | 229 | fun mapToRtcEngineConfig(map: Map<*, *>): RtcEngineConfig { 230 | return RtcEngineConfig().apply { 231 | mAppId = map["appId"] as String 232 | (map["areaCode"] as? Number)?.toInt()?.let { mAreaCode = it } 233 | (map["logConfig"] as? Map<*, *>)?.let { mLogConfig = mapToLogConfig(it) } 234 | } 235 | } 236 | 237 | fun mapToAudioRecordingConfiguration(map: Map<*, *>): AudioRecordingConfiguration { 238 | return AudioRecordingConfiguration().apply { 239 | (map["filePath"] as? String)?.let { filePath = it } 240 | (map["recordingQuality"] as? Number)?.let { recordingQuality = it.toInt() } 241 | (map["recordingPosition"] as? Number)?.let { recordingPosition = it.toInt() } 242 | (map["recordingSampleRate"] as? Number)?.let { recordingSampleRate = it.toInt() } 243 | (map["recordingChannel"] as? Number)?.let { recordingChannel = it.toInt() } 244 | } 245 | } 246 | 247 | fun mapToEncryptionConfig(map: Map<*, *>): EncryptionConfig { 248 | return EncryptionConfig().apply { 249 | (map["encryptionMode"] as? Number)?.let { encryptionMode = intToEncryptionMode(it.toInt()) } 250 | (map["encryptionKey"] as? String)?.let { encryptionKey = it } 251 | (map["encryptionKdfSalt"] as? List<*>)?.let { list -> 252 | for (i in list.indices) { 253 | (list[i] as? Number)?.let { 254 | encryptionKdfSalt[i] = it.toByte() 255 | } 256 | } 257 | } 258 | } 259 | } 260 | 261 | fun mapToClientRoleOptions(map: Map<*, *>): ClientRoleOptions { 262 | return ClientRoleOptions().apply { 263 | (map["audienceLatencyLevel"] as? Number)?.let { audienceLatencyLevel = it.toInt() } 264 | } 265 | } 266 | 267 | fun mapToLogConfig(map: Map<*, *>): RtcEngineConfig.LogConfig { 268 | return RtcEngineConfig.LogConfig().apply { 269 | (map["filePath"] as? String)?.let { filePath = it } 270 | (map["fileSize"] as? Number)?.let { fileSize = it.toInt() } 271 | (map["level"] as? Number)?.let { level = it.toInt() } 272 | } 273 | } 274 | 275 | fun mapToDataStreamConfig(map: Map<*, *>): DataStreamConfig { 276 | return DataStreamConfig().apply { 277 | (map["syncWithAudio"] as? Boolean)?.let { syncWithAudio = it } 278 | (map["ordered"] as? Boolean)?.let { ordered = it } 279 | } 280 | } 281 | 282 | fun mapToVirtualBackgroundSource(map: Map<*, *>): VirtualBackgroundSource { 283 | return VirtualBackgroundSource().apply { 284 | (map["backgroundSourceType"] as? Number)?.let { backgroundSourceType = it.toInt() } 285 | (map["color"] as? Map<*, *>)?.let { color = mapToColor(it) } 286 | (map["source"] as? String)?.let { source = it } 287 | (map["blur_degree"] as? Int)?.let { blur_degree = it } 288 | } 289 | } 290 | 291 | fun mapToEchoTestConfiguration(map: Map<*, *>): EchoTestConfiguration { 292 | return EchoTestConfiguration().apply { 293 | (map["enableAudio"] as? Boolean)?.let { enableAudio = it } 294 | (map["enableVideo"] as? Boolean)?.let { enableVideo = it } 295 | (map["token"] as? String)?.let { token = it } 296 | (map["channelId"] as? String)?.let { channelId = it } 297 | } 298 | } 299 | 300 | fun mapToMediaRecorderConfiguration(map: Map<*, *>): AgoraMediaRecorder.MediaRecorderConfiguration { 301 | return AgoraMediaRecorder.MediaRecorderConfiguration( 302 | map["storagePath"] as String, 303 | (map["containerFormat"] as Number).toInt(), 304 | (map["streamType"] as Number).toInt(), 305 | (map["maxDurationMs"] as Number).toInt(), 306 | (map["recorderInfoUpdateInterval"] as Number).toInt() 307 | ) 308 | } 309 | 310 | fun mapToContentInspectModule(map: Map<*, *>): ContentInspectConfig.ContentInspectModule { 311 | return ContentInspectConfig.ContentInspectModule().apply { 312 | (map["type"] as? Number)?.let { type = it.toInt() } 313 | (map["interval"] as? Number)?.let { interval = it.toInt() } 314 | } 315 | } 316 | 317 | fun mapToContentInspectConfig(map: Map<*, *>): ContentInspectConfig { 318 | return ContentInspectConfig().apply { 319 | (map["extraInfo"] as? String)?.let { extraInfo = it } 320 | (map["modules"] as? List<*>)?.let { list -> 321 | list.forEachIndexed { index, item -> 322 | (item as? Map<*, *>)?.let { 323 | modules[index] = mapToContentInspectModule(it) 324 | } 325 | } 326 | moduleCount = list.size 327 | } 328 | } 329 | } 330 | 331 | fun mapToLocalAccessPointConfiguration(map: Map<*, *>): LocalAccessPointConfiguration { 332 | return LocalAccessPointConfiguration().apply { 333 | (map["ipList"] as? List<*>)?.let { list -> 334 | ipList = arrayListOf().apply { 335 | list.forEach { item -> 336 | (item as? String)?.let { add(it) } 337 | } 338 | } 339 | } 340 | (map["domainList"] as? List<*>)?.let { list -> 341 | domainList = arrayListOf().apply { 342 | list.forEach { item -> 343 | (item as? String)?.let { add(it) } 344 | } 345 | } 346 | } 347 | (map["verifyDomainName"] as? String)?.let { verifyDomainName = it } 348 | (map["mode"] as? Number)?.let { mode = it.toInt() } 349 | } 350 | } 351 | 352 | fun mapToVideoDenoiserOptions(map: Map<*, *>): VideoDenoiserOptions { 353 | return VideoDenoiserOptions().apply { 354 | (map["mode"] as? Number)?.let { denoiserMode = it.toInt() } 355 | (map["level"] as? Number)?.let { denoiserLevel = it.toInt() } 356 | } 357 | } 358 | 359 | fun mapToLowLightEnhanceOptions(map: Map<*, *>): LowLightEnhanceOptions { 360 | return LowLightEnhanceOptions().apply { 361 | (map["mode"] as? Number)?.let { lowlightEnhanceMode = it.toInt() } 362 | (map["level"] as? Number)?.let { lowlightEnhanceLevel = it.toInt() } 363 | } 364 | } 365 | 366 | fun mapToColorEnhanceOptions(map: Map<*, *>): ColorEnhanceOptions { 367 | return ColorEnhanceOptions().apply { 368 | (map["strengthLevel"] as? Number)?.let { strengthLevel = it.toFloat() } 369 | (map["skinProtectLevel"] as? Number)?.let { skinProtectLevel = it.toFloat() } 370 | } 371 | } 372 | 373 | fun mapToScreenCaptureParameters(map: Map<*, *>): ScreenCaptureParameters { 374 | return ScreenCaptureParameters().apply { 375 | (map["captureAudio"] as? Boolean)?.let { captureAudio = it } 376 | (map["audioParams"] as? Map<*, *>)?.let { 377 | audioCaptureParameters = mapToAudioCaptureParameters(it) 378 | } 379 | (map["captureVideo"] as? Boolean)?.let { captureVideo = it } 380 | (map["videoParams"] as? Map<*, *>)?.let { 381 | videoCaptureParameters = mapToVideoCaptureParameters(it) 382 | } 383 | } 384 | } 385 | 386 | fun mapToVideoCaptureParameters(map: Map<*, *>): ScreenCaptureParameters.VideoCaptureParameters { 387 | return ScreenCaptureParameters.VideoCaptureParameters().apply { 388 | (map["bitrate"] as? Number)?.let { bitrate = it.toInt() } 389 | (map["frameRate"] as? Number)?.let { framerate = it.toInt() } 390 | (map["dimensions"] as? Map<*, *>)?.let { it -> 391 | mapToVideoDimensions(it).let { 392 | width = it.width 393 | height = it.height 394 | } 395 | } 396 | (map["contentHint"] as? Number)?.let { contentHint = it.toInt() } 397 | } 398 | } 399 | 400 | fun mapToAudioCaptureParameters(map: Map<*, *>): ScreenCaptureParameters.AudioCaptureParameters { 401 | return ScreenCaptureParameters.AudioCaptureParameters().apply { 402 | (map["sampleRate"] as? Number)?.let { sampleRate = it.toInt() } 403 | (map["channels"] as? Number)?.let { channels = it.toInt() } 404 | (map["captureSignalVolume"] as? Number)?.let { captureSignalVolume = it.toInt() } 405 | (map["allowCaptureCurrentApp"] as? Boolean)?.let { allowCaptureCurrentApp = it } 406 | } 407 | } 408 | 409 | fun mapToSpatialAudioParams(map: Map<*, *>): SpatialAudioParams { 410 | return SpatialAudioParams().apply { 411 | (map["speaker_azimuth"] as? Number)?.let { spk_azimuth = it.toDouble() } 412 | (map["speaker_elevation"] as? Number)?.let { spk_elevation = it.toDouble() } 413 | (map["speaker_distance"] as? Number)?.let { spk_distance = it.toDouble() } 414 | (map["speaker_orientation"] as? Number)?.let { spk_orientation = it.toInt() } 415 | (map["enable_blur"] as? Boolean)?.let { enable_blur = it } 416 | (map["enable_air_absorb"] as? Boolean)?.let { enable_air_absorb = it } 417 | } 418 | } 419 | -------------------------------------------------------------------------------- /android/src/main/java/io/agora/rtc/base/Callback.kt: -------------------------------------------------------------------------------- 1 | package io.agora.rtc.base 2 | 3 | import io.agora.rtc.Constants 4 | import io.agora.rtc.RtcEngine 5 | import kotlin.math.abs 6 | 7 | abstract class Callback { 8 | fun code(code: Int?, runnable: ((Int?) -> Any?)? = null) { 9 | if (code == null || code < 0) { 10 | val newCode = abs(code ?: Constants.ERR_NOT_INITIALIZED) 11 | failure(newCode.toString(), RtcEngine.getErrorDescription(newCode)) 12 | return 13 | } 14 | 15 | val res = if (runnable != null) runnable(code) else Unit 16 | if (res is Unit) { 17 | success(null) 18 | } else { 19 | success(res) 20 | } 21 | } 22 | 23 | fun resolve(source: T?, runnable: (T) -> Any?) { 24 | if (source == null) { 25 | val code = Constants.ERR_NOT_INITIALIZED 26 | failure(code.toString(), RtcEngine.getErrorDescription(code)) 27 | return 28 | } 29 | 30 | val res = runnable(source) 31 | if (res is Unit) { 32 | success(null) 33 | } else { 34 | success(res) 35 | } 36 | } 37 | 38 | abstract fun success(data: Any?) 39 | 40 | abstract fun failure(code: String, message: String) 41 | } 42 | -------------------------------------------------------------------------------- /android/src/main/java/io/agora/rtc/base/EnumCovertor.kt: -------------------------------------------------------------------------------- 1 | package io.agora.rtc.base 2 | 3 | import io.agora.rtc.internal.EncryptionConfig 4 | import io.agora.rtc.live.LiveInjectStreamConfig 5 | import io.agora.rtc.live.LiveTranscoding 6 | import io.agora.rtc.video.CameraCapturerConfiguration 7 | import io.agora.rtc.video.VideoEncoderConfiguration 8 | 9 | fun intToFrameRate(@Annotations.AgoraVideoFrameRate intValue: Int): VideoEncoderConfiguration.FRAME_RATE { 10 | for (value in VideoEncoderConfiguration.FRAME_RATE.values()) { 11 | if (value.value == intValue) { 12 | return value 13 | } 14 | } 15 | throw RuntimeException("VideoEncoderConfiguration.FRAME_RATE not contains $intValue") 16 | } 17 | 18 | fun intToOrientationMode(@Annotations.AgoraVideoOutputOrientationMode intValue: Int): VideoEncoderConfiguration.ORIENTATION_MODE { 19 | for (value in VideoEncoderConfiguration.ORIENTATION_MODE.values()) { 20 | if (value.value == intValue) { 21 | return value 22 | } 23 | } 24 | throw RuntimeException("VideoEncoderConfiguration.ORIENTATION_MODE not contains $intValue") 25 | } 26 | 27 | fun intToDegradationPreference(@Annotations.AgoraDegradationPreference intValue: Int): VideoEncoderConfiguration.DEGRADATION_PREFERENCE { 28 | for (value in VideoEncoderConfiguration.DEGRADATION_PREFERENCE.values()) { 29 | if (value.value == intValue) { 30 | return value 31 | } 32 | } 33 | throw RuntimeException("VideoEncoderConfiguration.DEGRADATION_PREFERENCE not contains $intValue") 34 | } 35 | 36 | fun intToLiveTranscodingAudioSampleRate(@Annotations.AgoraAudioSampleRateType intValue: Int): LiveTranscoding.AudioSampleRateType { 37 | for (value in LiveTranscoding.AudioSampleRateType.values()) { 38 | if (LiveTranscoding.AudioSampleRateType.getValue(value) == intValue) { 39 | return value 40 | } 41 | } 42 | throw RuntimeException("LiveTranscoding.AudioSampleRateType not contains $intValue") 43 | } 44 | 45 | fun intToLiveInjectStreamConfigAudioSampleRate(@Annotations.AgoraAudioSampleRateType intValue: Int): LiveInjectStreamConfig.AudioSampleRateType { 46 | for (value in LiveInjectStreamConfig.AudioSampleRateType.values()) { 47 | if (LiveInjectStreamConfig.AudioSampleRateType.getValue(value) == intValue) { 48 | return value 49 | } 50 | } 51 | throw RuntimeException("LiveInjectStreamConfig.AudioSampleRateType not contains $intValue") 52 | } 53 | 54 | fun intToAudioCodecProfile(@Annotations.AgoraAudioCodecProfileType intValue: Int): LiveTranscoding.AudioCodecProfileType { 55 | for (value in LiveTranscoding.AudioCodecProfileType.values()) { 56 | if (LiveTranscoding.AudioCodecProfileType.getValue(value) == intValue) { 57 | return value 58 | } 59 | } 60 | throw RuntimeException("LiveTranscoding.AudioCodecProfileType not contains $intValue") 61 | } 62 | 63 | fun intToVideoCodecProfile(@Annotations.AgoraVideoCodecProfileType intValue: Int): LiveTranscoding.VideoCodecProfileType { 64 | for (value in LiveTranscoding.VideoCodecProfileType.values()) { 65 | if (LiveTranscoding.VideoCodecProfileType.getValue(value) == intValue) { 66 | return value 67 | } 68 | } 69 | throw RuntimeException("LiveTranscoding.VideoCodecProfileType not contains $intValue") 70 | } 71 | 72 | fun intToVideoCodecType(@Annotations.AgoraVideoCodecType intValue: Int): LiveTranscoding.VideoCodecType { 73 | for (value in LiveTranscoding.VideoCodecType.values()) { 74 | if (LiveTranscoding.VideoCodecType.getValue(value) == intValue) { 75 | return value 76 | } 77 | } 78 | throw RuntimeException("LiveTranscoding.VideoCodecType not contains $intValue") 79 | } 80 | 81 | fun intToCapturerOutputPreference(@Annotations.AgoraCameraCaptureOutputPreference intValue: Int): CameraCapturerConfiguration.CAPTURER_OUTPUT_PREFERENCE { 82 | for (value in CameraCapturerConfiguration.CAPTURER_OUTPUT_PREFERENCE.values()) { 83 | if (value.value == intValue) { 84 | return value 85 | } 86 | } 87 | throw RuntimeException("CameraCapturerConfiguration.CAPTURER_OUTPUT_PREFERENCE not contains $intValue") 88 | } 89 | 90 | fun intToCameraDirection(@Annotations.AgoraCameraDirection intValue: Int): CameraCapturerConfiguration.CAMERA_DIRECTION { 91 | for (value in CameraCapturerConfiguration.CAMERA_DIRECTION.values()) { 92 | if (value.value == intValue) { 93 | return value 94 | } 95 | } 96 | throw RuntimeException("CameraCapturerConfiguration.CAMERA_DIRECTION not contains $intValue") 97 | } 98 | 99 | fun intToEncryptionMode(@Annotations.AgoraEncryptionMode intValue: Int): EncryptionConfig.EncryptionMode { 100 | for (value in EncryptionConfig.EncryptionMode.values()) { 101 | if (value.value == intValue) { 102 | return value 103 | } 104 | } 105 | throw RuntimeException("EncryptionConfig.EncryptionMode not contains $intValue") 106 | } 107 | -------------------------------------------------------------------------------- /android/src/main/java/io/agora/rtc/base/Extensions.kt: -------------------------------------------------------------------------------- 1 | package io.agora.rtc.base 2 | 3 | import android.graphics.Rect 4 | import io.agora.rtc.AgoraMediaRecorder 5 | import io.agora.rtc.IRtcEngineEventHandler.* 6 | import io.agora.rtc.models.UserInfo 7 | 8 | fun UserInfo.toMap(): Map { 9 | return hashMapOf( 10 | "uid" to uid.toUInt().toLong(), 11 | "userAccount" to userAccount 12 | ) 13 | } 14 | 15 | fun LocalAudioStats.toMap(): Map { 16 | return hashMapOf( 17 | "numChannels" to numChannels, 18 | "sentSampleRate" to sentSampleRate, 19 | "sentBitrate" to sentBitrate, 20 | "txPacketLossRate" to txPacketLossRate 21 | ) 22 | } 23 | 24 | fun RtcStats.toMap(): Map { 25 | return hashMapOf( 26 | "duration" to totalDuration, 27 | "txBytes" to txBytes, 28 | "rxBytes" to rxBytes, 29 | "txAudioBytes" to txAudioBytes, 30 | "txVideoBytes" to txVideoBytes, 31 | "rxAudioBytes" to rxAudioBytes, 32 | "rxVideoBytes" to rxVideoBytes, 33 | "txKBitRate" to txKBitRate, 34 | "rxKBitRate" to rxKBitRate, 35 | "txAudioKBitRate" to txAudioKBitRate, 36 | "rxAudioKBitRate" to rxAudioKBitRate, 37 | "txVideoKBitRate" to txVideoKBitRate, 38 | "rxVideoKBitRate" to rxVideoKBitRate, 39 | "userCount" to users, 40 | "lastmileDelay" to lastmileDelay, 41 | "txPacketLossRate" to txPacketLossRate, 42 | "rxPacketLossRate" to rxPacketLossRate, 43 | "cpuTotalUsage" to cpuTotalUsage, 44 | "cpuAppUsage" to cpuAppUsage, 45 | "gatewayRtt" to gatewayRtt, 46 | "memoryAppUsageRatio" to memoryAppUsageRatio, 47 | "memoryTotalUsageRatio" to memoryTotalUsageRatio, 48 | "memoryAppUsageInKbytes" to memoryAppUsageInKbytes 49 | ) 50 | } 51 | 52 | fun Rect.toMap(): Map { 53 | return hashMapOf( 54 | "left" to left, 55 | "top" to top, 56 | "right" to right, 57 | "bottom" to bottom 58 | ) 59 | } 60 | 61 | fun RemoteAudioStats.toMap(): Map { 62 | return hashMapOf( 63 | "uid" to uid.toUInt().toLong(), 64 | "quality" to quality, 65 | "networkTransportDelay" to networkTransportDelay, 66 | "jitterBufferDelay" to jitterBufferDelay, 67 | "audioLossRate" to audioLossRate, 68 | "numChannels" to numChannels, 69 | "receivedSampleRate" to receivedSampleRate, 70 | "receivedBitrate" to receivedBitrate, 71 | "totalFrozenTime" to totalFrozenTime, 72 | "frozenRate" to frozenRate, 73 | "totalActiveTime" to totalActiveTime, 74 | "publishDuration" to publishDuration, 75 | "qoeQuality" to qoeQuality, 76 | "qualityChangedReason" to qualityChangedReason, 77 | "mosValue" to mosValue 78 | ) 79 | } 80 | 81 | fun LocalVideoStats.toMap(): Map { 82 | return hashMapOf( 83 | "sentBitrate" to sentBitrate, 84 | "sentFrameRate" to sentFrameRate, 85 | "encoderOutputFrameRate" to encoderOutputFrameRate, 86 | "rendererOutputFrameRate" to rendererOutputFrameRate, 87 | "targetBitrate" to targetBitrate, 88 | "targetFrameRate" to targetFrameRate, 89 | "qualityAdaptIndication" to qualityAdaptIndication, 90 | "encodedBitrate" to encodedBitrate, 91 | "encodedFrameWidth" to encodedFrameWidth, 92 | "encodedFrameHeight" to encodedFrameHeight, 93 | "encodedFrameCount" to encodedFrameCount, 94 | "codecType" to codecType, 95 | "txPacketLossRate" to txPacketLossRate, 96 | "captureFrameRate" to captureFrameRate, 97 | "captureBrightnessLevel" to captureBrightnessLevel 98 | ) 99 | } 100 | 101 | fun RemoteVideoStats.toMap(): Map { 102 | return hashMapOf( 103 | "uid" to uid.toUInt().toLong(), 104 | "delay" to delay, 105 | "width" to width, 106 | "height" to height, 107 | "receivedBitrate" to receivedBitrate, 108 | "decoderOutputFrameRate" to decoderOutputFrameRate, 109 | "rendererOutputFrameRate" to rendererOutputFrameRate, 110 | "packetLossRate" to packetLossRate, 111 | "rxStreamType" to rxStreamType, 112 | "totalFrozenTime" to totalFrozenTime, 113 | "frozenRate" to frozenRate, 114 | "totalActiveTime" to totalActiveTime, 115 | "publishDuration" to publishDuration 116 | ) 117 | } 118 | 119 | fun AudioVolumeInfo.toMap(): Map { 120 | return hashMapOf( 121 | "uid" to uid.toUInt().toLong(), 122 | "volume" to volume, 123 | "vad" to vad, 124 | "channelId" to channelId 125 | ) 126 | } 127 | 128 | fun Array.toMapList(): List> { 129 | return List(size) { this[it].toMap() } 130 | } 131 | 132 | fun LastmileProbeResult.LastmileProbeOneWayResult.toMap(): Map { 133 | return hashMapOf( 134 | "packetLossRate" to packetLossRate, 135 | "jitter" to jitter, 136 | "availableBandwidth" to availableBandwidth 137 | ) 138 | } 139 | 140 | fun LastmileProbeResult.toMap(): Map { 141 | return hashMapOf( 142 | "state" to state, 143 | "rtt" to rtt, 144 | "uplinkReport" to uplinkReport.toMap(), 145 | "downlinkReport" to downlinkReport.toMap() 146 | ) 147 | } 148 | 149 | fun AgoraFacePositionInfo.toMap(): Map { 150 | return hashMapOf( 151 | "x" to x, 152 | "y" to y, 153 | "width" to width, 154 | "height" to height, 155 | "distance" to distance 156 | ) 157 | } 158 | 159 | fun AudioFileInfo.toMap(): Map { 160 | return hashMapOf( 161 | "filePath" to filePath, 162 | "durationMs" to durationMs 163 | ) 164 | } 165 | 166 | fun AgoraMediaRecorder.RecorderInfo.toMap(): Map { 167 | return hashMapOf( 168 | "fileName" to fileName, 169 | "durationMs" to durationMs, 170 | "fileSize" to fileSize 171 | ) 172 | } 173 | 174 | fun WlAccStats.toMap(): Map { 175 | return hashMapOf( 176 | "e2eDelayPercent" to e2eDelayPercent, 177 | "frozenRatioPercent" to frozenRatioPercent, 178 | "lossRatePercent" to lossRatePercent 179 | ) 180 | } 181 | 182 | fun Array.toMapList(): List> { 183 | return List(size) { this[it].toMap() } 184 | } 185 | 186 | @ExperimentalUnsignedTypes 187 | internal fun Number.toNativeUInt(): Int { 188 | return toLong().toUInt().toInt() 189 | } 190 | -------------------------------------------------------------------------------- /android/src/main/java/io/agora/rtc/base/MediaObserver.kt: -------------------------------------------------------------------------------- 1 | package io.agora.rtc.base 2 | 3 | import androidx.annotation.IntRange 4 | import io.agora.rtc.IMetadataObserver 5 | import java.util.* 6 | import java.util.concurrent.atomic.AtomicInteger 7 | 8 | class MediaObserver( 9 | private val emit: (data: Map?) -> Unit 10 | ) : IMetadataObserver { 11 | private var maxMetadataSize = AtomicInteger(1024) 12 | private var metadataList = Collections.synchronizedList(mutableListOf()) 13 | 14 | fun addMetadata(metadata: String) { 15 | metadataList.add(metadata) 16 | } 17 | 18 | fun setMaxMetadataSize(@IntRange(from = 0, to = 1024) size: Int) { 19 | maxMetadataSize.set(size) 20 | } 21 | 22 | override fun onReadyToSendMetadata(timeStampMs: Long): ByteArray? { 23 | if (metadataList.size > 0) { 24 | return metadataList.removeAt(0).toByteArray() 25 | } 26 | return null 27 | } 28 | 29 | override fun getMaxMetadataSize(): Int { 30 | return maxMetadataSize.get() 31 | } 32 | 33 | override fun onMetadataReceived(buffer: ByteArray, uid: Int, timeStampMs: Long) { 34 | emit( 35 | hashMapOf( 36 | "data" to arrayListOf(String(buffer), uid.toUInt().toLong(), timeStampMs) 37 | ) 38 | ) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /android/src/main/java/io/agora/rtc/base/RtcChannelEvent.kt: -------------------------------------------------------------------------------- 1 | package io.agora.rtc.base 2 | 3 | import androidx.annotation.IntRange 4 | import io.agora.rtc.IRtcChannelEventHandler 5 | import io.agora.rtc.IRtcEngineEventHandler 6 | import io.agora.rtc.RtcChannel 7 | 8 | class RtcChannelEvents { 9 | companion object { 10 | const val Warning = "Warning" 11 | const val Error = "Error" 12 | const val JoinChannelSuccess = "JoinChannelSuccess" 13 | const val RejoinChannelSuccess = "RejoinChannelSuccess" 14 | const val LeaveChannel = "LeaveChannel" 15 | const val ClientRoleChanged = "ClientRoleChanged" 16 | const val UserJoined = "UserJoined" 17 | const val UserOffline = "UserOffline" 18 | const val ConnectionStateChanged = "ConnectionStateChanged" 19 | const val ConnectionLost = "ConnectionLost" 20 | const val TokenPrivilegeWillExpire = "TokenPrivilegeWillExpire" 21 | const val RequestToken = "RequestToken" 22 | const val ActiveSpeaker = "ActiveSpeaker" 23 | const val VideoSizeChanged = "VideoSizeChanged" 24 | const val RemoteVideoStateChanged = "RemoteVideoStateChanged" 25 | const val RemoteAudioStateChanged = "RemoteAudioStateChanged" 26 | const val LocalPublishFallbackToAudioOnly = "LocalPublishFallbackToAudioOnly" 27 | const val RemoteSubscribeFallbackToAudioOnly = "RemoteSubscribeFallbackToAudioOnly" 28 | const val RtcStats = "RtcStats" 29 | const val NetworkQuality = "NetworkQuality" 30 | const val RemoteVideoStats = "RemoteVideoStats" 31 | const val RemoteAudioStats = "RemoteAudioStats" 32 | const val RtmpStreamingStateChanged = "RtmpStreamingStateChanged" 33 | const val TranscodingUpdated = "TranscodingUpdated" 34 | const val StreamInjectedStatus = "StreamInjectedStatus" 35 | const val StreamMessage = "StreamMessage" 36 | const val StreamMessageError = "StreamMessageError" 37 | const val ChannelMediaRelayStateChanged = "ChannelMediaRelayStateChanged" 38 | const val ChannelMediaRelayEvent = "ChannelMediaRelayEvent" 39 | const val MetadataReceived = "MetadataReceived" 40 | const val AudioPublishStateChanged = "AudioPublishStateChanged" 41 | const val VideoPublishStateChanged = "VideoPublishStateChanged" 42 | const val AudioSubscribeStateChanged = "AudioSubscribeStateChanged" 43 | const val VideoSubscribeStateChanged = "VideoSubscribeStateChanged" 44 | const val RtmpStreamingEvent = "RtmpStreamingEvent" 45 | const val UserSuperResolutionEnabled = "UserSuperResolutionEnabled" 46 | const val ProxyConnected = "ProxyConnected" 47 | const val ClientRoleChangeFailed = "ClientRoleChangeFailed" 48 | const val FirstRemoteVideoFrame = "FirstRemoteVideoFrame" 49 | 50 | fun toMap(): Map { 51 | return hashMapOf( 52 | "Warning" to Warning, 53 | "Error" to Error, 54 | "JoinChannelSuccess" to JoinChannelSuccess, 55 | "RejoinChannelSuccess" to RejoinChannelSuccess, 56 | "LeaveChannel" to LeaveChannel, 57 | "ClientRoleChanged" to ClientRoleChanged, 58 | "UserJoined" to UserJoined, 59 | "UserOffline" to UserOffline, 60 | "ConnectionStateChanged" to ConnectionStateChanged, 61 | "ConnectionLost" to ConnectionLost, 62 | "TokenPrivilegeWillExpire" to TokenPrivilegeWillExpire, 63 | "RequestToken" to RequestToken, 64 | "ActiveSpeaker" to ActiveSpeaker, 65 | "VideoSizeChanged" to VideoSizeChanged, 66 | "RemoteVideoStateChanged" to RemoteVideoStateChanged, 67 | "RemoteAudioStateChanged" to RemoteAudioStateChanged, 68 | "LocalPublishFallbackToAudioOnly" to LocalPublishFallbackToAudioOnly, 69 | "RemoteSubscribeFallbackToAudioOnly" to RemoteSubscribeFallbackToAudioOnly, 70 | "RtcStats" to RtcStats, 71 | "NetworkQuality" to NetworkQuality, 72 | "RemoteVideoStats" to RemoteVideoStats, 73 | "RemoteAudioStats" to RemoteAudioStats, 74 | "RtmpStreamingStateChanged" to RtmpStreamingStateChanged, 75 | "TranscodingUpdated" to TranscodingUpdated, 76 | "StreamInjectedStatus" to StreamInjectedStatus, 77 | "StreamMessage" to StreamMessage, 78 | "StreamMessageError" to StreamMessageError, 79 | "ChannelMediaRelayStateChanged" to ChannelMediaRelayStateChanged, 80 | "ChannelMediaRelayEvent" to ChannelMediaRelayEvent, 81 | "MetadataReceived" to MetadataReceived, 82 | "AudioPublishStateChanged" to AudioPublishStateChanged, 83 | "VideoPublishStateChanged" to VideoPublishStateChanged, 84 | "AudioSubscribeStateChanged" to AudioSubscribeStateChanged, 85 | "VideoSubscribeStateChanged" to VideoSubscribeStateChanged, 86 | "RtmpStreamingEvent" to RtmpStreamingEvent, 87 | "UserSuperResolutionEnabled" to UserSuperResolutionEnabled, 88 | "ProxyConnected" to ProxyConnected, 89 | "ClientRoleChangeFailed" to ClientRoleChangeFailed, 90 | "FirstRemoteVideoFrame" to FirstRemoteVideoFrame 91 | ) 92 | } 93 | } 94 | } 95 | 96 | class RtcChannelEventHandler( 97 | private val emitter: (methodName: String, data: Map?) -> Unit 98 | ) : IRtcChannelEventHandler() { 99 | companion object { 100 | const val PREFIX = "io.agora.rtc." 101 | } 102 | 103 | private fun callback(methodName: String, channel: RtcChannel?, vararg data: Any?) { 104 | channel?.let { 105 | emitter( 106 | methodName, hashMapOf( 107 | "channelId" to it.channelId(), 108 | "data" to data.toList() 109 | ) 110 | ) 111 | } 112 | } 113 | 114 | override fun onChannelWarning(rtcChannel: RtcChannel?, @Annotations.AgoraWarningCode warn: Int) { 115 | callback(RtcChannelEvents.Warning, rtcChannel, warn) 116 | } 117 | 118 | override fun onChannelError(rtcChannel: RtcChannel?, @Annotations.AgoraErrorCode err: Int) { 119 | callback(RtcChannelEvents.Error, rtcChannel, err) 120 | } 121 | 122 | override fun onJoinChannelSuccess(rtcChannel: RtcChannel?, uid: Int, elapsed: Int) { 123 | callback( 124 | RtcChannelEvents.JoinChannelSuccess, 125 | rtcChannel, 126 | rtcChannel?.channelId(), 127 | uid.toUInt().toLong(), 128 | elapsed 129 | ) 130 | } 131 | 132 | override fun onRejoinChannelSuccess(rtcChannel: RtcChannel?, uid: Int, elapsed: Int) { 133 | callback( 134 | RtcChannelEvents.RejoinChannelSuccess, 135 | rtcChannel, 136 | rtcChannel?.channelId(), 137 | uid.toUInt().toLong(), 138 | elapsed 139 | ) 140 | } 141 | 142 | override fun onLeaveChannel(rtcChannel: RtcChannel?, stats: IRtcEngineEventHandler.RtcStats?) { 143 | callback(RtcChannelEvents.LeaveChannel, rtcChannel, stats?.toMap()) 144 | } 145 | 146 | override fun onClientRoleChanged( 147 | rtcChannel: RtcChannel?, 148 | @Annotations.AgoraClientRole oldRole: Int, 149 | @Annotations.AgoraClientRole newRole: Int 150 | ) { 151 | callback(RtcChannelEvents.ClientRoleChanged, rtcChannel, oldRole, newRole) 152 | } 153 | 154 | override fun onUserJoined(rtcChannel: RtcChannel?, uid: Int, elapsed: Int) { 155 | callback(RtcChannelEvents.UserJoined, rtcChannel, uid.toUInt().toLong(), elapsed) 156 | } 157 | 158 | override fun onUserOffline( 159 | rtcChannel: RtcChannel?, 160 | uid: Int, 161 | @Annotations.AgoraUserOfflineReason reason: Int 162 | ) { 163 | callback(RtcChannelEvents.UserOffline, rtcChannel, uid.toUInt().toLong(), reason) 164 | } 165 | 166 | override fun onConnectionStateChanged( 167 | rtcChannel: RtcChannel?, 168 | @Annotations.AgoraConnectionStateType state: Int, 169 | @Annotations.AgoraConnectionChangedReason reason: Int 170 | ) { 171 | callback(RtcChannelEvents.ConnectionStateChanged, rtcChannel, state, reason) 172 | } 173 | 174 | override fun onConnectionLost(rtcChannel: RtcChannel?) { 175 | callback(RtcChannelEvents.ConnectionLost, rtcChannel) 176 | } 177 | 178 | override fun onTokenPrivilegeWillExpire(rtcChannel: RtcChannel?, token: String?) { 179 | callback(RtcChannelEvents.TokenPrivilegeWillExpire, rtcChannel, token) 180 | } 181 | 182 | override fun onRequestToken(rtcChannel: RtcChannel?) { 183 | callback(RtcChannelEvents.RequestToken, rtcChannel) 184 | } 185 | 186 | override fun onActiveSpeaker(rtcChannel: RtcChannel?, uid: Int) { 187 | callback(RtcChannelEvents.ActiveSpeaker, rtcChannel, uid.toUInt().toLong()) 188 | } 189 | 190 | override fun onVideoSizeChanged( 191 | rtcChannel: RtcChannel?, 192 | uid: Int, 193 | width: Int, 194 | height: Int, 195 | @IntRange(from = 0, to = 360) rotation: Int 196 | ) { 197 | callback( 198 | RtcChannelEvents.VideoSizeChanged, 199 | rtcChannel, 200 | uid.toUInt().toLong(), 201 | width, 202 | height, 203 | rotation 204 | ) 205 | } 206 | 207 | override fun onRemoteVideoStateChanged( 208 | rtcChannel: RtcChannel?, 209 | uid: Int, 210 | @Annotations.AgoraVideoRemoteState state: Int, 211 | @Annotations.AgoraVideoRemoteStateReason reason: Int, 212 | elapsed: Int 213 | ) { 214 | callback( 215 | RtcChannelEvents.RemoteVideoStateChanged, 216 | rtcChannel, 217 | uid.toUInt().toLong(), 218 | state, 219 | reason, 220 | elapsed 221 | ) 222 | } 223 | 224 | override fun onRemoteAudioStateChanged( 225 | rtcChannel: RtcChannel?, 226 | uid: Int, 227 | @Annotations.AgoraAudioRemoteState state: Int, 228 | @Annotations.AgoraAudioRemoteStateReason reason: Int, 229 | elapsed: Int 230 | ) { 231 | callback( 232 | RtcChannelEvents.RemoteAudioStateChanged, 233 | rtcChannel, 234 | uid.toUInt().toLong(), 235 | state, 236 | reason, 237 | elapsed 238 | ) 239 | } 240 | 241 | override fun onLocalPublishFallbackToAudioOnly( 242 | rtcChannel: RtcChannel?, 243 | isFallbackOrRecover: Boolean 244 | ) { 245 | callback(RtcChannelEvents.LocalPublishFallbackToAudioOnly, rtcChannel, isFallbackOrRecover) 246 | } 247 | 248 | override fun onRemoteSubscribeFallbackToAudioOnly( 249 | rtcChannel: RtcChannel?, 250 | uid: Int, 251 | isFallbackOrRecover: Boolean 252 | ) { 253 | callback( 254 | RtcChannelEvents.RemoteSubscribeFallbackToAudioOnly, 255 | rtcChannel, 256 | uid.toUInt().toLong(), 257 | isFallbackOrRecover 258 | ) 259 | } 260 | 261 | override fun onRtcStats(rtcChannel: RtcChannel?, stats: IRtcEngineEventHandler.RtcStats?) { 262 | callback(RtcChannelEvents.RtcStats, rtcChannel, stats?.toMap()) 263 | } 264 | 265 | override fun onNetworkQuality( 266 | rtcChannel: RtcChannel?, 267 | uid: Int, 268 | @Annotations.AgoraNetworkQuality txQuality: Int, 269 | @Annotations.AgoraNetworkQuality rxQuality: Int 270 | ) { 271 | callback( 272 | RtcChannelEvents.NetworkQuality, 273 | rtcChannel, 274 | uid.toUInt().toLong(), 275 | txQuality, 276 | rxQuality 277 | ) 278 | } 279 | 280 | override fun onRemoteVideoStats( 281 | rtcChannel: RtcChannel?, 282 | stats: IRtcEngineEventHandler.RemoteVideoStats? 283 | ) { 284 | callback(RtcChannelEvents.RemoteVideoStats, rtcChannel, stats?.toMap()) 285 | } 286 | 287 | override fun onRemoteAudioStats( 288 | rtcChannel: RtcChannel?, 289 | stats: IRtcEngineEventHandler.RemoteAudioStats? 290 | ) { 291 | callback(RtcChannelEvents.RemoteAudioStats, rtcChannel, stats?.toMap()) 292 | } 293 | 294 | override fun onRtmpStreamingStateChanged( 295 | rtcChannel: RtcChannel?, 296 | url: String?, 297 | @Annotations.AgoraRtmpStreamingState state: Int, 298 | @Annotations.AgoraRtmpStreamingErrorCode errCode: Int 299 | ) { 300 | callback(RtcChannelEvents.RtmpStreamingStateChanged, rtcChannel, url, state, errCode) 301 | } 302 | 303 | override fun onTranscodingUpdated(rtcChannel: RtcChannel?) { 304 | callback(RtcChannelEvents.TranscodingUpdated, rtcChannel) 305 | } 306 | 307 | override fun onStreamInjectedStatus( 308 | rtcChannel: RtcChannel?, 309 | url: String?, 310 | uid: Int, 311 | @Annotations.AgoraInjectStreamStatus status: Int 312 | ) { 313 | callback(RtcChannelEvents.StreamInjectedStatus, rtcChannel, url, uid.toUInt().toLong(), status) 314 | } 315 | 316 | override fun onStreamMessage(rtcChannel: RtcChannel?, uid: Int, streamId: Int, data: ByteArray?) { 317 | callback( 318 | RtcChannelEvents.StreamMessage, 319 | rtcChannel, 320 | uid.toUInt().toLong(), 321 | streamId, 322 | data?.let { String(it, Charsets.UTF_8) }) 323 | } 324 | 325 | override fun onStreamMessageError( 326 | rtcChannel: RtcChannel?, 327 | uid: Int, 328 | streamId: Int, 329 | @Annotations.AgoraErrorCode error: Int, 330 | missed: Int, 331 | cached: Int 332 | ) { 333 | callback( 334 | RtcChannelEvents.StreamMessageError, 335 | rtcChannel, 336 | uid.toUInt().toLong(), 337 | streamId, 338 | error, 339 | missed, 340 | cached 341 | ) 342 | } 343 | 344 | override fun onChannelMediaRelayStateChanged( 345 | rtcChannel: RtcChannel?, 346 | @Annotations.AgoraChannelMediaRelayState state: Int, 347 | @Annotations.AgoraChannelMediaRelayError code: Int 348 | ) { 349 | callback(RtcChannelEvents.ChannelMediaRelayStateChanged, rtcChannel, state, code) 350 | } 351 | 352 | override fun onChannelMediaRelayEvent( 353 | rtcChannel: RtcChannel?, 354 | @Annotations.AgoraChannelMediaRelayEvent code: Int 355 | ) { 356 | callback(RtcChannelEvents.ChannelMediaRelayEvent, rtcChannel, code) 357 | } 358 | 359 | override fun onAudioPublishStateChanged( 360 | rtcChannel: RtcChannel?, 361 | @Annotations.AgoraStreamPublishState oldState: Int, 362 | @Annotations.AgoraStreamPublishState newState: Int, 363 | elapseSinceLastState: Int 364 | ) { 365 | callback( 366 | RtcChannelEvents.AudioPublishStateChanged, 367 | rtcChannel, 368 | rtcChannel?.channelId(), 369 | oldState, 370 | newState, 371 | elapseSinceLastState 372 | ) 373 | } 374 | 375 | override fun onVideoPublishStateChanged( 376 | rtcChannel: RtcChannel?, 377 | @Annotations.AgoraStreamPublishState oldState: Int, 378 | @Annotations.AgoraStreamPublishState newState: Int, 379 | elapseSinceLastState: Int 380 | ) { 381 | callback( 382 | RtcChannelEvents.VideoPublishStateChanged, 383 | rtcChannel, 384 | rtcChannel?.channelId(), 385 | oldState, 386 | newState, 387 | elapseSinceLastState 388 | ) 389 | } 390 | 391 | override fun onAudioSubscribeStateChanged( 392 | rtcChannel: RtcChannel?, 393 | uid: Int, 394 | @Annotations.AgoraStreamSubscribeState oldState: Int, 395 | @Annotations.AgoraStreamSubscribeState newState: Int, 396 | elapseSinceLastState: Int 397 | ) { 398 | callback( 399 | RtcChannelEvents.AudioSubscribeStateChanged, 400 | rtcChannel, 401 | rtcChannel?.channelId(), 402 | uid.toUInt().toLong(), 403 | oldState, 404 | newState, 405 | elapseSinceLastState 406 | ) 407 | } 408 | 409 | override fun onVideoSubscribeStateChanged( 410 | rtcChannel: RtcChannel?, 411 | uid: Int, 412 | @Annotations.AgoraStreamSubscribeState oldState: Int, 413 | @Annotations.AgoraStreamSubscribeState newState: Int, 414 | elapseSinceLastState: Int 415 | ) { 416 | callback( 417 | RtcChannelEvents.VideoSubscribeStateChanged, 418 | rtcChannel, 419 | rtcChannel?.channelId(), 420 | uid.toUInt().toLong(), 421 | oldState, 422 | newState, 423 | elapseSinceLastState 424 | ) 425 | } 426 | 427 | override fun onRtmpStreamingEvent( 428 | rtcChannel: RtcChannel?, 429 | url: String?, 430 | @Annotations.AgoraRtmpStreamingEvent errCode: Int 431 | ) { 432 | callback(RtcChannelEvents.RtmpStreamingEvent, rtcChannel, url, errCode) 433 | } 434 | 435 | override fun onUserSuperResolutionEnabled( 436 | rtcChannel: RtcChannel?, 437 | uid: Int, 438 | enabled: Boolean, 439 | @Annotations.AgoraSuperResolutionStateReason reason: Int 440 | ) { 441 | callback( 442 | RtcChannelEvents.UserSuperResolutionEnabled, 443 | rtcChannel, 444 | uid.toUInt().toLong(), 445 | enabled, 446 | reason 447 | ) 448 | } 449 | 450 | override fun onProxyConnected( 451 | rtcChannel: RtcChannel?, 452 | uid: Int, 453 | proxyType: Int, 454 | localProxyIp: String?, 455 | elapsed: Int 456 | ) { 457 | callback( 458 | RtcChannelEvents.ProxyConnected, 459 | rtcChannel, 460 | rtcChannel?.channelId(), 461 | uid.toUInt().toLong(), 462 | proxyType, 463 | localProxyIp, 464 | elapsed 465 | ) 466 | } 467 | 468 | override fun onClientRoleChangeFailed(rtcChannel: RtcChannel?, reason: Int, currentRole: Int) { 469 | callback(RtcChannelEvents.ClientRoleChangeFailed, rtcChannel, reason, currentRole) 470 | } 471 | 472 | override fun onFirstRemoteVideoFrame( 473 | rtcChannel: RtcChannel?, 474 | uid: Int, 475 | width: Int, 476 | height: Int, 477 | elapsed: Int 478 | ) { 479 | callback( 480 | RtcChannelEvents.FirstRemoteVideoFrame, 481 | rtcChannel, 482 | uid.toUInt().toLong(), 483 | width, 484 | height, 485 | elapsed 486 | ) 487 | } 488 | } 489 | -------------------------------------------------------------------------------- /android/src/main/java/io/agora/rtc/base/RtcEnginePlugin.kt: -------------------------------------------------------------------------------- 1 | package io.agora.rtc.base 2 | 3 | import io.agora.rtc.RtcEngine 4 | 5 | /** 6 | * A [RtcEnginePlugin] allows developers to interact with the [RtcEngine] which created from flutter 7 | * side. 8 | */ 9 | interface RtcEnginePlugin { 10 | 11 | /** 12 | * This callback will be called when the [RtcEngine] is created by 13 | * [RtcEngine.createWithContext](https://docs.agora.io/cn/Video/API%20Reference/flutter/agora_rtc_engine/RtcEngine/createWithContext.html) 14 | * function from flutter. 15 | * 16 | * NOTE that you should not call [RtcEngine.destroy], because it will also destroy the `RtcEngine` 17 | * used by flutter side. 18 | * 19 | * @param rtcEngine The same [RtcEngine] used by flutter side 20 | */ 21 | fun onRtcEngineCreated(rtcEngine: RtcEngine?) 22 | 23 | /** 24 | * This callback will be called when the [RtcEngine.destroy](https://docs.agora.io/cn/Video/API%20Reference/flutter/v4.0.7/rtc_channel/RtcChannel/destroy.html) 25 | * function is called from flutter. 26 | */ 27 | fun onRtcEngineDestroyed() 28 | 29 | companion object Registrant { 30 | /** 31 | * Register a [RtcEnginePlugin]. The [plugin] will be called when the [RtcEngine] is created from 32 | * flutter side. 33 | */ 34 | fun register(plugin: RtcEnginePlugin) { 35 | RtcEngineRegistry.instance.add(plugin = plugin) 36 | } 37 | 38 | /** 39 | * Unregister a previously registered [RtcEnginePlugin]. 40 | */ 41 | fun unregister(plugin: RtcEnginePlugin) { 42 | RtcEngineRegistry.instance.remove(pluginClass = plugin.javaClass) 43 | } 44 | } 45 | } 46 | 47 | 48 | -------------------------------------------------------------------------------- /android/src/main/java/io/agora/rtc/base/RtcEngineRegistry.kt: -------------------------------------------------------------------------------- 1 | package io.agora.rtc.base 2 | 3 | import io.agora.rtc.RtcEngine 4 | import java.util.HashMap 5 | 6 | /** 7 | * The [RtcEngineRegistry] is response to add, remove and notify the callback when [RtcEngine] is created 8 | * from flutter side. 9 | */ 10 | internal class RtcEngineRegistry private constructor() : RtcEnginePlugin { 11 | companion object { 12 | val instance: RtcEngineRegistry by lazy { RtcEngineRegistry() } 13 | } 14 | 15 | private val plugins: MutableMap, RtcEnginePlugin> = HashMap() 16 | 17 | /** 18 | * Add a [RtcEnginePlugin]. 19 | */ 20 | fun add(plugin: RtcEnginePlugin) { 21 | if (plugins.containsKey(plugin.javaClass)) return 22 | plugins[plugin.javaClass] = plugin 23 | } 24 | 25 | /** 26 | * Remove the previously added [RtcEnginePlugin]. 27 | */ 28 | fun remove(pluginClass: Class) { 29 | plugins.remove(pluginClass) 30 | } 31 | 32 | override fun onRtcEngineCreated(rtcEngine: RtcEngine?) { 33 | for (plugin in plugins.values) { 34 | plugin.onRtcEngineCreated(rtcEngine) 35 | } 36 | } 37 | 38 | override fun onRtcEngineDestroyed() { 39 | for (plugin in plugins.values) { 40 | plugin.onRtcEngineDestroyed() 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /android/src/main/java/io/agora/rtc/base/RtcSurfaceView.kt: -------------------------------------------------------------------------------- 1 | package io.agora.rtc.base 2 | 3 | import android.content.Context 4 | import android.view.SurfaceView 5 | import android.widget.FrameLayout 6 | import io.agora.rtc.RtcChannel 7 | import io.agora.rtc.RtcEngine 8 | import io.agora.rtc.video.VideoCanvas 9 | import java.lang.ref.WeakReference 10 | 11 | class RtcSurfaceView( 12 | context: Context 13 | ) : FrameLayout(context) { 14 | private var surface: SurfaceView 15 | private var canvas: VideoCanvas? = null 16 | private var isMediaOverlay = false 17 | private var onTop = false 18 | private var channel: WeakReference? = null 19 | 20 | init { 21 | try { 22 | surface = RtcEngine.CreateRendererView(context) 23 | } catch (e: UnsatisfiedLinkError) { 24 | throw RuntimeException("Please init RtcEngine first!") 25 | } 26 | addView(surface) 27 | } 28 | 29 | fun setZOrderMediaOverlay(isMediaOverlay: Boolean) { 30 | this.isMediaOverlay = isMediaOverlay 31 | try { 32 | removeView(surface) 33 | surface.setZOrderMediaOverlay(isMediaOverlay) 34 | addView(surface) 35 | } catch (e: Exception) { 36 | e.printStackTrace() 37 | } 38 | } 39 | 40 | fun setZOrderOnTop(onTop: Boolean) { 41 | this.onTop = onTop 42 | try { 43 | removeView(surface) 44 | surface.setZOrderOnTop(onTop) 45 | addView(surface) 46 | } catch (e: Exception) { 47 | e.printStackTrace() 48 | } 49 | } 50 | 51 | fun setData(engine: RtcEngine, channel: RtcChannel?, uid: Number) { 52 | this.channel = if (channel != null) WeakReference(channel) else null 53 | canvas = canvas ?: VideoCanvas(surface) 54 | canvas?.channelId = this.channel?.get()?.channelId() 55 | canvas?.uid = uid.toNativeUInt() 56 | setupVideoCanvas(engine) 57 | } 58 | 59 | fun resetVideoCanvas(engine: RtcEngine) { 60 | canvas?.let { 61 | val canvas = 62 | VideoCanvas(null, it.renderMode, it.channelId, it.uid, it.mirrorMode) 63 | if (canvas.uid == 0) { 64 | engine.setupLocalVideo(canvas) 65 | } else { 66 | engine.setupRemoteVideo(canvas) 67 | } 68 | } 69 | } 70 | 71 | private fun setupVideoCanvas(engine: RtcEngine) { 72 | removeAllViews() 73 | surface = RtcEngine.CreateRendererView(context.applicationContext) 74 | surface.setZOrderMediaOverlay(isMediaOverlay) 75 | surface.setZOrderOnTop(onTop) 76 | addView(surface) 77 | surface.layout(0, 0, width, height) 78 | canvas?.let { canvas -> 79 | canvas.view = surface 80 | if (canvas.uid == 0) { 81 | engine.setupLocalVideo(canvas) 82 | } else { 83 | engine.setupRemoteVideo(canvas) 84 | } 85 | } 86 | } 87 | 88 | fun setRenderMode(engine: RtcEngine, @Annotations.AgoraVideoRenderMode renderMode: Int) { 89 | canvas?.renderMode = renderMode 90 | setupRenderMode(engine) 91 | } 92 | 93 | fun setMirrorMode(engine: RtcEngine, @Annotations.AgoraVideoMirrorMode mirrorMode: Int) { 94 | canvas?.mirrorMode = mirrorMode 95 | setupRenderMode(engine) 96 | } 97 | 98 | private fun setupRenderMode(engine: RtcEngine) { 99 | canvas?.let { canvas -> 100 | if (canvas.uid == 0) { 101 | engine.setLocalRenderMode(canvas.renderMode, canvas.mirrorMode) 102 | } else { 103 | channel?.get()?.let { 104 | it.setRemoteRenderMode(canvas.uid, canvas.renderMode, canvas.mirrorMode) 105 | return@setupRenderMode 106 | } 107 | engine.setRemoteRenderMode(canvas.uid, canvas.renderMode, canvas.mirrorMode) 108 | } 109 | } 110 | } 111 | 112 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { 113 | val width: Int = MeasureSpec.getSize(widthMeasureSpec) 114 | val height: Int = MeasureSpec.getSize(heightMeasureSpec) 115 | surface.layout(0, 0, width, height) 116 | super.onMeasure(widthMeasureSpec, heightMeasureSpec) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /android/src/main/java/io/agora/rtc/base/RtcTextureView.kt: -------------------------------------------------------------------------------- 1 | package io.agora.rtc.base 2 | 3 | import android.content.Context 4 | import android.view.TextureView 5 | import android.widget.FrameLayout 6 | import io.agora.rtc.RtcChannel 7 | import io.agora.rtc.RtcEngine 8 | import io.agora.rtc.video.VideoCanvas 9 | import java.lang.ref.WeakReference 10 | 11 | class RtcTextureView( 12 | context: Context 13 | ) : FrameLayout(context) { 14 | private var texture: TextureView 15 | private var canvas: VideoCanvas? = null 16 | private var channel: WeakReference? = null 17 | 18 | init { 19 | try { 20 | texture = RtcEngine.CreateTextureView(context) 21 | } catch (e: UnsatisfiedLinkError) { 22 | throw RuntimeException("Please init RtcEngine first!") 23 | } 24 | addView(texture) 25 | } 26 | 27 | fun setData(engine: RtcEngine, channel: RtcChannel?, uid: Number) { 28 | this.channel = if (channel != null) WeakReference(channel) else null 29 | canvas = canvas ?: VideoCanvas(texture) 30 | canvas?.channelId = this.channel?.get()?.channelId() 31 | canvas?.uid = uid.toNativeUInt() 32 | setupVideoCanvas(engine) 33 | } 34 | 35 | fun resetVideoCanvas(engine: RtcEngine) { 36 | canvas?.let { 37 | val canvas = 38 | VideoCanvas(null, it.renderMode, it.channelId, it.uid, it.mirrorMode) 39 | if (canvas.uid == 0) { 40 | engine.setupLocalVideo(canvas) 41 | } else { 42 | engine.setupRemoteVideo(canvas) 43 | } 44 | } 45 | } 46 | 47 | private fun setupVideoCanvas(engine: RtcEngine) { 48 | removeAllViews() 49 | texture = RtcEngine.CreateTextureView(context.applicationContext) 50 | addView(texture) 51 | texture.layout(0, 0, width, height) 52 | canvas?.let { canvas -> 53 | canvas.view = texture 54 | if (canvas.uid == 0) { 55 | engine.setupLocalVideo(canvas) 56 | } else { 57 | engine.setupRemoteVideo(canvas) 58 | } 59 | } 60 | } 61 | 62 | fun setRenderMode(engine: RtcEngine, @Annotations.AgoraVideoRenderMode renderMode: Int) { 63 | canvas?.renderMode = renderMode 64 | setupRenderMode(engine) 65 | } 66 | 67 | fun setMirrorMode(engine: RtcEngine, @Annotations.AgoraVideoMirrorMode mirrorMode: Int) { 68 | canvas?.mirrorMode = mirrorMode 69 | setupRenderMode(engine) 70 | } 71 | 72 | private fun setupRenderMode(engine: RtcEngine) { 73 | canvas?.let { canvas -> 74 | if (canvas.uid == 0) { 75 | engine.setLocalRenderMode(canvas.renderMode, canvas.mirrorMode) 76 | } else { 77 | channel?.get()?.let { 78 | it.setRemoteRenderMode(canvas.uid, canvas.renderMode, canvas.mirrorMode) 79 | return@setupRenderMode 80 | } 81 | engine.setRemoteRenderMode(canvas.uid, canvas.renderMode, canvas.mirrorMode) 82 | } 83 | } 84 | } 85 | 86 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { 87 | val width: Int = MeasureSpec.getSize(widthMeasureSpec) 88 | val height: Int = MeasureSpec.getSize(heightMeasureSpec) 89 | texture.layout(0, 0, width, height) 90 | super.onMeasure(widthMeasureSpec, heightMeasureSpec) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /android/src/main/java/io/agora/rtc/uni/AgoraRtcChannelModule.kt: -------------------------------------------------------------------------------- 1 | package io.agora.rtc.uni 2 | 3 | import com.alibaba.fastjson.JSONObject 4 | import io.agora.rtc.RtcEngine 5 | import io.agora.rtc.base.RtcChannelManager 6 | import io.agora.rtc.base.RtcEngineEventHandler 7 | import io.agora.rtc.base.RtcEngineManager 8 | import io.dcloud.feature.uniapp.annotation.UniJSMethod 9 | import io.dcloud.feature.uniapp.bridge.UniJSCallback 10 | import io.dcloud.feature.uniapp.common.UniModule 11 | 12 | class AgoraRtcChannelModule : UniModule() { 13 | companion object { 14 | var manager: RtcChannelManager? = null 15 | private set 16 | } 17 | 18 | private val manager = RtcChannelManager { methodName, data -> emit(methodName, data) } 19 | 20 | init { 21 | AgoraRtcChannelModule.manager = manager 22 | } 23 | 24 | private fun emit(methodName: String, data: Map?) { 25 | mUniSDKInstance.fireGlobalEventCallback("${RtcEngineEventHandler.PREFIX}$methodName", data) 26 | } 27 | 28 | @UniJSMethod(uiThread = false) 29 | fun callMethod(params: JSONObject?, callback: UniJSCallback?) { 30 | params?.getString("method")?.let { methodName -> 31 | manager.javaClass.declaredMethods.find { it.name == methodName }?.let { function -> 32 | function.let { method -> 33 | try { 34 | val parameters = mutableListOf() 35 | params.getJSONObject("args")?.toMap()?.toMutableMap()?.let { 36 | if (methodName == "create") { 37 | it["engine"] = RtcEngineManager.engine 38 | } 39 | parameters.add(it) 40 | } 41 | method.invoke(manager, *parameters.toTypedArray(), UniCallback(callback)) 42 | } catch (e: Exception) { 43 | e.printStackTrace() 44 | } 45 | } 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /android/src/main/java/io/agora/rtc/uni/AgoraRtcEngineModule.kt: -------------------------------------------------------------------------------- 1 | package io.agora.rtc.uni 2 | 3 | import com.alibaba.fastjson.JSONObject 4 | import io.agora.rtc.base.RtcEngineEventHandler 5 | import io.agora.rtc.base.RtcEngineManager 6 | import io.dcloud.feature.uniapp.annotation.UniJSMethod 7 | import io.dcloud.feature.uniapp.bridge.UniJSCallback 8 | import io.dcloud.feature.uniapp.common.UniModule 9 | 10 | class AgoraRtcEngineModule : UniModule() { 11 | companion object { 12 | var manager: RtcEngineManager? = null 13 | private set 14 | } 15 | 16 | private val manager = RtcEngineManager(emit = { methodName, data -> emit(methodName, data) }) 17 | 18 | init { 19 | AgoraRtcEngineModule.manager = manager 20 | } 21 | 22 | private fun emit(methodName: String, data: Map?) { 23 | mUniSDKInstance.fireGlobalEventCallback("${RtcEngineEventHandler.PREFIX}$methodName", data) 24 | } 25 | 26 | @UniJSMethod(uiThread = false) 27 | fun callMethod(params: JSONObject, callback: UniJSCallback?) { 28 | params.getString("method")?.let { methodName -> 29 | manager.javaClass.declaredMethods.find { it.name == methodName }?.let { function -> 30 | function.let { method -> 31 | try { 32 | val parameters = mutableListOf() 33 | params.getJSONObject("args")?.toMap()?.toMutableMap()?.let { 34 | if (methodName == "create") { 35 | it["context"] = mUniSDKInstance.context.applicationContext 36 | } 37 | parameters.add(it) 38 | } 39 | method.invoke(manager, *parameters.toTypedArray(), UniCallback(callback)) 40 | } catch (e: Exception) { 41 | e.printStackTrace() 42 | } 43 | } 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /android/src/main/java/io/agora/rtc/uni/AgoraRtcSurfaceView.kt: -------------------------------------------------------------------------------- 1 | package io.agora.rtc.uni 2 | 3 | import android.content.Context 4 | import com.alibaba.fastjson.JSONObject 5 | import io.agora.rtc.base.RtcChannelManager 6 | import io.agora.rtc.base.RtcEngineManager 7 | import io.agora.rtc.base.RtcSurfaceView 8 | import io.dcloud.feature.uniapp.UniSDKInstance 9 | import io.dcloud.feature.uniapp.ui.action.AbsComponentData 10 | import io.dcloud.feature.uniapp.ui.component.AbsVContainer 11 | import io.dcloud.feature.uniapp.ui.component.UniComponent 12 | import io.dcloud.feature.uniapp.ui.component.UniComponentProp 13 | 14 | class AgoraRtcSurfaceView( 15 | instance: UniSDKInstance?, 16 | parent: AbsVContainer<*>?, 17 | componentData: AbsComponentData<*>?) 18 | : UniComponent(instance, parent, componentData) { 19 | override fun initComponentHostView(context: Context): RtcSurfaceView { 20 | return RtcSurfaceView(context.applicationContext) 21 | } 22 | 23 | @UniComponentProp(name = "zOrderMediaOverlay") 24 | fun setZOrderMediaOverlay(isMediaOverlay: Boolean) { 25 | hostView.setZOrderMediaOverlay(isMediaOverlay) 26 | } 27 | 28 | @UniComponentProp(name = "zOrderOnTop") 29 | fun setZOrderOnTop(onTop: Boolean) { 30 | hostView.setZOrderOnTop(onTop) 31 | } 32 | 33 | @UniComponentProp(name = "data") 34 | fun setData(data: JSONObject) { 35 | data.toMap().let { map -> 36 | val channel = (map["channelId"] as? String)?.let { RtcChannelManager[it] } 37 | RtcEngineManager.engine?.let { hostView.setData(it, channel, (map["uid"] as Number).toInt()) } 38 | } 39 | } 40 | 41 | @UniComponentProp(name = "renderMode") 42 | fun setRenderMode(renderMode: Int) { 43 | RtcEngineManager.engine?.let { hostView.setRenderMode(it, renderMode) } 44 | } 45 | 46 | @UniComponentProp(name = "mirrorMode") 47 | fun setMirrorMode(mirrorMode: Int) { 48 | RtcEngineManager.engine?.let { hostView.setMirrorMode(it, mirrorMode) } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /android/src/main/java/io/agora/rtc/uni/AgoraRtcTextureView.kt: -------------------------------------------------------------------------------- 1 | package io.agora.rtc.uni 2 | 3 | import android.content.Context 4 | import com.alibaba.fastjson.JSONObject 5 | import io.agora.rtc.base.RtcChannelManager 6 | import io.agora.rtc.base.RtcEngineManager 7 | import io.agora.rtc.base.RtcTextureView 8 | import io.dcloud.feature.uniapp.UniSDKInstance 9 | import io.dcloud.feature.uniapp.ui.action.AbsComponentData 10 | import io.dcloud.feature.uniapp.ui.component.AbsVContainer 11 | import io.dcloud.feature.uniapp.ui.component.UniComponent 12 | import io.dcloud.feature.uniapp.ui.component.UniComponentProp 13 | 14 | class AgoraRtcTextureView( 15 | instance: UniSDKInstance?, 16 | parent: AbsVContainer<*>?, 17 | componentData: AbsComponentData<*>?) 18 | : UniComponent(instance, parent, componentData) { 19 | override fun initComponentHostView(context: Context): RtcTextureView { 20 | return RtcTextureView(context.applicationContext) 21 | } 22 | 23 | @UniComponentProp(name = "data") 24 | fun setData(data: JSONObject) { 25 | data.toMap().let { map -> 26 | val channel = (map["channelId"] as? String)?.let { RtcChannelManager[it] } 27 | RtcEngineManager.engine?.let { hostView.setData(it, channel, (map["uid"] as Number).toInt()) } 28 | } 29 | } 30 | 31 | @UniComponentProp(name = "renderMode") 32 | fun setRenderMode(renderMode: Int) { 33 | RtcEngineManager.engine?.let { hostView.setRenderMode(it, renderMode) } 34 | } 35 | 36 | @UniComponentProp(name = "mirrorMode") 37 | fun setMirrorMode(mirrorMode: Int) { 38 | RtcEngineManager.engine?.let { hostView.setMirrorMode(it, mirrorMode) } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /android/src/main/java/io/agora/rtc/uni/UniCallback.kt: -------------------------------------------------------------------------------- 1 | package io.agora.rtc.uni 2 | 3 | import com.alibaba.fastjson.JSONObject 4 | import io.agora.rtc.base.Callback 5 | import io.dcloud.feature.uniapp.bridge.UniJSCallback 6 | 7 | class UniCallback( 8 | private val callback: UniJSCallback? 9 | ) : Callback() { 10 | override fun success(data: Any?) { 11 | callback?.invoke(data) 12 | } 13 | 14 | override fun failure(code: String, message: String) { 15 | callback?.invoke(JSONObject().apply { 16 | put("code", code) 17 | put("message", message) 18 | }) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | unpackage/ 2 | -------------------------------------------------------------------------------- /example/App.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 18 | -------------------------------------------------------------------------------- /example/common/agora.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // Get your own App ID at https://dashboard.agora.io/ 3 | "appId": YOUR_APP_ID, 4 | // Please refer to https://docs.agora.io/en/Agora%20Platform/token 5 | "token": YOUR_TOEKN, 6 | "channelId": YOUR_CHANNEL_ID, 7 | "uid": YOUR_UID, 8 | "stringUid": YOUR_STRING_UID 9 | } 10 | -------------------------------------------------------------------------------- /example/components/Agora-RTC-JS/RtcSurfaceView.nvue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 36 | 37 | 40 | -------------------------------------------------------------------------------- /example/components/Agora-RTC-JS/RtcTextureView.nvue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 27 | 28 | 31 | -------------------------------------------------------------------------------- /example/components/Agora-RTC-JS/common/RtcEvents.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | //# sourceMappingURL=RtcEvents.js.map -------------------------------------------------------------------------------- /example/components/Agora-RTC-JS/common/RtcEvents.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":[],"names":[],"mappings":"","sourcesContent":[]} -------------------------------------------------------------------------------- /example/components/Agora-RTC-JS/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | Object.defineProperty(exports, "RtcChannel", { 7 | enumerable: true, 8 | get: function () { 9 | return _RtcChannel.default; 10 | } 11 | }); 12 | exports.default = void 0; 13 | 14 | var _RtcEngine = _interopRequireDefault(require("./common/RtcEngine.native")); 15 | 16 | var _RtcChannel = _interopRequireDefault(require("./common/RtcChannel.native")); 17 | 18 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 19 | 20 | var _default = _RtcEngine.default; 21 | exports.default = _default; 22 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /example/components/Agora-RTC-JS/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["index.ts"],"names":["RtcEngine"],"mappings":";;;;;;;;;;;;;AAAA;;AACA;;;;eAEeA,kB","sourcesContent":["import RtcEngine from './common/RtcEngine.native';\nimport RtcChannel from './common/RtcChannel.native';\n\nexport default RtcEngine;\nexport { RtcChannel };\n"]} -------------------------------------------------------------------------------- /example/components/uni-icons/icons.js: -------------------------------------------------------------------------------- 1 | export default { 2 | "pulldown": "\ue588", 3 | "refreshempty": "\ue461", 4 | "back": "\ue471", 5 | "forward": "\ue470", 6 | "more": "\ue507", 7 | "more-filled": "\ue537", 8 | "scan": "\ue612", 9 | "qq": "\ue264", 10 | "weibo": "\ue260", 11 | "weixin": "\ue261", 12 | "pengyouquan": "\ue262", 13 | "loop": "\ue565", 14 | "refresh": "\ue407", 15 | "refresh-filled": "\ue437", 16 | "arrowthindown": "\ue585", 17 | "arrowthinleft": "\ue586", 18 | "arrowthinright": "\ue587", 19 | "arrowthinup": "\ue584", 20 | "undo-filled": "\ue7d6", 21 | "undo": "\ue406", 22 | "redo": "\ue405", 23 | "redo-filled": "\ue7d9", 24 | "bars": "\ue563", 25 | "chatboxes": "\ue203", 26 | "camera": "\ue301", 27 | "chatboxes-filled": "\ue233", 28 | "camera-filled": "\ue7ef", 29 | "cart-filled": "\ue7f4", 30 | "cart": "\ue7f5", 31 | "checkbox-filled": "\ue442", 32 | "checkbox": "\ue7fa", 33 | "arrowleft": "\ue582", 34 | "arrowdown": "\ue581", 35 | "arrowright": "\ue583", 36 | "smallcircle-filled": "\ue801", 37 | "arrowup": "\ue580", 38 | "circle": "\ue411", 39 | "eye-filled": "\ue568", 40 | "eye-slash-filled": "\ue822", 41 | "eye-slash": "\ue823", 42 | "eye": "\ue824", 43 | "flag-filled": "\ue825", 44 | "flag": "\ue508", 45 | "gear-filled": "\ue532", 46 | "reload": "\ue462", 47 | "gear": "\ue502", 48 | "hand-thumbsdown-filled": "\ue83b", 49 | "hand-thumbsdown": "\ue83c", 50 | "hand-thumbsup-filled": "\ue83d", 51 | "heart-filled": "\ue83e", 52 | "hand-thumbsup": "\ue83f", 53 | "heart": "\ue840", 54 | "home": "\ue500", 55 | "info": "\ue504", 56 | "home-filled": "\ue530", 57 | "info-filled": "\ue534", 58 | "circle-filled": "\ue441", 59 | "chat-filled": "\ue847", 60 | "chat": "\ue263", 61 | "mail-open-filled": "\ue84d", 62 | "email-filled": "\ue231", 63 | "mail-open": "\ue84e", 64 | "email": "\ue201", 65 | "checkmarkempty": "\ue472", 66 | "list": "\ue562", 67 | "locked-filled": "\ue856", 68 | "locked": "\ue506", 69 | "map-filled": "\ue85c", 70 | "map-pin": "\ue85e", 71 | "map-pin-ellipse": "\ue864", 72 | "map": "\ue364", 73 | "minus-filled": "\ue440", 74 | "mic-filled": "\ue332", 75 | "minus": "\ue410", 76 | "micoff": "\ue360", 77 | "mic": "\ue302", 78 | "clear": "\ue434", 79 | "smallcircle": "\ue868", 80 | "close": "\ue404", 81 | "closeempty": "\ue460", 82 | "paperclip": "\ue567", 83 | "paperplane": "\ue503", 84 | "paperplane-filled": "\ue86e", 85 | "person-filled": "\ue131", 86 | "contact-filled": "\ue130", 87 | "person": "\ue101", 88 | "contact": "\ue100", 89 | "images-filled": "\ue87a", 90 | "phone": "\ue200", 91 | "images": "\ue87b", 92 | "image": "\ue363", 93 | "image-filled": "\ue877", 94 | "location-filled": "\ue333", 95 | "location": "\ue303", 96 | "plus-filled": "\ue439", 97 | "plus": "\ue409", 98 | "plusempty": "\ue468", 99 | "help-filled": "\ue535", 100 | "help": "\ue505", 101 | "navigate-filled": "\ue884", 102 | "navigate": "\ue501", 103 | "mic-slash-filled": "\ue892", 104 | "search": "\ue466", 105 | "settings": "\ue560", 106 | "sound": "\ue590", 107 | "sound-filled": "\ue8a1", 108 | "spinner-cycle": "\ue465", 109 | "download-filled": "\ue8a4", 110 | "personadd-filled": "\ue132", 111 | "videocam-filled": "\ue8af", 112 | "personadd": "\ue102", 113 | "upload": "\ue402", 114 | "upload-filled": "\ue8b1", 115 | "starhalf": "\ue463", 116 | "star-filled": "\ue438", 117 | "star": "\ue408", 118 | "trash": "\ue401", 119 | "phone-filled": "\ue230", 120 | "compose": "\ue400", 121 | "videocam": "\ue300", 122 | "trash-filled": "\ue8dc", 123 | "download": "\ue403", 124 | "chatbubble-filled": "\ue232", 125 | "chatbubble": "\ue202", 126 | "cloud-download": "\ue8e4", 127 | "cloud-upload-filled": "\ue8e5", 128 | "cloud-upload": "\ue8e6", 129 | "cloud-download-filled": "\ue8e9", 130 | "headphones":"\ue8bf", 131 | "shop":"\ue609" 132 | } 133 | -------------------------------------------------------------------------------- /example/components/uni-icons/uni.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/Agora-Uniapp-SDK/b6c4fedb0415a35e4891a2689123ad81db7635e6/example/components/uni-icons/uni.ttf -------------------------------------------------------------------------------- /example/components/uni-indexed-list/uni-indexed-list-item.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 58 | 59 | 143 | -------------------------------------------------------------------------------- /example/components/uni-indexed-list/uni-indexed-list.vue: -------------------------------------------------------------------------------- 1 | 31 | 244 | 319 | -------------------------------------------------------------------------------- /example/js_sdk/wa-permission/permission.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 本模块封装了Android、iOS的应用权限判断、打开应用权限设置界面、以及位置系统服务是否开启 3 | */ 4 | 5 | var isIos 6 | // #ifdef APP-PLUS 7 | isIos = (plus.os.name == "iOS") 8 | // #endif 9 | 10 | // 判断推送权限是否开启 11 | function judgeIosPermissionPush() { 12 | var result = false; 13 | var UIApplication = plus.ios.import("UIApplication"); 14 | var app = UIApplication.sharedApplication(); 15 | var enabledTypes = 0; 16 | if (app.currentUserNotificationSettings) { 17 | var settings = app.currentUserNotificationSettings(); 18 | enabledTypes = settings.plusGetAttribute("types"); 19 | console.log("enabledTypes1:" + enabledTypes); 20 | if (enabledTypes == 0) { 21 | console.log("推送权限没有开启"); 22 | } else { 23 | result = true; 24 | console.log("已经开启推送功能!") 25 | } 26 | plus.ios.deleteObject(settings); 27 | } else { 28 | enabledTypes = app.enabledRemoteNotificationTypes(); 29 | if (enabledTypes == 0) { 30 | console.log("推送权限没有开启!"); 31 | } else { 32 | result = true; 33 | console.log("已经开启推送功能!") 34 | } 35 | console.log("enabledTypes2:" + enabledTypes); 36 | } 37 | plus.ios.deleteObject(app); 38 | plus.ios.deleteObject(UIApplication); 39 | return result; 40 | } 41 | 42 | // 判断定位权限是否开启 43 | function judgeIosPermissionLocation() { 44 | var result = false; 45 | var cllocationManger = plus.ios.import("CLLocationManager"); 46 | var status = cllocationManger.authorizationStatus(); 47 | result = (status != 2) 48 | console.log("定位权限开启:" + result); 49 | // 以下代码判断了手机设备的定位是否关闭,推荐另行使用方法 checkSystemEnableLocation 50 | /* var enable = cllocationManger.locationServicesEnabled(); 51 | var status = cllocationManger.authorizationStatus(); 52 | console.log("enable:" + enable); 53 | console.log("status:" + status); 54 | if (enable && status != 2) { 55 | result = true; 56 | console.log("手机定位服务已开启且已授予定位权限"); 57 | } else { 58 | console.log("手机系统的定位没有打开或未给予定位权限"); 59 | } */ 60 | plus.ios.deleteObject(cllocationManger); 61 | return result; 62 | } 63 | 64 | // 判断麦克风权限是否开启 65 | function judgeIosPermissionRecord() { 66 | var result = false; 67 | var avaudiosession = plus.ios.import("AVAudioSession"); 68 | var avaudio = avaudiosession.sharedInstance(); 69 | var permissionStatus = avaudio.recordPermission(); 70 | console.log("permissionStatus:" + permissionStatus); 71 | if (permissionStatus == 1684369017 || permissionStatus == 1970168948) { 72 | console.log("麦克风权限没有开启"); 73 | } else { 74 | result = true; 75 | console.log("麦克风权限已经开启"); 76 | } 77 | plus.ios.deleteObject(avaudiosession); 78 | return result; 79 | } 80 | 81 | // 判断相机权限是否开启 82 | function judgeIosPermissionCamera() { 83 | var result = false; 84 | var AVCaptureDevice = plus.ios.import("AVCaptureDevice"); 85 | var authStatus = AVCaptureDevice.authorizationStatusForMediaType('vide'); 86 | console.log("authStatus:" + authStatus); 87 | if (authStatus == 3) { 88 | result = true; 89 | console.log("相机权限已经开启"); 90 | } else { 91 | console.log("相机权限没有开启"); 92 | } 93 | plus.ios.deleteObject(AVCaptureDevice); 94 | return result; 95 | } 96 | 97 | // 判断相册权限是否开启 98 | function judgeIosPermissionPhotoLibrary() { 99 | var result = false; 100 | var PHPhotoLibrary = plus.ios.import("PHPhotoLibrary"); 101 | var authStatus = PHPhotoLibrary.authorizationStatus(); 102 | console.log("authStatus:" + authStatus); 103 | if (authStatus == 3) { 104 | result = true; 105 | console.log("相册权限已经开启"); 106 | } else { 107 | console.log("相册权限没有开启"); 108 | } 109 | plus.ios.deleteObject(PHPhotoLibrary); 110 | return result; 111 | } 112 | 113 | // 判断通讯录权限是否开启 114 | function judgeIosPermissionContact() { 115 | var result = false; 116 | var CNContactStore = plus.ios.import("CNContactStore"); 117 | var cnAuthStatus = CNContactStore.authorizationStatusForEntityType(0); 118 | if (cnAuthStatus == 3) { 119 | result = true; 120 | console.log("通讯录权限已经开启"); 121 | } else { 122 | console.log("通讯录权限没有开启"); 123 | } 124 | plus.ios.deleteObject(CNContactStore); 125 | return result; 126 | } 127 | 128 | // 判断日历权限是否开启 129 | function judgeIosPermissionCalendar() { 130 | var result = false; 131 | var EKEventStore = plus.ios.import("EKEventStore"); 132 | var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(0); 133 | if (ekAuthStatus == 3) { 134 | result = true; 135 | console.log("日历权限已经开启"); 136 | } else { 137 | console.log("日历权限没有开启"); 138 | } 139 | plus.ios.deleteObject(EKEventStore); 140 | return result; 141 | } 142 | 143 | // 判断备忘录权限是否开启 144 | function judgeIosPermissionMemo() { 145 | var result = false; 146 | var EKEventStore = plus.ios.import("EKEventStore"); 147 | var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(1); 148 | if (ekAuthStatus == 3) { 149 | result = true; 150 | console.log("备忘录权限已经开启"); 151 | } else { 152 | console.log("备忘录权限没有开启"); 153 | } 154 | plus.ios.deleteObject(EKEventStore); 155 | return result; 156 | } 157 | 158 | // Android权限查询 159 | function requestAndroidPermission(permissionID) { 160 | return new Promise((resolve, reject) => { 161 | plus.android.requestPermissions( 162 | [permissionID], // 理论上支持多个权限同时查询,但实际上本函数封装只处理了一个权限的情况。有需要的可自行扩展封装 163 | function(resultObj) { 164 | var result = 0; 165 | for (var i = 0; i < resultObj.granted.length; i++) { 166 | var grantedPermission = resultObj.granted[i]; 167 | console.log('已获取的权限:' + grantedPermission); 168 | result = 1 169 | } 170 | for (var i = 0; i < resultObj.deniedPresent.length; i++) { 171 | var deniedPresentPermission = resultObj.deniedPresent[i]; 172 | console.log('拒绝本次申请的权限:' + deniedPresentPermission); 173 | result = 0 174 | } 175 | for (var i = 0; i < resultObj.deniedAlways.length; i++) { 176 | var deniedAlwaysPermission = resultObj.deniedAlways[i]; 177 | console.log('永久拒绝申请的权限:' + deniedAlwaysPermission); 178 | result = -1 179 | } 180 | resolve(result); 181 | // 若所需权限被拒绝,则打开APP设置界面,可以在APP设置界面打开相应权限 182 | // if (result != 1) { 183 | // gotoAppPermissionSetting() 184 | // } 185 | }, 186 | function(error) { 187 | console.log('申请权限错误:' + error.code + " = " + error.message); 188 | resolve({ 189 | code: error.code, 190 | message: error.message 191 | }); 192 | } 193 | ); 194 | }); 195 | } 196 | 197 | // 使用一个方法,根据参数判断权限 198 | function judgeIosPermission(permissionID) { 199 | if (permissionID == "location") { 200 | return judgeIosPermissionLocation() 201 | } else if (permissionID == "camera") { 202 | return judgeIosPermissionCamera() 203 | } else if (permissionID == "photoLibrary") { 204 | return judgeIosPermissionPhotoLibrary() 205 | } else if (permissionID == "record") { 206 | return judgeIosPermissionRecord() 207 | } else if (permissionID == "push") { 208 | return judgeIosPermissionPush() 209 | } else if (permissionID == "contact") { 210 | return judgeIosPermissionContact() 211 | } else if (permissionID == "calendar") { 212 | return judgeIosPermissionCalendar() 213 | } else if (permissionID == "memo") { 214 | return judgeIosPermissionMemo() 215 | } 216 | return false; 217 | } 218 | 219 | // 跳转到**应用**的权限页面 220 | function gotoAppPermissionSetting() { 221 | if (isIos) { 222 | var UIApplication = plus.ios.import("UIApplication"); 223 | var application2 = UIApplication.sharedApplication(); 224 | var NSURL2 = plus.ios.import("NSURL"); 225 | // var setting2 = NSURL2.URLWithString("prefs:root=LOCATION_SERVICES"); 226 | var setting2 = NSURL2.URLWithString("app-settings:"); 227 | application2.openURL(setting2); 228 | 229 | plus.ios.deleteObject(setting2); 230 | plus.ios.deleteObject(NSURL2); 231 | plus.ios.deleteObject(application2); 232 | } else { 233 | // console.log(plus.device.vendor); 234 | var Intent = plus.android.importClass("android.content.Intent"); 235 | var Settings = plus.android.importClass("android.provider.Settings"); 236 | var Uri = plus.android.importClass("android.net.Uri"); 237 | var mainActivity = plus.android.runtimeMainActivity(); 238 | var intent = new Intent(); 239 | intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); 240 | var uri = Uri.fromParts("package", mainActivity.getPackageName(), null); 241 | intent.setData(uri); 242 | mainActivity.startActivity(intent); 243 | } 244 | } 245 | 246 | // 检查系统的设备服务是否开启 247 | // var checkSystemEnableLocation = async function () { 248 | function checkSystemEnableLocation() { 249 | if (isIos) { 250 | var result = false; 251 | var cllocationManger = plus.ios.import("CLLocationManager"); 252 | var result = cllocationManger.locationServicesEnabled(); 253 | console.log("系统定位开启:" + result); 254 | plus.ios.deleteObject(cllocationManger); 255 | return result; 256 | } else { 257 | var context = plus.android.importClass("android.content.Context"); 258 | var locationManager = plus.android.importClass("android.location.LocationManager"); 259 | var main = plus.android.runtimeMainActivity(); 260 | var mainSvr = main.getSystemService(context.LOCATION_SERVICE); 261 | var result = mainSvr.isProviderEnabled(locationManager.GPS_PROVIDER); 262 | console.log("系统定位开启:" + result); 263 | return result 264 | } 265 | } 266 | 267 | module.exports = { 268 | judgeIosPermission: judgeIosPermission, 269 | requestAndroidPermission: requestAndroidPermission, 270 | checkSystemEnableLocation: checkSystemEnableLocation, 271 | gotoAppPermissionSetting: gotoAppPermissionSetting 272 | } 273 | -------------------------------------------------------------------------------- /example/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App' 3 | 4 | Vue.config.productionTip = false 5 | 6 | App.mpType = 'app' 7 | 8 | const app = new Vue({ 9 | ...App 10 | }) 11 | app.$mount() 12 | -------------------------------------------------------------------------------- /example/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "example", 3 | "appid" : "__UNI__92DB9B2", 4 | "description" : "", 5 | "versionName" : "1.0.0", 6 | "versionCode" : "100", 7 | "transformPx" : false, 8 | /* 5+App特有相关 */ 9 | "app-plus" : { 10 | "usingComponents" : true, 11 | "nvueCompiler" : "uni-app", 12 | "compilerVersion" : 3, 13 | "splashscreen" : { 14 | "alwaysShowBeforeRender" : true, 15 | "waiting" : true, 16 | "autoclose" : true, 17 | "delay" : 0 18 | }, 19 | /* 模块配置 */ 20 | "modules" : {}, 21 | /* 应用发布信息 */ 22 | "distribute" : { 23 | /* android打包配置 */ 24 | "android" : { 25 | "permissions" : [ 26 | "", 27 | "", 28 | "", 29 | "", 30 | "", 31 | "", 32 | "", 33 | "", 34 | "", 35 | "", 36 | "", 37 | "", 38 | "", 39 | "", 40 | "" 41 | ], 42 | "abiFilters" : [ "armeabi-v7a", "arm64-v8a", "x86" ] 43 | }, 44 | /* ios打包配置 */ 45 | "ios" : {}, 46 | /* SDK配置 */ 47 | "sdkConfigs" : { 48 | "ad" : {} 49 | } 50 | }, 51 | "nativePlugins" : { 52 | "Agora-RTC" : { 53 | "__plugin_info__" : { 54 | "name" : "Agora音视频插件", 55 | "description" : "Agora官方维护的音视频插件,并且在GitHub上开源,欢迎大家积极参与问题反馈和代码贡献", 56 | "platforms" : "Android,iOS", 57 | "url" : "https://ext.dcloud.net.cn/plugin?id=3720", 58 | "android_package_name" : "io.agora.uniExample", 59 | "ios_bundle_id" : "io.agora.uniExample", 60 | "isCloud" : true, 61 | "bought" : 1, 62 | "pid" : "3720", 63 | "parameters" : {} 64 | } 65 | } 66 | } 67 | }, 68 | /* 快应用特有相关 */ 69 | "quickapp" : {}, 70 | /* 小程序特有相关 */ 71 | "mp-weixin" : { 72 | "appid" : "", 73 | "setting" : { 74 | "urlCheck" : false 75 | }, 76 | "usingComponents" : true 77 | }, 78 | "mp-alipay" : { 79 | "usingComponents" : true 80 | }, 81 | "mp-baidu" : { 82 | "usingComponents" : true 83 | }, 84 | "mp-toutiao" : { 85 | "usingComponents" : true 86 | }, 87 | "uniStatistics" : { 88 | "enable" : false 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /example/pages.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages 3 | { 4 | "path": "pages/index/index", 5 | "style": { 6 | "navigationBarTitleText": "APIExample" 7 | } 8 | }, 9 | { 10 | "path": "pages/Advanced/MultiChannel", 11 | "style": { 12 | "navigationBarTitleText": "MultiChannel" 13 | } 14 | }, 15 | { 16 | "path": "pages/Basic/JoinChannelAudio", 17 | "style": { 18 | "navigationBarTitleText": "JoinChannelAudio" 19 | } 20 | }, 21 | { 22 | "path": "pages/Basic/JoinChannelVideo", 23 | "style": { 24 | "navigationBarTitleText": "JoinChannelVideo" 25 | } 26 | }, 27 | { 28 | "path": "pages/Basic/StringUid", 29 | "style": { 30 | "navigationBarTitleText": "StringUid" 31 | } 32 | } 33 | ], 34 | "globalStyle": { 35 | "navigationBarTextStyle": "black", 36 | "navigationBarTitleText": "uni-app", 37 | "navigationBarBackgroundColor": "#F8F8F8", 38 | "backgroundColor": "#F8F8F8" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /example/pages/Advanced/MultiChannel.nvue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 193 | 194 | 226 | -------------------------------------------------------------------------------- /example/pages/Basic/JoinChannelAudio.nvue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 104 | 105 | 125 | -------------------------------------------------------------------------------- /example/pages/Basic/JoinChannelVideo.nvue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 122 | 123 | 159 | -------------------------------------------------------------------------------- /example/pages/Basic/StringUid.nvue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 92 | 93 | 113 | -------------------------------------------------------------------------------- /example/pages/index/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 36 | -------------------------------------------------------------------------------- /example/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/Agora-Uniapp-SDK/b6c4fedb0415a35e4891a2689123ad81db7635e6/example/static/logo.png -------------------------------------------------------------------------------- /example/uni.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * 这里是uni-app内置的常用样式变量 3 | * 4 | * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 5 | * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App 6 | * 7 | */ 8 | 9 | /** 10 | * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 11 | * 12 | * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 13 | */ 14 | 15 | /* 颜色变量 */ 16 | 17 | /* 行为相关颜色 */ 18 | $uni-color-primary: #007aff; 19 | $uni-color-success: #4cd964; 20 | $uni-color-warning: #f0ad4e; 21 | $uni-color-error: #dd524d; 22 | 23 | /* 文字基本颜色 */ 24 | $uni-text-color:#333;//基本色 25 | $uni-text-color-inverse:#fff;//反色 26 | $uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息 27 | $uni-text-color-placeholder: #808080; 28 | $uni-text-color-disable:#c0c0c0; 29 | 30 | /* 背景颜色 */ 31 | $uni-bg-color:#ffffff; 32 | $uni-bg-color-grey:#f8f8f8; 33 | $uni-bg-color-hover:#f1f1f1;//点击状态颜色 34 | $uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色 35 | 36 | /* 边框颜色 */ 37 | $uni-border-color:#c8c7cc; 38 | 39 | /* 尺寸变量 */ 40 | 41 | /* 文字尺寸 */ 42 | $uni-font-size-sm:24rpx; 43 | $uni-font-size-base:28rpx; 44 | $uni-font-size-lg:32rpx; 45 | 46 | /* 图片尺寸 */ 47 | $uni-img-size-sm:40rpx; 48 | $uni-img-size-base:52rpx; 49 | $uni-img-size-lg:80rpx; 50 | 51 | /* Border Radius */ 52 | $uni-border-radius-sm: 4rpx; 53 | $uni-border-radius-base: 6rpx; 54 | $uni-border-radius-lg: 12rpx; 55 | $uni-border-radius-circle: 50%; 56 | 57 | /* 水平间距 */ 58 | $uni-spacing-row-sm: 10px; 59 | $uni-spacing-row-base: 20rpx; 60 | $uni-spacing-row-lg: 30rpx; 61 | 62 | /* 垂直间距 */ 63 | $uni-spacing-col-sm: 8rpx; 64 | $uni-spacing-col-base: 16rpx; 65 | $uni-spacing-col-lg: 24rpx; 66 | 67 | /* 透明度 */ 68 | $uni-opacity-disabled: 0.3; // 组件禁用态的透明度 69 | 70 | /* 文章场景相关 */ 71 | $uni-color-title: #2C405A; // 文章标题颜色 72 | $uni-font-size-title:40rpx; 73 | $uni-color-subtitle: #555555; // 二级标题颜色 74 | $uni-font-size-subtitle:36rpx; 75 | $uni-color-paragraph: #3F536E; // 文章段落颜色 76 | $uni-font-size-paragraph:30rpx; -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | zipName="AgoraRtcSDK" 5 | temp="/var/tmp" 6 | libs="ios/libs" 7 | 8 | mkdir -p $temp 9 | 10 | version=$(grep \"version\" package.json | grep -Eo '\d+.\d+.\d+(-rc.\d+)?' | sed 's/-rc//g') 11 | url="$1" 12 | echo $zipName "$version" 13 | 14 | if [ ! -f $temp/$zipName"$version".zip ]; then 15 | echo "the zip file not exists, start downloading..." 16 | curl -o $temp/$zipName"$version".zip "${url:=https://download.agora.io/sdk/release/AgoraRtcEngine_iOS-${version}.zip}" 17 | fi 18 | 19 | echo "start unzip SDK..." 20 | unzip -o -q $temp/$zipName"$version".zip -d $temp/$zipName"$version" 21 | sh to-framework.sh $temp/$zipName"$version"/ 22 | 23 | echo "start transfer dynamic framework to $libs..." 24 | rm -rf $libs 25 | mkdir -p $libs 26 | 27 | cp -rp $temp/$zipName"$version"/ALL_ARCHITECTURE/ $libs/ 28 | 29 | echo "install finished" 30 | -------------------------------------------------------------------------------- /ios/AgoraRtcUniPlugin.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint AgoraRtcUniPlugin.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see https://guides.cocoapods.org/syntax/podspec.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |spec| 10 | 11 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 12 | # 13 | # These will help people to find your library, and whilst it 14 | # can feel like a chore to fill in it's definitely to your advantage. The 15 | # summary should be tweet-length, and the description more in depth. 16 | # 17 | 18 | spec.name = "AgoraRtcUniPlugin" 19 | spec.version = "0.0.1" 20 | spec.summary = "AgoraRtcUniPlugin." 21 | 22 | # This description is used to generate tags and improve search results. 23 | # * Think: What does it do? Why did you write it? What is the focus? 24 | # * Try to keep it short, snappy and to the point. 25 | # * Write the description between the DESC delimiters below. 26 | # * Finally, don't worry about the indent, CocoaPods strips it! 27 | spec.description = "AgoraRtcUniPlugin." 28 | 29 | spec.homepage = "https://github.com/AgoraIO-Community/Agora-Uniapp-SDK" 30 | # spec.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" 31 | 32 | 33 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 34 | # 35 | # Licensing your code is important. See https://choosealicense.com for more info. 36 | # CocoaPods will detect a license file if there is a named LICENSE* 37 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 38 | # 39 | 40 | spec.license = "MIT (Agora)" 41 | # spec.license = { :type => "MIT", :file => "FILE_LICENSE" } 42 | 43 | 44 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 45 | # 46 | # Specify the authors of the library, with email addresses. Email addresses 47 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 48 | # accepts just a name if you'd rather not provide an email address. 49 | # 50 | # Specify a social_media_url where others can refer to, for example a twitter 51 | # profile URL. 52 | # 53 | 54 | spec.author = { "HUI" => "luxuhui@agora.io" } 55 | # Or just: spec.author = "HUI" 56 | # spec.authors = { "HUI" => "luxuhui@agora.io" } 57 | # spec.social_media_url = "https://twitter.com/HUI" 58 | 59 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 60 | # 61 | # If this Pod runs only on iOS or OS X, then specify the platform and 62 | # the deployment target. You can optionally include the target after the platform. 63 | # 64 | 65 | # spec.platform = :ios 66 | spec.platform = :ios, "8.0" 67 | 68 | # When using multiple platforms 69 | # spec.ios.deployment_target = "5.0" 70 | # spec.osx.deployment_target = "10.7" 71 | # spec.watchos.deployment_target = "2.0" 72 | # spec.tvos.deployment_target = "9.0" 73 | 74 | 75 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 76 | # 77 | # Specify the location from where the source should be retrieved. 78 | # Supports git, hg, bzr, svn and HTTP. 79 | # 80 | 81 | spec.source = { :git => "https://github.com/AgoraIO-Community/Agora-Uniapp-SDK.git", :tag => "#{spec.version}" } 82 | 83 | 84 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 85 | # 86 | # CocoaPods is smart about how it includes source code. For source files 87 | # giving a folder will include any swift, h, m, mm, c & cpp files. 88 | # For header files it will include any header in the folder. 89 | # Not including the public_header_files will make all headers public. 90 | # 91 | 92 | spec.source_files = "AgoraRtcUniPlugin/**/*.{h,m,swift}" 93 | spec.exclude_files = "AgoraRtcUniPlugin/Uni/AgoraRtcUniPlugin-bridging-header.h" 94 | 95 | # spec.public_header_files = "Classes/**/*.h" 96 | spec.project_header_files = "AgoraRtcUniPlugin/**/*.{h}" 97 | 98 | 99 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 100 | # 101 | # A list of resources included with the Pod. These are copied into the 102 | # target bundle with a build phase script. Anything else will be cleaned. 103 | # You can preserve files from being cleaned, please don't preserve 104 | # non-essential files like tests, examples and documentation. 105 | # 106 | 107 | # spec.resource = "icon.png" 108 | # spec.resources = "Resources/*.png" 109 | 110 | # spec.preserve_paths = "FilesToSave", "MoreFilesToSave" 111 | 112 | 113 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 114 | # 115 | # Link your library with frameworks, or libraries. Libraries do not include 116 | # the lib prefix of their name. 117 | # 118 | 119 | # spec.framework = "SomeFramework" 120 | # spec.frameworks = "SomeFramework", "AnotherFramework" 121 | 122 | # spec.library = "iconv" 123 | # spec.libraries = "iconv", "xml2" 124 | 125 | 126 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 127 | # 128 | # If your library depends on compiler flags you can set them in the xcconfig hash 129 | # where they will only apply to your library. If you depend on other Podspecs 130 | # you can include multiple dependencies to ensure it works. 131 | 132 | # spec.requires_arc = true 133 | spec.static_framework = true 134 | spec.module_map = false 135 | 136 | spec.xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(SRCROOT)/../../SDK/inc\"/**", "SWIFT_OBJC_BRIDGING_HEADER" => "${PODS_TARGET_SRCROOT}/AgoraRtcUniPlugin/Uni/AgoraRtcUniPlugin-bridging-header.h", 'PRODUCT_BUNDLE_IDENTIFIER' => 'io.agora.AgoraRtcUniPlugin', "OTHER_CFLAGS" => "-fno-objc-msgsend-selector-stubs" } 137 | spec.dependency "AgoraRtcEngine_Special_iOS", "3.7.3.4" 138 | 139 | end 140 | -------------------------------------------------------------------------------- /ios/AgoraRtcUniPlugin.xcodeproj/xcshareddata/xcschemes/AgoraRtcUniPlugin.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 57 | 58 | 59 | 60 | 62 | 63 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /ios/AgoraRtcUniPlugin/Base/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | -------------------------------------------------------------------------------- /ios/AgoraRtcUniPlugin/Base/AgoraRtcEngineKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // AgoraRtcEngineKit.h 3 | // RCTAgora 4 | // 5 | // Created by LXH on 2020/4/22. 6 | // Copyright © 2020 Syan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSUInteger, AgoraRtcQualityReportFormat) { 12 | AgoraRtc_QualityReportFormat_Json = 0, 13 | AgoraRtc_QualityReportFormat_Html = 1, 14 | }; 15 | 16 | typedef NS_ENUM(NSUInteger, AgoraRtcAppType) { 17 | AgoraRtc_APP_TYPE_NATIVE = 0, 18 | AgoraRtc_APP_TYPE_COCOS = 1, 19 | AgoraRtc_APP_TYPE_UNITY = 2, 20 | AgoraRtc_APP_TYPE_ELECTRON = 3, 21 | AgoraRtc_APP_TYPE_FLUTTER = 4, 22 | AgoraRtc_APP_TYPE_UNREAL = 5, 23 | AgoraRtc_APP_TYPE_XAMARIN = 6, 24 | AgoraRtc_APP_TYPE_APICLOUD = 7, 25 | AgoraRtc_APP_TYPE_REACTNATIVE = 8 26 | }; 27 | 28 | @protocol AgoraRtcEngineExtensionDelegate 29 | @optional 30 | - (void)rtcEngine:(AgoraRtcEngineKit *_Nonnull)engine 31 | audioTransportQualityOfUid:(NSUInteger)uid 32 | delay:(NSUInteger)delay 33 | lost:(NSUInteger)lost; 34 | - (void)rtcEngine:(AgoraRtcEngineKit *_Nonnull)engine 35 | videoTransportQualityOfUid:(NSUInteger)uid 36 | delay:(NSUInteger)delay 37 | lost:(NSUInteger)lost; 38 | @end 39 | 40 | @interface AgoraRtcEngineKit (AgoraExtension) 41 | 42 | + (instancetype _Nonnull)sharedEngineWithAppId:(NSString *_Nonnull)appId 43 | extensionDelegate: 44 | (id _Nullable) 45 | delegate; 46 | 47 | /** Sets the profile to control the RTC engine. 48 | * 49 | * @param profile SDK profile in JSON format. 50 | * @param merge Whether to merge the profile data with the original value. 51 | */ 52 | - (int)setProfile:(NSString *_Nonnull)profile merge:(BOOL)merge; 53 | 54 | /** Set wrapper frame type by language wrapper. 55 | * 56 | * @param appType wrapper frame type. 57 | */ 58 | - (int)setAppType:(AgoraRtcAppType)appType; 59 | 60 | /** END OF COMMON METHODS */ 61 | 62 | /** BEGIN OF AUDIO METHODS */ 63 | 64 | /** 65 | * Enable recap 66 | * 67 | * @param interval ≤ 0: Disabled, > 0: Interval in ms. 68 | */ 69 | - (int)enableRecap:(NSInteger)interval; 70 | 71 | /** 72 | * Start playing recap conversation 73 | * 74 | */ 75 | - (int)playRecap; 76 | 77 | - (int)enableAudioQualityIndication:(BOOL)enabled; 78 | - (int)enableTransportQualityIndication:(BOOL)enabled; 79 | 80 | - (int)setVideoProfileEx:(NSInteger)width 81 | andHeight:(NSInteger)height 82 | andFrameRate:(NSInteger)frameRate 83 | andBitrate:(NSInteger)andBitrate; 84 | 85 | - (int)sendReportData:(NSData *_Nonnull)data type:(NSInteger)type; 86 | /** END OF AUDIO METHODS */ 87 | 88 | /** Queries internal states 89 | * @param parameters 90 | * json string, array type 91 | * @return a json string 92 | */ 93 | - (NSString *_Nullable)getParameters:(NSString *_Nonnull)parameters; 94 | 95 | /** 96 | * Generates a URL linking to the call quality reports. @param channel The 97 | channel name specified in the joinChannel method. 98 | * @param listenerUid The uid of the listener. 99 | * @param speakerUid The uid of the speaker. 100 | * @param reportFormat The format of the report. 101 | AgoraRtc_QualityReportFormat_Json (0): JSON.: Returns 102 | the quality report data in Json. AgoraRtc_QualityReportFormat_Html (1): HTML.: 103 | Returns a report in HTML format, displayed on a web browser or WebVIEW 104 | components. 105 | * 106 | * @return 0 when executed successfully. return minus value when failed. return 107 | AgoraRtc_Error_Invalid_Argument (-2):Invalid argument. return 108 | AgoraRtc_Error_Buffer_Too_Small (-6):The buffer length is too small. 109 | */ 110 | - (NSString *_Nullable)makeQualityReportUrl:(NSString *_Nonnull)channel 111 | listenerUid:(NSUInteger)listenerUid 112 | speakerrUid:(NSUInteger)speakerUid 113 | reportFormat: 114 | (AgoraRtcQualityReportFormat)reportFormat; 115 | 116 | /********************************************************* 117 | * Large group conference call (experiment) - END 118 | *********************************************************/ 119 | @end 120 | -------------------------------------------------------------------------------- /ios/AgoraRtcUniPlugin/Base/Callback.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Callback.swift 3 | // RCTAgora 4 | // 5 | // Created by LXH on 2020/4/9. 6 | // Copyright © 2020 Syan. All rights reserved. 7 | // 8 | 9 | import AgoraRtcKit 10 | import Foundation 11 | 12 | @objc 13 | protocol Callback: class { 14 | func success(_ data: Any?) 15 | 16 | func failure(_ code: String, _ message: String) 17 | } 18 | 19 | extension Callback { 20 | func code(_ code: Int32?, _ runnable: ((Int32?) -> Any?)? = nil) { 21 | if code == nil || code! < 0 { 22 | let newCode = abs(Int(code ?? Int32(AgoraErrorCode.notInitialized.rawValue))) 23 | failure(String(newCode), AgoraRtcEngineKit.getErrorDescription(newCode) ?? "") 24 | return 25 | } 26 | 27 | let res = runnable?(code) 28 | success(res) 29 | } 30 | 31 | func resolve(_ source: T?, _ runnable: (T) -> Any?) { 32 | guard let source = source else { 33 | let code = AgoraErrorCode.notInitialized.rawValue 34 | failure(String(code), AgoraRtcEngineKit.getErrorDescription(code) ?? "") 35 | return 36 | } 37 | 38 | let res = runnable(source) 39 | success(res) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ios/AgoraRtcUniPlugin/Base/Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extensions.swift 3 | // RCTAgora 4 | // 5 | // Created by LXH on 2020/4/10. 6 | // Copyright © 2020 Syan. All rights reserved. 7 | // 8 | 9 | import AgoraRtcKit 10 | import Foundation 11 | 12 | extension AgoraUserInfo { 13 | func toMap() -> [String: Any?] { 14 | return [ 15 | "uid": uid, 16 | "userAccount": userAccount, 17 | ] 18 | } 19 | } 20 | 21 | extension AgoraRtcLocalAudioStats { 22 | func toMap() -> [String: Any?] { 23 | return [ 24 | "numChannels": numChannels, 25 | "sentSampleRate": sentSampleRate, 26 | "sentBitrate": sentBitrate, 27 | "txPacketLossRate": txPacketLossRate, 28 | ] 29 | } 30 | } 31 | 32 | extension AgoraChannelStats { 33 | func toMap() -> [String: Any?] { 34 | return [ 35 | "duration": duration, 36 | "txBytes": txBytes, 37 | "rxBytes": rxBytes, 38 | "txAudioBytes": txAudioBytes, 39 | "txVideoBytes": txVideoBytes, 40 | "rxAudioBytes": rxAudioBytes, 41 | "rxVideoBytes": rxVideoBytes, 42 | "txKBitRate": txKBitrate, 43 | "rxKBitRate": rxKBitrate, 44 | "txAudioKBitRate": txAudioKBitrate, 45 | "rxAudioKBitRate": rxAudioKBitrate, 46 | "txVideoKBitRate": txVideoKBitrate, 47 | "rxVideoKBitRate": rxVideoKBitrate, 48 | "userCount": userCount, 49 | "lastmileDelay": lastmileDelay, 50 | "txPacketLossRate": txPacketLossRate, 51 | "rxPacketLossRate": rxPacketLossRate, 52 | "cpuTotalUsage": cpuTotalUsage, 53 | "cpuAppUsage": cpuAppUsage, 54 | "gatewayRtt": gatewayRtt, 55 | "memoryAppUsageRatio": memoryAppUsageRatio, 56 | "memoryTotalUsageRatio": memoryTotalUsageRatio, 57 | "memoryAppUsageInKbytes": memoryAppUsageInKbytes, 58 | ] 59 | } 60 | } 61 | 62 | extension CGRect { 63 | func toMap() -> [String: Any?] { 64 | return [ 65 | "left": origin.x, 66 | "top": origin.y, 67 | "right": origin.x + size.width, 68 | "bottom": origin.y + size.height, 69 | ] 70 | } 71 | } 72 | 73 | extension AgoraRtcRemoteAudioStats { 74 | func toMap() -> [String: Any?] { 75 | return [ 76 | "uid": uid, 77 | "quality": quality, 78 | "networkTransportDelay": networkTransportDelay, 79 | "jitterBufferDelay": jitterBufferDelay, 80 | "audioLossRate": audioLossRate, 81 | "numChannels": numChannels, 82 | "receivedSampleRate": receivedSampleRate, 83 | "receivedBitrate": receivedBitrate, 84 | "totalFrozenTime": totalFrozenTime, 85 | "frozenRate": frozenRate, 86 | "totalActiveTime": totalActiveTime, 87 | "publishDuration": publishDuration, 88 | "qoeQuality": qoeQuality, 89 | "qualityChangedReason": qualityChangedReason, 90 | "mosValue": mosValue, 91 | ] 92 | } 93 | } 94 | 95 | extension AgoraRtcLocalVideoStats { 96 | func toMap() -> [String: Any?] { 97 | return [ 98 | "sentBitrate": sentBitrate, 99 | "sentFrameRate": sentFrameRate, 100 | "encoderOutputFrameRate": encoderOutputFrameRate, 101 | "rendererOutputFrameRate": rendererOutputFrameRate, 102 | "targetBitrate": sentTargetBitrate, 103 | "targetFrameRate": sentTargetFrameRate, 104 | "qualityAdaptIndication": qualityAdaptIndication.rawValue, 105 | "encodedBitrate": encodedBitrate, 106 | "encodedFrameWidth": encodedFrameWidth, 107 | "encodedFrameHeight": encodedFrameHeight, 108 | "encodedFrameCount": encodedFrameCount, 109 | "codecType": codecType.rawValue, 110 | "txPacketLossRate": txPacketLossRate, 111 | "captureFrameRate": captureFrameRate, 112 | "captureBrightnessLevel": captureBrightnessLevel.rawValue, 113 | ] 114 | } 115 | } 116 | 117 | extension AgoraRtcRemoteVideoStats { 118 | func toMap() -> [String: Any?] { 119 | return [ 120 | "uid": uid, 121 | "delay": delay, 122 | "width": width, 123 | "height": height, 124 | "receivedBitrate": receivedBitrate, 125 | "decoderOutputFrameRate": decoderOutputFrameRate, 126 | "rendererOutputFrameRate": rendererOutputFrameRate, 127 | "packetLossRate": packetLossRate, 128 | "rxStreamType": rxStreamType.rawValue, 129 | "totalFrozenTime": totalFrozenTime, 130 | "frozenRate": frozenRate, 131 | "totalActiveTime": totalActiveTime, 132 | "publishDuration": publishDuration, 133 | ] 134 | } 135 | } 136 | 137 | extension AgoraRtcAudioVolumeInfo { 138 | func toMap() -> [String: Any?] { 139 | return [ 140 | "uid": uid, 141 | "volume": volume, 142 | "vad": vad, 143 | "channelId": channelId, 144 | ] 145 | } 146 | } 147 | 148 | extension Array where Element: AgoraRtcAudioVolumeInfo { 149 | func toMapList() -> [[String: Any?]] { 150 | var list = [[String: Any?]]() 151 | forEach { 152 | list.append($0.toMap()) 153 | } 154 | return list 155 | } 156 | } 157 | 158 | extension AgoraLastmileProbeOneWayResult { 159 | func toMap() -> [String: Any?] { 160 | return [ 161 | "packetLossRate": packetLossRate, 162 | "jitter": jitter, 163 | "availableBandwidth": availableBandwidth, 164 | ] 165 | } 166 | } 167 | 168 | extension AgoraLastmileProbeResult { 169 | func toMap() -> [String: Any?] { 170 | return [ 171 | "state": state.rawValue, 172 | "rtt": rtt, 173 | "uplinkReport": uplinkReport.toMap(), 174 | "downlinkReport": downlinkReport.toMap(), 175 | ] 176 | } 177 | } 178 | 179 | extension AgoraFacePositionInfo { 180 | func toMap() -> [String: Any?] { 181 | return [ 182 | "x": x, 183 | "y": y, 184 | "width": width, 185 | "height": height, 186 | "distance": distance, 187 | ] 188 | } 189 | } 190 | 191 | extension AgoraRtcAudioFileInfo { 192 | func toMap() -> [String: Any?] { 193 | return [ 194 | "filePath": filePath, 195 | "durationMs": durationMs, 196 | ] 197 | } 198 | } 199 | 200 | extension AgoraMediaRecorderInfo { 201 | func toMap() -> [String: Any?] { 202 | return [ 203 | "fileName": recorderFileName, 204 | "durationMs": durationMs, 205 | "fileSize": fileSize 206 | ] 207 | } 208 | } 209 | 210 | extension AgoraWlAccStats { 211 | func toMap() -> [String: Any?] { 212 | return [ 213 | "e2eDelayPercent": e2eDelayPercent, 214 | "frozenRatioPercent": frozenRatioPercent, 215 | "lossRatePercent": lossRatePercent 216 | ] 217 | } 218 | } 219 | 220 | extension Array where Element: AgoraFacePositionInfo { 221 | func toMapList() -> [[String: Any?]] { 222 | var list = [[String: Any?]]() 223 | forEach { 224 | list.append($0.toMap()) 225 | } 226 | return list 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /ios/AgoraRtcUniPlugin/Base/MediaObserver.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MediaObserver.swift 3 | // RCTAgora 4 | // 5 | // Created by LXH on 2020/4/10. 6 | // Copyright © 2020 Syan. All rights reserved. 7 | // 8 | 9 | import AgoraRtcKit 10 | import Foundation 11 | 12 | class MediaObserver: NSObject { 13 | private var emitter: (_ data: [String: Any?]?) -> Void 14 | private var maxMetadataSize = 1024 15 | private var metadataList = [String]() 16 | 17 | init(_ emitter: @escaping (_ data: [String: Any?]?) -> Void) { 18 | self.emitter = emitter 19 | } 20 | 21 | func addMetadata(_ metadata: String) { 22 | metadataList.append(metadata) 23 | } 24 | 25 | func setMaxMetadataSize(_ size: Int) { 26 | maxMetadataSize = size 27 | } 28 | } 29 | 30 | extension MediaObserver: AgoraMediaMetadataDataSource { 31 | func metadataMaxSize() -> Int { 32 | return maxMetadataSize 33 | } 34 | 35 | func readyToSendMetadata(atTimestamp _: TimeInterval) -> Data? { 36 | if metadataList.count > 0 { 37 | return metadataList.remove(at: 0).data(using: .utf8) 38 | } 39 | return nil 40 | } 41 | } 42 | 43 | extension MediaObserver: AgoraMediaMetadataDelegate { 44 | func receiveMetadata(_ data: Data, fromUser uid: Int, atTimestamp timestamp: TimeInterval) { 45 | emitter([ 46 | "data": [String(data: data, encoding: .utf8) ?? "", uid, timestamp], 47 | ]) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ios/AgoraRtcUniPlugin/Base/RtcChannelEvent.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RtcChannelEvent.swift 3 | // RCTAgora 4 | // 5 | // Created by LXH on 2020/4/10. 6 | // Copyright © 2020 Syan. All rights reserved. 7 | // 8 | 9 | import AgoraRtcKit 10 | import Foundation 11 | 12 | class RtcChannelEvents { 13 | static let Warning = "Warning" 14 | static let Error = "Error" 15 | static let JoinChannelSuccess = "JoinChannelSuccess" 16 | static let RejoinChannelSuccess = "RejoinChannelSuccess" 17 | static let LeaveChannel = "LeaveChannel" 18 | static let ClientRoleChanged = "ClientRoleChanged" 19 | static let UserJoined = "UserJoined" 20 | static let UserOffline = "UserOffline" 21 | static let ConnectionStateChanged = "ConnectionStateChanged" 22 | static let ConnectionLost = "ConnectionLost" 23 | static let TokenPrivilegeWillExpire = "TokenPrivilegeWillExpire" 24 | static let RequestToken = "RequestToken" 25 | static let ActiveSpeaker = "ActiveSpeaker" 26 | static let VideoSizeChanged = "VideoSizeChanged" 27 | static let RemoteVideoStateChanged = "RemoteVideoStateChanged" 28 | static let RemoteAudioStateChanged = "RemoteAudioStateChanged" 29 | static let LocalPublishFallbackToAudioOnly = "LocalPublishFallbackToAudioOnly" 30 | static let RemoteSubscribeFallbackToAudioOnly = "RemoteSubscribeFallbackToAudioOnly" 31 | static let RtcStats = "RtcStats" 32 | static let NetworkQuality = "NetworkQuality" 33 | static let RemoteVideoStats = "RemoteVideoStats" 34 | static let RemoteAudioStats = "RemoteAudioStats" 35 | static let RtmpStreamingStateChanged = "RtmpStreamingStateChanged" 36 | static let TranscodingUpdated = "TranscodingUpdated" 37 | static let StreamInjectedStatus = "StreamInjectedStatus" 38 | static let StreamMessage = "StreamMessage" 39 | static let StreamMessageError = "StreamMessageError" 40 | static let ChannelMediaRelayStateChanged = "ChannelMediaRelayStateChanged" 41 | static let ChannelMediaRelayEvent = "ChannelMediaRelayEvent" 42 | static let MetadataReceived = "MetadataReceived" 43 | static let AudioPublishStateChanged = "AudioPublishStateChanged" 44 | static let VideoPublishStateChanged = "VideoPublishStateChanged" 45 | static let AudioSubscribeStateChanged = "AudioSubscribeStateChanged" 46 | static let VideoSubscribeStateChanged = "VideoSubscribeStateChanged" 47 | static let RtmpStreamingEvent = "RtmpStreamingEvent" 48 | static let UserSuperResolutionEnabled = "UserSuperResolutionEnabled" 49 | static let ProxyConnected = "ProxyConnected" 50 | static let ClientRoleChangeFailed = "ClientRoleChangeFailed" 51 | static let FirstRemoteVideoFrame = "FirstRemoteVideoFrame" 52 | 53 | static func toMap() -> [String: String] { 54 | return [ 55 | "Warning": Warning, 56 | "Error": Error, 57 | "JoinChannelSuccess": JoinChannelSuccess, 58 | "RejoinChannelSuccess": RejoinChannelSuccess, 59 | "LeaveChannel": LeaveChannel, 60 | "ClientRoleChanged": ClientRoleChanged, 61 | "UserJoined": UserJoined, 62 | "UserOffline": UserOffline, 63 | "ConnectionStateChanged": ConnectionStateChanged, 64 | "ConnectionLost": ConnectionLost, 65 | "TokenPrivilegeWillExpire": TokenPrivilegeWillExpire, 66 | "RequestToken": RequestToken, 67 | "ActiveSpeaker": ActiveSpeaker, 68 | "VideoSizeChanged": VideoSizeChanged, 69 | "RemoteVideoStateChanged": RemoteVideoStateChanged, 70 | "RemoteAudioStateChanged": RemoteAudioStateChanged, 71 | "LocalPublishFallbackToAudioOnly": LocalPublishFallbackToAudioOnly, 72 | "RemoteSubscribeFallbackToAudioOnly": RemoteSubscribeFallbackToAudioOnly, 73 | "RtcStats": RtcStats, 74 | "NetworkQuality": NetworkQuality, 75 | "RemoteVideoStats": RemoteVideoStats, 76 | "RemoteAudioStats": RemoteAudioStats, 77 | "RtmpStreamingStateChanged": RtmpStreamingStateChanged, 78 | "TranscodingUpdated": TranscodingUpdated, 79 | "StreamInjectedStatus": StreamInjectedStatus, 80 | "StreamMessage": StreamMessage, 81 | "StreamMessageError": StreamMessageError, 82 | "ChannelMediaRelayStateChanged": ChannelMediaRelayStateChanged, 83 | "ChannelMediaRelayEvent": ChannelMediaRelayEvent, 84 | "MetadataReceived": MetadataReceived, 85 | "AudioPublishStateChanged": AudioPublishStateChanged, 86 | "VideoPublishStateChanged": VideoPublishStateChanged, 87 | "AudioSubscribeStateChanged": AudioSubscribeStateChanged, 88 | "VideoSubscribeStateChanged": VideoSubscribeStateChanged, 89 | "RtmpStreamingEvent": RtmpStreamingEvent, 90 | "UserSuperResolutionEnabled": UserSuperResolutionEnabled, 91 | "ProxyConnected": ProxyConnected, 92 | "ClientRoleChangeFailed": ClientRoleChangeFailed, 93 | "FirstRemoteVideoFrame": FirstRemoteVideoFrame, 94 | ] 95 | } 96 | } 97 | 98 | class RtcChannelEventHandler: NSObject { 99 | static let PREFIX = "io.agora.rtc." 100 | 101 | var emitter: (_ methodName: String, _ data: [String: Any?]?) -> Void 102 | 103 | init(_ emitter: @escaping (_ methodName: String, _ data: [String: Any?]?) -> Void) { 104 | self.emitter = emitter 105 | } 106 | 107 | private func callback(_ methodName: String, _ channel: AgoraRtcChannel, _ data: Any?...) { 108 | emitter(methodName, [ 109 | "channelId": channel.getId(), 110 | "data": data, 111 | ]) 112 | } 113 | } 114 | 115 | extension RtcChannelEventHandler: AgoraRtcChannelDelegate { 116 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, didOccurWarning warningCode: AgoraWarningCode) { 117 | callback(RtcChannelEvents.Warning, rtcChannel, warningCode.rawValue) 118 | } 119 | 120 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, didOccurError errorCode: AgoraErrorCode) { 121 | callback(RtcChannelEvents.Error, rtcChannel, errorCode.rawValue) 122 | } 123 | 124 | public func rtcChannelDidJoin(_ rtcChannel: AgoraRtcChannel, withUid uid: UInt, elapsed: Int) { 125 | callback(RtcChannelEvents.JoinChannelSuccess, rtcChannel, rtcChannel.getId(), uid, elapsed) 126 | } 127 | 128 | public func rtcChannelDidRejoin(_ rtcChannel: AgoraRtcChannel, withUid uid: UInt, elapsed: Int) { 129 | callback(RtcChannelEvents.RejoinChannelSuccess, rtcChannel, rtcChannel.getId(), uid, elapsed) 130 | } 131 | 132 | public func rtcChannelDidLeave(_ rtcChannel: AgoraRtcChannel, with stats: AgoraChannelStats) { 133 | callback(RtcChannelEvents.LeaveChannel, rtcChannel, stats.toMap()) 134 | } 135 | 136 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, didClientRoleChanged oldRole: AgoraClientRole, newRole: AgoraClientRole) { 137 | callback(RtcChannelEvents.ClientRoleChanged, rtcChannel, oldRole.rawValue, newRole.rawValue) 138 | } 139 | 140 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, didJoinedOfUid uid: UInt, elapsed: Int) { 141 | callback(RtcChannelEvents.UserJoined, rtcChannel, uid, elapsed) 142 | } 143 | 144 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, didOfflineOfUid uid: UInt, reason: AgoraUserOfflineReason) { 145 | callback(RtcChannelEvents.UserOffline, rtcChannel, uid, reason.rawValue) 146 | } 147 | 148 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, connectionChangedTo state: AgoraConnectionStateType, reason: AgoraConnectionChangedReason) { 149 | callback(RtcChannelEvents.ConnectionStateChanged, rtcChannel, state.rawValue, reason.rawValue) 150 | } 151 | 152 | public func rtcChannelDidLost(_ rtcChannel: AgoraRtcChannel) { 153 | callback(RtcChannelEvents.ConnectionLost, rtcChannel) 154 | } 155 | 156 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, tokenPrivilegeWillExpire token: String) { 157 | callback(RtcChannelEvents.TokenPrivilegeWillExpire, rtcChannel, token) 158 | } 159 | 160 | public func rtcChannelRequestToken(_ rtcChannel: AgoraRtcChannel) { 161 | callback(RtcChannelEvents.RequestToken, rtcChannel) 162 | } 163 | 164 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, activeSpeaker speakerUid: UInt) { 165 | callback(RtcChannelEvents.ActiveSpeaker, rtcChannel, speakerUid) 166 | } 167 | 168 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, videoSizeChangedOfUid uid: UInt, size: CGSize, rotation: Int) { 169 | callback(RtcChannelEvents.VideoSizeChanged, rtcChannel, uid, Int(size.width), Int(size.height), rotation) 170 | } 171 | 172 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, remoteVideoStateChangedOfUid uid: UInt, state: AgoraVideoRemoteState, reason: AgoraVideoRemoteStateReason, elapsed: Int) { 173 | callback(RtcChannelEvents.RemoteVideoStateChanged, rtcChannel, uid, state.rawValue, reason.rawValue, elapsed) 174 | } 175 | 176 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, remoteAudioStateChangedOfUid uid: UInt, state: AgoraAudioRemoteState, reason: AgoraAudioRemoteStateReason, elapsed: Int) { 177 | callback(RtcChannelEvents.RemoteAudioStateChanged, rtcChannel, uid, state.rawValue, reason.rawValue, elapsed) 178 | } 179 | 180 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, didLocalPublishFallbackToAudioOnly isFallbackOrRecover: Bool) { 181 | callback(RtcChannelEvents.LocalPublishFallbackToAudioOnly, rtcChannel, isFallbackOrRecover) 182 | } 183 | 184 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, didRemoteSubscribeFallbackToAudioOnly isFallbackOrRecover: Bool, byUid uid: UInt) { 185 | callback(RtcChannelEvents.RemoteSubscribeFallbackToAudioOnly, rtcChannel, uid, isFallbackOrRecover) 186 | } 187 | 188 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, reportRtcStats stats: AgoraChannelStats) { 189 | callback(RtcChannelEvents.RtcStats, rtcChannel, stats.toMap()) 190 | } 191 | 192 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, networkQuality uid: UInt, txQuality: AgoraNetworkQuality, rxQuality: AgoraNetworkQuality) { 193 | callback(RtcChannelEvents.NetworkQuality, rtcChannel, uid, txQuality.rawValue, rxQuality.rawValue) 194 | } 195 | 196 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, remoteVideoStats stats: AgoraRtcRemoteVideoStats) { 197 | callback(RtcChannelEvents.RemoteVideoStats, rtcChannel, stats.toMap()) 198 | } 199 | 200 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, remoteAudioStats stats: AgoraRtcRemoteAudioStats) { 201 | callback(RtcChannelEvents.RemoteAudioStats, rtcChannel, stats.toMap()) 202 | } 203 | 204 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, rtmpStreamingChangedToState url: String, state: AgoraRtmpStreamingState, errorCode: AgoraRtmpStreamingErrorCode) { 205 | callback(RtcChannelEvents.RtmpStreamingStateChanged, rtcChannel, url, state.rawValue, errorCode.rawValue) 206 | } 207 | 208 | public func rtcChannelTranscodingUpdated(_ rtcChannel: AgoraRtcChannel) { 209 | callback(RtcChannelEvents.TranscodingUpdated, rtcChannel) 210 | } 211 | 212 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, streamInjectedStatusOfUrl url: String, uid: UInt, status: AgoraInjectStreamStatus) { 213 | callback(RtcChannelEvents.StreamInjectedStatus, rtcChannel, url, uid, status.rawValue) 214 | } 215 | 216 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, receiveStreamMessageFromUid uid: UInt, streamId: Int, data: Data) { 217 | callback(RtcChannelEvents.StreamMessage, rtcChannel, uid, streamId, String(data: data, encoding: .utf8)) 218 | } 219 | 220 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, didOccurStreamMessageErrorFromUid uid: UInt, streamId: Int, error: Int, missed: Int, cached: Int) { 221 | callback(RtcChannelEvents.StreamMessageError, rtcChannel, uid, streamId, error, missed, cached) 222 | } 223 | 224 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, channelMediaRelayStateDidChange state: AgoraChannelMediaRelayState, error: AgoraChannelMediaRelayError) { 225 | callback(RtcChannelEvents.ChannelMediaRelayStateChanged, rtcChannel, state.rawValue, error.rawValue) 226 | } 227 | 228 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, didReceive event: AgoraChannelMediaRelayEvent) { 229 | callback(RtcChannelEvents.ChannelMediaRelayEvent, rtcChannel, event.rawValue) 230 | } 231 | 232 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, didAudioPublishStateChange oldState: AgoraStreamPublishState, newState: AgoraStreamPublishState, elapseSinceLastState: Int) { 233 | callback(RtcChannelEvents.AudioPublishStateChanged, rtcChannel, rtcChannel.getId(), oldState.rawValue, newState.rawValue, elapseSinceLastState) 234 | } 235 | 236 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, didVideoPublishStateChange oldState: AgoraStreamPublishState, newState: AgoraStreamPublishState, elapseSinceLastState: Int) { 237 | callback(RtcChannelEvents.VideoPublishStateChanged, rtcChannel, rtcChannel.getId(), oldState.rawValue, newState.rawValue, elapseSinceLastState) 238 | } 239 | 240 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, didAudioSubscribeStateChange uid: UInt, oldState: AgoraStreamSubscribeState, newState: AgoraStreamSubscribeState, elapseSinceLastState: Int) { 241 | callback(RtcChannelEvents.AudioSubscribeStateChanged, rtcChannel, rtcChannel.getId(), uid, oldState.rawValue, newState.rawValue, elapseSinceLastState) 242 | } 243 | 244 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, didVideoSubscribeStateChange uid: UInt, oldState: AgoraStreamSubscribeState, newState: AgoraStreamSubscribeState, elapseSinceLastState: Int) { 245 | callback(RtcChannelEvents.VideoSubscribeStateChanged, rtcChannel, rtcChannel.getId(), uid, oldState.rawValue, newState.rawValue, elapseSinceLastState) 246 | } 247 | 248 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, rtmpStreamingEventWithUrl url: String, eventCode: AgoraRtmpStreamingEvent) { 249 | callback(RtcChannelEvents.RtmpStreamingEvent, rtcChannel, url, eventCode.rawValue) 250 | } 251 | 252 | public func rtcChannel(_ rtcChannel: AgoraRtcChannel, superResolutionEnabledOfUid uid: UInt, enabled: Bool, reason: AgoraSuperResolutionStateReason) { 253 | callback(RtcChannelEvents.UserSuperResolutionEnabled, rtcChannel, uid, enabled, reason.rawValue) 254 | } 255 | 256 | func rtcChannel(_ rtcChannel: AgoraRtcChannel, didProxyConnected uid: UInt, proxyType: AgoraProxyType, localProxyIp: String, elapsed: Int) { 257 | callback(RtcChannelEvents.ProxyConnected, rtcChannel, rtcChannel.getId(), uid, proxyType.rawValue, localProxyIp, elapsed) 258 | } 259 | 260 | func rtcChannel(_ rtcChannel: AgoraRtcChannel, didClientRoleChangeFailed reason: AgoraClientRoleChangeFailedReason, currentRole: AgoraClientRole) { 261 | callback(RtcChannelEvents.ClientRoleChangeFailed, rtcChannel, reason.rawValue, currentRole.rawValue) 262 | } 263 | 264 | func rtcChannel(_ rtcChannel: AgoraRtcChannel, firstRemoteVideoFrameOfUid uid: UInt, size: CGSize, elapsed: Int) { 265 | callback(RtcChannelEvents.FirstRemoteVideoFrame, rtcChannel, uid, Int(size.width), Int(size.height), elapsed) 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /ios/AgoraRtcUniPlugin/Base/RtcEnginePlugin.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | NS_ASSUME_NONNULL_BEGIN 5 | 6 | /** 7 | * A `RtcEnginePlugin` allows developers to interact with the `RtcEngine` which created from flutter 8 | * side. 9 | */ 10 | @protocol RtcEnginePlugin 11 | 12 | /** 13 | * This callback will be called when the `RtcEngine` is created by 14 | [RtcEngine.createWithContext](https://docs.agora.io/cn/Video/API%20Reference/flutter/agora_rtc_engine/RtcEngine/createWithContext.html) 15 | * function from flutter. 16 | 17 | * NOTE that you should not call `AgoraRtcEngineKit.destroy`, because it will also destroy the `RtcEngine` 18 | * used by flutter side. 19 | * 20 | * @param rtcEngine The same `AgoraRtcEngineKit` used by flutter side 21 | */ 22 | - (void)onRtcEngineCreated:(AgoraRtcEngineKit *_Nullable)rtcEngine; 23 | 24 | /** 25 | * This callback will be called when the [RtcEngine.destroy](https://docs.agora.io/cn/Video/API%20Reference/flutter/v4.0.7/rtc_channel/RtcChannel/destroy.html) 26 | * function is called from flutter. 27 | */ 28 | - (void)onRtcEngineDestroyed; 29 | @end 30 | 31 | NS_ASSUME_NONNULL_END 32 | -------------------------------------------------------------------------------- /ios/AgoraRtcUniPlugin/Base/RtcEnginePluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /** 4 | * Class for register the `RtcEnginePlugin`. 5 | */ 6 | @objc 7 | public class RtcEnginePluginRegistrant: NSObject { 8 | /** 9 | * Register a `RtcEnginePlugin`. The `plugin` will be called when the `RtcEngine` is created from 10 | * flutter side. 11 | */ 12 | @objc public static func register(_ plugin: RtcEnginePlugin) { 13 | RtcEngineRegistry.shared.add(plugin) 14 | } 15 | 16 | /** 17 | * Unregister a previously registered `RtcEnginePlugin`. 18 | */ 19 | @objc public static func unregister(_ plugin: RtcEnginePlugin) { 20 | RtcEngineRegistry.shared.remove(plugin) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ios/AgoraRtcUniPlugin/Base/RtcEngineRegistry.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import AgoraRtcKit 3 | 4 | fileprivate struct PluginKey: Hashable, Equatable { 5 | let type: AnyClass 6 | 7 | public static func == (lhs: PluginKey, rhs: PluginKey) -> Bool { 8 | return lhs.type == rhs.type 9 | } 10 | 11 | public func hash(into hasher: inout Hasher) { 12 | hasher.combine(ObjectIdentifier(type)) 13 | } 14 | } 15 | 16 | /** 17 | * The `RtcEngineRegistry` is response to add, remove and notify the callback when `RtcEngine` is created 18 | * from flutter side. 19 | */ 20 | internal class RtcEngineRegistry : NSObject { 21 | 22 | private override init() {} 23 | 24 | static let shared = RtcEngineRegistry() 25 | 26 | private var plugins: [PluginKey : RtcEnginePlugin] = [PluginKey: RtcEnginePlugin]() 27 | 28 | /** 29 | * Add a `RtcEnginePlugin`. 30 | */ 31 | func add(_ plugin: RtcEnginePlugin) { 32 | let key = PluginKey(type: type(of: plugin)) 33 | guard plugins[key] == nil else { 34 | return 35 | } 36 | plugins[key] = plugin 37 | } 38 | 39 | /** 40 | * Remove the previously added `RtcEnginePlugin`. 41 | */ 42 | func remove(_ plugin: RtcEnginePlugin) { 43 | plugins.removeValue(forKey: PluginKey(type: type(of: plugin))) 44 | } 45 | } 46 | 47 | extension RtcEngineRegistry : RtcEnginePlugin { 48 | // MARK: - protocol from RtcEnginePlugin 49 | public func onRtcEngineCreated(_ rtcEngine: AgoraRtcEngineKit?) { 50 | for (_, plugin) in plugins { 51 | plugin.onRtcEngineCreated(rtcEngine) 52 | } 53 | } 54 | 55 | // MARK: - protocol from RtcEnginePlugin 56 | public func onRtcEngineDestroyed() { 57 | for (_, plugin) in plugins { 58 | plugin.onRtcEngineDestroyed() 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /ios/AgoraRtcUniPlugin/Base/RtcSurfaceView.swift: -------------------------------------------------------------------------------- 1 | import AgoraRtcKit 2 | import Foundation 3 | import UIKit 4 | 5 | class RtcSurfaceView: UIView { 6 | private var surface: UIView 7 | private var canvas: AgoraRtcVideoCanvas? 8 | private weak var channel: AgoraRtcChannel? 9 | 10 | override init(frame: CGRect) { 11 | surface = UIView(frame: CGRect(origin: CGPoint(x: 0, y: 0), size: frame.size)) 12 | super.init(frame: frame) 13 | addSubview(surface) 14 | addObserver(self, forKeyPath: observerForKeyPath(), options: .new, context: nil) 15 | } 16 | 17 | func observerForKeyPath() -> String { 18 | return "frame" 19 | } 20 | 21 | @available(*, unavailable) 22 | required init?(coder _: NSCoder) { 23 | fatalError("init(coder:) has not been implemented") 24 | } 25 | 26 | deinit { 27 | canvas = nil 28 | removeObserver(self, forKeyPath: observerForKeyPath(), context: nil) 29 | } 30 | 31 | func setData(_ engine: AgoraRtcEngineKit, _ channel: AgoraRtcChannel?, _ uid: UInt) { 32 | self.channel = channel 33 | if canvas == nil { 34 | canvas = AgoraRtcVideoCanvas() 35 | canvas!.view = surface 36 | } 37 | canvas!.channelId = channel?.getId() 38 | canvas!.uid = uid 39 | setupVideoCanvas(engine) 40 | } 41 | 42 | func resetVideoCanvas(_ engine: AgoraRtcEngineKit) { 43 | if let it = self.canvas { 44 | let canvas = AgoraRtcVideoCanvas() 45 | canvas.view = nil 46 | canvas.renderMode = it.renderMode 47 | canvas.channelId = it.channelId 48 | canvas.uid = it.uid 49 | canvas.mirrorMode = it.mirrorMode 50 | 51 | if canvas.uid == 0 { 52 | engine.setupLocalVideo(canvas) 53 | } else { 54 | engine.setupRemoteVideo(canvas) 55 | } 56 | } 57 | } 58 | 59 | private func setupVideoCanvas(_ engine: AgoraRtcEngineKit) { 60 | subviews.forEach { 61 | $0.removeFromSuperview() 62 | } 63 | surface = UIView(frame: CGRect(origin: CGPoint(x: 0, y: 0), size: bounds.size)) 64 | addSubview(surface) 65 | if let canvas = self.canvas { 66 | canvas.view = surface 67 | if canvas.uid == 0 { 68 | engine.setupLocalVideo(canvas) 69 | } else { 70 | engine.setupRemoteVideo(canvas) 71 | } 72 | } 73 | } 74 | 75 | func setRenderMode(_ engine: AgoraRtcEngineKit, _ renderMode: UInt) { 76 | canvas?.renderMode = AgoraVideoRenderMode(rawValue: renderMode)! 77 | setupRenderMode(engine) 78 | } 79 | 80 | func setMirrorMode(_ engine: AgoraRtcEngineKit, _ mirrorMode: UInt) { 81 | canvas?.mirrorMode = AgoraVideoMirrorMode(rawValue: mirrorMode)! 82 | setupRenderMode(engine) 83 | } 84 | 85 | private func setupRenderMode(_ engine: AgoraRtcEngineKit) { 86 | if let canvas = self.canvas { 87 | if canvas.uid == 0 { 88 | engine.setLocalRenderMode(canvas.renderMode, mirrorMode: canvas.mirrorMode) 89 | } else { 90 | if let channel = channel { 91 | channel.setRemoteRenderMode(canvas.uid, renderMode: canvas.renderMode, mirrorMode: canvas.mirrorMode) 92 | } else { 93 | engine.setRemoteRenderMode(canvas.uid, renderMode: canvas.renderMode, mirrorMode: canvas.mirrorMode) 94 | } 95 | } 96 | } 97 | } 98 | 99 | override func observeValue(forKeyPath keyPath: String?, of _: Any?, change: [NSKeyValueChangeKey: Any]?, context _: UnsafeMutableRawPointer?) { 100 | if keyPath == observerForKeyPath() { 101 | if let rect = change?[.newKey] as? CGRect { 102 | surface.frame = CGRect(origin: CGPoint(x: 0, y: 0), size: rect.size) 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /ios/AgoraRtcUniPlugin/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /ios/AgoraRtcUniPlugin/Uni/AgoraRtcChannelModule.h: -------------------------------------------------------------------------------- 1 | // 2 | // AgoraRtcChannelModule.h 3 | // AgoraRtcUniPlugin 4 | // 5 | // Created by LXH on 2020/12/14. 6 | // 7 | 8 | #import 9 | #import "WeexSDK.h" 10 | 11 | @interface AgoraRtcChannelModule : NSObject 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ios/AgoraRtcUniPlugin/Uni/AgoraRtcChannelModule.m: -------------------------------------------------------------------------------- 1 | // 2 | // AgoraRtcChannelModule.m 3 | // AgoraRtcUniPlugin 4 | // 5 | // Created by LXH on 2020/12/14. 6 | // 7 | 8 | #import "AgoraRtcChannelModule.h" 9 | 10 | #if __has_include() 11 | #import 12 | #else 13 | // Support project import fallback if the generated compatibility header 14 | // is not copied when this plugin is created as a library. 15 | // https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816 16 | #import "AgoraRtcUniPlugin-Swift.h" 17 | #endif 18 | 19 | @implementation AgoraRtcChannelModule 20 | 21 | @synthesize weexInstance = _weexInstance; 22 | 23 | - (instancetype)init 24 | { 25 | self = [super init]; 26 | if (self) { 27 | [self initManager]; 28 | } 29 | return self; 30 | } 31 | 32 | - (void)setWeexInstance:(WXSDKInstance *)weexInstance { 33 | _weexInstance = weexInstance; 34 | } 35 | 36 | WX_EXPORT_METHOD(@selector(callMethod::)) 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /ios/AgoraRtcUniPlugin/Uni/AgoraRtcChannelModule.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AgoraRtcChannelModule.swift 3 | // AgoraRtcUniPlugin 4 | // 5 | // Created by LXH on 2020/12/14. 6 | // 7 | 8 | import Foundation 9 | 10 | fileprivate struct AssociatedKeys { 11 | static var manager: UInt8 = 0 12 | } 13 | 14 | public extension AgoraRtcChannelModule { 15 | private var manager: RtcChannelManager? { 16 | get { 17 | return objc_getAssociatedObject(self, &AssociatedKeys.manager) as? RtcChannelManager 18 | } 19 | set { 20 | objc_setAssociatedObject(self, &AssociatedKeys.manager, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 21 | } 22 | } 23 | 24 | @objc 25 | func initManager() { 26 | manager = RtcChannelManager() { [weak self] methodName, data in 27 | self?.emit(methodName, data) 28 | } 29 | } 30 | 31 | private func emit(_ methodName: String, _ data: Dictionary?) { 32 | weexInstance?.fireGlobalEvent("\(RtcChannelEventHandler.PREFIX)\(methodName)", params: data) 33 | } 34 | 35 | private weak var engine: AgoraRtcEngineKit? { 36 | return (weexInstance.module(for: AgoraRtcEngineModule.classForCoder()) as? AgoraRtcEngineModule)?.engine 37 | } 38 | 39 | func channel(_ channelId: String) -> AgoraRtcChannel? { 40 | return manager?[channelId] 41 | } 42 | 43 | @objc 44 | func callMethod(_ params: NSDictionary, _ callback: @escaping WXModuleCallback) { 45 | if let methodName = params["method"] as? String { 46 | if let args = params["args"] as? NSDictionary { 47 | if methodName == "create" { 48 | args.setValue(engine, forKey: "engine") 49 | } 50 | manager?.perform(NSSelectorFromString(methodName + "::"), with: args, with: UniCallback(callback)) 51 | } else { 52 | manager?.perform(NSSelectorFromString(methodName + ":"), with: UniCallback(callback)) 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ios/AgoraRtcUniPlugin/Uni/AgoraRtcEngineModule.h: -------------------------------------------------------------------------------- 1 | // 2 | // AgoraRtcEngineModule.h 3 | // AgoraRtcUniPlugin 4 | // 5 | // Created by LXH on 2020/12/14. 6 | // 7 | 8 | #import 9 | #import "WeexSDK.h" 10 | 11 | @interface AgoraRtcEngineModule : NSObject 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ios/AgoraRtcUniPlugin/Uni/AgoraRtcEngineModule.m: -------------------------------------------------------------------------------- 1 | // 2 | // AgoraRtcEngineModule.m 3 | // AgoraRtcUniPlugin 4 | // 5 | // Created by LXH on 2020/12/14. 6 | // 7 | 8 | #import "AgoraRtcEngineModule.h" 9 | 10 | #if __has_include() 11 | #import 12 | #else 13 | // Support project import fallback if the generated compatibility header 14 | // is not copied when this plugin is created as a library. 15 | // https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816 16 | #import "AgoraRtcUniPlugin-Swift.h" 17 | #endif 18 | 19 | @implementation AgoraRtcEngineModule 20 | 21 | @synthesize weexInstance = _weexInstance; 22 | 23 | - (instancetype)init 24 | { 25 | self = [super init]; 26 | if (self) { 27 | [self initManager]; 28 | } 29 | return self; 30 | } 31 | 32 | - (void)setWeexInstance:(WXSDKInstance *)weexInstance { 33 | _weexInstance = weexInstance; 34 | } 35 | 36 | WX_EXPORT_METHOD(@selector(callMethod::)) 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /ios/AgoraRtcUniPlugin/Uni/AgoraRtcEngineModule.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AgoraRtcEngineModule.swift 3 | // AgoraRtcUniPlugin 4 | // 5 | // Created by LXH on 2020/12/14. 6 | // 7 | 8 | import Foundation 9 | 10 | fileprivate struct AssociatedKeys { 11 | static var manager: UInt8 = 0 12 | } 13 | 14 | public extension AgoraRtcEngineModule { 15 | private var manager: RtcEngineManager? { 16 | get { 17 | return objc_getAssociatedObject(self, &AssociatedKeys.manager) as? RtcEngineManager 18 | } 19 | set { 20 | objc_setAssociatedObject(self, &AssociatedKeys.manager, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 21 | } 22 | } 23 | 24 | @objc 25 | func initManager() { 26 | manager = RtcEngineManager() { [weak self] methodName, data in 27 | self?.emit(methodName, data) 28 | } 29 | } 30 | 31 | private func emit(_ methodName: String, _ data: Dictionary?) { 32 | weexInstance?.fireGlobalEvent("\(RtcEngineEventHandler.PREFIX)\(methodName)", params: data) 33 | } 34 | 35 | weak var engine: AgoraRtcEngineKit? { 36 | return manager?.engine 37 | } 38 | 39 | @objc 40 | func callMethod(_ params: NSDictionary, _ callback: @escaping WXModuleCallback) { 41 | if let methodName = params["method"] as? String { 42 | if let args = params["args"] as? NSDictionary { 43 | manager?.perform(NSSelectorFromString(methodName + "::"), with: args, with: UniCallback(callback)) 44 | } else { 45 | manager?.perform(NSSelectorFromString(methodName + ":"), with: UniCallback(callback)) 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ios/AgoraRtcUniPlugin/Uni/AgoraRtcSurfaceView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AgoraRtcSurfaceView.swift 3 | // AgoraRtcUniPlugin 4 | // 5 | // Created by LXH on 2020/12/14. 6 | // 7 | 8 | import Foundation 9 | 10 | @objc(AgoraRtcSurfaceView) 11 | public class AgoraRtcSurfaceView : WXComponent { 12 | @objc 13 | override init() { 14 | super.init() 15 | } 16 | 17 | @objc 18 | override init(ref: String, type: String, styles: [AnyHashable : Any]?, attributes: [AnyHashable : Any]? = nil, events: [Any]?, weexInstance: WXSDKInstance) { 19 | super.init(ref: ref, type: type, styles: styles, attributes: attributes, events: events, weexInstance: weexInstance) 20 | } 21 | 22 | @objc 23 | public override func loadView() -> UIView { 24 | let view = RtcView() 25 | view.setEngine(engine) 26 | view.setChannel(channel(_:)) 27 | return view 28 | } 29 | 30 | @objc 31 | public override func viewDidLoad() { 32 | if let view = self.view as? RtcView { 33 | view.setData(attributes["data"] as! NSDictionary) 34 | view.setRenderMode(NSNumber(value: WXConvert.nsuInteger(attributes["renderMode"]))) 35 | view.setMirrorMode(NSNumber(value: WXConvert.nsuInteger(attributes["mirrorMode"]))) 36 | } 37 | } 38 | 39 | @objc 40 | public override func updateAttributes(_ attributes: [AnyHashable : Any] = [:]) { 41 | if let view = self.view as? RtcView { 42 | if let data = attributes["data"] as? NSDictionary { 43 | view.setData(data) 44 | } 45 | if let renderMode = attributes["renderMode"] { 46 | view.setRenderMode(NSNumber(value: WXConvert.nsuInteger(renderMode))) 47 | } 48 | if let mirrorMode = attributes["mirrorMode"] { 49 | view.setMirrorMode(NSNumber(value: WXConvert.nsuInteger(mirrorMode))) 50 | } 51 | } 52 | } 53 | 54 | private func engine() -> AgoraRtcEngineKit? { 55 | return (weexInstance?.module(for: AgoraRtcEngineModule.classForCoder()) as? AgoraRtcEngineModule)?.engine 56 | } 57 | 58 | private func channel(_ channelId: String) -> AgoraRtcChannel? { 59 | return (weexInstance?.module(for: AgoraRtcChannelModule.classForCoder()) as? AgoraRtcChannelModule)?.channel(channelId) 60 | } 61 | } 62 | 63 | @objc(RtcView) 64 | class RtcView: RtcSurfaceView { 65 | private var getEngine: (() -> AgoraRtcEngineKit?)? 66 | private var getChannel: ((_ channelId: String) -> AgoraRtcChannel?)? 67 | 68 | func setEngine(_ getEngine: @escaping () -> AgoraRtcEngineKit?) { 69 | self.getEngine = getEngine 70 | } 71 | 72 | func setChannel(_ getChannel: @escaping (_ channelId: String) -> AgoraRtcChannel?) { 73 | self.getChannel = getChannel 74 | } 75 | 76 | @objc func setRenderMode(_ renderMode: NSNumber) { 77 | if let engine = getEngine?() { 78 | setRenderMode(engine, renderMode.uintValue) 79 | } 80 | } 81 | 82 | @objc func setData(_ data: NSDictionary) { 83 | var channel: AgoraRtcChannel? = nil 84 | if let channelId = data["channelId"] as? String { 85 | channel = getChannel?(channelId) 86 | } 87 | if let engine = getEngine?() { 88 | setData(engine, channel, WXConvert.nsuInteger(data["uid"])) 89 | } 90 | } 91 | 92 | @objc func setMirrorMode(_ mirrorMode: NSNumber) { 93 | if let engine = getEngine?() { 94 | setMirrorMode(engine, mirrorMode.uintValue) 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /ios/AgoraRtcUniPlugin/Uni/AgoraRtcUniPlugin-bridging-header.h: -------------------------------------------------------------------------------- 1 | // 2 | // AgoraRtcUniPlugin-bridging-header.h 3 | // AgoraRtcUniPlugin 4 | // 5 | // Created by LXH on 2020/12/14. 6 | // 7 | #import "WeexSDK.h" 8 | #import "AgoraRtcEngineKit.h" 9 | #import "AgoraRtcEngineModule.h" 10 | #import "AgoraRtcChannelModule.h" 11 | #import "RtcEnginePlugin.h" 12 | -------------------------------------------------------------------------------- /ios/AgoraRtcUniPlugin/Uni/UniCallback.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UniCallback.swift 3 | // AgoraRtcUniPlugin 4 | // 5 | // Created by LXH on 2020/12/14. 6 | // 7 | 8 | import Foundation 9 | import AgoraRtcKit 10 | 11 | @objc(UniCallback) 12 | class UniCallback: NSObject, Callback { 13 | private var callback: WXModuleCallback? 14 | 15 | init(_ callback: WXModuleCallback?) { 16 | self.callback = callback 17 | } 18 | 19 | func success(_ data: Any?) { 20 | callback?(data) 21 | } 22 | 23 | func failure(_ code: String, _ message: String) { 24 | callback?(["code":code, "message":message]) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ios/libs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AgoraIO-Community/Agora-Uniapp-SDK/b6c4fedb0415a35e4891a2689123ad81db7635e6/ios/libs/.gitkeep -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "agora-uniapp-sdk", 3 | "version": "3.7.2", 4 | "description": "Agora RTC SDK For Uni-App", 5 | "main": "lib/commonjs/index", 6 | "module": "lib/module/index", 7 | "types": "lib/typescript/src/index.d.ts", 8 | "source": "src/index", 9 | "files": [ 10 | "src", 11 | "lib", 12 | "android", 13 | "ios", 14 | "cpp", 15 | "!lib/typescript/example", 16 | "!**/__tests__", 17 | "!**/__fixtures__", 18 | "!**/__mocks__" 19 | ], 20 | "scripts": { 21 | "test": "jest", 22 | "typescript": "tsc --noEmit", 23 | "lint": "eslint \"**/*.{js,ts,tsx}\"", 24 | "prepare": "bob build", 25 | "release": "release-it", 26 | "example": "yarn --cwd example", 27 | "pods": "cd example && pod-install --quiet", 28 | "bootstrap": "yarn example && yarn && yarn pods" 29 | }, 30 | "keywords": [ 31 | "agora", 32 | "audio", 33 | "video", 34 | "chat", 35 | "live" 36 | ], 37 | "repository": "https://github.com/AgoraIO-Community/Agora-Uniapp-SDK", 38 | "author": "HUI (https://github.com/LichKing-2234)", 39 | "license": "MIT", 40 | "bugs": { 41 | "url": "https://github.com/AgoraIO-Community/Agora-Uniapp-SDK/issues" 42 | }, 43 | "homepage": "https://github.com/AgoraIO-Community/Agora-Uniapp-SDK#readme", 44 | "devDependencies": { 45 | "@commitlint/config-conventional": "^8.3.4", 46 | "@dcloudio/types": "*", 47 | "@react-native-community/bob": "^0.16.2", 48 | "@react-native-community/eslint-config": "^3.0.2", 49 | "@release-it/bumper": "^3.0.1", 50 | "@release-it/conventional-changelog": "^1.1.4", 51 | "@types/jest": "^26.0.0", 52 | "commitlint": "^8.3.5", 53 | "cz-conventional-changelog": "^3.3.0", 54 | "eslint": "^8.4.1", 55 | "eslint-config-prettier": "^8.5.0", 56 | "eslint-plugin-ft-flow": "^2.0.3", 57 | "eslint-plugin-prettier": "^4.0.0", 58 | "husky": "^4.2.5", 59 | "jest": "^26.0.1", 60 | "prettier": "^2.0.5", 61 | "release-it": "^13.5.8", 62 | "typescript": "^3.8.3" 63 | }, 64 | "jest": { 65 | "modulePathIgnorePatterns": [ 66 | "/example/node_modules", 67 | "/lib/" 68 | ] 69 | }, 70 | "husky": { 71 | "hooks": { 72 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS", 73 | "pre-commit": "yarn lint && yarn typescript" 74 | } 75 | }, 76 | "commitlint": { 77 | "extends": [ 78 | "@commitlint/config-conventional" 79 | ] 80 | }, 81 | "release-it": { 82 | "git": { 83 | "commitMessage": "chore: release ${version}", 84 | "tagName": "v${version}" 85 | }, 86 | "npm": { 87 | "publish": false 88 | }, 89 | "github": { 90 | "release": true 91 | }, 92 | "plugins": { 93 | "@release-it/conventional-changelog": { 94 | "preset": "angular", 95 | "infile": "CHANGELOG.md" 96 | } 97 | } 98 | }, 99 | "eslintConfig": { 100 | "root": true, 101 | "extends": [ 102 | "@react-native-community", 103 | "prettier" 104 | ], 105 | "rules": { 106 | "prettier/prettier": [ 107 | "error", 108 | { 109 | "quoteProps": "consistent", 110 | "singleQuote": true, 111 | "tabWidth": 2, 112 | "trailingComma": "es5", 113 | "useTabs": false 114 | } 115 | ] 116 | } 117 | }, 118 | "eslintIgnore": [ 119 | "example/", 120 | "node_modules/", 121 | "lib/" 122 | ], 123 | "prettier": { 124 | "quoteProps": "consistent", 125 | "singleQuote": true, 126 | "tabWidth": 2, 127 | "trailingComma": "es5", 128 | "useTabs": false 129 | }, 130 | "@react-native-community/bob": { 131 | "source": "src", 132 | "output": "lib", 133 | "targets": [ 134 | "commonjs", 135 | "module", 136 | "typescript" 137 | ] 138 | }, 139 | "config": { 140 | "commitizen": { 141 | "path": "./node_modules/cz-conventional-changelog" 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /package/Agora-RTC/android/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /package/Agora-RTC/ios/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /package/Agora-RTC/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Agora音视频插件", 3 | "id": "Agora-RTC", 4 | "version": "3.7.2", 5 | "description": "Agora官方维护的音视频插件,并且在GitHub上开源,欢迎大家积极参与问题反馈和代码贡献", 6 | "_dp_type": "nativeplugin", 7 | "_dp_nativeplugin": { 8 | "android": { 9 | "plugins": [ 10 | { 11 | "type": "module", 12 | "name": "Agora-RTC-EngineModule", 13 | "class": "io.agora.rtc.uni.AgoraRtcEngineModule" 14 | }, 15 | { 16 | "type": "module", 17 | "name": "Agora-RTC-ChannelModule", 18 | "class": "io.agora.rtc.uni.AgoraRtcChannelModule" 19 | }, 20 | { 21 | "type": "component", 22 | "name": "Agora-RTC-SurfaceView", 23 | "class": "io.agora.rtc.uni.AgoraRtcSurfaceView" 24 | }, 25 | { 26 | "type": "component", 27 | "name": "Agora-RTC-TextureView", 28 | "class": "io.agora.rtc.uni.AgoraRtcTextureView" 29 | } 30 | ], 31 | "integrateType": "aar", 32 | "dependencies": [ 33 | "io.agora.rtc:agora-special-full:3.7.2.4", 34 | "io.agora.rtc:full-screen-sharing:3.7.2.4", 35 | "org.jetbrains.kotlin:kotlin-stdlib:1.3.50" 36 | ], 37 | "compileOptions": { 38 | "sourceCompatibility": "1.8", 39 | "targetCompatibility": "1.8" 40 | }, 41 | "abis": [ 42 | "armeabi-v7a", 43 | "arm64-v8a", 44 | "x86", 45 | "x86_64" 46 | ], 47 | "minSdkVersion": "19" 48 | }, 49 | "ios": { 50 | "plugins": [ 51 | { 52 | "type": "module", 53 | "name": "Agora-RTC-EngineModule", 54 | "class": "AgoraRtcEngineModule" 55 | }, 56 | { 57 | "type": "module", 58 | "name": "Agora-RTC-ChannelModule", 59 | "class": "AgoraRtcChannelModule" 60 | }, 61 | { 62 | "type": "component", 63 | "name": "Agora-RTC-SurfaceView", 64 | "class": "AgoraRtcSurfaceView" 65 | } 66 | ], 67 | "integrateType": "framework", 68 | "frameworks": [ 69 | "AgoraCore.xcframework", 70 | "Agorafdkaac.xcframework", 71 | "Agoraffmpeg.xcframework", 72 | "AgoraRtcKit.xcframework", 73 | "AgoraSoundTouch.xcframework" 74 | ], 75 | "embedFrameworks": [ 76 | "AgoraCore.xcframework", 77 | "Agorafdkaac.xcframework", 78 | "Agoraffmpeg.xcframework", 79 | "AgoraRtcKit.xcframework", 80 | "AgoraSoundTouch.xcframework" 81 | ], 82 | "privacies": [ 83 | "NSCameraUsageDescription", 84 | "NSMicrophoneUsageDescription" 85 | ], 86 | "embedSwift": true 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/components/Agora-RTC-JS/RtcSurfaceView.nvue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 36 | 37 | 40 | -------------------------------------------------------------------------------- /src/components/Agora-RTC-JS/RtcTextureView.nvue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 27 | 28 | 31 | -------------------------------------------------------------------------------- /src/components/Agora-RTC-JS/index.ts: -------------------------------------------------------------------------------- 1 | import RtcEngine from './common/RtcEngine.native'; 2 | import RtcChannel from './common/RtcChannel.native'; 3 | 4 | export default RtcEngine; 5 | export { RtcChannel }; 6 | -------------------------------------------------------------------------------- /src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "Agora-RTC-JS", 3 | "name": "Agora音视频插件(JS)", 4 | "version": "3.7.2", 5 | "description": "Agora音视频插件的JS包装,便于开发者调用Native", 6 | "keywords": [ 7 | "Agora", 8 | "声网", 9 | "音频", 10 | "视频", 11 | "直播" 12 | ], 13 | "dcloudext": { 14 | "category": [ 15 | "前端组件", 16 | "通用组件" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /to-framework.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | 4 | echo "Start building framework..." 5 | 6 | function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; } 7 | 8 | AgoraIosFrameworkDir=$1 9 | cd $AgoraIosFrameworkDir || exit 10 | 11 | iphoneOsArch="armv7_arm64" 12 | sdk_ver=$(plutil -extract CFBundleShortVersionString xml1 -o - ./AgoraRtcKit.xcframework/ios-armv7_arm64/AgoraRtcKit.framework/Info.plist | sed -n "s/.*\(.*\)<\/string>.*/\1/p") 13 | echo "sdkver: $sdk_ver" 14 | if [ "x$sdk_ver" = "x" ]; then 15 | sdk_ver=$(plutil -extract CFBundleShortVersionString xml1 -o - ./AgoraRtcKit.xcframework/ios-arm64_armv7/AgoraRtcKit.framework/Info.plist | sed -n "s/.*\(.*\)<\/string>.*/\1/p") 16 | iphoneOsArch="arm64_armv7" 17 | echo "sdkver: $sdk_ver" 18 | fi 19 | min_support_ver=3.4.5 20 | support_m1_sim=n 21 | if version_ge $sdk_ver $min_support_ver; then 22 | support_m1_sim=y 23 | echo "SDK supports M1 simulator" 24 | fi 25 | cur_path=$(pwd) 26 | framework_suffix=".xcframework" 27 | frameworks="" 28 | for file in $(ls $cur_path); do 29 | echo $file 30 | if [[ $file == *$framework_suffix* ]]; then 31 | frameworks="$frameworks $file" 32 | fi 33 | done 34 | 35 | echo "Frameworks found:$frameworks" 36 | mkdir ALL_ARCHITECTURE 37 | for framework in $frameworks; do 38 | binary_name=${framework%.*} 39 | echo "framework_name is $binary_name" 40 | echo "iphoneOsArch: $iphoneOsArch" 41 | # cp -rf $binary_name.xcframework/ios-$iphoneOsArch/$binary_name.framework ./ 42 | cp -rf $binary_name.xcframework/ios-$iphoneOsArch/$binary_name.framework ALL_ARCHITECTURE/ 43 | 44 | if [ "x$support_m1_sim" = "xy" ]; then 45 | if [ -d "$binary_name.xcframework/ios-arm64_x86_64-simulator" ]; then 46 | lipo -remove arm64 $binary_name.xcframework/ios-arm64_x86_64-simulator/$binary_name.framework/$binary_name -output $binary_name.xcframework/ios-arm64_x86_64-simulator/$binary_name.framework/$binary_name 47 | lipo -create $binary_name.xcframework/ios-arm64_x86_64-simulator/$binary_name.framework/$binary_name ALL_ARCHITECTURE/$binary_name.framework/$binary_name -o ALL_ARCHITECTURE/$binary_name.framework/$binary_name 48 | elif [ -d "$binary_name.xcframework/ios-x86_64_arm64-simulator" ]; then 49 | lipo -remove arm64 $binary_name.xcframework/ios-x86_64_arm64-simulator/$binary_name.framework/$binary_name -output $binary_name.xcframework/ios-x86_64_arm64-simulator/$binary_name.framework/$binary_name 50 | lipo -create $binary_name.xcframework/ios-x86_64_arm64-simulator/$binary_name.framework/$binary_name ALL_ARCHITECTURE/$binary_name.framework/$binary_name -o ALL_ARCHITECTURE/$binary_name.framework/$binary_name 51 | else 52 | lipo -create $binary_name.xcframework/ios-x86_64-simulator/$binary_name.framework/$binary_name ALL_ARCHITECTURE/$binary_name.framework/$binary_name -o ALL_ARCHITECTURE/$binary_name.framework/$binary_name 53 | fi 54 | else 55 | lipo -create $binary_name.xcframework/ios-x86_64-simulator/$binary_name.framework/$binary_name ALL_ARCHITECTURE/$binary_name.framework/$binary_name -o ALL_ARCHITECTURE/$binary_name.framework/$binary_name 56 | fi 57 | 58 | # rm -rf $binary_name.xcframework 59 | done 60 | 61 | echo "Build framework successfully." 62 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./src", 4 | "allowUnreachableCode": false, 5 | "allowUnusedLabels": false, 6 | "esModuleInterop": true, 7 | "importsNotUsedAsValues": "error", 8 | "forceConsistentCasingInFileNames": true, 9 | "jsx": "react", 10 | "lib": ["esnext"], 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "noFallthroughCasesInSwitch": true, 14 | "noImplicitReturns": true, 15 | "noImplicitUseStrict": false, 16 | "noStrictGenericChecks": false, 17 | "noUnusedLocals": true, 18 | "noUnusedParameters": true, 19 | "resolveJsonModule": true, 20 | "skipLibCheck": true, 21 | "strict": true, 22 | "target": "esnext", 23 | "types": ["@dcloudio/types"] 24 | } 25 | } 26 | --------------------------------------------------------------------------------