├── .github └── workflows │ └── main.yml ├── .gitignore ├── .metadata ├── CHANGELOG.md ├── LICENSE ├── README.md ├── example ├── .gitignore ├── .metadata ├── README.md ├── lib │ ├── AppId.dart │ └── main.dart ├── macos │ ├── .gitignore │ ├── Flutter │ │ ├── Flutter-Debug.xcconfig │ │ ├── Flutter-Release.xcconfig │ │ └── GeneratedPluginRegistrant.swift │ ├── Podfile │ ├── Podfile.lock │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── app_icon_1024.png │ │ │ ├── app_icon_128.png │ │ │ ├── app_icon_16.png │ │ │ ├── app_icon_256.png │ │ │ ├── app_icon_32.png │ │ │ ├── app_icon_512.png │ │ │ └── app_icon_64.png │ │ ├── Base.lproj │ │ └── MainMenu.xib │ │ ├── Configs │ │ ├── AppInfo.xcconfig │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── Warnings.xcconfig │ │ ├── DebugProfile.entitlements │ │ ├── Info.plist │ │ ├── MainFlutterWindow.swift │ │ └── Release.entitlements ├── pubspec.lock ├── pubspec.yaml ├── test │ └── widget_test.dart └── windows │ ├── .gitignore │ ├── CMakeLists.txt │ ├── flutter │ ├── CMakeLists.txt │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake │ └── runner │ ├── CMakeLists.txt │ ├── Runner.rc │ ├── flutter_window.cpp │ ├── flutter_window.h │ ├── main.cpp │ ├── resource.h │ ├── resources │ └── app_icon.ico │ ├── run_loop.cpp │ ├── run_loop.h │ ├── runner.exe.manifest │ ├── utils.cpp │ ├── utils.h │ ├── win32_window.cpp │ └── win32_window.h ├── lib ├── agora_rtm.dart └── src │ ├── agora_rtm_channel.dart │ ├── agora_rtm_client.dart │ ├── agora_rtm_plugin.dart │ └── utils.dart ├── macos ├── Classes │ ├── AgoraRtmPlugin.swift │ ├── RTMChannel.swift │ └── RTMClient.swift └── agora_rtm.podspec ├── pubspec.lock ├── pubspec.yaml └── windows ├── .gitignore ├── CMakeLists.txt ├── RTMChannel.cpp ├── RTMClient.cpp ├── agora_rtm_plugin.cpp ├── include └── agora_rtm │ ├── RTMChannel.h │ ├── RTMClient.h │ └── agora_rtm_plugin.h └── sdk ├── dll └── agora_rtm_sdk.dll ├── include ├── IAgoraRtmCallManager.h └── IAgoraRtmService.h └── lib └── agora_rtm_sdk.lib /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Main 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | - '*/*' 8 | - '!master' 9 | 10 | jobs: 11 | build-macos: 12 | runs-on: macos-latest 13 | steps: 14 | - uses: actions/checkout@v1 15 | - uses: subosito/flutter-action@v1.4.0 16 | with: 17 | channel: 'dev' 18 | - run: | 19 | flutter config --enable-macos-desktop && cd example 20 | flutter packages get && flutter build macos 21 | 22 | build-windows: 23 | runs-on: windows-latest 24 | steps: 25 | - uses: actions/checkout@v1 26 | - uses: subosito/flutter-action@v1.4.0 27 | with: 28 | channel: 'dev' 29 | - run: | 30 | flutter config --enable-windows-desktop && cd example 31 | flutter packages get && flutter build windows -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | /build/ 32 | 33 | # Android related 34 | **/android/**/gradle-wrapper.jar 35 | **/android/.gradle 36 | **/android/captures/ 37 | **/android/gradlew 38 | **/android/gradlew.bat 39 | **/android/local.properties 40 | **/android/**/GeneratedPluginRegistrant.java 41 | 42 | # iOS/XCode related 43 | **/ios/**/*.mode1v3 44 | **/ios/**/*.mode2v3 45 | **/ios/**/*.moved-aside 46 | **/ios/**/*.pbxuser 47 | **/ios/**/*.perspectivev3 48 | **/ios/**/*sync/ 49 | **/ios/**/.sconsign.dblite 50 | **/ios/**/.tags* 51 | **/ios/**/.vagrant/ 52 | **/ios/**/DerivedData/ 53 | **/ios/**/Icon? 54 | **/ios/**/Pods/ 55 | **/ios/**/.symlinks/ 56 | **/ios/**/profile 57 | **/ios/**/xcuserdata 58 | **/ios/.generated/ 59 | **/ios/Flutter/App.framework 60 | **/ios/Flutter/Flutter.framework 61 | **/ios/Flutter/Generated.xcconfig 62 | **/ios/Flutter/app.flx 63 | **/ios/Flutter/app.zip 64 | **/ios/Flutter/flutter_assets/ 65 | **/ios/Flutter/flutter_export_environment.sh 66 | **/ios/ServiceDefinitions.json 67 | **/ios/Runner/GeneratedPluginRegistrant.* 68 | 69 | # Exceptions to above rules. 70 | !**/ios/**/default.mode1v3 71 | !**/ios/**/default.mode2v3 72 | !**/ios/**/default.pbxuser 73 | !**/ios/**/default.perspectivev3 74 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 75 | *.arb 76 | i18n.dart 77 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: fabeb2a16f1d008ab8230f450c49141d35669798 8 | channel: unknown 9 | 10 | project_type: plugin 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.0.1 2 | 3 | * TODO: Describe initial release. 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | TODO: Add your license here. 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # agora_rtm 2 | 3 | A new flutter plugin project. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter 8 | [plug-in package](https://flutter.dev/developing-packages/), 9 | a specialized package that includes platform-specific implementation code for 10 | Android and/or iOS. 11 | 12 | For help getting started with Flutter, view our 13 | [online documentation](https://flutter.dev/docs), which offers tutorials, 14 | samples, guidance on mobile development, and a full API reference. 15 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | /build/ 32 | 33 | # Web related 34 | lib/generated_plugin_registrant.dart 35 | 36 | # Exceptions to above rules. 37 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 38 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: fabeb2a16f1d008ab8230f450c49141d35669798 8 | channel: unknown 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # agora_rtm_example 2 | 3 | Demonstrates how to use the agora_rtm plugin. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/lib/AppId.dart: -------------------------------------------------------------------------------- 1 | // git update-index --assume-unchanged AppId.dart 2 | const agoraAppId = 'YOUR APP ID'; 3 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:agora_rtm/agora_rtm.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import 'AppId.dart'; 5 | 6 | void main() => runApp(MyApp()); 7 | 8 | class MyApp extends StatefulWidget { 9 | @override 10 | _MyAppState createState() => _MyAppState(); 11 | } 12 | 13 | class _MyAppState extends State { 14 | bool _isLogin = false; 15 | bool _isInChannel = false; 16 | 17 | final _userNameController = TextEditingController(); 18 | final _channelNameController = TextEditingController(); 19 | final _channelMessageController = TextEditingController(); 20 | 21 | final _infoStrings = []; 22 | 23 | AgoraRtmClient _client; 24 | AgoraRtmChannel _channel; 25 | 26 | @override 27 | void initState() { 28 | super.initState(); 29 | _createClient(); 30 | } 31 | 32 | @override 33 | Widget build(BuildContext context) { 34 | return MaterialApp( 35 | home: Scaffold( 36 | appBar: AppBar( 37 | title: const Text('Agora Real Time Message'), 38 | ), 39 | body: Container( 40 | padding: const EdgeInsets.all(16), 41 | child: Column( 42 | children: [ 43 | _buildLogin(), 44 | _buildJoinChannel(), 45 | _buildSendChannelMessage(), 46 | _buildInfoList(), 47 | ], 48 | ), 49 | ), 50 | ), 51 | ); 52 | } 53 | 54 | void _createClient() async { 55 | _client = await AgoraRtmClient.createInstance(agoraAppId); 56 | _client.onConnectionStateChanged = (int state, int reason) { 57 | _log('Connection state changed: ' + 58 | state.toString() + 59 | ', reason: ' + 60 | reason.toString()); 61 | if (state == 5) { 62 | _client.logout(); 63 | _log('Logout.'); 64 | setState(() { 65 | _isLogin = false; 66 | }); 67 | } 68 | }; 69 | } 70 | 71 | Future _createChannel(String name) async { 72 | AgoraRtmChannel channel = await _client.createChannel(name); 73 | channel.onMemberJoined = (AgoraRtmMember member) { 74 | _log( 75 | "Member joined: " + member.userId + ', channel: ' + member.channelId); 76 | }; 77 | channel.onMemberLeft = (AgoraRtmMember member) { 78 | _log("Member left: " + member.userId + ', channel: ' + member.channelId); 79 | }; 80 | channel.onMessageReceived = 81 | (AgoraRtmMessage message, AgoraRtmMember member) { 82 | _log("Channel msg: " + member.userId + ", msg: " + message.text); 83 | }; 84 | return channel; 85 | } 86 | 87 | static TextStyle textStyle = TextStyle(fontSize: 18, color: Colors.blue); 88 | 89 | Widget _buildLogin() { 90 | return Row(children: [ 91 | _isLogin 92 | ? new Expanded( 93 | child: new Text('User Id: ' + _userNameController.text, 94 | style: textStyle)) 95 | : new Expanded( 96 | child: new TextField( 97 | controller: _userNameController, 98 | decoration: InputDecoration(hintText: 'Input your user id'))), 99 | new OutlineButton( 100 | child: Text(_isLogin ? 'Logout' : 'Login', style: textStyle), 101 | onPressed: _toggleLogin, 102 | ) 103 | ]); 104 | } 105 | 106 | Widget _buildJoinChannel() { 107 | if (!_isLogin) { 108 | return Container(); 109 | } 110 | return Row(children: [ 111 | _isInChannel 112 | ? new Expanded( 113 | child: new Text('Channel: ' + _channelNameController.text, 114 | style: textStyle)) 115 | : new Expanded( 116 | child: new TextField( 117 | controller: _channelNameController, 118 | decoration: InputDecoration(hintText: 'Input channel id'))), 119 | new OutlineButton( 120 | child: Text(_isInChannel ? 'Leave Channel' : 'Join Channel', 121 | style: textStyle), 122 | onPressed: _toggleJoinChannel, 123 | ) 124 | ]); 125 | } 126 | 127 | Widget _buildSendChannelMessage() { 128 | if (!_isLogin || !_isInChannel) { 129 | return Container(); 130 | } 131 | return Row(children: [ 132 | new Expanded( 133 | child: new TextField( 134 | controller: _channelMessageController, 135 | decoration: InputDecoration(hintText: 'Input channel message'))), 136 | new OutlineButton( 137 | child: Text('Send to Channel', style: textStyle), 138 | onPressed: _toggleSendChannelMessage, 139 | ) 140 | ]); 141 | } 142 | 143 | Widget _buildInfoList() { 144 | return Expanded( 145 | child: Container( 146 | child: ListView.builder( 147 | itemExtent: 24, 148 | itemBuilder: (context, i) { 149 | return ListTile( 150 | contentPadding: const EdgeInsets.all(0.0), 151 | title: Text(_infoStrings[i]), 152 | ); 153 | }, 154 | itemCount: _infoStrings.length, 155 | ))); 156 | } 157 | 158 | void _toggleLogin() async { 159 | if (_isLogin) { 160 | try { 161 | await _client.logout(); 162 | _log('Logout success.'); 163 | 164 | setState(() { 165 | _isLogin = false; 166 | _isInChannel = false; 167 | }); 168 | } catch (errorCode) { 169 | _log('Logout error: ' + errorCode.toString()); 170 | } 171 | } else { 172 | String userId = _userNameController.text; 173 | if (userId.isEmpty) { 174 | _log('Please input your user id to login.'); 175 | return; 176 | } 177 | 178 | try { 179 | await _client.login(null, userId); 180 | _log('Login success: ' + userId); 181 | setState(() { 182 | _isLogin = true; 183 | }); 184 | } catch (errorCode) { 185 | _log('Login error: ' + errorCode.toString()); 186 | } 187 | } 188 | } 189 | 190 | void _toggleJoinChannel() async { 191 | if (_isInChannel) { 192 | try { 193 | await _channel.leave(); 194 | _log('Leave channel success.'); 195 | _client.releaseChannel(_channel.channelId); 196 | _channelMessageController.text = null; 197 | 198 | setState(() { 199 | _isInChannel = false; 200 | }); 201 | } catch (errorCode) { 202 | _log('Leave channel error: ' + errorCode.toString()); 203 | } 204 | } else { 205 | String channelId = _channelNameController.text; 206 | if (channelId.isEmpty) { 207 | _log('Please input channel id to join.'); 208 | return; 209 | } 210 | 211 | try { 212 | _channel = await _createChannel(channelId); 213 | await _channel.join(); 214 | _log('Join channel success.'); 215 | 216 | setState(() { 217 | _isInChannel = true; 218 | }); 219 | } catch (errorCode) { 220 | _log('Join channel error: ' + errorCode.toString()); 221 | } 222 | } 223 | } 224 | 225 | void _toggleSendChannelMessage() async { 226 | String text = _channelMessageController.text; 227 | if (text.isEmpty) { 228 | _log('Please input text to send.'); 229 | return; 230 | } 231 | try { 232 | await _channel.sendMessage(AgoraRtmMessage.fromText(text)); 233 | _log('Send channel message success.'); 234 | } catch (errorCode) { 235 | _log('Send channel message error: ' + errorCode.toString()); 236 | } 237 | } 238 | 239 | void _log(String info) { 240 | print(info); 241 | setState(() { 242 | _infoStrings.insert(0, info); 243 | }); 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /example/macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/xcuserdata/ 7 | -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 3 | #include "ephemeral/Flutter-Generated.xcconfig" 4 | -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 3 | #include "ephemeral/Flutter-Generated.xcconfig" 4 | -------------------------------------------------------------------------------- /example/macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | import agora_rtm 9 | 10 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 11 | AgoraRtmPlugin.register(with: registry.registrar(forPlugin: "AgoraRtmPlugin")) 12 | } 13 | -------------------------------------------------------------------------------- /example/macos/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.11' 2 | 3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 | 6 | project 'Runner', { 7 | 'Debug' => :debug, 8 | 'Profile' => :release, 9 | 'Release' => :release, 10 | } 11 | 12 | def flutter_root 13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) 14 | unless File.exist?(generated_xcode_build_settings_path) 15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" 16 | end 17 | 18 | File.foreach(generated_xcode_build_settings_path) do |line| 19 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 20 | return matches[1].strip if matches 21 | end 22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" 23 | end 24 | 25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 26 | 27 | flutter_macos_podfile_setup 28 | 29 | target 'Runner' do 30 | use_frameworks! 31 | use_modular_headers! 32 | 33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) 34 | end 35 | 36 | post_install do |installer| 37 | installer.pods_project.targets.each do |target| 38 | flutter_additional_macos_build_settings(target) 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /example/macos/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - agora_rtm (0.0.1): 3 | - AgoraRtm_macOS (= 1.2.2) 4 | - FlutterMacOS 5 | - AgoraRtm_macOS (1.2.2) 6 | - FlutterMacOS (1.0.0) 7 | 8 | DEPENDENCIES: 9 | - agora_rtm (from `Flutter/ephemeral/.symlinks/plugins/agora_rtm/macos`) 10 | - FlutterMacOS (from `Flutter/ephemeral`) 11 | 12 | SPEC REPOS: 13 | trunk: 14 | - AgoraRtm_macOS 15 | 16 | EXTERNAL SOURCES: 17 | agora_rtm: 18 | :path: Flutter/ephemeral/.symlinks/plugins/agora_rtm/macos 19 | FlutterMacOS: 20 | :path: Flutter/ephemeral 21 | 22 | SPEC CHECKSUMS: 23 | agora_rtm: f1874eee2d7e936646c86dbcd045d7fa5089f9a6 24 | AgoraRtm_macOS: fab43aa22a627ceee1409c6981fca0786c7d79cf 25 | FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424 26 | 27 | PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c 28 | 29 | COCOAPODS: 1.10.0 30 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 51; 7 | objects = { 8 | 9 | /* Begin PBXAggregateTarget section */ 10 | 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { 11 | isa = PBXAggregateTarget; 12 | buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; 13 | buildPhases = ( 14 | 33CC111E2044C6BF0003C045 /* ShellScript */, 15 | ); 16 | dependencies = ( 17 | ); 18 | name = "Flutter Assemble"; 19 | productName = FLX; 20 | }; 21 | /* End PBXAggregateTarget section */ 22 | 23 | /* Begin PBXBuildFile section */ 24 | 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; 25 | 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; 26 | 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 27 | 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 28 | 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; 29 | A0A9DDB52AB480BC1744591D /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 62D202D448FDE88D3A67AC3C /* Pods_Runner.framework */; }; 30 | /* End PBXBuildFile section */ 31 | 32 | /* Begin PBXContainerItemProxy section */ 33 | 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { 34 | isa = PBXContainerItemProxy; 35 | containerPortal = 33CC10E52044A3C60003C045 /* Project object */; 36 | proxyType = 1; 37 | remoteGlobalIDString = 33CC111A2044C6BA0003C045; 38 | remoteInfo = FLX; 39 | }; 40 | /* End PBXContainerItemProxy section */ 41 | 42 | /* Begin PBXCopyFilesBuildPhase section */ 43 | 33CC110E2044A8840003C045 /* Bundle Framework */ = { 44 | isa = PBXCopyFilesBuildPhase; 45 | buildActionMask = 2147483647; 46 | dstPath = ""; 47 | dstSubfolderSpec = 10; 48 | files = ( 49 | ); 50 | name = "Bundle Framework"; 51 | runOnlyForDeploymentPostprocessing = 0; 52 | }; 53 | /* End PBXCopyFilesBuildPhase section */ 54 | 55 | /* Begin PBXFileReference section */ 56 | 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 57 | 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; 58 | 33CC10ED2044A3C60003C045 /* agora_rtm_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = agora_rtm_example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 60 | 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; 61 | 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 62 | 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; 63 | 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; 64 | 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; 65 | 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; 66 | 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; 67 | 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; 68 | 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 69 | 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; 70 | 62D202D448FDE88D3A67AC3C /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 71 | 779321701D2BDA3B677B0FE0 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 72 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 73 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; 74 | D4A0DE3A302FA966DD3F8202 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 75 | F5B89D111EE56541862AEC67 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 76 | /* End PBXFileReference section */ 77 | 78 | /* Begin PBXFrameworksBuildPhase section */ 79 | 33CC10EA2044A3C60003C045 /* Frameworks */ = { 80 | isa = PBXFrameworksBuildPhase; 81 | buildActionMask = 2147483647; 82 | files = ( 83 | A0A9DDB52AB480BC1744591D /* Pods_Runner.framework in Frameworks */, 84 | ); 85 | runOnlyForDeploymentPostprocessing = 0; 86 | }; 87 | /* End PBXFrameworksBuildPhase section */ 88 | 89 | /* Begin PBXGroup section */ 90 | 101301A771AD6DD686A46D32 /* Pods */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | D4A0DE3A302FA966DD3F8202 /* Pods-Runner.debug.xcconfig */, 94 | F5B89D111EE56541862AEC67 /* Pods-Runner.release.xcconfig */, 95 | 779321701D2BDA3B677B0FE0 /* Pods-Runner.profile.xcconfig */, 96 | ); 97 | name = Pods; 98 | path = Pods; 99 | sourceTree = ""; 100 | }; 101 | 33BA886A226E78AF003329D5 /* Configs */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 33E5194F232828860026EE4D /* AppInfo.xcconfig */, 105 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 106 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 107 | 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, 108 | ); 109 | path = Configs; 110 | sourceTree = ""; 111 | }; 112 | 33CC10E42044A3C60003C045 = { 113 | isa = PBXGroup; 114 | children = ( 115 | 33FAB671232836740065AC1E /* Runner */, 116 | 33CEB47122A05771004F2AC0 /* Flutter */, 117 | 33CC10EE2044A3C60003C045 /* Products */, 118 | D73912EC22F37F3D000D13A0 /* Frameworks */, 119 | 101301A771AD6DD686A46D32 /* Pods */, 120 | ); 121 | sourceTree = ""; 122 | }; 123 | 33CC10EE2044A3C60003C045 /* Products */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | 33CC10ED2044A3C60003C045 /* agora_rtm_example.app */, 127 | ); 128 | name = Products; 129 | sourceTree = ""; 130 | }; 131 | 33CC11242044D66E0003C045 /* Resources */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 33CC10F22044A3C60003C045 /* Assets.xcassets */, 135 | 33CC10F42044A3C60003C045 /* MainMenu.xib */, 136 | 33CC10F72044A3C60003C045 /* Info.plist */, 137 | ); 138 | name = Resources; 139 | path = ..; 140 | sourceTree = ""; 141 | }; 142 | 33CEB47122A05771004F2AC0 /* Flutter */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, 146 | 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, 147 | 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, 148 | 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, 149 | ); 150 | path = Flutter; 151 | sourceTree = ""; 152 | }; 153 | 33FAB671232836740065AC1E /* Runner */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | 33CC10F02044A3C60003C045 /* AppDelegate.swift */, 157 | 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, 158 | 33E51913231747F40026EE4D /* DebugProfile.entitlements */, 159 | 33E51914231749380026EE4D /* Release.entitlements */, 160 | 33CC11242044D66E0003C045 /* Resources */, 161 | 33BA886A226E78AF003329D5 /* Configs */, 162 | ); 163 | path = Runner; 164 | sourceTree = ""; 165 | }; 166 | D73912EC22F37F3D000D13A0 /* Frameworks */ = { 167 | isa = PBXGroup; 168 | children = ( 169 | 62D202D448FDE88D3A67AC3C /* Pods_Runner.framework */, 170 | ); 171 | name = Frameworks; 172 | sourceTree = ""; 173 | }; 174 | /* End PBXGroup section */ 175 | 176 | /* Begin PBXNativeTarget section */ 177 | 33CC10EC2044A3C60003C045 /* Runner */ = { 178 | isa = PBXNativeTarget; 179 | buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; 180 | buildPhases = ( 181 | 48DFC3ED86A8B7A1070274EE /* [CP] Check Pods Manifest.lock */, 182 | 33CC10E92044A3C60003C045 /* Sources */, 183 | 33CC10EA2044A3C60003C045 /* Frameworks */, 184 | 33CC10EB2044A3C60003C045 /* Resources */, 185 | 33CC110E2044A8840003C045 /* Bundle Framework */, 186 | 3399D490228B24CF009A79C7 /* ShellScript */, 187 | ); 188 | buildRules = ( 189 | ); 190 | dependencies = ( 191 | 33CC11202044C79F0003C045 /* PBXTargetDependency */, 192 | ); 193 | name = Runner; 194 | productName = Runner; 195 | productReference = 33CC10ED2044A3C60003C045 /* agora_rtm_example.app */; 196 | productType = "com.apple.product-type.application"; 197 | }; 198 | /* End PBXNativeTarget section */ 199 | 200 | /* Begin PBXProject section */ 201 | 33CC10E52044A3C60003C045 /* Project object */ = { 202 | isa = PBXProject; 203 | attributes = { 204 | LastSwiftUpdateCheck = 0920; 205 | LastUpgradeCheck = 0930; 206 | ORGANIZATIONNAME = "The Flutter Authors"; 207 | TargetAttributes = { 208 | 33CC10EC2044A3C60003C045 = { 209 | CreatedOnToolsVersion = 9.2; 210 | LastSwiftMigration = 1100; 211 | ProvisioningStyle = Automatic; 212 | SystemCapabilities = { 213 | com.apple.Sandbox = { 214 | enabled = 1; 215 | }; 216 | }; 217 | }; 218 | 33CC111A2044C6BA0003C045 = { 219 | CreatedOnToolsVersion = 9.2; 220 | ProvisioningStyle = Manual; 221 | }; 222 | }; 223 | }; 224 | buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; 225 | compatibilityVersion = "Xcode 8.0"; 226 | developmentRegion = en; 227 | hasScannedForEncodings = 0; 228 | knownRegions = ( 229 | en, 230 | Base, 231 | ); 232 | mainGroup = 33CC10E42044A3C60003C045; 233 | productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; 234 | projectDirPath = ""; 235 | projectRoot = ""; 236 | targets = ( 237 | 33CC10EC2044A3C60003C045 /* Runner */, 238 | 33CC111A2044C6BA0003C045 /* Flutter Assemble */, 239 | ); 240 | }; 241 | /* End PBXProject section */ 242 | 243 | /* Begin PBXResourcesBuildPhase section */ 244 | 33CC10EB2044A3C60003C045 /* Resources */ = { 245 | isa = PBXResourcesBuildPhase; 246 | buildActionMask = 2147483647; 247 | files = ( 248 | 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, 249 | 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, 250 | ); 251 | runOnlyForDeploymentPostprocessing = 0; 252 | }; 253 | /* End PBXResourcesBuildPhase section */ 254 | 255 | /* Begin PBXShellScriptBuildPhase section */ 256 | 3399D490228B24CF009A79C7 /* ShellScript */ = { 257 | isa = PBXShellScriptBuildPhase; 258 | buildActionMask = 2147483647; 259 | files = ( 260 | ); 261 | inputFileListPaths = ( 262 | ); 263 | inputPaths = ( 264 | ); 265 | outputFileListPaths = ( 266 | ); 267 | outputPaths = ( 268 | ); 269 | runOnlyForDeploymentPostprocessing = 0; 270 | shellPath = /bin/sh; 271 | shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; 272 | }; 273 | 33CC111E2044C6BF0003C045 /* ShellScript */ = { 274 | isa = PBXShellScriptBuildPhase; 275 | buildActionMask = 2147483647; 276 | files = ( 277 | ); 278 | inputFileListPaths = ( 279 | Flutter/ephemeral/FlutterInputs.xcfilelist, 280 | ); 281 | inputPaths = ( 282 | Flutter/ephemeral/tripwire, 283 | ); 284 | outputFileListPaths = ( 285 | Flutter/ephemeral/FlutterOutputs.xcfilelist, 286 | ); 287 | outputPaths = ( 288 | ); 289 | runOnlyForDeploymentPostprocessing = 0; 290 | shellPath = /bin/sh; 291 | shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh\ntouch Flutter/ephemeral/tripwire\n"; 292 | }; 293 | 48DFC3ED86A8B7A1070274EE /* [CP] Check Pods Manifest.lock */ = { 294 | isa = PBXShellScriptBuildPhase; 295 | buildActionMask = 2147483647; 296 | files = ( 297 | ); 298 | inputFileListPaths = ( 299 | ); 300 | inputPaths = ( 301 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 302 | "${PODS_ROOT}/Manifest.lock", 303 | ); 304 | name = "[CP] Check Pods Manifest.lock"; 305 | outputFileListPaths = ( 306 | ); 307 | outputPaths = ( 308 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 309 | ); 310 | runOnlyForDeploymentPostprocessing = 0; 311 | shellPath = /bin/sh; 312 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 313 | showEnvVarsInLog = 0; 314 | }; 315 | /* End PBXShellScriptBuildPhase section */ 316 | 317 | /* Begin PBXSourcesBuildPhase section */ 318 | 33CC10E92044A3C60003C045 /* Sources */ = { 319 | isa = PBXSourcesBuildPhase; 320 | buildActionMask = 2147483647; 321 | files = ( 322 | 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, 323 | 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, 324 | 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, 325 | ); 326 | runOnlyForDeploymentPostprocessing = 0; 327 | }; 328 | /* End PBXSourcesBuildPhase section */ 329 | 330 | /* Begin PBXTargetDependency section */ 331 | 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { 332 | isa = PBXTargetDependency; 333 | target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; 334 | targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; 335 | }; 336 | /* End PBXTargetDependency section */ 337 | 338 | /* Begin PBXVariantGroup section */ 339 | 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { 340 | isa = PBXVariantGroup; 341 | children = ( 342 | 33CC10F52044A3C60003C045 /* Base */, 343 | ); 344 | name = MainMenu.xib; 345 | path = Runner; 346 | sourceTree = ""; 347 | }; 348 | /* End PBXVariantGroup section */ 349 | 350 | /* Begin XCBuildConfiguration section */ 351 | 338D0CE9231458BD00FA5F75 /* Profile */ = { 352 | isa = XCBuildConfiguration; 353 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 354 | buildSettings = { 355 | ALWAYS_SEARCH_USER_PATHS = NO; 356 | CLANG_ANALYZER_NONNULL = YES; 357 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 358 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 359 | CLANG_CXX_LIBRARY = "libc++"; 360 | CLANG_ENABLE_MODULES = YES; 361 | CLANG_ENABLE_OBJC_ARC = YES; 362 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 363 | CLANG_WARN_BOOL_CONVERSION = YES; 364 | CLANG_WARN_CONSTANT_CONVERSION = YES; 365 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 366 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 367 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 368 | CLANG_WARN_EMPTY_BODY = YES; 369 | CLANG_WARN_ENUM_CONVERSION = YES; 370 | CLANG_WARN_INFINITE_RECURSION = YES; 371 | CLANG_WARN_INT_CONVERSION = YES; 372 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 373 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 374 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 375 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 376 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 377 | CODE_SIGN_IDENTITY = "-"; 378 | COPY_PHASE_STRIP = NO; 379 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 380 | ENABLE_NS_ASSERTIONS = NO; 381 | ENABLE_STRICT_OBJC_MSGSEND = YES; 382 | GCC_C_LANGUAGE_STANDARD = gnu11; 383 | GCC_NO_COMMON_BLOCKS = YES; 384 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 385 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 386 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 387 | GCC_WARN_UNUSED_FUNCTION = YES; 388 | GCC_WARN_UNUSED_VARIABLE = YES; 389 | MACOSX_DEPLOYMENT_TARGET = 10.11; 390 | MTL_ENABLE_DEBUG_INFO = NO; 391 | SDKROOT = macosx; 392 | SWIFT_COMPILATION_MODE = wholemodule; 393 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 394 | }; 395 | name = Profile; 396 | }; 397 | 338D0CEA231458BD00FA5F75 /* Profile */ = { 398 | isa = XCBuildConfiguration; 399 | baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; 400 | buildSettings = { 401 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 402 | CLANG_ENABLE_MODULES = YES; 403 | CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; 404 | CODE_SIGN_STYLE = Automatic; 405 | COMBINE_HIDPI_IMAGES = YES; 406 | FRAMEWORK_SEARCH_PATHS = ( 407 | "$(inherited)", 408 | "$(PROJECT_DIR)/Flutter/ephemeral", 409 | ); 410 | INFOPLIST_FILE = Runner/Info.plist; 411 | LD_RUNPATH_SEARCH_PATHS = ( 412 | "$(inherited)", 413 | "@executable_path/../Frameworks", 414 | ); 415 | PROVISIONING_PROFILE_SPECIFIER = ""; 416 | SWIFT_VERSION = 5.0; 417 | }; 418 | name = Profile; 419 | }; 420 | 338D0CEB231458BD00FA5F75 /* Profile */ = { 421 | isa = XCBuildConfiguration; 422 | buildSettings = { 423 | CODE_SIGN_STYLE = Manual; 424 | PRODUCT_NAME = "$(TARGET_NAME)"; 425 | }; 426 | name = Profile; 427 | }; 428 | 33CC10F92044A3C60003C045 /* Debug */ = { 429 | isa = XCBuildConfiguration; 430 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 431 | buildSettings = { 432 | ALWAYS_SEARCH_USER_PATHS = NO; 433 | CLANG_ANALYZER_NONNULL = YES; 434 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 435 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 436 | CLANG_CXX_LIBRARY = "libc++"; 437 | CLANG_ENABLE_MODULES = YES; 438 | CLANG_ENABLE_OBJC_ARC = YES; 439 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 440 | CLANG_WARN_BOOL_CONVERSION = YES; 441 | CLANG_WARN_CONSTANT_CONVERSION = YES; 442 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 443 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 444 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 445 | CLANG_WARN_EMPTY_BODY = YES; 446 | CLANG_WARN_ENUM_CONVERSION = YES; 447 | CLANG_WARN_INFINITE_RECURSION = YES; 448 | CLANG_WARN_INT_CONVERSION = YES; 449 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 450 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 451 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 452 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 453 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 454 | CODE_SIGN_IDENTITY = "-"; 455 | COPY_PHASE_STRIP = NO; 456 | DEBUG_INFORMATION_FORMAT = dwarf; 457 | ENABLE_STRICT_OBJC_MSGSEND = YES; 458 | ENABLE_TESTABILITY = YES; 459 | GCC_C_LANGUAGE_STANDARD = gnu11; 460 | GCC_DYNAMIC_NO_PIC = NO; 461 | GCC_NO_COMMON_BLOCKS = YES; 462 | GCC_OPTIMIZATION_LEVEL = 0; 463 | GCC_PREPROCESSOR_DEFINITIONS = ( 464 | "DEBUG=1", 465 | "$(inherited)", 466 | ); 467 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 468 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 469 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 470 | GCC_WARN_UNUSED_FUNCTION = YES; 471 | GCC_WARN_UNUSED_VARIABLE = YES; 472 | MACOSX_DEPLOYMENT_TARGET = 10.11; 473 | MTL_ENABLE_DEBUG_INFO = YES; 474 | ONLY_ACTIVE_ARCH = YES; 475 | SDKROOT = macosx; 476 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 477 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 478 | }; 479 | name = Debug; 480 | }; 481 | 33CC10FA2044A3C60003C045 /* Release */ = { 482 | isa = XCBuildConfiguration; 483 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 484 | buildSettings = { 485 | ALWAYS_SEARCH_USER_PATHS = NO; 486 | CLANG_ANALYZER_NONNULL = YES; 487 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 488 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 489 | CLANG_CXX_LIBRARY = "libc++"; 490 | CLANG_ENABLE_MODULES = YES; 491 | CLANG_ENABLE_OBJC_ARC = YES; 492 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 493 | CLANG_WARN_BOOL_CONVERSION = YES; 494 | CLANG_WARN_CONSTANT_CONVERSION = YES; 495 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 496 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 497 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 498 | CLANG_WARN_EMPTY_BODY = YES; 499 | CLANG_WARN_ENUM_CONVERSION = YES; 500 | CLANG_WARN_INFINITE_RECURSION = YES; 501 | CLANG_WARN_INT_CONVERSION = YES; 502 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 503 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 504 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 505 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 506 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 507 | CODE_SIGN_IDENTITY = "-"; 508 | COPY_PHASE_STRIP = NO; 509 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 510 | ENABLE_NS_ASSERTIONS = NO; 511 | ENABLE_STRICT_OBJC_MSGSEND = YES; 512 | GCC_C_LANGUAGE_STANDARD = gnu11; 513 | GCC_NO_COMMON_BLOCKS = YES; 514 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 515 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 516 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 517 | GCC_WARN_UNUSED_FUNCTION = YES; 518 | GCC_WARN_UNUSED_VARIABLE = YES; 519 | MACOSX_DEPLOYMENT_TARGET = 10.11; 520 | MTL_ENABLE_DEBUG_INFO = NO; 521 | SDKROOT = macosx; 522 | SWIFT_COMPILATION_MODE = wholemodule; 523 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 524 | }; 525 | name = Release; 526 | }; 527 | 33CC10FC2044A3C60003C045 /* Debug */ = { 528 | isa = XCBuildConfiguration; 529 | baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; 530 | buildSettings = { 531 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 532 | CLANG_ENABLE_MODULES = YES; 533 | CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; 534 | CODE_SIGN_STYLE = Automatic; 535 | COMBINE_HIDPI_IMAGES = YES; 536 | FRAMEWORK_SEARCH_PATHS = ( 537 | "$(inherited)", 538 | "$(PROJECT_DIR)/Flutter/ephemeral", 539 | ); 540 | INFOPLIST_FILE = Runner/Info.plist; 541 | LD_RUNPATH_SEARCH_PATHS = ( 542 | "$(inherited)", 543 | "@executable_path/../Frameworks", 544 | ); 545 | PROVISIONING_PROFILE_SPECIFIER = ""; 546 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 547 | SWIFT_VERSION = 5.0; 548 | }; 549 | name = Debug; 550 | }; 551 | 33CC10FD2044A3C60003C045 /* Release */ = { 552 | isa = XCBuildConfiguration; 553 | baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; 554 | buildSettings = { 555 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 556 | CLANG_ENABLE_MODULES = YES; 557 | CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; 558 | CODE_SIGN_STYLE = Automatic; 559 | COMBINE_HIDPI_IMAGES = YES; 560 | FRAMEWORK_SEARCH_PATHS = ( 561 | "$(inherited)", 562 | "$(PROJECT_DIR)/Flutter/ephemeral", 563 | ); 564 | INFOPLIST_FILE = Runner/Info.plist; 565 | LD_RUNPATH_SEARCH_PATHS = ( 566 | "$(inherited)", 567 | "@executable_path/../Frameworks", 568 | ); 569 | PROVISIONING_PROFILE_SPECIFIER = ""; 570 | SWIFT_VERSION = 5.0; 571 | }; 572 | name = Release; 573 | }; 574 | 33CC111C2044C6BA0003C045 /* Debug */ = { 575 | isa = XCBuildConfiguration; 576 | buildSettings = { 577 | CODE_SIGN_STYLE = Manual; 578 | PRODUCT_NAME = "$(TARGET_NAME)"; 579 | }; 580 | name = Debug; 581 | }; 582 | 33CC111D2044C6BA0003C045 /* Release */ = { 583 | isa = XCBuildConfiguration; 584 | buildSettings = { 585 | CODE_SIGN_STYLE = Automatic; 586 | PRODUCT_NAME = "$(TARGET_NAME)"; 587 | }; 588 | name = Release; 589 | }; 590 | /* End XCBuildConfiguration section */ 591 | 592 | /* Begin XCConfigurationList section */ 593 | 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { 594 | isa = XCConfigurationList; 595 | buildConfigurations = ( 596 | 33CC10F92044A3C60003C045 /* Debug */, 597 | 33CC10FA2044A3C60003C045 /* Release */, 598 | 338D0CE9231458BD00FA5F75 /* Profile */, 599 | ); 600 | defaultConfigurationIsVisible = 0; 601 | defaultConfigurationName = Release; 602 | }; 603 | 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { 604 | isa = XCConfigurationList; 605 | buildConfigurations = ( 606 | 33CC10FC2044A3C60003C045 /* Debug */, 607 | 33CC10FD2044A3C60003C045 /* Release */, 608 | 338D0CEA231458BD00FA5F75 /* Profile */, 609 | ); 610 | defaultConfigurationIsVisible = 0; 611 | defaultConfigurationName = Release; 612 | }; 613 | 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { 614 | isa = XCConfigurationList; 615 | buildConfigurations = ( 616 | 33CC111C2044C6BA0003C045 /* Debug */, 617 | 33CC111D2044C6BA0003C045 /* Release */, 618 | 338D0CEB231458BD00FA5F75 /* Profile */, 619 | ); 620 | defaultConfigurationIsVisible = 0; 621 | defaultConfigurationName = Release; 622 | }; 623 | /* End XCConfigurationList section */ 624 | }; 625 | rootObject = 33CC10E52044A3C60003C045 /* Project object */; 626 | } 627 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "size": "16x16", 5 | "idiom": "mac", 6 | "filename": "app_icon_16.png", 7 | "scale": "1x" 8 | }, 9 | { 10 | "size": "16x16", 11 | "idiom": "mac", 12 | "filename": "app_icon_32.png", 13 | "scale": "2x" 14 | }, 15 | { 16 | "size": "32x32", 17 | "idiom": "mac", 18 | "filename": "app_icon_32.png", 19 | "scale": "1x" 20 | }, 21 | { 22 | "size": "32x32", 23 | "idiom": "mac", 24 | "filename": "app_icon_64.png", 25 | "scale": "2x" 26 | }, 27 | { 28 | "size": "128x128", 29 | "idiom": "mac", 30 | "filename": "app_icon_128.png", 31 | "scale": "1x" 32 | }, 33 | { 34 | "size": "128x128", 35 | "idiom": "mac", 36 | "filename": "app_icon_256.png", 37 | "scale": "2x" 38 | }, 39 | { 40 | "size": "256x256", 41 | "idiom": "mac", 42 | "filename": "app_icon_256.png", 43 | "scale": "1x" 44 | }, 45 | { 46 | "size": "256x256", 47 | "idiom": "mac", 48 | "filename": "app_icon_512.png", 49 | "scale": "2x" 50 | }, 51 | { 52 | "size": "512x512", 53 | "idiom": "mac", 54 | "filename": "app_icon_512.png", 55 | "scale": "1x" 56 | }, 57 | { 58 | "size": "512x512", 59 | "idiom": "mac", 60 | "filename": "app_icon_1024.png", 61 | "scale": "2x" 62 | } 63 | ], 64 | "info": { 65 | "version": 1, 66 | "author": "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woodemi/Flutter-RTM-Desktop/33b9259d353b20be6c4f9b7c95c9d38df2df3cea/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woodemi/Flutter-RTM-Desktop/33b9259d353b20be6c4f9b7c95c9d38df2df3cea/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woodemi/Flutter-RTM-Desktop/33b9259d353b20be6c4f9b7c95c9d38df2df3cea/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woodemi/Flutter-RTM-Desktop/33b9259d353b20be6c4f9b7c95c9d38df2df3cea/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woodemi/Flutter-RTM-Desktop/33b9259d353b20be6c4f9b7c95c9d38df2df3cea/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woodemi/Flutter-RTM-Desktop/33b9259d353b20be6c4f9b7c95c9d38df2df3cea/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woodemi/Flutter-RTM-Desktop/33b9259d353b20be6c4f9b7c95c9d38df2df3cea/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /example/macos/Runner/Base.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = agora_rtm_example 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.example.agoraRtm 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2020 com.example. All rights reserved. 15 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /example/macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.client 10 | 11 | com.apple.security.network.server 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /example/macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /example/macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController.init() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.network.client 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | agora_rtm: 5 | dependency: "direct dev" 6 | description: 7 | path: ".." 8 | relative: true 9 | source: path 10 | version: "0.0.1" 11 | async: 12 | dependency: transitive 13 | description: 14 | name: async 15 | url: "https://pub.flutter-io.cn" 16 | source: hosted 17 | version: "2.5.0-nullsafety.3" 18 | boolean_selector: 19 | dependency: transitive 20 | description: 21 | name: boolean_selector 22 | url: "https://pub.flutter-io.cn" 23 | source: hosted 24 | version: "2.1.0-nullsafety.3" 25 | characters: 26 | dependency: transitive 27 | description: 28 | name: characters 29 | url: "https://pub.flutter-io.cn" 30 | source: hosted 31 | version: "1.1.0-nullsafety.5" 32 | charcode: 33 | dependency: transitive 34 | description: 35 | name: charcode 36 | url: "https://pub.flutter-io.cn" 37 | source: hosted 38 | version: "1.2.0-nullsafety.3" 39 | clock: 40 | dependency: transitive 41 | description: 42 | name: clock 43 | url: "https://pub.flutter-io.cn" 44 | source: hosted 45 | version: "1.1.0-nullsafety.3" 46 | collection: 47 | dependency: transitive 48 | description: 49 | name: collection 50 | url: "https://pub.flutter-io.cn" 51 | source: hosted 52 | version: "1.15.0-nullsafety.5" 53 | cupertino_icons: 54 | dependency: "direct main" 55 | description: 56 | name: cupertino_icons 57 | url: "https://pub.flutter-io.cn" 58 | source: hosted 59 | version: "0.1.3" 60 | fake_async: 61 | dependency: transitive 62 | description: 63 | name: fake_async 64 | url: "https://pub.flutter-io.cn" 65 | source: hosted 66 | version: "1.2.0-nullsafety.3" 67 | flutter: 68 | dependency: "direct main" 69 | description: flutter 70 | source: sdk 71 | version: "0.0.0" 72 | flutter_test: 73 | dependency: "direct dev" 74 | description: flutter 75 | source: sdk 76 | version: "0.0.0" 77 | matcher: 78 | dependency: transitive 79 | description: 80 | name: matcher 81 | url: "https://pub.flutter-io.cn" 82 | source: hosted 83 | version: "0.12.10-nullsafety.3" 84 | meta: 85 | dependency: transitive 86 | description: 87 | name: meta 88 | url: "https://pub.flutter-io.cn" 89 | source: hosted 90 | version: "1.3.0-nullsafety.6" 91 | path: 92 | dependency: transitive 93 | description: 94 | name: path 95 | url: "https://pub.flutter-io.cn" 96 | source: hosted 97 | version: "1.8.0-nullsafety.3" 98 | sky_engine: 99 | dependency: transitive 100 | description: flutter 101 | source: sdk 102 | version: "0.0.99" 103 | source_span: 104 | dependency: transitive 105 | description: 106 | name: source_span 107 | url: "https://pub.flutter-io.cn" 108 | source: hosted 109 | version: "1.8.0-nullsafety.4" 110 | stack_trace: 111 | dependency: transitive 112 | description: 113 | name: stack_trace 114 | url: "https://pub.flutter-io.cn" 115 | source: hosted 116 | version: "1.10.0-nullsafety.6" 117 | stream_channel: 118 | dependency: transitive 119 | description: 120 | name: stream_channel 121 | url: "https://pub.flutter-io.cn" 122 | source: hosted 123 | version: "2.1.0-nullsafety.3" 124 | string_scanner: 125 | dependency: transitive 126 | description: 127 | name: string_scanner 128 | url: "https://pub.flutter-io.cn" 129 | source: hosted 130 | version: "1.1.0-nullsafety.3" 131 | term_glyph: 132 | dependency: transitive 133 | description: 134 | name: term_glyph 135 | url: "https://pub.flutter-io.cn" 136 | source: hosted 137 | version: "1.2.0-nullsafety.3" 138 | test_api: 139 | dependency: transitive 140 | description: 141 | name: test_api 142 | url: "https://pub.flutter-io.cn" 143 | source: hosted 144 | version: "0.2.19-nullsafety.6" 145 | typed_data: 146 | dependency: transitive 147 | description: 148 | name: typed_data 149 | url: "https://pub.flutter-io.cn" 150 | source: hosted 151 | version: "1.3.0-nullsafety.5" 152 | vector_math: 153 | dependency: transitive 154 | description: 155 | name: vector_math 156 | url: "https://pub.flutter-io.cn" 157 | source: hosted 158 | version: "2.1.0-nullsafety.5" 159 | sdks: 160 | dart: ">=2.12.0-0.0 <3.0.0" 161 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: agora_rtm_example 2 | description: Demonstrates how to use the agora_rtm plugin. 3 | publish_to: 'none' 4 | 5 | environment: 6 | sdk: ">=2.1.0 <3.0.0" 7 | 8 | dependencies: 9 | flutter: 10 | sdk: flutter 11 | 12 | # The following adds the Cupertino Icons font to your application. 13 | # Use with the CupertinoIcons class for iOS style icons. 14 | cupertino_icons: ^0.1.2 15 | 16 | dev_dependencies: 17 | flutter_test: 18 | sdk: flutter 19 | 20 | agora_rtm: 21 | path: ../ 22 | 23 | # For information on the generic Dart part of this file, see the 24 | # following page: https://dart.dev/tools/pub/pubspec 25 | 26 | # The following section is specific to Flutter. 27 | flutter: 28 | 29 | # The following line ensures that the Material Icons font is 30 | # included with your application, so that you can use the icons in 31 | # the material Icons class. 32 | uses-material-design: true 33 | 34 | # To add assets to your application, add an assets section, like this: 35 | # assets: 36 | # - images/a_dot_burr.jpeg 37 | # - images/a_dot_ham.jpeg 38 | 39 | # An image asset can refer to one or more resolution-specific "variants", see 40 | # https://flutter.dev/assets-and-images/#resolution-aware. 41 | 42 | # For details regarding adding assets from package dependencies, see 43 | # https://flutter.dev/assets-and-images/#from-packages 44 | 45 | # To add custom fonts to your application, add a fonts section here, 46 | # in this "flutter" section. Each entry in this list should have a 47 | # "family" key with the font family name, and a "fonts" key with a 48 | # list giving the asset and other descriptors for the font. For 49 | # example: 50 | # fonts: 51 | # - family: Schyler 52 | # fonts: 53 | # - asset: fonts/Schyler-Regular.ttf 54 | # - asset: fonts/Schyler-Italic.ttf 55 | # style: italic 56 | # - family: Trajan Pro 57 | # fonts: 58 | # - asset: fonts/TrajanPro.ttf 59 | # - asset: fonts/TrajanPro_Bold.ttf 60 | # weight: 700 61 | # 62 | # For details regarding fonts from package dependencies, 63 | # see https://flutter.dev/custom-fonts/#from-packages 64 | -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:agora_rtm_example/main.dart'; 9 | import 'package:flutter/material.dart'; 10 | import 'package:flutter_test/flutter_test.dart'; 11 | 12 | void main() { 13 | testWidgets('Verify Platform version', (WidgetTester tester) async { 14 | // Build our app and trigger a frame. 15 | await tester.pumpWidget(MyApp()); 16 | 17 | // Verify that platform version is retrieved. 18 | expect( 19 | find.byWidgetPredicate( 20 | (Widget widget) => 21 | widget is Text && widget.data.startsWith('Running on:'), 22 | ), 23 | findsOneWidget, 24 | ); 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /example/windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /example/windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | project(agora_rtm_example LANGUAGES CXX) 3 | 4 | set(BINARY_NAME "agora_rtm_example") 5 | 6 | cmake_policy(SET CMP0063 NEW) 7 | 8 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") 9 | 10 | # Configure build options. 11 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 12 | if(IS_MULTICONFIG) 13 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" 14 | CACHE STRING "" FORCE) 15 | else() 16 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 17 | set(CMAKE_BUILD_TYPE "Debug" CACHE 18 | STRING "Flutter build mode" FORCE) 19 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 20 | "Debug" "Profile" "Release") 21 | endif() 22 | endif() 23 | 24 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") 25 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") 26 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") 27 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") 28 | 29 | # Use Unicode for all projects. 30 | add_definitions(-DUNICODE -D_UNICODE) 31 | 32 | # Compilation settings that should be applied to most targets. 33 | function(APPLY_STANDARD_SETTINGS TARGET) 34 | target_compile_features(${TARGET} PUBLIC cxx_std_17) 35 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") 36 | target_compile_options(${TARGET} PRIVATE /EHsc) 37 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") 38 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") 39 | endfunction() 40 | 41 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 42 | 43 | # Flutter library and tool build rules. 44 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 45 | 46 | # Application build 47 | add_subdirectory("runner") 48 | 49 | # Generated plugin build rules, which manage building the plugins and adding 50 | # them to the application. 51 | include(flutter/generated_plugins.cmake) 52 | 53 | 54 | # === Installation === 55 | # Support files are copied into place next to the executable, so that it can 56 | # run in place. This is done instead of making a separate bundle (as on Linux) 57 | # so that building and running from within Visual Studio will work. 58 | set(BUILD_BUNDLE_DIR "$") 59 | # Make the "install" step default, as it's required to run. 60 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) 61 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 62 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 63 | endif() 64 | 65 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 66 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") 67 | 68 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 69 | COMPONENT Runtime) 70 | 71 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 72 | COMPONENT Runtime) 73 | 74 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 75 | COMPONENT Runtime) 76 | 77 | if(PLUGIN_BUNDLED_LIBRARIES) 78 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 79 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 80 | COMPONENT Runtime) 81 | endif() 82 | 83 | # Fully re-copy the assets directory on each build to avoid having stale files 84 | # from a previous install. 85 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 86 | install(CODE " 87 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 88 | " COMPONENT Runtime) 89 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 90 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 91 | 92 | # Install the AOT library on non-Debug builds only. 93 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 94 | CONFIGURATIONS Profile;Release 95 | COMPONENT Runtime) 96 | -------------------------------------------------------------------------------- /example/windows/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 4 | 5 | # Configuration provided via flutter tool. 6 | include(${EPHEMERAL_DIR}/generated_config.cmake) 7 | 8 | # TODO: Move the rest of this into files in ephemeral. See 9 | # https://github.com/flutter/flutter/issues/57146. 10 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") 11 | 12 | # === Flutter Library === 13 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") 14 | 15 | # Published to parent scope for install step. 16 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 17 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 18 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 19 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) 20 | 21 | list(APPEND FLUTTER_LIBRARY_HEADERS 22 | "flutter_export.h" 23 | "flutter_windows.h" 24 | "flutter_messenger.h" 25 | "flutter_plugin_registrar.h" 26 | ) 27 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") 28 | add_library(flutter INTERFACE) 29 | target_include_directories(flutter INTERFACE 30 | "${EPHEMERAL_DIR}" 31 | ) 32 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") 33 | add_dependencies(flutter flutter_assemble) 34 | 35 | # === Wrapper === 36 | list(APPEND CPP_WRAPPER_SOURCES_CORE 37 | "core_implementations.cc" 38 | "standard_codec.cc" 39 | ) 40 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") 41 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN 42 | "plugin_registrar.cc" 43 | ) 44 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") 45 | list(APPEND CPP_WRAPPER_SOURCES_APP 46 | "flutter_engine.cc" 47 | "flutter_view_controller.cc" 48 | ) 49 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") 50 | 51 | # Wrapper sources needed for a plugin. 52 | add_library(flutter_wrapper_plugin STATIC 53 | ${CPP_WRAPPER_SOURCES_CORE} 54 | ${CPP_WRAPPER_SOURCES_PLUGIN} 55 | ) 56 | apply_standard_settings(flutter_wrapper_plugin) 57 | set_target_properties(flutter_wrapper_plugin PROPERTIES 58 | POSITION_INDEPENDENT_CODE ON) 59 | set_target_properties(flutter_wrapper_plugin PROPERTIES 60 | CXX_VISIBILITY_PRESET hidden) 61 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) 62 | target_include_directories(flutter_wrapper_plugin PUBLIC 63 | "${WRAPPER_ROOT}/include" 64 | ) 65 | add_dependencies(flutter_wrapper_plugin flutter_assemble) 66 | 67 | # Wrapper sources needed for the runner. 68 | add_library(flutter_wrapper_app STATIC 69 | ${CPP_WRAPPER_SOURCES_CORE} 70 | ${CPP_WRAPPER_SOURCES_APP} 71 | ) 72 | apply_standard_settings(flutter_wrapper_app) 73 | target_link_libraries(flutter_wrapper_app PUBLIC flutter) 74 | target_include_directories(flutter_wrapper_app PUBLIC 75 | "${WRAPPER_ROOT}/include" 76 | ) 77 | add_dependencies(flutter_wrapper_app flutter_assemble) 78 | 79 | # === Flutter tool backend === 80 | # _phony_ is a non-existent file to force this command to run every time, 81 | # since currently there's no way to get a full input/output list from the 82 | # flutter tool. 83 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") 84 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) 85 | add_custom_command( 86 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 87 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} 88 | ${CPP_WRAPPER_SOURCES_APP} 89 | ${PHONY_OUTPUT} 90 | COMMAND ${CMAKE_COMMAND} -E env 91 | ${FLUTTER_TOOL_ENVIRONMENT} 92 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" 93 | windows-x64 $ 94 | VERBATIM 95 | ) 96 | add_custom_target(flutter_assemble DEPENDS 97 | "${FLUTTER_LIBRARY}" 98 | ${FLUTTER_LIBRARY_HEADERS} 99 | ${CPP_WRAPPER_SOURCES_CORE} 100 | ${CPP_WRAPPER_SOURCES_PLUGIN} 101 | ${CPP_WRAPPER_SOURCES_APP} 102 | ) 103 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | #include "generated_plugin_registrant.h" 6 | 7 | #include 8 | 9 | void RegisterPlugins(flutter::PluginRegistry* registry) { 10 | AgoraRtmPluginRegisterWithRegistrar( 11 | registry->GetRegistrarForPlugin("AgoraRtmPlugin")); 12 | } 13 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 6 | #define GENERATED_PLUGIN_REGISTRANT_ 7 | 8 | #include 9 | 10 | // Registers Flutter plugins. 11 | void RegisterPlugins(flutter::PluginRegistry* registry); 12 | 13 | #endif // GENERATED_PLUGIN_REGISTRANT_ 14 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | agora_rtm 7 | ) 8 | 9 | set(PLUGIN_BUNDLED_LIBRARIES) 10 | 11 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 12 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 13 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 14 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 15 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 16 | endforeach(plugin) 17 | -------------------------------------------------------------------------------- /example/windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | project(runner LANGUAGES CXX) 3 | 4 | add_executable(${BINARY_NAME} WIN32 5 | "flutter_window.cpp" 6 | "main.cpp" 7 | "run_loop.cpp" 8 | "utils.cpp" 9 | "win32_window.cpp" 10 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 11 | "Runner.rc" 12 | "runner.exe.manifest" 13 | ) 14 | apply_standard_settings(${BINARY_NAME}) 15 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 16 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 17 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 18 | add_dependencies(${BINARY_NAME} flutter_assemble) 19 | -------------------------------------------------------------------------------- /example/windows/runner/Runner.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | #include "resource.h" 5 | 6 | #define APSTUDIO_READONLY_SYMBOLS 7 | ///////////////////////////////////////////////////////////////////////////// 8 | // 9 | // Generated from the TEXTINCLUDE 2 resource. 10 | // 11 | #include "winres.h" 12 | 13 | ///////////////////////////////////////////////////////////////////////////// 14 | #undef APSTUDIO_READONLY_SYMBOLS 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | // English (United States) resources 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_APP_ICON ICON "resources\\app_icon.ico" 56 | 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Version 61 | // 62 | 63 | #ifdef FLUTTER_BUILD_NUMBER 64 | #define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER 65 | #else 66 | #define VERSION_AS_NUMBER 1,0,0 67 | #endif 68 | 69 | #ifdef FLUTTER_BUILD_NAME 70 | #define VERSION_AS_STRING #FLUTTER_BUILD_NAME 71 | #else 72 | #define VERSION_AS_STRING "1.0.0" 73 | #endif 74 | 75 | VS_VERSION_INFO VERSIONINFO 76 | FILEVERSION VERSION_AS_NUMBER 77 | PRODUCTVERSION VERSION_AS_NUMBER 78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 79 | #ifdef _DEBUG 80 | FILEFLAGS VS_FF_DEBUG 81 | #else 82 | FILEFLAGS 0x0L 83 | #endif 84 | FILEOS VOS__WINDOWS32 85 | FILETYPE VFT_APP 86 | FILESUBTYPE 0x0L 87 | BEGIN 88 | BLOCK "StringFileInfo" 89 | BEGIN 90 | BLOCK "040904e4" 91 | BEGIN 92 | VALUE "CompanyName", "com.example" "\0" 93 | VALUE "FileDescription", "Demonstrates how to use the agora_rtm plugin." "\0" 94 | VALUE "FileVersion", VERSION_AS_STRING "\0" 95 | VALUE "InternalName", "agora_rtm_example" "\0" 96 | VALUE "LegalCopyright", "Copyright (C) 2020 com.example. All rights reserved." "\0" 97 | VALUE "OriginalFilename", "agora_rtm_example.exe" "\0" 98 | VALUE "ProductName", "agora_rtm_example" "\0" 99 | VALUE "ProductVersion", VERSION_AS_STRING "\0" 100 | END 101 | END 102 | BLOCK "VarFileInfo" 103 | BEGIN 104 | VALUE "Translation", 0x409, 1252 105 | END 106 | END 107 | 108 | #endif // English (United States) resources 109 | ///////////////////////////////////////////////////////////////////////////// 110 | 111 | 112 | 113 | #ifndef APSTUDIO_INVOKED 114 | ///////////////////////////////////////////////////////////////////////////// 115 | // 116 | // Generated from the TEXTINCLUDE 3 resource. 117 | // 118 | 119 | 120 | ///////////////////////////////////////////////////////////////////////////// 121 | #endif // not APSTUDIO_INVOKED 122 | -------------------------------------------------------------------------------- /example/windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(RunLoop* run_loop, 8 | const flutter::DartProject& project) 9 | : run_loop_(run_loop), project_(project) {} 10 | 11 | FlutterWindow::~FlutterWindow() {} 12 | 13 | bool FlutterWindow::OnCreate() { 14 | if (!Win32Window::OnCreate()) { 15 | return false; 16 | } 17 | 18 | RECT frame = GetClientArea(); 19 | 20 | // The size here must match the window dimensions to avoid unnecessary surface 21 | // creation / destruction in the startup path. 22 | flutter_controller_ = std::make_unique( 23 | frame.right - frame.left, frame.bottom - frame.top, project_); 24 | // Ensure that basic setup of the controller was successful. 25 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 26 | return false; 27 | } 28 | RegisterPlugins(flutter_controller_->engine()); 29 | run_loop_->RegisterFlutterInstance(flutter_controller_->engine()); 30 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 31 | return true; 32 | } 33 | 34 | void FlutterWindow::OnDestroy() { 35 | if (flutter_controller_) { 36 | run_loop_->UnregisterFlutterInstance(flutter_controller_->engine()); 37 | flutter_controller_ = nullptr; 38 | } 39 | 40 | Win32Window::OnDestroy(); 41 | } 42 | 43 | LRESULT 44 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 45 | WPARAM const wparam, 46 | LPARAM const lparam) noexcept { 47 | // Give Flutter, including plugins, an opporutunity to handle window messages. 48 | if (flutter_controller_) { 49 | std::optional result = 50 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 51 | lparam); 52 | if (result) { 53 | return *result; 54 | } 55 | } 56 | 57 | switch (message) { 58 | case WM_FONTCHANGE: 59 | flutter_controller_->engine()->ReloadSystemFonts(); 60 | break; 61 | } 62 | 63 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 64 | } 65 | -------------------------------------------------------------------------------- /example/windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "run_loop.h" 10 | #include "win32_window.h" 11 | 12 | // A window that does nothing but host a Flutter view. 13 | class FlutterWindow : public Win32Window { 14 | public: 15 | // Creates a new FlutterWindow driven by the |run_loop|, hosting a 16 | // Flutter view running |project|. 17 | explicit FlutterWindow(RunLoop* run_loop, 18 | const flutter::DartProject& project); 19 | virtual ~FlutterWindow(); 20 | 21 | protected: 22 | // Win32Window: 23 | bool OnCreate() override; 24 | void OnDestroy() override; 25 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 26 | LPARAM const lparam) noexcept override; 27 | 28 | private: 29 | // The run loop driving events for this window. 30 | RunLoop* run_loop_; 31 | 32 | // The project to run. 33 | flutter::DartProject project_; 34 | 35 | // The Flutter instance hosted by this window. 36 | std::unique_ptr flutter_controller_; 37 | }; 38 | 39 | #endif // RUNNER_FLUTTER_WINDOW_H_ 40 | -------------------------------------------------------------------------------- /example/windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "run_loop.h" 7 | #include "utils.h" 8 | 9 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 10 | _In_ wchar_t *command_line, _In_ int show_command) { 11 | // Attach to console when present (e.g., 'flutter run') or create a 12 | // new console when running with a debugger. 13 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 14 | CreateAndAttachConsole(); 15 | } 16 | 17 | // Initialize COM, so that it is available for use in the library and/or 18 | // plugins. 19 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 20 | 21 | RunLoop run_loop; 22 | 23 | flutter::DartProject project(L"data"); 24 | FlutterWindow window(&run_loop, project); 25 | Win32Window::Point origin(10, 10); 26 | Win32Window::Size size(1280, 720); 27 | if (!window.CreateAndShow(L"agora_rtm_example", origin, size)) { 28 | return EXIT_FAILURE; 29 | } 30 | window.SetQuitOnClose(true); 31 | 32 | run_loop.Run(); 33 | 34 | ::CoUninitialize(); 35 | return EXIT_SUCCESS; 36 | } 37 | -------------------------------------------------------------------------------- /example/windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /example/windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woodemi/Flutter-RTM-Desktop/33b9259d353b20be6c4f9b7c95c9d38df2df3cea/example/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /example/windows/runner/run_loop.cpp: -------------------------------------------------------------------------------- 1 | #include "run_loop.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | RunLoop::RunLoop() {} 8 | 9 | RunLoop::~RunLoop() {} 10 | 11 | void RunLoop::Run() { 12 | bool keep_running = true; 13 | TimePoint next_flutter_event_time = TimePoint::clock::now(); 14 | while (keep_running) { 15 | std::chrono::nanoseconds wait_duration = 16 | std::max(std::chrono::nanoseconds(0), 17 | next_flutter_event_time - TimePoint::clock::now()); 18 | ::MsgWaitForMultipleObjects( 19 | 0, nullptr, FALSE, static_cast(wait_duration.count() / 1000), 20 | QS_ALLINPUT); 21 | bool processed_events = false; 22 | MSG message; 23 | // All pending Windows messages must be processed; MsgWaitForMultipleObjects 24 | // won't return again for items left in the queue after PeekMessage. 25 | while (::PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) { 26 | processed_events = true; 27 | if (message.message == WM_QUIT) { 28 | keep_running = false; 29 | break; 30 | } 31 | ::TranslateMessage(&message); 32 | ::DispatchMessage(&message); 33 | // Allow Flutter to process messages each time a Windows message is 34 | // processed, to prevent starvation. 35 | next_flutter_event_time = 36 | std::min(next_flutter_event_time, ProcessFlutterMessages()); 37 | } 38 | // If the PeekMessage loop didn't run, process Flutter messages. 39 | if (!processed_events) { 40 | next_flutter_event_time = 41 | std::min(next_flutter_event_time, ProcessFlutterMessages()); 42 | } 43 | } 44 | } 45 | 46 | void RunLoop::RegisterFlutterInstance( 47 | flutter::FlutterEngine* flutter_instance) { 48 | flutter_instances_.insert(flutter_instance); 49 | } 50 | 51 | void RunLoop::UnregisterFlutterInstance( 52 | flutter::FlutterEngine* flutter_instance) { 53 | flutter_instances_.erase(flutter_instance); 54 | } 55 | 56 | RunLoop::TimePoint RunLoop::ProcessFlutterMessages() { 57 | TimePoint next_event_time = TimePoint::max(); 58 | for (auto instance : flutter_instances_) { 59 | std::chrono::nanoseconds wait_duration = instance->ProcessMessages(); 60 | if (wait_duration != std::chrono::nanoseconds::max()) { 61 | next_event_time = 62 | std::min(next_event_time, TimePoint::clock::now() + wait_duration); 63 | } 64 | } 65 | return next_event_time; 66 | } 67 | -------------------------------------------------------------------------------- /example/windows/runner/run_loop.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_RUN_LOOP_H_ 2 | #define RUNNER_RUN_LOOP_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | // A runloop that will service events for Flutter instances as well 10 | // as native messages. 11 | class RunLoop { 12 | public: 13 | RunLoop(); 14 | ~RunLoop(); 15 | 16 | // Prevent copying 17 | RunLoop(RunLoop const&) = delete; 18 | RunLoop& operator=(RunLoop const&) = delete; 19 | 20 | // Runs the run loop until the application quits. 21 | void Run(); 22 | 23 | // Registers the given Flutter instance for event servicing. 24 | void RegisterFlutterInstance( 25 | flutter::FlutterEngine* flutter_instance); 26 | 27 | // Unregisters the given Flutter instance from event servicing. 28 | void UnregisterFlutterInstance( 29 | flutter::FlutterEngine* flutter_instance); 30 | 31 | private: 32 | using TimePoint = std::chrono::steady_clock::time_point; 33 | 34 | // Processes all currently pending messages for registered Flutter instances. 35 | TimePoint ProcessFlutterMessages(); 36 | 37 | std::set flutter_instances_; 38 | }; 39 | 40 | #endif // RUNNER_RUN_LOOP_H_ 41 | -------------------------------------------------------------------------------- /example/windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /example/windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /example/windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | // Creates a console for the process, and redirects stdout and stderr to 5 | // it for both the runner and the Flutter library. 6 | void CreateAndAttachConsole(); 7 | 8 | #endif // RUNNER_UTILS_H_ 9 | -------------------------------------------------------------------------------- /example/windows/runner/win32_window.cpp: -------------------------------------------------------------------------------- 1 | #include "win32_window.h" 2 | 3 | #include 4 | 5 | #include "resource.h" 6 | 7 | namespace { 8 | 9 | constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; 10 | 11 | // The number of Win32Window objects that currently exist. 12 | static int g_active_window_count = 0; 13 | 14 | using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); 15 | 16 | // Scale helper to convert logical scaler values to physical using passed in 17 | // scale factor 18 | int Scale(int source, double scale_factor) { 19 | return static_cast(source * scale_factor); 20 | } 21 | 22 | // Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. 23 | // This API is only needed for PerMonitor V1 awareness mode. 24 | void EnableFullDpiSupportIfAvailable(HWND hwnd) { 25 | HMODULE user32_module = LoadLibraryA("User32.dll"); 26 | if (!user32_module) { 27 | return; 28 | } 29 | auto enable_non_client_dpi_scaling = 30 | reinterpret_cast( 31 | GetProcAddress(user32_module, "EnableNonClientDpiScaling")); 32 | if (enable_non_client_dpi_scaling != nullptr) { 33 | enable_non_client_dpi_scaling(hwnd); 34 | FreeLibrary(user32_module); 35 | } 36 | } 37 | 38 | } // namespace 39 | 40 | // Manages the Win32Window's window class registration. 41 | class WindowClassRegistrar { 42 | public: 43 | ~WindowClassRegistrar() = default; 44 | 45 | // Returns the singleton registar instance. 46 | static WindowClassRegistrar* GetInstance() { 47 | if (!instance_) { 48 | instance_ = new WindowClassRegistrar(); 49 | } 50 | return instance_; 51 | } 52 | 53 | // Returns the name of the window class, registering the class if it hasn't 54 | // previously been registered. 55 | const wchar_t* GetWindowClass(); 56 | 57 | // Unregisters the window class. Should only be called if there are no 58 | // instances of the window. 59 | void UnregisterWindowClass(); 60 | 61 | private: 62 | WindowClassRegistrar() = default; 63 | 64 | static WindowClassRegistrar* instance_; 65 | 66 | bool class_registered_ = false; 67 | }; 68 | 69 | WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; 70 | 71 | const wchar_t* WindowClassRegistrar::GetWindowClass() { 72 | if (!class_registered_) { 73 | WNDCLASS window_class{}; 74 | window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); 75 | window_class.lpszClassName = kWindowClassName; 76 | window_class.style = CS_HREDRAW | CS_VREDRAW; 77 | window_class.cbClsExtra = 0; 78 | window_class.cbWndExtra = 0; 79 | window_class.hInstance = GetModuleHandle(nullptr); 80 | window_class.hIcon = 81 | LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); 82 | window_class.hbrBackground = 0; 83 | window_class.lpszMenuName = nullptr; 84 | window_class.lpfnWndProc = Win32Window::WndProc; 85 | RegisterClass(&window_class); 86 | class_registered_ = true; 87 | } 88 | return kWindowClassName; 89 | } 90 | 91 | void WindowClassRegistrar::UnregisterWindowClass() { 92 | UnregisterClass(kWindowClassName, nullptr); 93 | class_registered_ = false; 94 | } 95 | 96 | Win32Window::Win32Window() { 97 | ++g_active_window_count; 98 | } 99 | 100 | Win32Window::~Win32Window() { 101 | --g_active_window_count; 102 | Destroy(); 103 | } 104 | 105 | bool Win32Window::CreateAndShow(const std::wstring& title, 106 | const Point& origin, 107 | const Size& size) { 108 | Destroy(); 109 | 110 | const wchar_t* window_class = 111 | WindowClassRegistrar::GetInstance()->GetWindowClass(); 112 | 113 | const POINT target_point = {static_cast(origin.x), 114 | static_cast(origin.y)}; 115 | HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); 116 | UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); 117 | double scale_factor = dpi / 96.0; 118 | 119 | HWND window = CreateWindow( 120 | window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 121 | Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), 122 | Scale(size.width, scale_factor), Scale(size.height, scale_factor), 123 | nullptr, nullptr, GetModuleHandle(nullptr), this); 124 | 125 | if (!window) { 126 | return false; 127 | } 128 | 129 | return OnCreate(); 130 | } 131 | 132 | // static 133 | LRESULT CALLBACK Win32Window::WndProc(HWND const window, 134 | UINT const message, 135 | WPARAM const wparam, 136 | LPARAM const lparam) noexcept { 137 | if (message == WM_NCCREATE) { 138 | auto window_struct = reinterpret_cast(lparam); 139 | SetWindowLongPtr(window, GWLP_USERDATA, 140 | reinterpret_cast(window_struct->lpCreateParams)); 141 | 142 | auto that = static_cast(window_struct->lpCreateParams); 143 | EnableFullDpiSupportIfAvailable(window); 144 | that->window_handle_ = window; 145 | } else if (Win32Window* that = GetThisFromHandle(window)) { 146 | return that->MessageHandler(window, message, wparam, lparam); 147 | } 148 | 149 | return DefWindowProc(window, message, wparam, lparam); 150 | } 151 | 152 | LRESULT 153 | Win32Window::MessageHandler(HWND hwnd, 154 | UINT const message, 155 | WPARAM const wparam, 156 | LPARAM const lparam) noexcept { 157 | switch (message) { 158 | case WM_DESTROY: 159 | window_handle_ = nullptr; 160 | Destroy(); 161 | if (quit_on_close_) { 162 | PostQuitMessage(0); 163 | } 164 | return 0; 165 | 166 | case WM_DPICHANGED: { 167 | auto newRectSize = reinterpret_cast(lparam); 168 | LONG newWidth = newRectSize->right - newRectSize->left; 169 | LONG newHeight = newRectSize->bottom - newRectSize->top; 170 | 171 | SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, 172 | newHeight, SWP_NOZORDER | SWP_NOACTIVATE); 173 | 174 | return 0; 175 | } 176 | case WM_SIZE: 177 | RECT rect = GetClientArea(); 178 | if (child_content_ != nullptr) { 179 | // Size and position the child window. 180 | MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, 181 | rect.bottom - rect.top, TRUE); 182 | } 183 | return 0; 184 | 185 | case WM_ACTIVATE: 186 | if (child_content_ != nullptr) { 187 | SetFocus(child_content_); 188 | } 189 | return 0; 190 | } 191 | 192 | return DefWindowProc(window_handle_, message, wparam, lparam); 193 | } 194 | 195 | void Win32Window::Destroy() { 196 | OnDestroy(); 197 | 198 | if (window_handle_) { 199 | DestroyWindow(window_handle_); 200 | window_handle_ = nullptr; 201 | } 202 | if (g_active_window_count == 0) { 203 | WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); 204 | } 205 | } 206 | 207 | Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { 208 | return reinterpret_cast( 209 | GetWindowLongPtr(window, GWLP_USERDATA)); 210 | } 211 | 212 | void Win32Window::SetChildContent(HWND content) { 213 | child_content_ = content; 214 | SetParent(content, window_handle_); 215 | RECT frame = GetClientArea(); 216 | 217 | MoveWindow(content, frame.left, frame.top, frame.right - frame.left, 218 | frame.bottom - frame.top, true); 219 | 220 | SetFocus(child_content_); 221 | } 222 | 223 | RECT Win32Window::GetClientArea() { 224 | RECT frame; 225 | GetClientRect(window_handle_, &frame); 226 | return frame; 227 | } 228 | 229 | HWND Win32Window::GetHandle() { 230 | return window_handle_; 231 | } 232 | 233 | void Win32Window::SetQuitOnClose(bool quit_on_close) { 234 | quit_on_close_ = quit_on_close; 235 | } 236 | 237 | bool Win32Window::OnCreate() { 238 | // No-op; provided for subclasses. 239 | return true; 240 | } 241 | 242 | void Win32Window::OnDestroy() { 243 | // No-op; provided for subclasses. 244 | } 245 | -------------------------------------------------------------------------------- /example/windows/runner/win32_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_WIN32_WINDOW_H_ 2 | #define RUNNER_WIN32_WINDOW_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // A class abstraction for a high DPI-aware Win32 Window. Intended to be 11 | // inherited from by classes that wish to specialize with custom 12 | // rendering and input handling 13 | class Win32Window { 14 | public: 15 | struct Point { 16 | unsigned int x; 17 | unsigned int y; 18 | Point(unsigned int x, unsigned int y) : x(x), y(y) {} 19 | }; 20 | 21 | struct Size { 22 | unsigned int width; 23 | unsigned int height; 24 | Size(unsigned int width, unsigned int height) 25 | : width(width), height(height) {} 26 | }; 27 | 28 | Win32Window(); 29 | virtual ~Win32Window(); 30 | 31 | // Creates and shows a win32 window with |title| and position and size using 32 | // |origin| and |size|. New windows are created on the default monitor. Window 33 | // sizes are specified to the OS in physical pixels, hence to ensure a 34 | // consistent size to will treat the width height passed in to this function 35 | // as logical pixels and scale to appropriate for the default monitor. Returns 36 | // true if the window was created successfully. 37 | bool CreateAndShow(const std::wstring& title, 38 | const Point& origin, 39 | const Size& size); 40 | 41 | // Release OS resources associated with window. 42 | void Destroy(); 43 | 44 | // Inserts |content| into the window tree. 45 | void SetChildContent(HWND content); 46 | 47 | // Returns the backing Window handle to enable clients to set icon and other 48 | // window properties. Returns nullptr if the window has been destroyed. 49 | HWND GetHandle(); 50 | 51 | // If true, closing this window will quit the application. 52 | void SetQuitOnClose(bool quit_on_close); 53 | 54 | // Return a RECT representing the bounds of the current client area. 55 | RECT GetClientArea(); 56 | 57 | protected: 58 | // Processes and route salient window messages for mouse handling, 59 | // size change and DPI. Delegates handling of these to member overloads that 60 | // inheriting classes can handle. 61 | virtual LRESULT MessageHandler(HWND window, 62 | UINT const message, 63 | WPARAM const wparam, 64 | LPARAM const lparam) noexcept; 65 | 66 | // Called when CreateAndShow is called, allowing subclass window-related 67 | // setup. Subclasses should return false if setup fails. 68 | virtual bool OnCreate(); 69 | 70 | // Called when Destroy is called. 71 | virtual void OnDestroy(); 72 | 73 | private: 74 | friend class WindowClassRegistrar; 75 | 76 | // OS callback called by message pump. Handles the WM_NCCREATE message which 77 | // is passed when the non-client area is being created and enables automatic 78 | // non-client DPI scaling so that the non-client area automatically 79 | // responsponds to changes in DPI. All other messages are handled by 80 | // MessageHandler. 81 | static LRESULT CALLBACK WndProc(HWND const window, 82 | UINT const message, 83 | WPARAM const wparam, 84 | LPARAM const lparam) noexcept; 85 | 86 | // Retrieves a class instance pointer for |window| 87 | static Win32Window* GetThisFromHandle(HWND const window) noexcept; 88 | 89 | bool quit_on_close_ = false; 90 | 91 | // window handle for top level window. 92 | HWND window_handle_ = nullptr; 93 | 94 | // window handle for hosted content. 95 | HWND child_content_ = nullptr; 96 | }; 97 | 98 | #endif // RUNNER_WIN32_WINDOW_H_ 99 | -------------------------------------------------------------------------------- /lib/agora_rtm.dart: -------------------------------------------------------------------------------- 1 | export 'src/agora_rtm_channel.dart'; 2 | export 'src/agora_rtm_client.dart'; 3 | export 'src/utils.dart'; -------------------------------------------------------------------------------- /lib/src/agora_rtm_channel.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/services.dart'; 4 | 5 | import 'agora_rtm_plugin.dart'; 6 | import 'utils.dart'; 7 | 8 | class AgoraRtmChannelException implements Exception { 9 | final reason; 10 | final code; 11 | 12 | AgoraRtmChannelException(this.reason, this.code) : super(); 13 | 14 | Map toJson() => {"reason": reason, "code": code}; 15 | 16 | @override 17 | String toString() { 18 | return this.reason; 19 | } 20 | } 21 | 22 | class AgoraRtmChannel { 23 | /// Occurs when you receive error events. 24 | void Function(dynamic error) onError; 25 | 26 | /// Occurs when receiving a channel message. 27 | void Function(AgoraRtmMessage message, AgoraRtmMember fromMember) 28 | onMessageReceived; 29 | 30 | /// Occurs when a user joins the channel. 31 | void Function(AgoraRtmMember member) onMemberJoined; 32 | 33 | /// Occurs when a channel member leaves the channel. 34 | void Function(AgoraRtmMember member) onMemberLeft; 35 | 36 | final String channelId; 37 | final int _clientIndex; 38 | 39 | bool _closed; 40 | 41 | BasicMessageChannel messageChannel; 42 | 43 | Future _eventListener(dynamic event) async { 44 | final Map map = event; 45 | switch (map['event']) { 46 | case 'onMessageReceived': 47 | AgoraRtmMessage message = AgoraRtmMessage.fromJson(map['message']); 48 | AgoraRtmMember member = AgoraRtmMember.fromJson(map); 49 | this?.onMessageReceived(message, member); 50 | break; 51 | case 'onMemberJoined': 52 | AgoraRtmMember member = AgoraRtmMember.fromJson(map); 53 | this?.onMemberJoined(member); 54 | break; 55 | case 'onMemberLeft': 56 | AgoraRtmMember member = AgoraRtmMember.fromJson(map); 57 | this?.onMemberLeft(member); 58 | } 59 | } 60 | 61 | AgoraRtmChannel(this._clientIndex, this.channelId) { 62 | _closed = false; 63 | messageChannel = new BasicMessageChannel( 64 | 'io.agora.rtm.client$_clientIndex.channel$channelId', 65 | StandardMessageCodec()); 66 | messageChannel.setMessageHandler(_eventListener); 67 | } 68 | 69 | Future _callNative(String methodName, dynamic arguments) { 70 | return AgoraRtmPlugin.callMethodForChannel(methodName, { 71 | 'clientIndex': _clientIndex, 72 | 'channelId': channelId, 73 | 'args': arguments 74 | }); 75 | } 76 | 77 | Future join() async { 78 | final res = await _callNative("join", null); 79 | if (res["errorCode"] != 0) 80 | throw AgoraRtmChannelException( 81 | "join failed errorCode:${res['errorCode']}", res['errorCode']); 82 | } 83 | 84 | Future sendMessage(AgoraRtmMessage message) async { 85 | final res = await _callNative("sendMessage", {'message': message.text}); 86 | if (res["errorCode"] != 0) 87 | throw AgoraRtmChannelException( 88 | "sendMessage failed errorCode:${res['errorCode']}", res['errorCode']); 89 | } 90 | 91 | Future leave() async { 92 | final res = await _callNative("leave", null); 93 | if (res["errorCode"] != 0) 94 | throw AgoraRtmChannelException( 95 | "leave failed errorCode:${res['errorCode']}", res['errorCode']); 96 | } 97 | 98 | Future close() async { 99 | if (_closed) return null; 100 | messageChannel.setMessageHandler(null); 101 | _closed = true; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /lib/src/agora_rtm_client.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/services.dart'; 4 | 5 | import 'agora_rtm_channel.dart'; 6 | import 'agora_rtm_plugin.dart'; 7 | 8 | class AgoraRtmClientException implements Exception { 9 | final reason; 10 | final code; 11 | 12 | AgoraRtmClientException(this.reason, this.code) : super(); 13 | 14 | Map toJson() => {"reason": reason, "code": code}; 15 | 16 | @override 17 | String toString() { 18 | return this.reason; 19 | } 20 | } 21 | 22 | class AgoraMessageHandler { 23 | final _messageController = StreamController(); 24 | 25 | Stream get stream => _messageController.stream; 26 | 27 | Future handleMessage(dynamic message) async { 28 | _messageController.add(message); 29 | } 30 | } 31 | 32 | class AgoraRtmClient { 33 | static var _clients = {}; 34 | 35 | /// Initializes an [AgoraRtmClient] instance 36 | /// 37 | /// The Agora RTM SDK supports multiple [AgoraRtmClient] instances. 38 | static Future createInstance(String appId) async { 39 | final res = await AgoraRtmPlugin.callMethodForStatic( 40 | "createInstance", {'appId': appId}); 41 | if (res["errorCode"] != 0) 42 | throw AgoraRtmClientException( 43 | "Create client failed errorCode:${res['errorCode']}", 44 | res['errorCode']); 45 | final index = res['index']; 46 | AgoraRtmClient client = AgoraRtmClient._(index); 47 | _clients[index] = client; 48 | return _clients[index]; 49 | } 50 | 51 | /// Occurs when the connection state between the SDK and the Agora RTM system changes. 52 | void Function(int state, int reason) onConnectionStateChanged; 53 | 54 | /// Occurs when you receive error events. 55 | void Function() onError; 56 | 57 | var _channels = {}; 58 | 59 | bool _closed; 60 | 61 | final int _clientIndex; 62 | StreamSubscription _clientSubscription; 63 | 64 | // FIXME Windows `EventChannel` not implemented yet 65 | Stream _createEventStream(int clientIndex) { 66 | var messageHandler = AgoraMessageHandler(); 67 | BasicMessageChannel( 68 | 'io.agora.rtm.client$clientIndex', StandardMessageCodec()) 69 | .setMessageHandler(messageHandler.handleMessage); 70 | return messageHandler.stream; 71 | } 72 | 73 | _eventListener(dynamic event) { 74 | final Map map = event; 75 | switch (map['event']) { 76 | case 'onConnectionStateChanged': 77 | int state = map['state']; 78 | int reason = map['reason']; 79 | this?.onConnectionStateChanged(state, reason); 80 | break; 81 | } 82 | } 83 | 84 | AgoraRtmClient._(this._clientIndex) { 85 | _closed = false; 86 | _clientSubscription = _createEventStream(_clientIndex) 87 | .listen(_eventListener, onError: onError); 88 | } 89 | 90 | Future _callNative(String methodName, dynamic arguments) { 91 | return AgoraRtmPlugin.callMethodForClient( 92 | methodName, {'clientIndex': _clientIndex, 'args': arguments}); 93 | } 94 | 95 | /// Allows a user to log in the Agora RTM system. 96 | /// 97 | /// The string length of userId must be less than 64 bytes with the following character scope: 98 | /// - The 26 lowercase English letters: a to z 99 | /// - The 26 uppercase English letters: A to Z 100 | /// - The 10 numbers: 0 to 9 101 | /// - Space 102 | /// - "!", "#", "$", "%", "&", "(", ")", "+", "-", ":", ";", "<", "=", ".", ">", "?", "@", "]", "[", "^", "_", " {", "}", "|", "~", "," 103 | /// Do not set userId as null and do not start with a space. 104 | /// If you log in with the same user ID from a different instance, you will be kicked out of your previous login and removed from previously joined channels. 105 | Future login(String token, String userId) async { 106 | final res = await _callNative("login", {'token': token, 'userId': userId}); 107 | if (res["errorCode"] != 0) 108 | throw AgoraRtmClientException( 109 | "login failed errorCode:${res['errorCode']}", res['errorCode']); 110 | } 111 | 112 | /// Allows a user to log out of the Agora RTM system. 113 | Future logout() async { 114 | final res = await _callNative("logout", null); 115 | if (res["errorCode"] != 0) 116 | throw AgoraRtmClientException( 117 | "logout failed errorCode:${res['errorCode']}", res['errorCode']); 118 | } 119 | 120 | /// Creates an [AgoraRtmChannel]. 121 | /// 122 | /// channelId is the unique channel name of the Agora RTM session. The string length must not exceed 64 bytes with the following character scope: 123 | /// - The 26 lowercase English letters: a to z 124 | /// - The 26 uppercase English letters: A to Z 125 | /// - The 10 numbers: 0 to 9 126 | /// - Space 127 | /// - "!", "#", "$", "%", "&", "(", ")", "+", "-", ":", ";", "<", "=", ".", ">", "?", "@", "]", "[", "^", "_", " {", "}", "|", "~", "," 128 | /// channelId cannot be empty or set as nil. 129 | Future createChannel(String channelId) async { 130 | final res = await _callNative("createChannel", {'channelId': channelId}); 131 | if (res['errorCode'] != 0) 132 | throw AgoraRtmClientException( 133 | "createChannel failed errorCode:${res['errorCode']}", 134 | res['errorCode']); 135 | AgoraRtmChannel channel = AgoraRtmChannel(_clientIndex, channelId); 136 | _channels[channelId] = channel; 137 | return _channels[channelId]; 138 | } 139 | 140 | /// Releases an [AgoraRtmChannel]. 141 | Future releaseChannel(String channelId) async { 142 | final res = await _callNative("releaseChannel", {'channelId': channelId}); 143 | if (res['errorCode'] != 0) 144 | throw AgoraRtmClientException( 145 | "releaseChannel failed errorCode:${res['errorCode']}", 146 | res['errorCode']); 147 | _channels[channelId]?.close(); 148 | _channels.removeWhere((String channelId, AgoraRtmChannel channel) => 149 | [channelId].contains(channel)); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /lib/src/agora_rtm_plugin.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | 3 | mixin AgoraRtmPlugin { 4 | static const MethodChannel _methodChannel = 5 | const MethodChannel("io.agora.rtm"); 6 | 7 | static Future _sendMethodMessage( 8 | String call, String method, Map arguments) { 9 | return _methodChannel 10 | .invokeMethod(method, {"call": call, "params": arguments}); 11 | } 12 | 13 | static Future callMethodForStatic(String name, Map arguments) { 14 | return _sendMethodMessage("static", name, arguments); 15 | } 16 | 17 | static Future callMethodForClient(String name, Map arguments) { 18 | return _sendMethodMessage("AgoraRtmClient", name, arguments); 19 | } 20 | 21 | static Future callMethodForChannel(String name, Map arguments) { 22 | return _sendMethodMessage("AgoraRtmChannel", name, arguments); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/src/utils.dart: -------------------------------------------------------------------------------- 1 | class AgoraRtmMessage { 2 | String text; 3 | int ts; 4 | bool offline; 5 | 6 | AgoraRtmMessage(this.text, this.ts, this.offline); 7 | 8 | AgoraRtmMessage.fromText(String text) : text = text; 9 | 10 | AgoraRtmMessage.fromJson(Map json) 11 | : text = json['text'], 12 | ts = json['ts'], 13 | offline = json['offline']; 14 | 15 | Map toJson() => {'text': text, 'ts': ts, 'offline': offline}; 16 | 17 | @override 18 | String toString() { 19 | return "{text: $text, ts: $ts, offline: $offline}"; 20 | } 21 | } 22 | 23 | class AgoraRtmMember { 24 | String userId; 25 | String channelId; 26 | 27 | AgoraRtmMember(this.userId, this.channelId); 28 | 29 | AgoraRtmMember.fromJson(Map json) 30 | : userId = json['userId'], 31 | channelId = json['channelId']; 32 | 33 | Map toJson() => { 34 | 'userId': userId, 35 | 'channelId': channelId, 36 | }; 37 | 38 | @override 39 | String toString() { 40 | return "{uid: $userId, cid: $channelId}"; 41 | } 42 | } -------------------------------------------------------------------------------- /macos/Classes/AgoraRtmPlugin.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | import AgoraRtmKit 4 | 5 | public class AgoraRtmPlugin: NSObject, FlutterPlugin { 6 | public static func register(with registrar: FlutterPluginRegistrar) { 7 | let channel = FlutterMethodChannel(name: "io.agora.rtm", binaryMessenger: registrar.messenger) 8 | let instance = AgoraRtmPlugin() 9 | instance.methodChannel = channel 10 | instance.messenger = registrar.messenger 11 | registrar.addMethodCallDelegate(instance, channel: channel) 12 | } 13 | 14 | var methodChannel: FlutterMethodChannel! 15 | 16 | var nextClientIndex = 0 17 | var agoraClients = Dictionary() 18 | 19 | var messenger: FlutterBinaryMessenger! 20 | 21 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 22 | let methodName = call.method 23 | guard let arguments = call.arguments as? Dictionary, 24 | let callType = arguments["call"] as? String, 25 | let params = arguments["params"] as? Dictionary else { 26 | result(["errorCode": -2, "reason": FlutterMethodNotImplemented]) 27 | return 28 | } 29 | 30 | switch callType { 31 | case "static": 32 | handleStaticMethod(methodName, params: params, result: result) 33 | case "AgoraRtmClient": 34 | handleAgoraRtmClientMethod(methodName, params: params, result: result) 35 | case "AgoraRtmChannel": 36 | handleAgoraRtmChannelMethod(methodName, params: params, result: result) 37 | default: 38 | result(["errorCode": -2, "reason": FlutterMethodNotImplemented]) 39 | } 40 | } 41 | 42 | private func handleStaticMethod(_ name: String, params: [String: Any], result: @escaping FlutterResult) { 43 | switch name { 44 | case "createInstance": 45 | guard let appId = params["appId"] as? String else { 46 | result(["errorCode": -1]) 47 | return 48 | } 49 | 50 | while (nil != agoraClients[nextClientIndex]) { 51 | nextClientIndex += 1 52 | } 53 | 54 | guard let rtmClient = RTMClient.create(appId: appId, clientIndex: nextClientIndex, messenger: messenger) else { 55 | result(["errorCode": -1]) 56 | return 57 | } 58 | agoraClients[nextClientIndex] = rtmClient 59 | result(["errorCode": 0, "index": nextClientIndex]) 60 | nextClientIndex += 1 61 | default: 62 | result(FlutterMethodNotImplemented) 63 | } 64 | } 65 | 66 | private func handleAgoraRtmClientMethod(_ name: String, params: [String: Any], result: @escaping FlutterResult) { 67 | guard let clientIndex = params["clientIndex"] as? Int, 68 | let rtmClient = agoraClients[clientIndex] else { 69 | result(["errorCode": -1]) 70 | return 71 | } 72 | let args = params["args"] as? Dictionary 73 | 74 | switch name { 75 | case "login": 76 | let token = args?["token"] as? String 77 | let userId = args?["userId"] as! String 78 | rtmClient.kit.login(byToken: token, user: userId) { errorCode in 79 | result(["errorCode": errorCode.rawValue]) 80 | } 81 | case "logout": 82 | rtmClient.kit.logout { errorCode in 83 | result(["errorCode": errorCode.rawValue]) 84 | } 85 | case "createChannel": 86 | let channelId = args?["channelId"] as! String 87 | guard let rtmChannel = RTMChannel.create(clientIndex, channelId: channelId, messenger: messenger, kit: rtmClient.kit) else { 88 | result(["errorCode": -1]) 89 | return 90 | } 91 | rtmClient.channels[channelId] = rtmChannel 92 | result(["errorCode": 0]) 93 | case "releaseChannel": 94 | let channelId = args?["channelId"] as! String 95 | guard let rtmChannel = rtmClient.channels[channelId] else { 96 | result(["errorCode": -1]) 97 | return 98 | } 99 | rtmClient.kit.destroyChannel(withId: channelId) 100 | rtmClient.channels[channelId] = nil 101 | result(["errorCode": 0]) 102 | default: 103 | result(FlutterMethodNotImplemented) 104 | } 105 | } 106 | 107 | private func handleAgoraRtmChannelMethod(_ name: String, params: [String: Any], result: @escaping FlutterResult) { 108 | guard let clientIndex = params["clientIndex"] as? Int, 109 | let channelId = params["channelId"] as? String, 110 | let rtmClient = agoraClients[clientIndex], 111 | let rtmChannel = rtmClient.channels[channelId] else { 112 | result(["errorCode": -1]) 113 | return 114 | } 115 | let args = params["args"] as? Dictionary 116 | 117 | switch name { 118 | case "join": 119 | rtmChannel.channel.join { errorCode in 120 | result(["errorCode": errorCode.rawValue]) 121 | } 122 | case "sendMessage": 123 | let message = args?["message"] as! String 124 | rtmChannel.channel.send(AgoraRtmMessage(text: message)) { errorCode in 125 | result(["errorCode": errorCode.rawValue]) 126 | } 127 | case "leave": 128 | rtmChannel.channel.leave { errorCode in 129 | result(["errorCode": errorCode.rawValue]) 130 | } 131 | default: 132 | result(FlutterMethodNotImplemented) 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /macos/Classes/RTMChannel.swift: -------------------------------------------------------------------------------- 1 | import FlutterMacOS 2 | import AgoraRtmKit 3 | 4 | class RTMChannel: NSObject { 5 | public var channel: AgoraRtmChannel! 6 | 7 | private var messageChannel: FlutterBasicMessageChannel! 8 | 9 | static func create(_ clientIndex: Int, channelId: String, messenger: FlutterBinaryMessenger, kit: AgoraRtmKit) -> RTMChannel? { 10 | let rtmChannel = RTMChannel() 11 | let messageChannel = FlutterBasicMessageChannel(name: "io.agora.rtm.client\(clientIndex).channel\(channelId)", binaryMessenger: messenger) 12 | guard let channel = kit.createChannel(withId: channelId, delegate: rtmChannel) else { 13 | return nil 14 | } 15 | rtmChannel.channel = channel 16 | rtmChannel.messageChannel = messageChannel 17 | return rtmChannel 18 | } 19 | 20 | private func sendClientEvent(_ name: String, params: Dictionary) { 21 | var p = params 22 | p["event"] = name 23 | messageChannel.sendMessage(p) 24 | } 25 | } 26 | 27 | extension RTMChannel: AgoraRtmChannelDelegate { 28 | func channel(_ channel: AgoraRtmChannel, memberJoined member: AgoraRtmMember) { 29 | sendClientEvent("onMemberJoined", params: ["userId": member.userId, "channelId": member.channelId]) 30 | } 31 | 32 | func channel(_ channel: AgoraRtmChannel, memberLeft member: AgoraRtmMember) { 33 | sendClientEvent("onMemberLeft", params: ["userId": member.userId, "channelId": member.channelId]) 34 | } 35 | 36 | func channel(_ channel: AgoraRtmChannel, messageReceived message: AgoraRtmMessage, from member: AgoraRtmMember) { 37 | sendClientEvent("onMessageReceived", params: [ 38 | "userId": member.userId, 39 | "channelId": member.channelId, 40 | "message": [ 41 | "text": message.text, 42 | "ts": message.serverReceivedTs, 43 | "offline": message.isOfflineMessage, 44 | ], 45 | ]) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /macos/Classes/RTMClient.swift: -------------------------------------------------------------------------------- 1 | import FlutterMacOS 2 | import FlutterMacOS 3 | import AgoraRtmKit 4 | 5 | class RTMClient: NSObject { 6 | public var kit: AgoraRtmKit! 7 | 8 | public var channels = Dictionary() 9 | 10 | private var messageChannel: FlutterBasicMessageChannel! 11 | 12 | static func create(appId: String, clientIndex: Int, messenger: FlutterBinaryMessenger) -> RTMClient? { 13 | let client = RTMClient() 14 | let messageChannel = FlutterBasicMessageChannel(name: "io.agora.rtm.client\(clientIndex)", binaryMessenger: messenger) 15 | guard let kit = AgoraRtmKit(appId: appId, delegate: client) else { 16 | return nil 17 | } 18 | client.kit = kit 19 | client.messageChannel = messageChannel 20 | return client 21 | } 22 | 23 | private func sendClientEvent(_ name: String, params: Dictionary) { 24 | var p = params 25 | p["event"] = name 26 | messageChannel.sendMessage(p) 27 | } 28 | } 29 | 30 | // MARK: - AgoraRtmDelegate 31 | extension RTMClient: AgoraRtmDelegate { 32 | func rtmKit(_ kit: AgoraRtmKit, connectionStateChanged state: AgoraRtmConnectionState, reason: AgoraRtmConnectionChangeReason) { 33 | sendClientEvent("onConnectionStateChanged", params: ["state": state.rawValue, "reason": reason.rawValue]) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /macos/agora_rtm.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. 3 | # Run `pod lib lint agora_rtm.podspec' to validate before publishing. 4 | # 5 | Pod::Spec.new do |s| 6 | s.name = 'agora_rtm' 7 | s.version = '0.0.1' 8 | s.summary = 'A new flutter plugin project.' 9 | s.description = <<-DESC 10 | A new flutter plugin project. 11 | DESC 12 | s.homepage = 'http://example.com' 13 | s.license = { :file => '../LICENSE' } 14 | s.author = { 'Your Company' => 'email@example.com' } 15 | s.source = { :path => '.' } 16 | s.source_files = 'Classes/**/*' 17 | s.dependency 'FlutterMacOS' 18 | s.dependency 'AgoraRtm_macOS', '1.2.2' 19 | s.static_framework = true 20 | 21 | s.platform = :osx, '10.11' 22 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } 23 | s.swift_version = '5.0' 24 | end 25 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.flutter-io.cn" 9 | source: hosted 10 | version: "2.5.0-nullsafety.3" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.flutter-io.cn" 16 | source: hosted 17 | version: "2.1.0-nullsafety.3" 18 | characters: 19 | dependency: transitive 20 | description: 21 | name: characters 22 | url: "https://pub.flutter-io.cn" 23 | source: hosted 24 | version: "1.1.0-nullsafety.5" 25 | charcode: 26 | dependency: transitive 27 | description: 28 | name: charcode 29 | url: "https://pub.flutter-io.cn" 30 | source: hosted 31 | version: "1.2.0-nullsafety.3" 32 | clock: 33 | dependency: transitive 34 | description: 35 | name: clock 36 | url: "https://pub.flutter-io.cn" 37 | source: hosted 38 | version: "1.1.0-nullsafety.3" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.flutter-io.cn" 44 | source: hosted 45 | version: "1.15.0-nullsafety.5" 46 | fake_async: 47 | dependency: transitive 48 | description: 49 | name: fake_async 50 | url: "https://pub.flutter-io.cn" 51 | source: hosted 52 | version: "1.2.0-nullsafety.3" 53 | flutter: 54 | dependency: "direct main" 55 | description: flutter 56 | source: sdk 57 | version: "0.0.0" 58 | flutter_test: 59 | dependency: "direct dev" 60 | description: flutter 61 | source: sdk 62 | version: "0.0.0" 63 | matcher: 64 | dependency: transitive 65 | description: 66 | name: matcher 67 | url: "https://pub.flutter-io.cn" 68 | source: hosted 69 | version: "0.12.10-nullsafety.3" 70 | meta: 71 | dependency: transitive 72 | description: 73 | name: meta 74 | url: "https://pub.flutter-io.cn" 75 | source: hosted 76 | version: "1.3.0-nullsafety.6" 77 | path: 78 | dependency: transitive 79 | description: 80 | name: path 81 | url: "https://pub.flutter-io.cn" 82 | source: hosted 83 | version: "1.8.0-nullsafety.3" 84 | sky_engine: 85 | dependency: transitive 86 | description: flutter 87 | source: sdk 88 | version: "0.0.99" 89 | source_span: 90 | dependency: transitive 91 | description: 92 | name: source_span 93 | url: "https://pub.flutter-io.cn" 94 | source: hosted 95 | version: "1.8.0-nullsafety.4" 96 | stack_trace: 97 | dependency: transitive 98 | description: 99 | name: stack_trace 100 | url: "https://pub.flutter-io.cn" 101 | source: hosted 102 | version: "1.10.0-nullsafety.6" 103 | stream_channel: 104 | dependency: transitive 105 | description: 106 | name: stream_channel 107 | url: "https://pub.flutter-io.cn" 108 | source: hosted 109 | version: "2.1.0-nullsafety.3" 110 | string_scanner: 111 | dependency: transitive 112 | description: 113 | name: string_scanner 114 | url: "https://pub.flutter-io.cn" 115 | source: hosted 116 | version: "1.1.0-nullsafety.3" 117 | term_glyph: 118 | dependency: transitive 119 | description: 120 | name: term_glyph 121 | url: "https://pub.flutter-io.cn" 122 | source: hosted 123 | version: "1.2.0-nullsafety.3" 124 | test_api: 125 | dependency: transitive 126 | description: 127 | name: test_api 128 | url: "https://pub.flutter-io.cn" 129 | source: hosted 130 | version: "0.2.19-nullsafety.6" 131 | typed_data: 132 | dependency: transitive 133 | description: 134 | name: typed_data 135 | url: "https://pub.flutter-io.cn" 136 | source: hosted 137 | version: "1.3.0-nullsafety.5" 138 | vector_math: 139 | dependency: transitive 140 | description: 141 | name: vector_math 142 | url: "https://pub.flutter-io.cn" 143 | source: hosted 144 | version: "2.1.0-nullsafety.5" 145 | sdks: 146 | dart: ">=2.12.0-0.0 <3.0.0" 147 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: agora_rtm 2 | description: A new flutter plugin project. 3 | version: 0.0.1 4 | author: 5 | homepage: 6 | 7 | environment: 8 | sdk: ">=2.1.0 <3.0.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | 14 | dev_dependencies: 15 | flutter_test: 16 | sdk: flutter 17 | 18 | # For information on the generic Dart part of this file, see the 19 | # following page: https://dart.dev/tools/pub/pubspec 20 | 21 | # The following section is specific to Flutter. 22 | flutter: 23 | # This section identifies this Flutter project as a plugin project. 24 | # The 'pluginClass' and Android 'package' identifiers should not ordinarily 25 | # be modified. They are used by the tooling to maintain consistency when 26 | # adding or updating assets for this project. 27 | # 28 | # NOTE: This new plugin description format is not supported on Flutter's 29 | # stable channel as of 1.9.1. A plugin published using this format will not 30 | # work for most clients until the next major stable release. 31 | # However, it is required in order to declare macOS support. 32 | plugin: 33 | platforms: 34 | macos: 35 | pluginClass: AgoraRtmPlugin 36 | windows: 37 | pluginClass: AgoraRtmPlugin 38 | 39 | # To add assets to your plugin package, add an assets section, like this: 40 | # assets: 41 | # - images/a_dot_burr.jpeg 42 | # - images/a_dot_ham.jpeg 43 | # 44 | # For details regarding assets in packages, see 45 | # https://flutter.dev/assets-and-images/#from-packages 46 | # 47 | # An image asset can refer to one or more resolution-specific "variants", see 48 | # https://flutter.dev/assets-and-images/#resolution-aware. 49 | 50 | # To add custom fonts to your plugin package, add a fonts section here, 51 | # in this "flutter" section. Each entry in this list should have a 52 | # "family" key with the font family name, and a "fonts" key with a 53 | # list giving the asset and other descriptors for the font. For 54 | # example: 55 | # fonts: 56 | # - family: Schyler 57 | # fonts: 58 | # - asset: fonts/Schyler-Regular.ttf 59 | # - asset: fonts/Schyler-Italic.ttf 60 | # style: italic 61 | # - family: Trajan Pro 62 | # fonts: 63 | # - asset: fonts/TrajanPro.ttf 64 | # - asset: fonts/TrajanPro_Bold.ttf 65 | # weight: 700 66 | # 67 | # For details regarding fonts in packages, see 68 | # https://flutter.dev/custom-fonts/#from-packages 69 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | set(PROJECT_NAME "agora_rtm") 3 | project(${PROJECT_NAME} LANGUAGES CXX) 4 | 5 | set(PLUGIN_NAME "${PROJECT_NAME}_plugin") 6 | 7 | add_library(${PLUGIN_NAME} SHARED 8 | "${PLUGIN_NAME}.cpp" 9 | "RTMChannel.cpp" 10 | "RTMClient.cpp" 11 | ) 12 | apply_standard_settings(${PLUGIN_NAME}) 13 | set_target_properties(${PLUGIN_NAME} PROPERTIES 14 | CXX_VISIBILITY_PRESET hidden) 15 | target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) 16 | target_include_directories(${PLUGIN_NAME} 17 | INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include" 18 | PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/sdk/include") 19 | find_library(AGORA_RTM_LIB agora_rtm_sdk "${CMAKE_CURRENT_SOURCE_DIR}/sdk/lib") 20 | target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin ${AGORA_RTM_LIB}) 21 | 22 | # List of absolute paths to libraries that should be bundled with the plugin 23 | set(agora_rtm_bundled_libraries 24 | "${CMAKE_CURRENT_SOURCE_DIR}/sdk/dll/agora_rtm_sdk.dll" 25 | PARENT_SCOPE 26 | ) 27 | -------------------------------------------------------------------------------- /windows/RTMChannel.cpp: -------------------------------------------------------------------------------- 1 | #include "include/agora_rtm/RTMChannel.h" 2 | 3 | #include 4 | 5 | namespace agora::rtm 6 | { 7 | RTMChannel::RTMChannel(long client_index, const std::string& channel_id, flutter::BinaryMessenger* messenger, IRtmService* rtm_service) 8 | { 9 | messageChannel = std::make_unique>( 10 | messenger, 11 | "io.agora.rtm.client" + std::to_string(client_index) + ".channel" + channel_id, 12 | &flutter::StandardMessageCodec::GetInstance()); 13 | 14 | channel = rtm_service->createChannel(channel_id.c_str(), this); 15 | } 16 | 17 | RTMChannel::~RTMChannel() 18 | { 19 | if (channel != nullptr) 20 | channel->release(); 21 | channel = nullptr; 22 | } 23 | 24 | void RTMChannel::onMemberJoined(IChannelMember* member) 25 | { 26 | SendChannelEvent("onMemberJoined", EncodableMap{ 27 | {EncodableValue("userId"), EncodableValue(member->getUserId())}, 28 | {EncodableValue("channelId"), EncodableValue(member->getChannelId())}, 29 | }); 30 | } 31 | 32 | void RTMChannel::onMemberLeft(IChannelMember* member) 33 | { 34 | SendChannelEvent("onMemberLeft", EncodableMap{ 35 | {EncodableValue("userId"), EncodableValue(member->getUserId())}, 36 | {EncodableValue("channelId"), EncodableValue(member->getChannelId())}, 37 | }); 38 | } 39 | 40 | void RTMChannel::onMessageReceived(const char* userId, const IMessage* message) 41 | { 42 | SendChannelEvent("onMessageReceived", EncodableMap{ 43 | {EncodableValue("userId"), EncodableValue(userId)}, 44 | {EncodableValue("channelId"), EncodableValue(channel->getId())}, 45 | {EncodableValue("message"), EncodableValue(EncodableMap{ 46 | {EncodableValue("text"), EncodableValue(message->getText())}, 47 | {EncodableValue("ts"), EncodableValue(message->getServerReceivedTs())}, 48 | {EncodableValue("offline"), EncodableValue(message->isOfflineMessage())}, 49 | })}, 50 | }); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /windows/RTMClient.cpp: -------------------------------------------------------------------------------- 1 | #include "include/agora_rtm/RTMClient.h" 2 | 3 | #include 4 | 5 | namespace agora::rtm 6 | { 7 | RTMClient::RTMClient(const std::string& app_id, long client_index, flutter::BinaryMessenger* messenger) 8 | { 9 | messageChannel = std::make_unique>( 10 | messenger, 11 | "io.agora.rtm.client" + std::to_string(client_index), 12 | &flutter::StandardMessageCodec::GetInstance()); 13 | 14 | rtmService = createRtmService(); 15 | rtmService->initialize(app_id.c_str(), this); 16 | } 17 | 18 | RTMClient::~RTMClient() 19 | { 20 | for (auto channelPair : channels) 21 | delete channelPair.second; 22 | channels.clear(); 23 | 24 | rtmService->release(); 25 | } 26 | 27 | #pragma region IRtmServiceEventHandler 28 | void RTMClient::onConnectionStateChanged(CONNECTION_STATE state, CONNECTION_CHANGE_REASON reason) 29 | { 30 | SendClientEvent("onConnectionStateChanged", EncodableMap{ 31 | {EncodableValue("state"), EncodableValue(state)}, 32 | {EncodableValue("reason"), EncodableValue(reason)}, 33 | }); 34 | } 35 | #pragma endregion 36 | } 37 | -------------------------------------------------------------------------------- /windows/agora_rtm_plugin.cpp: -------------------------------------------------------------------------------- 1 | #include "include/agora_rtm/agora_rtm_plugin.h" 2 | 3 | // This must be included before many other Windows headers. 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include "include/agora_rtm/RTMClient.h" 14 | 15 | using namespace agora::rtm; 16 | 17 | namespace { 18 | 19 | using flutter::EncodableMap; 20 | using flutter::EncodableValue; 21 | 22 | class AgoraRtmPlugin : public flutter::Plugin { 23 | public: 24 | static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar); 25 | 26 | AgoraRtmPlugin(flutter::PluginRegistrarWindows* registrar); 27 | 28 | virtual ~AgoraRtmPlugin(); 29 | 30 | private: 31 | // Called when a method is called on this plugin's channel from Dart. 32 | void HandleMethodCall( 33 | const flutter::MethodCall &method_call, 34 | std::unique_ptr> result); 35 | 36 | flutter::PluginRegistrarWindows* registrar; 37 | 38 | long nextClientIndex{ 0 }; 39 | std::map agoraClients{}; 40 | 41 | void HandleStaticMethod(const std::string& method_name, EncodableMap& params, 42 | const std::unique_ptr>& result); 43 | void HandleAgoraRtmClientMethod(const std::string& method_name, EncodableMap& params, 44 | const std::unique_ptr>& result); 45 | void HandleAgoraRtmChannelMethod(const std::string& method_name, EncodableMap& params, 46 | const std::unique_ptr>& result); 47 | }; 48 | 49 | // static 50 | void AgoraRtmPlugin::RegisterWithRegistrar( 51 | flutter::PluginRegistrarWindows *registrar) { 52 | auto channel = 53 | std::make_unique>( 54 | registrar->messenger(), "io.agora.rtm", 55 | &flutter::StandardMethodCodec::GetInstance()); 56 | 57 | auto plugin = std::make_unique(registrar); 58 | 59 | channel->SetMethodCallHandler( 60 | [plugin_pointer = plugin.get()](const auto &call, auto result) { 61 | plugin_pointer->HandleMethodCall(call, std::move(result)); 62 | }); 63 | 64 | registrar->AddPlugin(std::move(plugin)); 65 | } 66 | 67 | AgoraRtmPlugin::AgoraRtmPlugin(flutter::PluginRegistrarWindows* registrar) : registrar(registrar) {} 68 | 69 | AgoraRtmPlugin::~AgoraRtmPlugin() { 70 | for (auto clientPair : agoraClients) 71 | delete clientPair.second; 72 | agoraClients.clear(); 73 | } 74 | 75 | void AgoraRtmPlugin::HandleMethodCall( 76 | const flutter::MethodCall &method_call, 77 | std::unique_ptr> result) { 78 | auto methodName = method_call.method_name(); 79 | auto arguments = std::get(*method_call.arguments()); 80 | auto callType = std::get(arguments[EncodableValue("call")]); 81 | auto params = std::get(arguments[EncodableValue("params")]); 82 | 83 | if ("static" == callType) 84 | HandleStaticMethod(methodName, params, result); 85 | else if ("AgoraRtmClient" == callType) 86 | HandleAgoraRtmClientMethod(methodName, params, result); 87 | else if ("AgoraRtmChannel" == callType) 88 | HandleAgoraRtmChannelMethod(methodName, params, result); 89 | else 90 | result->NotImplemented(); 91 | } 92 | 93 | void AgoraRtmPlugin::HandleStaticMethod(const std::string& method_name, EncodableMap& params, 94 | const std::unique_ptr>& result) 95 | { 96 | if ("createInstance" == method_name) 97 | { 98 | if (params.count(EncodableValue("appId")) == 0) 99 | { 100 | result->Success(EncodableMap{ 101 | {"errorCode", -1}, 102 | }); 103 | return; 104 | } 105 | auto appId = std::get(params[EncodableValue("appId")]); 106 | 107 | while (agoraClients.count(nextClientIndex) > 0) 108 | nextClientIndex++; 109 | 110 | auto rtmClient = new RTMClient(appId, nextClientIndex, registrar->messenger()); 111 | result->Success(EncodableMap{ 112 | {"errorCode", 0}, 113 | {"index", nextClientIndex}, 114 | }); 115 | agoraClients[nextClientIndex] = rtmClient; 116 | nextClientIndex++; 117 | } 118 | else 119 | result->NotImplemented(); 120 | } 121 | 122 | void AgoraRtmPlugin::HandleAgoraRtmClientMethod(const std::string& method_name, EncodableMap& params, 123 | const std::unique_ptr>& result) 124 | { 125 | auto clientIndex = std::get(params[EncodableValue("clientIndex")]); 126 | auto args = params[EncodableValue("args")].IsNull() ? EncodableMap() : std::get(params[EncodableValue("args")]); 127 | 128 | if (agoraClients.count(clientIndex) == 0) 129 | { 130 | result->Success(EncodableMap{ 131 | {"errorCode", -1}, 132 | }); 133 | return; 134 | } 135 | auto rtmClient = agoraClients[clientIndex]; 136 | 137 | if ("login" == method_name) 138 | { 139 | auto token = args[EncodableValue("token")].IsNull() ? "" : std::get(args[EncodableValue("token")]); 140 | auto userId = std::get(args[EncodableValue("userId")]); 141 | auto errorCode = rtmClient->rtmService->login(token.c_str(), userId.c_str()); 142 | result->Success(EncodableMap{ 143 | {"errorCode", errorCode}, 144 | }); 145 | } 146 | else if ("logout" == method_name) 147 | { 148 | auto errorCode = rtmClient->rtmService->logout(); 149 | result->Success(EncodableMap{ 150 | {"errorCode", errorCode}, 151 | }); 152 | } 153 | else if ("createChannel" == method_name) 154 | { 155 | auto channelId = std::get(args[EncodableValue("channelId")]); 156 | auto rtmChannel = new RTMChannel(clientIndex, channelId, registrar->messenger(), rtmClient->rtmService); 157 | if (rtmChannel == nullptr) 158 | { 159 | result->Success(EncodableMap{ 160 | {"errorCode", -1}, 161 | }); 162 | return; 163 | } 164 | rtmClient->channels[channelId] = rtmChannel; 165 | result->Success(EncodableMap{ 166 | {"errorCode", 0}, 167 | }); 168 | } 169 | else if ("releaseChannel" == method_name) 170 | { 171 | auto channelId = std::get(args[EncodableValue("channelId")]); 172 | auto rtmChannel = rtmClient->channels[channelId]; 173 | if (rtmChannel == nullptr) 174 | { 175 | result->Success(EncodableMap{ 176 | {"errorCode", -1}, 177 | }); 178 | return; 179 | } 180 | delete rtmChannel; 181 | rtmClient->channels[channelId] = nullptr; 182 | result->Success(EncodableMap{ 183 | {"errorCode", 0}, 184 | }); 185 | } 186 | else 187 | result->NotImplemented(); 188 | } 189 | 190 | void AgoraRtmPlugin::HandleAgoraRtmChannelMethod(const std::string& method_name, EncodableMap& params, 191 | const std::unique_ptr>& result) 192 | { 193 | auto clientIndex = std::get(params[EncodableValue("clientIndex")]); 194 | auto channelId = std::get(params[EncodableValue("channelId")]); 195 | auto args = params[EncodableValue("args")].IsNull() ? EncodableMap() : std::get(params[EncodableValue("args")]); 196 | auto rtmClient = agoraClients[clientIndex]; 197 | 198 | if (rtmClient->channels.count(channelId) == 0) 199 | { 200 | result->Success(EncodableMap{ 201 | {"errorCode", -1}, 202 | }); 203 | } 204 | auto rtmChannel = rtmClient->channels[channelId]; 205 | 206 | if ("join" == method_name) 207 | { 208 | auto errorCode = rtmChannel->channel->join(); 209 | result->Success(EncodableMap{ 210 | {"errorCode", errorCode}, 211 | }); 212 | } 213 | else if ("sendMessage" == method_name) 214 | { 215 | auto message = std::get(args[EncodableValue("message")]); 216 | auto rtmMessage = rtmClient->rtmService->createMessage(); 217 | rtmMessage->setText(message.c_str()); 218 | auto errorCode = rtmChannel->channel->sendMessage(rtmMessage); 219 | rtmMessage->release(); 220 | result->Success(EncodableMap{ 221 | {"errorCode", errorCode}, 222 | }); 223 | } 224 | else if ("leave" == method_name) 225 | { 226 | auto errorCode = rtmChannel->channel->leave(); 227 | result->Success(EncodableMap{ 228 | {"errorCode", errorCode}, 229 | }); 230 | } 231 | else 232 | result->NotImplemented(); 233 | } 234 | 235 | } // namespace 236 | 237 | void AgoraRtmPluginRegisterWithRegistrar( 238 | FlutterDesktopPluginRegistrarRef registrar) { 239 | AgoraRtmPlugin::RegisterWithRegistrar( 240 | flutter::PluginRegistrarManager::GetInstance() 241 | ->GetRegistrar(registrar)); 242 | } 243 | -------------------------------------------------------------------------------- /windows/include/agora_rtm/RTMChannel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #include "IAgoraRtmService.h" 7 | 8 | namespace agora::rtm 9 | { 10 | using flutter::EncodableValue; 11 | using flutter::EncodableMap; 12 | 13 | class RTMChannel : IChannelEventHandler 14 | { 15 | public: 16 | RTMChannel(long client_index, const std::string& channel_id, flutter::BinaryMessenger* messenger, IRtmService* rtm_service); 17 | 18 | virtual ~RTMChannel(); 19 | 20 | IChannel* channel; 21 | 22 | #pragma region IRtmServiceEventHandler 23 | void onMemberJoined(IChannelMember* member) override; 24 | void onMemberLeft(IChannelMember* member) override; 25 | void onMessageReceived(const char* userId, const IMessage* message) override; 26 | #pragma endregion 27 | 28 | private: 29 | std::unique_ptr> messageChannel; 30 | 31 | void SendChannelEvent(std::string name, EncodableMap params) 32 | { 33 | params[EncodableValue("event")] = name; 34 | messageChannel->Send(EncodableValue(params)); 35 | } 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /windows/include/agora_rtm/RTMClient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #include "IAgoraRtmService.h" 7 | #include "RTMChannel.h" 8 | 9 | namespace agora::rtm 10 | { 11 | using flutter::EncodableValue; 12 | using flutter::EncodableMap; 13 | 14 | class RTMClient: IRtmServiceEventHandler 15 | { 16 | public: 17 | RTMClient(const std::string& app_id, long client_index, flutter::BinaryMessenger* messenger); 18 | 19 | virtual ~RTMClient(); 20 | 21 | IRtmService* rtmService; 22 | 23 | std::map channels{}; 24 | 25 | #pragma region IRtmServiceEventHandler 26 | void onConnectionStateChanged(CONNECTION_STATE state, CONNECTION_CHANGE_REASON reason) override; 27 | #pragma endregion 28 | 29 | private: 30 | std::unique_ptr> messageChannel; 31 | 32 | void SendClientEvent(std::string name, EncodableMap params) 33 | { 34 | params[EncodableValue("event")] = name; 35 | messageChannel->Send(EncodableValue(params)); 36 | } 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /windows/include/agora_rtm/agora_rtm_plugin.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_PLUGIN_AGORA_RTM_PLUGIN_H_ 2 | #define FLUTTER_PLUGIN_AGORA_RTM_PLUGIN_H_ 3 | 4 | #include 5 | 6 | #ifdef FLUTTER_PLUGIN_IMPL 7 | #define FLUTTER_PLUGIN_EXPORT __declspec(dllexport) 8 | #else 9 | #define FLUTTER_PLUGIN_EXPORT __declspec(dllimport) 10 | #endif 11 | 12 | #if defined(__cplusplus) 13 | extern "C" { 14 | #endif 15 | 16 | FLUTTER_PLUGIN_EXPORT void AgoraRtmPluginRegisterWithRegistrar( 17 | FlutterDesktopPluginRegistrarRef registrar); 18 | 19 | #if defined(__cplusplus) 20 | } // extern "C" 21 | #endif 22 | 23 | #endif // FLUTTER_PLUGIN_AGORA_RTM_PLUGIN_H_ 24 | -------------------------------------------------------------------------------- /windows/sdk/dll/agora_rtm_sdk.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woodemi/Flutter-RTM-Desktop/33b9259d353b20be6c4f9b7c95c9d38df2df3cea/windows/sdk/dll/agora_rtm_sdk.dll -------------------------------------------------------------------------------- /windows/sdk/include/IAgoraRtmCallManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // Agora's RTM SDK 3 | // 4 | // 5 | // Copyright (c) 2019 Agora.io. All rights reserved. 6 | // 7 | #pragma once 8 | 9 | #include "IAgoraRtmService.h" 10 | 11 | namespace agora { 12 | namespace rtm { 13 | 14 | /** 15 | @brief RETURNED TO THE CALLER. States of an outgoing call invitation. 16 | */ 17 | enum LOCAL_INVITATION_STATE { 18 | 19 | /** 20 | 0: RETURNED TO THE CALLER. The initial state of a call invitation (idle). 21 | */ 22 | LOCAL_INVITATION_STATE_IDLE = 0, 23 | 24 | /** 25 | 1: RETURNED TO THE CALLER. The call invitation is sent to the callee. 26 | */ 27 | LOCAL_INVITATION_STATE_SENT_TO_REMOTE = 1, 28 | 29 | /** 30 | 2: RETURNED TO THE CALLER. The call invitation is received by the callee. 31 | */ 32 | LOCAL_INVITATION_STATE_RECEIVED_BY_REMOTE = 2, 33 | 34 | /** 35 | 3: RETURNED TO THE CALLER. The call invitation is accepted by the callee. 36 | */ 37 | LOCAL_INVITATION_STATE_ACCEPTED_BY_REMOTE = 3, 38 | 39 | /** 40 | 4: RETURNED TO THE CALLER. The call invitation is declined by the callee. 41 | */ 42 | LOCAL_INVITATION_STATE_REFUSED_BY_REMOTE = 4, 43 | 44 | /** 45 | 5: RETURNED TO THE CALLER. You have canceled the call invitation. 46 | */ 47 | LOCAL_INVITATION_STATE_CANCELED = 5, 48 | 49 | /** 50 | 6: RETURNED TO THE CALLER. The call invitation fails. 51 | */ 52 | LOCAL_INVITATION_STATE_FAILURE = 6, 53 | }; 54 | 55 | /** 56 | @brief RETURNED TO THE CALLEE. States of an incoming call invitation. 57 | */ 58 | enum REMOTE_INVITATION_STATE { 59 | 60 | /** 61 | 0: RETURNED TO THE CALLEE. The initial state of a call invitation (idle). 62 | */ 63 | REMOTE_INVITATION_STATE_IDLE = 0, 64 | 65 | /** 66 | 1: RETURNED TO THE CALLEE. A call invitation from a remote caller is received. 67 | */ 68 | REMOTE_INVITATION_STATE_INVITATION_RECEIVED = 1, 69 | 70 | /** 71 | 2: RETURNED TO THE CALLEE. The message is sent to the caller that the call invitation is accepted. 72 | */ 73 | REMOTE_INVITATION_STATE_ACCEPT_SENT_TO_LOCAL = 2, 74 | 75 | /** 76 | 3: RETURNED TO THE CALLEE. You have declined the call invitation. 77 | */ 78 | REMOTE_INVITATION_STATE_REFUSED = 3, 79 | 80 | /** 81 | 4: RETURNED TO THE CALLEE. You have accepted the call invitation. 82 | */ 83 | REMOTE_INVITATION_STATE_ACCEPTED = 4, 84 | 85 | /** 86 | 5: RETURNED TO THE CALLEE. The call invitation is canceled by the remote caller. 87 | */ 88 | REMOTE_INVITATION_STATE_CANCELED = 5, 89 | 90 | /** 91 | 6: RETURNED TO THE CALLEE. The call invitation fails. 92 | */ 93 | REMOTE_INVITATION_STATE_FAILURE = 6, 94 | }; 95 | 96 | /** 97 | @brief RETURNED TO THE CALLER. Error codes of an outgoing call invitation. 98 | */ 99 | enum LOCAL_INVITATION_ERR_CODE { 100 | 101 | /** 102 | 0: RETURNED TO THE CALLER. The outgoing invitation succeeds. 103 | */ 104 | LOCAL_INVITATION_ERR_OK = 0, 105 | 106 | /** 107 | 1: RETURNED TO THE CALLER. The callee is offline. 108 | 109 | The SDK performs the following: 110 | - Keeps resending the call invitation to the callee, if the callee is offline. 111 | - Returns this error code, if the callee is still offline 30 seconds since the call invitation is sent. 112 | */ 113 | LOCAL_INVITATION_ERR_PEER_OFFLINE = 1, 114 | 115 | /** 116 | 2: RETURNED TO THE CALLER. The callee is online but has not ACKed to the call invitation 30 seconds since it is sent. 117 | */ 118 | LOCAL_INVITATION_ERR_PEER_NO_RESPONSE = 2, 119 | 120 | /** 121 | 3: RETURNED TO THE CALLER. SAVED FOR FUTURE USE. The call invitation expires 60 seconds since it is sent, if the callee ACKs to the call invitation but neither the caller or callee takes any further action (cancel, accpet, or decline it). 122 | */ 123 | LOCAL_INVITATION_ERR_INVITATION_EXPIRE = 3, 124 | 125 | /** 126 | 4: RETURNED TO THE CALLER. The caller is not logged in. 127 | */ 128 | LOCAL_INVITATION_ERR_NOT_LOGGEDIN = 4, 129 | }; 130 | 131 | /** 132 | @brief RETURNED TO THE CALLEE. Error codes of an incoming call invitation. 133 | */ 134 | enum REMOTE_INVITATION_ERR_CODE { 135 | 136 | /** 137 | 0: RETURNED TO THE CALLEE. The incoming calll invitation succeeds. 138 | */ 139 | REMOTE_INVITATION_ERR_OK = 0, 140 | 141 | /** 142 | 1: RETURNED TO THE CALLEE. The call invitation received by the callee fails: the callee is not online. 143 | */ 144 | REMOTE_INVITATION_ERR_PEER_OFFLINE = 1, 145 | 146 | /** 147 | 2: RETURNED TO THE CALLEE. The call invitation received by callee fails: the acceptance of the call invitation fails. 148 | */ 149 | REMOTE_INVITATION_ERR_ACCEPT_FAILURE = 2, 150 | 151 | /** 152 | 3: RETURNED TO THE CALLEE. The call invitation expires 60 seconds since it is sent, if the callee ACKs to the call invitation but neither the caller or callee takes any further action (cancel, accpet, or decline it). 153 | */ 154 | REMOTE_INVITATION_ERR_INVITATION_EXPIRE = 3, 155 | }; 156 | 157 | /** 158 | @brief Error codes of the call invitation methods. 159 | */ 160 | enum INVITATION_API_CALL_ERR_CODE { 161 | 162 | /** 163 | 0: The method call succeeds. 164 | */ 165 | INVITATION_API_CALL_ERR_OK = 0, 166 | 167 | /** 168 | 1: The method call fails. The argument is invalid. 169 | */ 170 | INVITATION_API_CALL_ERR_INVALID_ARGUMENT = 1, 171 | 172 | /** 173 | 2: The method call fails. The call invitation has not started. 174 | */ 175 | INVITATION_API_CALL_ERR_NOT_STARTED = 2, 176 | 177 | /** 178 | 3: The method call fails. The call invitation has ended. 179 | */ 180 | INVITATION_API_CALL_ERR_ALREADY_END = 3, // accepted, failure, canceled, refused 181 | 182 | /** 183 | 4: The method call fails. The call invitation is already accepted. 184 | */ 185 | INVITATION_API_CALL_ERR_ALREADY_ACCEPT = 4, // more details 186 | 187 | /** 188 | 5: The method call fails. The call invitation is already sent. 189 | */ 190 | INVITATION_API_CALL_ERR_ALREADY_SENT = 5, 191 | }; 192 | 193 | /** 194 | The class allowing the caller to retrieve the attributes of an outgoing call invitation. 195 | */ 196 | class ILocalCallInvitation 197 | { 198 | protected: 199 | virtual ~ILocalCallInvitation() {} 200 | public: 201 | 202 | /** 203 | Allows the caller to get the User ID of the callee. 204 | */ 205 | virtual const char *getCalleeId() const = 0; 206 | 207 | /** 208 | Allows the caller to set the call invitation content. 209 | 210 | @param content The content of the call invitation. The @p content must not exceed 8 KB in length if encoded in UTF-8. 211 | */ 212 | virtual void setContent(const char *content) = 0; 213 | 214 | /** 215 | Allows the caller to get the call invitation content. 216 | 217 | @note The caller sets the call invitation content using the \ref agora::rtm::ILocalCallInvitation::setContent "setContent" method. 218 | */ 219 | virtual const char *getContent() const = 0; 220 | 221 | /** 222 | Sets the channel ID. 223 | 224 | @note To intercommunicate with the legacy Agora Signaling SDK, you MUST set the channel ID. However, even if the callee successfully accepts the call invitation, the Agora RTM SDK does not join the channel of the specified channel ID. 225 | 226 | @param channelId The channel ID to be set. 227 | */ 228 | virtual void setChannelId(const char *channelId) = 0; 229 | 230 | /** 231 | Gets the channel ID. 232 | */ 233 | virtual const char *getChannelId() const = 0; 234 | 235 | /** 236 | Allows the caller to get the callee's response to the call invitation. 237 | 238 | @note The callee sets his/her response using the \ref agora::rtm::IRemoteCallInvitation::setResponse "setResponse" method. 239 | */ 240 | virtual const char *getResponse() const = 0; 241 | 242 | /** 243 | Allows the caller to get the state of the outgoing call invitation. 244 | 245 | @return State of the outgoing call invitation. See: #LOCAL_INVITATION_STATE. 246 | */ 247 | virtual LOCAL_INVITATION_STATE getState() const = 0; 248 | 249 | /** 250 | Releases all resources used by the ILocalCallInvitation instance. 251 | */ 252 | virtual void release() = 0; 253 | }; 254 | 255 | /** 256 | The class allowing the callee to retrieve the attributes of an incoming call invitation. 257 | */ 258 | class IRemoteCallInvitation 259 | { 260 | protected: 261 | virtual ~IRemoteCallInvitation() {} 262 | public: 263 | 264 | /** 265 | Allows the callee to get the User ID of the caller. 266 | */ 267 | virtual const char *getCallerId() const = 0; 268 | 269 | /** 270 | Allows the callee to get the call invitation content set by the caller. 271 | 272 | @note The caller sets the call invitation content using the \ref agora::rtm::ILocalCallInvitation::setContent "setContent" method. 273 | */ 274 | virtual const char *getContent() const = 0; 275 | 276 | /** 277 | Allows the callee to set a response to the call invitation. 278 | 279 | @param response The callee's response to the call invitation. The @p response must not exceed 8 KB in length if encoded in UTF-8. 280 | */ 281 | virtual void setResponse(const char *response) = 0; 282 | 283 | /** 284 | Allows the callee to get his/her response to the incoming call invitation. 285 | 286 | @note The callee sets a response to the incoming call invitation using the \ref agora::rtm::IRemoteCallInvitation::setResponse "setResponse" method. 287 | */ 288 | virtual const char *getResponse() const = 0; 289 | 290 | /** 291 | Gets the channel ID. 292 | */ 293 | virtual const char *getChannelId() const = 0; 294 | 295 | /** 296 | Allows the callee to get the state of the incoming call invitation. 297 | 298 | @return The state of the incoming call invitation See: #REMOTE_INVITATION_STATE. 299 | */ 300 | virtual REMOTE_INVITATION_STATE getState() const = 0; 301 | 302 | /** 303 | Releases all resources used by the IRemoteCallInvitation instance. 304 | */ 305 | virtual void release() = 0; 306 | }; 307 | 308 | /** 309 | Callbacks for the call invitation methods. 310 | */ 311 | class IRtmCallEventHandler 312 | { 313 | public: 314 | virtual ~IRtmCallEventHandler() 315 | { 316 | } 317 | 318 | /** 319 | Callback to the caller: occurs when the callee receives the call invitation. 320 | 321 | @param localInvitation An ILocalCallInvitation object. 322 | */ 323 | virtual void onLocalInvitationReceivedByPeer(ILocalCallInvitation *localInvitation) 324 | { 325 | (ILocalCallInvitation *) localInvitation; 326 | } 327 | 328 | /** 329 | Callback to the caller: occurs when the caller cancels a call invitation. 330 | 331 | @param localInvitation An ILocalCallInvitation object. 332 | */ 333 | virtual void onLocalInvitationCanceled(ILocalCallInvitation *localInvitation) 334 | { 335 | (ILocalCallInvitation *) localInvitation; 336 | } 337 | 338 | /** 339 | Callback to the caller: occurs when the life cycle of the outgoing call invitation ends in failure. 340 | 341 | @param localInvitation An ILocalCallInvitation object. 342 | @param errorCode The error code. See #LOCAL_INVITATION_ERR_CODE. 343 | */ 344 | virtual void onLocalInvitationFailure(ILocalCallInvitation *localInvitation, LOCAL_INVITATION_ERR_CODE errorCode) 345 | { 346 | (ILocalCallInvitation *) localInvitation; 347 | (LOCAL_INVITATION_ERR_CODE) errorCode; 348 | } 349 | 350 | /** 351 | Callback to the caller: occurs when the callee accepts the call invitation. 352 | 353 | @param localInvitation An ILocalCallInvitation object. 354 | @param response The callee's response to the call invitation. 355 | */ 356 | virtual void onLocalInvitationAccepted(ILocalCallInvitation *localInvitation, const char *response) 357 | { 358 | (ILocalCallInvitation *) localInvitation; 359 | (const char *) response; 360 | } 361 | 362 | /** 363 | Callback to the caller: occurs when the callee refuses the call invitation. 364 | 365 | @param localInvitation An ILocalCallInvitation object. 366 | @param response The callee's response to the call invitation. 367 | */ 368 | virtual void onLocalInvitationRefused(ILocalCallInvitation *localInvitation, const char *response) 369 | { 370 | (ILocalCallInvitation *) localInvitation; 371 | (const char *) response; 372 | } 373 | 374 | /** 375 | Callback for the callee: occurs when the callee refuses a call invitation. 376 | 377 | @param remoteInvitation An IRemoteCallInvitation object. 378 | */ 379 | virtual void onRemoteInvitationRefused(IRemoteCallInvitation *remoteInvitation) 380 | { 381 | (IRemoteCallInvitation *) remoteInvitation; 382 | } 383 | 384 | /** 385 | Callback to the callee: occurs when the callee accepts a call invitation. 386 | 387 | @param remoteInvitation An IRemoteCallInvitation object. 388 | */ 389 | virtual void onRemoteInvitationAccepted(IRemoteCallInvitation *remoteInvitation) 390 | { 391 | (IRemoteCallInvitation *) remoteInvitation; 392 | } 393 | 394 | /** 395 | Callback to the callee: occurs when the callee receives a call invitation. 396 | 397 | @param remoteInvitation An IRemoteCallInvitation object. 398 | */ 399 | virtual void onRemoteInvitationReceived(IRemoteCallInvitation *remoteInvitation) 400 | { 401 | (IRemoteCallInvitation *) remoteInvitation; 402 | } 403 | 404 | /** 405 | Callback to the callee: occurs when the life cycle of the incoming call invitation ends in failure. 406 | 407 | @param remoteInvitation An IRemoteCallInvitation object. 408 | @param errorCode The error code. See #REMOTE_INVITATION_ERR_CODE. 409 | */ 410 | virtual void onRemoteInvitationFailure(IRemoteCallInvitation *remoteInvitation, REMOTE_INVITATION_ERR_CODE errorCode) 411 | { 412 | (IRemoteCallInvitation *) remoteInvitation; 413 | } 414 | 415 | /** 416 | Callback to the callee: occurs when the caller cancels the call invitation. 417 | 418 | @param remoteInvitation An IRemoteCallInvitation object. 419 | */ 420 | virtual void onRemoteInvitationCanceled(IRemoteCallInvitation *remoteInvitation) 421 | { 422 | (IRemoteCallInvitation *) remoteInvitation; 423 | } 424 | }; 425 | 426 | /** 427 | RTM call manager methods. 428 | */ 429 | class IRtmCallManager 430 | { 431 | protected: 432 | virtual ~IRtmCallManager() {} 433 | public: 434 | 435 | /** 436 | Allows the caller to send a call invitation to the callee. 437 | 438 | @param invitation An \ref agora::rtm::ILocalCallInvitation "ILocalCallInvitation" object. 439 | @return 440 | - 0: Success. 441 | - <0: Failure. See #INVITATION_API_CALL_ERR_CODE for the error code. 442 | */ 443 | virtual int sendLocalInvitation(ILocalCallInvitation *invitation)= 0; 444 | 445 | /** 446 | Allows the callee to accept an incoming call invitation. 447 | 448 | @param invitation An \ref agora::rtm::IRemoteCallInvitation "IRemoteCallInvitation" object. 449 | @return 450 | - 0: Success. 451 | - <0: Failure. See #INVITATION_API_CALL_ERR_CODE for the error code. 452 | */ 453 | virtual int acceptRemoteInvitation(IRemoteCallInvitation *invitation) = 0; 454 | 455 | /** 456 | Allows the callee to decline an incoming call invitation. 457 | 458 | @param invitation An \ref agora::rtm::IRemoteCallInvitation "IRemoteCallInvitation" object. 459 | @return 460 | - 0: Success. 461 | - <0: Failure. See #INVITATION_API_CALL_ERR_CODE for the error code. 462 | */ 463 | virtual int refuseRemoteInvitation(IRemoteCallInvitation *invitation) = 0; 464 | 465 | /** 466 | Allows the caller to cancel an outgoing call invitation. 467 | 468 | @param invitation An \ref agora::rtm::ILocalCallInvitation "ILocalCallInvitation" object. 469 | @return 470 | - 0: Success. 471 | - <0: Failure. See #INVITATION_API_CALL_ERR_CODE for the error code. 472 | */ 473 | virtual int cancelLocalInvitation(ILocalCallInvitation *invitation) = 0; 474 | 475 | /** 476 | Creates an ILocalCallInvitation object. 477 | 478 | @param calleeId The Callee's user ID. 479 | @return An \ref agora::rtm::ILocalCallInvitation "ILocalCallInvitation" object. 480 | */ 481 | virtual ILocalCallInvitation *createLocalCallInvitation(const char *calleeId) = 0; 482 | 483 | // sync_call 484 | /** 485 | Releases all resources used by the IRtmCallManager instance. 486 | */ 487 | virtual void release() = 0; 488 | }; 489 | } 490 | } 491 | -------------------------------------------------------------------------------- /windows/sdk/lib/agora_rtm_sdk.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woodemi/Flutter-RTM-Desktop/33b9259d353b20be6c4f9b7c95c9d38df2df3cea/windows/sdk/lib/agora_rtm_sdk.lib --------------------------------------------------------------------------------