├── .github └── workflows │ └── publish.yml ├── .gitignore ├── .metadata ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ ├── main │ ├── AndroidManifest.xml │ └── kotlin │ │ └── org │ │ └── jitsi │ │ └── jitsi_meet_flutter_sdk │ │ ├── JitsiMeetEventStreamHandler.kt │ │ ├── JitsiMeetPlugin.kt │ │ └── WrapperJitsiMeetActivity.kt │ └── test │ └── kotlin │ └── org │ └── jitsi │ └── jitsi_meet_flutter_sdk │ └── JitsiMeetPluginTest.kt ├── example ├── .gitignore ├── README.md ├── analysis_options.yaml ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── org │ │ │ │ │ └── jitsi │ │ │ │ │ └── jitsi_meet_flutter_sdk_example │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable-v21 │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── values-night │ │ │ │ └── styles.xml │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── integration_test │ └── plugin_integration_test.dart ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Podfile │ ├── Podfile.lock │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ ├── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ │ └── LaunchImage.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ └── README.md │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h │ └── RunnerTests │ │ └── RunnerTests.swift ├── lib │ └── main.dart ├── pubspec.lock ├── pubspec.yaml └── test │ └── widget_test.dart ├── ios ├── .gitignore ├── Assets │ └── .gitkeep ├── Classes │ ├── JitsiMeetPlugin.swift │ └── JitsiMeetViewController.swift └── jitsi_meet_flutter_sdk.podspec ├── lib ├── jitsi_meet_flutter_sdk.dart └── src │ ├── features_flag │ ├── feature_flag_video_resolutions.dart │ └── feature_flags.dart │ ├── jitsi_meet.dart │ ├── jitsi_meet_conference_options.dart │ ├── jitsi_meet_event_listener.dart │ ├── jitsi_meet_method_channel.dart │ ├── jitsi_meet_platform_interface.dart │ ├── jitsi_meet_user_info.dart │ └── method_response.dart ├── pubspec.yaml ├── test ├── jitsi_meet_method_channel_test.dart └── jitsi_meet_test.dart ├── update-native-sdks.sh └── update-version.sh /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to pub.dev 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v[0-9]+.[0-9]+.[0-9]+*' 7 | 8 | jobs: 9 | publish: 10 | name: Publish on pub.dev 11 | runs-on: ubuntu-latest 12 | 13 | permissions: 14 | id-token: write # This is required for requesting the JWT 15 | contents: write 16 | 17 | steps: 18 | - name: Clone repository 19 | uses: actions/checkout@v3 20 | 21 | # This step adds the auth token for pub.dev 22 | - name: Set up Dart 23 | uses: dart-lang/setup-dart@v1 24 | with: 25 | sdk: stable 26 | 27 | - name: Set up Flutter 28 | uses: subosito/flutter-action@v2 29 | with: 30 | channel: stable 31 | cache: true 32 | 33 | - name: Publish to pub.dev 34 | id: pub_release 35 | uses: leancodepl/mobile-tools/.github/actions/pub-release@pub-release-v1 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 26 | /pubspec.lock 27 | **/doc/api/ 28 | .dart_tool/ 29 | .packages 30 | build/ 31 | sample_app/ 32 | -------------------------------------------------------------------------------- /.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. 5 | 6 | version: 7 | revision: 796c8ef79279f9c774545b3771238c3098dbefab 8 | channel: stable 9 | 10 | project_type: plugin 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 796c8ef79279f9c774545b3771238c3098dbefab 17 | base_revision: 796c8ef79279f9c774545b3771238c3098dbefab 18 | - platform: android 19 | create_revision: 796c8ef79279f9c774545b3771238c3098dbefab 20 | base_revision: 796c8ef79279f9c774545b3771238c3098dbefab 21 | - platform: ios 22 | create_revision: 796c8ef79279f9c774545b3771238c3098dbefab 23 | base_revision: 796c8ef79279f9c774545b3771238c3098dbefab 24 | 25 | # User provided section 26 | 27 | # List of Local paths (relative to this file) that should be 28 | # ignored by the migrate tool. 29 | # 30 | # Files that are not part of the templates will be ignored by default. 31 | unmanaged_files: 32 | - 'lib/main.dart' 33 | - 'ios/Runner.xcodeproj/project.pbxproj' 34 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 11.2.0 2 | 3 | 4 | 5 | ## 11.1.1 6 | 7 | * chore(deps): update native sdks to 11.1.0 [0039ad8](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/0039ad81157366fabd24b6a9c69ac360c2c52902). 8 | * Exclude Jitsi Meet activity from recents [7fcd733](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/7fcd733e73fe3e0c9236651ff5ca6792f7890f19). 9 | 10 | ## 11.0.2 11 | 12 | * changed customOverflowMenuButtonPressed to customButtonPressed [cc2e16e](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/cc2e16e600231aea0c6e2c407ef3e9a0af09e75d). 13 | * fix(wrapper-activity): Update broadcast event type for custom button press [6ed8c05](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/6ed8c0541291b91fee7cf51c5ccb937048237f70). 14 | 15 | ## 11.0.1 16 | 17 | * chore(deps): update ios native sdk to 11.0.1 [f0fbfe9](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/f0fbfe992015780d6f28330292ebd6776f643eb9). 18 | 19 | ## 11.0.0 20 | 21 | * chore(deps): update native sdks to 11.0.0 [166b62b](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/166b62bfe6d9b653db4eb13666eba77f3454701b). 22 | * Update minimum supported iOS version [cd20737](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/cd207374f5cb1b413471a710e5b03b63a5747174). 23 | * Add ProGuard rules information to README [134610f](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/134610fc724d9879b592c8752df1822584b5ae91). 24 | * [Feature] Add the possibility to control PiP programmatically (#94) [6b981b3](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/6b981b30c0491e899e1f6d3f34b7b1547085306e). 25 | * feat(example): use latest Jitsi Flutter SDK [6fb926a](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/6fb926adb98d8ad1231699fd20edb43a7488414b). 26 | 27 | ## 10.3.0 28 | 29 | * chore(deps): update native sdks to 10.3.0 [1d34c15](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/1d34c15615dcbd8a161b8850bfe0906584598161). 30 | 31 | ## 10.2.1 32 | 33 | * chore(deps): update native sdks to 10.2.1 [bc0d282](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/bc0d282f102aab555079ee8fd25d2b07e4d645ec). 34 | 35 | ## 10.2.0 36 | 37 | * chore(deps): update native sdks to 10.2.0 [5e100b9](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/5e100b98ea3de5069850668aafdb23ba3ddc654b). 38 | * example Podfile update [8074483](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/8074483cb9b33c5604fcbd7f69da601abbd34aab). 39 | * podspec file update [24762dd](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/24762ddb9fd436493abd680e3d89fa136d139d04). 40 | * Update update-native-sdks.sh [629c3be](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/629c3bed823f7ed6bf060fca883be04a93ce95f3). 41 | 42 | ## 10.1.2 43 | 44 | * chore(deps): update native sdks to 10.1.2 [4faf3e6](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/4faf3e6dc12e647527d0e762d1b1e8e67cf09ac1). 45 | 46 | ## 10.1.1 47 | 48 | * chore(sdks, version): bump to 10.1.1 [33aa116](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/33aa116c3d38a9e7e083fa8eeeebbd9bb886d39c). 49 | 50 | ## 10.1.0 51 | 52 | * chore(deps): update native sdks to 10.1.0 [b490e8a](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/b490e8a91afb02dabe83a52a5cf7789372599c11). 53 | * feat(event-listener): Add customOverflowMenuButtonPressed to JitsiMeetEventListener [3171f51](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/3171f518d237440990525c399629e97f1c817058). 54 | * Update README.md [0c1d270](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/0c1d2709f2656e97d02231262a83dffcaaff0ee6). 55 | 56 | ## 10.0.2 57 | 58 | * chore(deps): update native sdks to 10.0.1 [b186bc3](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/b186bc3a54c7719d04e4562b204ba3ee8400d656). 59 | 60 | ## 10.0.1 61 | 62 | * README file update [17e0694](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/17e06949bbfc861508bdb834a617f22427a88a61). 63 | 64 | ## 0.4.2 65 | 66 | * v0.4.2 [44025dc](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/44025dce27d9bc5d358b9bce7f6cea604c6fedd8). 67 | * chore(deps): update native sdks to 9.2.2 [c3b5fa3](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/c3b5fa3e12e7a471df2a1c7cfa963489659c3191). 68 | 69 | ## 0.4.2 70 | 71 | * chore(deps): update native sdks to 9.2.2 [c3b5fa3](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/c3b5fa3e12e7a471df2a1c7cfa963489659c3191). 72 | 73 | ## 0.4.1 74 | 75 | * chore(deps): update native sdks to 9.2.1 [5c6b7f9](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/5c6b7f9cfa18b21be67e2c26bb1715e37b1ba60e). 76 | 77 | ## 0.3.0 78 | 79 | * chore(deps): update native sdks to 9.1.0 [c8d476f](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/c8d476f52db9b84af1e9d7c418c069948c54bea8). 80 | 81 | ## 0.2.2 82 | 83 | * update native sdks to 9.0.2 [6ecd313](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/6ecd3132854280c95e855853e6b80d2bc90fb8c4). 84 | * Enhancement: Added Features Flags & Video Resolution (#13) [d1f08b3](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/d1f08b320c137c9e6fa14a58cfd795732c4882be). 85 | 86 | ## 0.2.0 87 | 88 | * chore(deps): update native sdks to 9.0.0 [98d05bf](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/98d05bf5183f70b5df8625a0e0ce620664344cd7). 89 | * Make it work on lockscreens as well (#10) [d369faa](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/d369faae3462a028c1e2958b9976b48d2d503649). 90 | * Update README.md [ada1230](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/ada123031bfc9ca4916b67c30926840249de2616). 91 | * fix: sample app link [bdf7051](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/bdf705101e4afb96420c375deee8bc9169e4693a). 92 | 93 | ## 0.1.9 94 | 95 | * chore(deps): update native sdks to 8.6.0 [c46ab1e](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/c46ab1e44c369b1441aa736ef8bbd0bf81f2275e). 96 | 97 | ## 0.1.8 98 | 99 | * feat: add update version script [6617dff](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/6617dff4599842c0e8d56c20a875d19299761296). 100 | * feat: add update sdks version script [6bce62a](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/6bce62acb9888050c6b32ab18c3135fe7ed64bea). 101 | * chore(deps): update native sdks to 8.5.0 [d1450c6](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/d1450c6df63b17feb25cea57d8dc2d9493b402ea). 102 | 103 | ## 0.1.7 104 | 105 | * Update native sdks to 8.4.0 [a305704](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/a3057044b0b2652859064192f439309e0578b438). 106 | 107 | ## 0.1.6 108 | 109 | * Update native sdks to 8.3.1 [ef178bf](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/ef178bf258e5d24ad143b1eadc34f546eabceb1b). 110 | 111 | * Format files using dart format [8edfc33](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/8edfc3384a0c6aac95e9707d6eea44dd5452dfb2). 112 | 113 | ## 0.1.5 114 | 115 | * Remove hardcoded call integration flag from swift plugin [cfbe467](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/cfbe467169ed3cd76983e7ef7aff8c0d46805a2a). 116 | 117 | * Improve documentation and pubspec file. 118 | 119 | ## 0.1.4 120 | 121 | * Add code documentation and improve and typos form docs [2e88857](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/2e88857e4db5fbe97fa162fcd9f3bd91c01dfd08). 122 | 123 | ## 0.1.3 124 | 125 | * Update the docs with the link to the sample app and the package add command. 126 | 127 | ## 0.1.2 128 | 129 | * Simplify import with one export file. 130 | 131 | ## 0.1.1 132 | 133 | * Metadata tweaks. 134 | 135 | ## 0.1.0 136 | 137 | * Initial release. 138 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | 204 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jitsi Meet Flutter SDK 2 | 3 | [![License](https://img.shields.io/badge/License-Apache%202.0-darkblue.svg)](https://opensource.org/licenses/Apache-2.0) 4 | [![pub package](https://img.shields.io/badge/pub-v11.2.0-blue)](https://pub.dev/packages/jitsi_meet_flutter_sdk) 5 | 6 | The Jitsi Meet Flutter SDK provides the same user experience as the Jitsi Meet app, in the form of a Flutter plugin so that you can embed and customize Jitsi Meet in your own Flutter app. 7 | 8 | ## Supported platforms 9 | 10 | | Platform | Supported | Notes | 11 | | -------- | --------- | --------------------------------- | 12 | | Android | ✅ | Minimum API level is 24 | 13 | | iOS | ✅ | Minimum supported version is 15.1 | 14 | | Web | ❌ | | 15 | 16 | ## Sample application using the Flutter 17 | 18 | If you want to see how easy integrating the Jitsi Meet Flutter SDK into a Flutter application is, take a look at the
19 | [sample applications repository](https://github.com/jitsi/jitsi-meet-sdk-samples/tree/master/flutter). 20 | 21 | ## Installation 22 | 23 | ### Add dependency 24 | 25 | Add the dependency from command-line 26 | ```bash 27 | $ flutter pub add jitsi_meet_flutter_sdk 28 | ``` 29 | 30 | The command above will add this to the `pubspec.yaml` file in your project (you can do this manually): 31 | ```yaml 32 | dependencies: 33 | jitsi_meet_flutter_sdk: ^11.2.0 34 | ``` 35 | 36 | ### Install 37 | 38 | Install the packages from the terminal: 39 | 40 | ```bash 41 | $ flutter pub get 42 | ``` 43 | 44 | ### Import files 45 | 46 | Import the following files into your dart code: 47 | 48 | ```dart 49 | import 'package:jitsi_meet_flutter_sdk/jitsi_meet_flutter_sdk.dart'; 50 | ``` 51 | 52 | ### Usage 53 | 54 | #### Join meeting 55 | 56 | Firstly, create a `JitsiMeet` object, then call the method `join` from it with a `JitsiMeetConferenceOptions` object 57 | 58 | ```dart 59 | var jitsiMeet = JitsiMeet(); 60 | var options = JitsiMeetConferenceOptions(room: 'jitsiIsAwesome'); 61 | jitsiMeet.join(options); 62 | ``` 63 | 64 | ## Configuration 65 | 66 | ### iOS 67 | 68 | Make sure in `Podfile` from `ios` directory you set the ios version `15.1 or higher` 69 | 70 | ``` 71 | platform :ios, '15.1' 72 | ``` 73 | 74 | The plugin requests camera and microphone access, make sure to include the required entries for `NSCameraUsageDescription` and `NSMicrophoneUsageDescription` in your `Info.plist` file from the `ios/Runner` directory. 75 | 76 | ``` 77 | NSCameraUsageDescription 78 | The app needs access to your camera for meetings. 79 | NSMicrophoneUsageDescription 80 | The app needs access to your microphone for meetings. 81 | ``` 82 | 83 | To use the screen sharing feature requires `Broadcast Upload Extension`. Please check the following to integrate it: [https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-ios-sdk/#screen-sharing-integration](https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-ios-sdk/#screen-sharing-integration). 84 | 85 | ### Android 86 | 87 | Go to `android/app/build.gradle` and make sure that the `minSdkVersion` is set to `at lest 24` 88 | 89 | ``` 90 | android { 91 | ... 92 | defaultConfig { 93 | ... 94 | minSdkVersion 24 95 | } 96 | } 97 | ``` 98 | 99 | 100 | The `application:label` field from the Jitsi Meet Android SDK will conflict with your application's one . Go to `android/app/src/main/AndroidManifest.xml` and add the tools library and `tools:replace="android:label"` to the application tag. 101 | 102 | ```xml 103 | 105 | 110 | ... 111 | 112 | 113 | ``` 114 | 115 | #### Using ProGuard 116 | 117 | If you are using ProGuard (common on Release builds) make sure you add [these rules](https://github.com/jitsi/jitsi-meet/blob/master/android/app/proguard-rules.pro) to your rules file. 118 | 119 | ## Using the API 120 | 121 | ### JitsiMeet 122 | 123 | The `JitsiMeet` class is the entry point for the sdk. It is used to launch the meeting screen, to send and receive all the events. 124 | 125 | 1. #### JitsiMeet() 126 | The constructor for the class. 127 | 128 | 129 | 2. #### join(JitsiMeetConferenceOptions options, [JitsiMeetEventListener? listener]) 130 | Joins a meeting with the given options and optionally a listener is given 131 | 132 | - `options` : meeting options 133 | - `listener` : event listener for events triggered by the native sdks 134 | 135 | 3. #### hangUp() 136 | 137 | The localParticipant leaves the current meeting. 138 | 139 | 4. #### setAudioMuted(bool muted) 140 | 141 | Sets the state of the localParticipant audio muted according to the `muted` parameter. 142 | 143 | 5. #### setVideoMuted(bool muted) 144 | Sets the state of the localParticipant video muted according to the `muted` parameter. 145 | 146 | 6. #### sendEndpointTextMessage({String? to, required String message}) 147 | Sends a message via the data channel to one particular participant or to all of them. If the `to` param is empty, the message will be sent to all the participants in the conference. 148 | 149 | In order to get the participantId, the `participantsJoined` event should be listened for, which have as a parameter the `participantId` and this should be stored somehow. 150 | 151 | 7. #### toggleScreenShare(bool enabled) 152 | Sets the state of the localParticipant screen sharing according to the `enabled` parameter. 153 | 154 | 8. #### openChat([String? to]) 155 | 156 | Opens the chat dialog. If `to` contains a valid participantId, the private chat with that particular participant will be opened. 157 | 158 | 9. #### sendChatMessage({String? to, required String message}) 159 | 160 | Sends a chat message via to one particular participant or to all of them. If the `to` param is empty, the message will be sent to all the participants in the conference. 161 | 162 | In order to get the participantId, the `participantsJoined` event should be listened for, which have as a parameter the `participantId` and this should be stored somehow. 163 | 164 | 10. #### closeChat() 165 | 166 | Closes the chat dialog. 167 | 168 | 11. #### retrieveParticipantsInfo() 169 | 170 | Sends and event that will trigger the `participantsInfoRetrieved` event which will contain participants information 171 | 172 | 173 | ### JitsiMeetConferenceOptions 174 | 175 | This object encapsulates all the options that can be tweaked when joining a conference. 176 | 177 | Example: 178 | 179 | ```dart 180 | var options = JitsiMeetConferenceOptions( 181 | serverURL: "https://meet.jit.si", 182 | room: "jitsiIsAwesomeWithFlutter", 183 | configOverrides: { 184 | "startWithAudioMuted": false, 185 | "startWithVideoMuted": false, 186 | "subject" : "Jitsi with Flutter", 187 | }, 188 | featureFlags: { 189 | "unsaferoomwarning.enabled": false 190 | }, 191 | userInfo: JitsiMeetUserInfo( 192 | displayName: "Flutter user", 193 | email: "user@example.com" 194 | ), 195 | ); 196 | ``` 197 | 198 | - All the values that can be added to the `configOverrides` can be found [here](https://github.com/jitsi/jitsi-meet/blob/master/config.js). 199 | 200 | - All the values that can be added to the `featureFlags` can be found [here](https://github.com/jitsi/jitsi-meet/blob/master/react/features/base/flags/constants.ts). 201 | 202 | - #### JitsiMeetUserInfo({String displayName, String email, String avatar}) 203 | The constructor for the JitsiMeetUserInfo. 204 | P.S. the avatar should be an url. 205 | 206 | ### JitsiMeetEventListener 207 | 208 | This class intends to be used as a listener for events that come from the native sdks. It will receive as arguments the event handlers 209 | 210 | 1. #### conferenceJoined(String url) 211 | 212 | Called when a conference was joined. 213 | - `url` : the conference URL 214 | 215 | 2. #### conferenceTerminated(String url, Object? error) 216 | 217 | Called when the active conference ends, be it because of user choice or because of a failure. 218 | 219 | - `url` : the conference URL 220 | - `error` : missing if the conference finished gracefully, otherwise contains the error message 221 | 222 | 3. #### conferenceWillJoin(String url) 223 | 224 | Called before a conference is joined. 225 | 226 | - url: the conference URL 227 | 228 | 4. #### participantJoined(String? email, String? name, String? role, String? participantId) 229 | 230 | Called when a participant has joined the conference. 231 | 232 | - `email` : the email of the participant. It may not be set if the remote participant didn't set one. 233 | - `name` : the name of the participant. 234 | - `role` : the role of the participant. 235 | - `participantId` : the id of the participant. 236 | 237 | 5. #### participantLeft(String? participantId) 238 | 239 | Called when a participant has left the conference. 240 | 241 | - `participantId` : the id of the participant that left. 242 | 243 | 6. #### audioMutedChanged(bool muted) 244 | 245 | Called when the local participant's audio is muted or unmuted. 246 | 247 | - `muted` : a boolean indicating whether the audio is muted or not. 248 | 249 | 7. #### videoMutedChanged(bool muted) 250 | 251 | Called when the local participant's video is muted or unmuted. 252 | 253 | - `muted` : a boolean indicating whether the video is muted or not. 254 | 255 | 8. #### endpointTextMessageReceived(String senderId, String message) 256 | 257 | Called when an endpoint text message is received. 258 | 259 | - `senderId` : the participantId of the sender 260 | - `message` : the content. 261 | 262 | 9. #### screenShareToggled(String participantId, bool sharing) 263 | 264 | Called when a participant starts or stops sharing his screen. 265 | 266 | - `participantId` : the id of the participant 267 | - `sharing` : the state of screen share 268 | 269 | 10. #### chatMessageReceived(String senderId, String message, bool isPrivate, String? timestamp) 270 | 271 | Called when a chat text message is received. 272 | 273 | - `senderId` : the id of the participant that sent the message. 274 | - `message` : the content of the message. 275 | - `isPrivate` : true if the message is private, false otherwise. 276 | - `timestamp` : the (optional) timestamp of the message. 277 | 278 | 11. #### chatToggled(bool isOpen) 279 | 280 | Called when the chat dialog is opened or closed. 281 | 282 | - `isOpen` : true if the chat dialog is open, false otherwise. 283 | 284 | 12. #### participantsInfoRetrieved(String participantsInfo) 285 | Called when `retrieveParticipantsInfo` action is called 286 | 287 | - `participantsInfo` : a list of participants information as a string. 288 | 289 | 13. #### readyToClose() 290 | Called when the SDK is ready to be closed. No meeting is happening at this point. 291 | 292 | #### Example of listener: 293 | 294 | ```dart 295 | var listener = JitsiMeetEventListener( 296 | conferenceJoined: (url) { 297 | debugPrint("conferenceJoined: url: $url"); 298 | }, 299 | 300 | participantJoined: (email, name, role, participantId) { 301 | debugPrint( 302 | "participantJoined: email: $email, name: $name, role: $role, " 303 | "participantId: $participantId", 304 | ); 305 | participants.add(participantId!); 306 | }, 307 | 308 | chatMessageReceived: (senderId, message, isPrivate) { 309 | debugPrint( 310 | "chatMessageReceived: senderId: $senderId, message: $message, " 311 | "isPrivate: $isPrivate", 312 | ); 313 | }, 314 | 315 | readyToClose: () { 316 | debugPrint("readyToClose"); 317 | }, 318 | ); 319 | ``` 320 | 321 | ## References 322 | 323 | While building this project inspiration from https://github.com/saibotma/jitsi_meet_wrapper was taken. 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | # Additional information about this file can be found at 4 | # https://dart.dev/guides/language/analysis-options 5 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .cxx 10 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | group 'org.jitsi.jitsi_meet_flutter_sdk' 2 | version '1.0-SNAPSHOT' 3 | 4 | buildscript { 5 | ext.kotlin_version = '1.7.21' 6 | repositories { 7 | google() 8 | mavenCentral() 9 | } 10 | 11 | dependencies { 12 | classpath 'com.android.tools.build:gradle:8.0.1' 13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 14 | } 15 | } 16 | 17 | rootProject.allprojects { 18 | repositories { 19 | maven { 20 | url "https://github.com/jitsi/jitsi-maven-repository/raw/master/releases" 21 | } 22 | google() 23 | mavenCentral() 24 | maven { url 'https://www.jitpack.io' } 25 | } 26 | } 27 | 28 | apply plugin: 'com.android.library' 29 | apply plugin: 'kotlin-android' 30 | 31 | android { 32 | namespace 'org.jitsi.jitsi_meet_flutter_sdk' 33 | compileSdkVersion 34 34 | 35 | compileOptions { 36 | sourceCompatibility JavaVersion.VERSION_1_8 37 | targetCompatibility JavaVersion.VERSION_1_8 38 | } 39 | 40 | kotlinOptions { 41 | jvmTarget = '1.8' 42 | } 43 | 44 | sourceSets { 45 | main.java.srcDirs += 'src/main/kotlin' 46 | test.java.srcDirs += 'src/test/kotlin' 47 | } 48 | 49 | defaultConfig { 50 | minSdkVersion 24 51 | } 52 | 53 | dependencies { 54 | testImplementation 'org.jetbrains.kotlin:kotlin-test' 55 | testImplementation 'org.mockito:mockito-core:5.0.0' 56 | } 57 | 58 | testOptions { 59 | unitTests.all { 60 | useJUnitPlatform() 61 | 62 | testLogging { 63 | events "passed", "skipped", "failed", "standardOut", "standardError" 64 | outputs.upToDateWhen {false} 65 | showStandardStreams = true 66 | } 67 | } 68 | } 69 | } 70 | 71 | dependencies { 72 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 73 | implementation ('org.jitsi.react:jitsi-meet-sdk:11.2.0') { transitive = true } 74 | } 75 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | ## For more details on how to configure your build environment visit 2 | # http://www.gradle.org/docs/current/userguide/build_environment.html 3 | # 4 | # Specifies the JVM arguments used for the daemon process. 5 | # The setting is particularly useful for tweaking memory settings. 6 | # Default value: -Xmx1024m -XX:MaxPermSize=256m 7 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 8 | # 9 | # When configured, Gradle will run in incubating parallel mode. 10 | # This option should only be used with decoupled projects. More details, visit 11 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 12 | # org.gradle.parallel=true 13 | #Thu Aug 08 11:03:52 EEST 2024 14 | android.useAndroidX=true 15 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jitsi/jitsi-meet-flutter-sdk/5a2d2be1d172952b92b01518c4801e778c47f4d5/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Aug 08 11:33:17 EEST 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'jitsi_meet_flutter_sdk' 2 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /android/src/main/kotlin/org/jitsi/jitsi_meet_flutter_sdk/JitsiMeetEventStreamHandler.kt: -------------------------------------------------------------------------------- 1 | package org.jitsi.jitsi_meet_flutter_sdk 2 | 3 | import io.flutter.plugin.common.EventChannel 4 | import java.io.Serializable 5 | 6 | class JitsiMeetEventStreamHandler private constructor() : EventChannel.StreamHandler { 7 | companion object { 8 | val instance = JitsiMeetEventStreamHandler() 9 | } 10 | 11 | private var eventSink: EventChannel.EventSink? = null 12 | 13 | override fun onListen(arguments: Any?, eventSink: EventChannel.EventSink?) { 14 | this.eventSink = eventSink 15 | } 16 | 17 | override fun onCancel(arguments: Any?) { 18 | eventSink = null 19 | } 20 | 21 | fun conferenceJoined(data: MutableMap?) { 22 | eventSink?.success(mapOf("event" to "conferenceJoined", "data" to data)) 23 | } 24 | 25 | fun conferenceTerminated(data: MutableMap?) { 26 | eventSink?.success(mapOf("event" to "conferenceTerminated", "data" to data)) 27 | } 28 | 29 | fun conferenceWillJoin(data: MutableMap?) { 30 | eventSink?.success(mapOf("event" to "conferenceWillJoin", "data" to data)) 31 | } 32 | 33 | fun participantJoined(data: MutableMap?) { 34 | eventSink?.success(mapOf("event" to "participantJoined", "data" to data)) 35 | } 36 | 37 | fun participantLeft(data: MutableMap?) { 38 | eventSink?.success(mapOf("event" to "participantLeft", "data" to data)) 39 | } 40 | 41 | fun audioMutedChanged(data: MutableMap?) { 42 | eventSink?.success(mapOf("event" to "audioMutedChanged", "data" to data)) 43 | } 44 | 45 | fun videoMutedChanged(data: MutableMap?) { 46 | eventSink?.success(mapOf("event" to "videoMutedChanged", "data" to data)) 47 | } 48 | 49 | fun endpointTextMessageReceived(data: MutableMap?) { 50 | eventSink?.success(mapOf("event" to "endpointTextMessageReceived", "data" to data)) 51 | } 52 | 53 | fun screenShareToggled(data: MutableMap?) { 54 | eventSink?.success(mapOf("event" to "screenShareToggled", "data" to data)) 55 | } 56 | 57 | fun chatMessageReceived(data: MutableMap?) { 58 | eventSink?.success(mapOf("event" to "chatMessageReceived", "data" to data)) 59 | } 60 | 61 | fun chatToggled(data: MutableMap?) { 62 | eventSink?.success(mapOf("event" to "chatToggled", "data" to data)) 63 | } 64 | 65 | fun participantsInfoRetrieved(data: MutableMap?) { 66 | eventSink?.success(mapOf("event" to "participantsInfoRetrieved", "data" to data)) 67 | } 68 | 69 | fun readyToClose() { 70 | eventSink?.success(mapOf("event" to "readyToClose")) 71 | } 72 | 73 | fun onOpened() { 74 | eventSink?.success(mapOf("event" to "opened")) 75 | } 76 | 77 | fun customButtonPressed(data: MutableMap?) { 78 | eventSink?.success(mapOf("event" to "customButtonPressed", "data" to data)) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /android/src/main/kotlin/org/jitsi/jitsi_meet_flutter_sdk/JitsiMeetPlugin.kt: -------------------------------------------------------------------------------- 1 | package org.jitsi.jitsi_meet_flutter_sdk 2 | 3 | import android.app.Activity 4 | import androidx.annotation.NonNull 5 | import android.content.Intent 6 | import android.os.Bundle 7 | import androidx.localbroadcastmanager.content.LocalBroadcastManager 8 | 9 | import io.flutter.embedding.engine.plugins.activity.ActivityAware 10 | import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding 11 | import io.flutter.embedding.engine.plugins.FlutterPlugin 12 | import io.flutter.plugin.common.EventChannel 13 | import io.flutter.plugin.common.MethodCall 14 | import io.flutter.plugin.common.MethodChannel 15 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler 16 | import io.flutter.plugin.common.MethodChannel.Result 17 | import org.jitsi.meet.sdk.BroadcastIntentHelper 18 | import org.jitsi.meet.sdk.JitsiMeetConferenceOptions 19 | import org.jitsi.meet.sdk.JitsiMeetUserInfo 20 | import org.jitsi.meet.sdk.BroadcastAction 21 | import org.jitsi.meet.sdk.* 22 | import java.net.URL 23 | 24 | /** JitsiMeetPlugin */ 25 | class JitsiMeetPlugin: FlutterPlugin, MethodCallHandler, ActivityAware { 26 | private lateinit var methodChannel : MethodChannel 27 | private lateinit var eventChannel: EventChannel 28 | private val eventStreamHandler = JitsiMeetEventStreamHandler.instance 29 | private var activity: Activity? = null 30 | 31 | override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { 32 | methodChannel = MethodChannel(flutterPluginBinding.binaryMessenger, "jitsi_meet_flutter_sdk") 33 | methodChannel.setMethodCallHandler(this) 34 | 35 | eventChannel = EventChannel(flutterPluginBinding.binaryMessenger, "jitsi_meet_flutter_sdk_events") 36 | eventChannel.setStreamHandler(eventStreamHandler) 37 | } 38 | 39 | override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { 40 | when (call.method) { 41 | "getPlatformVersion" -> {result.success("Android ${android.os.Build.VERSION.RELEASE}")} 42 | "join" -> join(call, result) 43 | "hangUp" -> hangUp(call, result) 44 | "setAudioMuted" -> setAudioMuted(call, result) 45 | "setVideoMuted" -> setVideoMuted(call, result) 46 | "sendEndpointTextMessage" -> sendEndpointTextMessage(call, result) 47 | "toggleScreenShare" -> toggleScreenShare(call, result) 48 | "openChat" -> openChat(call, result) 49 | "sendChatMessage" -> sendChatMessage(call, result) 50 | "closeChat" -> closeChat(call, result) 51 | "retrieveParticipantsInfo" -> retrieveParticipantsInfo(call, result) 52 | "enterPiP" -> enterPiP(call, result) 53 | else -> result.notImplemented() 54 | } 55 | } 56 | 57 | override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { 58 | methodChannel.setMethodCallHandler(null) 59 | } 60 | 61 | override fun onDetachedFromActivity() { 62 | this.activity = null 63 | } 64 | 65 | override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { 66 | onAttachedToActivity(binding) 67 | } 68 | 69 | override fun onAttachedToActivity(binding: ActivityPluginBinding) { 70 | this.activity = binding.activity 71 | } 72 | 73 | override fun onDetachedFromActivityForConfigChanges() { 74 | onDetachedFromActivity() 75 | } 76 | private fun join(call: MethodCall, result: Result) { 77 | val serverURL = if (call.argument("serverURL") != null) URL(call.argument("serverURL")) else null 78 | val room: String? = call.argument("room") 79 | val token: String? = call.argument("token") 80 | val featureFlags = call.argument>("featureFlags") 81 | val configOverrides = call.argument>("configOverrides") 82 | val rawUserInfo = call.argument>("userInfo") 83 | val displayName = rawUserInfo?.get("displayName") 84 | val email = rawUserInfo?.get("email") 85 | val avatar = if (rawUserInfo?.get("avatar") != null) URL(rawUserInfo.get("avatar")) else null 86 | val userInfo = JitsiMeetUserInfo().apply { 87 | if (displayName != null) this.displayName = displayName 88 | if (email != null) this.email = email 89 | if (avatar != null) this.avatar = avatar 90 | } 91 | 92 | val options = JitsiMeetConferenceOptions.Builder().run { 93 | if (serverURL != null) setServerURL(serverURL) 94 | if (room != null) setRoom(room) 95 | if (token != null) setToken(token) 96 | 97 | configOverrides?.forEach { (key, value) -> 98 | when (value) { 99 | is Boolean -> setConfigOverride(key, value) 100 | is Int -> setConfigOverride(key, value) 101 | is Array<*> -> setConfigOverride(key, value as Array) 102 | is List<*> -> { 103 | if (value.isNotEmpty() && value[0] is Map<*, *>) { 104 | val bundles = ArrayList() 105 | for (map in value) { 106 | val bundle = Bundle() 107 | (map as Map<*, *>).forEach { (k, v) -> 108 | bundle.putString(k.toString(), v.toString()) 109 | } 110 | bundles.add(bundle) 111 | } 112 | setConfigOverride(key, bundles) 113 | } else { 114 | setConfigOverride(key, value.toString()) 115 | } 116 | } 117 | else -> setConfigOverride(key, value.toString()) 118 | } 119 | } 120 | featureFlags?.forEach { (key, value) -> 121 | when (value) { 122 | is Boolean -> setFeatureFlag(key, value) 123 | is Int -> setFeatureFlag(key, value) 124 | else -> setFeatureFlag(key, value.toString()) 125 | } 126 | } 127 | if (userInfo != null) setUserInfo(userInfo) 128 | build() 129 | } 130 | 131 | WrapperJitsiMeetActivity.launch(activity!!, options) 132 | result.success("Successfully joined meeting $room") 133 | } 134 | 135 | private fun hangUp(call: MethodCall, result: Result) { 136 | val hangUpBroadcastIntent = BroadcastIntentHelper.buildHangUpIntent(); 137 | LocalBroadcastManager.getInstance(activity!!.applicationContext).sendBroadcast(hangUpBroadcastIntent) 138 | result.success("Succesfullly hung up") 139 | } 140 | 141 | private fun setAudioMuted(call: MethodCall, result: Result) { 142 | val muted = call.argument("muted") ?: false 143 | val audioMuteBroadcastIntent: Intent = BroadcastIntentHelper.buildSetAudioMutedIntent(muted) 144 | LocalBroadcastManager.getInstance(activity!!.applicationContext).sendBroadcast(audioMuteBroadcastIntent) 145 | result.success("Successfully set audio $muted") 146 | } 147 | 148 | private fun setVideoMuted(call: MethodCall, result: Result) { 149 | val muted = call.argument("muted") ?: false 150 | val videoMuteBroadcastIntent: Intent = BroadcastIntentHelper.buildSetVideoMutedIntent(muted) 151 | LocalBroadcastManager.getInstance(activity!!.applicationContext).sendBroadcast(videoMuteBroadcastIntent) 152 | result.success("Successfully set video $muted") 153 | } 154 | 155 | private fun sendEndpointTextMessage(call: MethodCall, result: Result) { 156 | val to = call.argument("to") 157 | val message = call.argument("message") 158 | val sendEndpointTextMessageBroadcastIntent: Intent = BroadcastIntentHelper.buildSendEndpointTextMessageIntent(to, message) 159 | LocalBroadcastManager.getInstance(activity!!.applicationContext).sendBroadcast(sendEndpointTextMessageBroadcastIntent) 160 | result.success("Successfully send endpoint text message $to") 161 | } 162 | 163 | private fun toggleScreenShare(call: MethodCall, result: Result) { 164 | val enabled = call.argument("enabled") ?: false 165 | val toggleScreenShareIntent: Intent = BroadcastIntentHelper.buildToggleScreenShareIntent(enabled) 166 | LocalBroadcastManager.getInstance(activity!!.applicationContext).sendBroadcast(toggleScreenShareIntent) 167 | result.success("Successfully toggled screen share $enabled") 168 | } 169 | 170 | private fun openChat(call: MethodCall, result: Result) { 171 | val to = call.argument("to") 172 | val openChatIntent: Intent = BroadcastIntentHelper.buildOpenChatIntent(to) 173 | LocalBroadcastManager.getInstance(activity!!.applicationContext).sendBroadcast(openChatIntent) 174 | result.success("Successfully opened chat $to") 175 | } 176 | 177 | private fun sendChatMessage(call: MethodCall, result: Result) { 178 | val to = call.argument("to") 179 | val message = call.argument("message") 180 | val sendChatMessageIntent: Intent = BroadcastIntentHelper.buildSendChatMessageIntent(to, message) 181 | LocalBroadcastManager.getInstance(activity!!.applicationContext).sendBroadcast(sendChatMessageIntent) 182 | result.success("Successfully sent chat message $to") 183 | 184 | } 185 | 186 | private fun closeChat(call: MethodCall, result: Result) { 187 | val closeChatIntent: Intent = BroadcastIntentHelper.buildCloseChatIntent() 188 | LocalBroadcastManager.getInstance(activity!!.applicationContext).sendBroadcast(closeChatIntent) 189 | result.success("Successfully closed chat") 190 | } 191 | 192 | private fun retrieveParticipantsInfo(call: MethodCall, result: Result) { 193 | val retrieveParticipantsInfoIntent: Intent = Intent("org.jitsi.meet.RETRIEVE_PARTICIPANTS_INFO"); 194 | LocalBroadcastManager.getInstance(activity!!.applicationContext).sendBroadcast(retrieveParticipantsInfoIntent) 195 | result.success("Successfully retrieved participants info") 196 | } 197 | 198 | private fun enterPiP(call: MethodCall, result: Result) { 199 | val enterPiPIntent = Intent("org.jitsi.meet.ENTER_PICTURE_IN_PICTURE"); 200 | LocalBroadcastManager.getInstance(activity!!.applicationContext).sendBroadcast(enterPiPIntent) 201 | result.success("Successfully entered PiP") 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /android/src/main/kotlin/org/jitsi/jitsi_meet_flutter_sdk/WrapperJitsiMeetActivity.kt: -------------------------------------------------------------------------------- 1 | package org.jitsi.jitsi_meet_flutter_sdk 2 | 3 | import android.app.Activity 4 | import android.content.BroadcastReceiver 5 | import android.content.Context 6 | import android.content.Intent 7 | import android.content.IntentFilter 8 | import android.os.Bundle 9 | import androidx.localbroadcastmanager.content.LocalBroadcastManager 10 | import org.jitsi.meet.sdk.BroadcastEvent 11 | import org.jitsi.meet.sdk.JitsiMeetActivity 12 | import android.app.KeyguardManager 13 | import android.view.WindowManager 14 | import android.os.Build 15 | import org.jitsi.meet.sdk.JitsiMeetConferenceOptions 16 | 17 | class WrapperJitsiMeetActivity : JitsiMeetActivity() { 18 | private val eventStreamHandler = JitsiMeetEventStreamHandler.instance 19 | private val broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() { 20 | override fun onReceive(context: Context, intent: Intent) { 21 | this@WrapperJitsiMeetActivity.onBroadcastReceived(intent) 22 | } 23 | } 24 | 25 | companion object { 26 | fun launch(context: Context, options: JitsiMeetConferenceOptions?) { 27 | val intent = Intent(context, WrapperJitsiMeetActivity::class.java) 28 | intent.action = "org.jitsi.meet.CONFERENCE" 29 | intent.putExtra("JitsiMeetConferenceOptions", options) 30 | if (context !is Activity) { 31 | intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK 32 | } 33 | context.startActivity(intent) 34 | } 35 | } 36 | 37 | override fun onCreate(savedInstanceState: Bundle?) { 38 | showOnLockscreen() 39 | super.onCreate(savedInstanceState) 40 | registerForBroadcastMessages() 41 | } 42 | 43 | private fun showOnLockscreen() { 44 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { 45 | setShowWhenLocked(true) 46 | setTurnScreenOn(true) 47 | } else { 48 | window.addFlags( 49 | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or 50 | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or 51 | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON 52 | ) 53 | } 54 | } 55 | 56 | private fun registerForBroadcastMessages() { 57 | val intentFilter = IntentFilter() 58 | for (eventType in BroadcastEvent.Type.values()) { 59 | intentFilter.addAction(eventType.action) 60 | } 61 | intentFilter.addAction("org.jitsi.meet.ENTER_PICTURE_IN_PICTURE") 62 | LocalBroadcastManager.getInstance(this) 63 | .registerReceiver(this.broadcastReceiver, intentFilter) 64 | } 65 | 66 | private fun onBroadcastReceived(intent: Intent?) { 67 | if (intent != null) { 68 | if (intent.action == "org.jitsi.meet.ENTER_PICTURE_IN_PICTURE") { 69 | enterPiP() 70 | } else { 71 | val event = BroadcastEvent(intent) 72 | val data = event.data 73 | when (event.type.action!!) { 74 | BroadcastEvent.Type.CONFERENCE_JOINED.action -> eventStreamHandler.conferenceJoined(data) 75 | BroadcastEvent.Type.CONFERENCE_TERMINATED.action -> eventStreamHandler.conferenceTerminated( 76 | data 77 | ) 78 | 79 | BroadcastEvent.Type.CONFERENCE_WILL_JOIN.action -> eventStreamHandler.conferenceWillJoin( 80 | data 81 | ) 82 | 83 | BroadcastEvent.Type.PARTICIPANT_JOINED.action -> eventStreamHandler.participantJoined(data) 84 | BroadcastEvent.Type.PARTICIPANT_LEFT.action -> eventStreamHandler.participantLeft(data) 85 | BroadcastEvent.Type.AUDIO_MUTED_CHANGED.action -> eventStreamHandler.audioMutedChanged(data) 86 | BroadcastEvent.Type.VIDEO_MUTED_CHANGED.action -> eventStreamHandler.videoMutedChanged(data) 87 | BroadcastEvent.Type.ENDPOINT_TEXT_MESSAGE_RECEIVED.action -> eventStreamHandler.endpointTextMessageReceived( 88 | data 89 | ) 90 | 91 | BroadcastEvent.Type.SCREEN_SHARE_TOGGLED.action -> eventStreamHandler.screenShareToggled( 92 | data 93 | ) 94 | 95 | BroadcastEvent.Type.CHAT_MESSAGE_RECEIVED.action -> eventStreamHandler.chatMessageReceived( 96 | data 97 | ) 98 | 99 | BroadcastEvent.Type.CHAT_TOGGLED.action -> eventStreamHandler.chatToggled(data) 100 | BroadcastEvent.Type.PARTICIPANTS_INFO_RETRIEVED.action -> eventStreamHandler.participantsInfoRetrieved( 101 | data 102 | ) 103 | 104 | BroadcastEvent.Type.READY_TO_CLOSE.action -> eventStreamHandler.readyToClose() 105 | 106 | BroadcastEvent.Type.CUSTOM_BUTTON_PRESSED.action -> eventStreamHandler.customButtonPressed( 107 | data 108 | ) 109 | 110 | else -> {} 111 | } 112 | } 113 | } 114 | } 115 | 116 | override fun onDestroy() { 117 | LocalBroadcastManager.getInstance(this).unregisterReceiver(this.broadcastReceiver) 118 | super.onDestroy() 119 | } 120 | 121 | fun enterPiP() { 122 | jitsiView?.enterPictureInPicture() 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /android/src/test/kotlin/org/jitsi/jitsi_meet_flutter_sdk/JitsiMeetPluginTest.kt: -------------------------------------------------------------------------------- 1 | package org.jitsi.jitsi_meet_flutter_sdk 2 | 3 | import io.flutter.plugin.common.MethodCall 4 | import io.flutter.plugin.common.MethodChannel 5 | import kotlin.test.Test 6 | import org.mockito.Mockito 7 | 8 | /* 9 | * This demonstrates a simple unit test of the Kotlin portion of this plugin's implementation. 10 | * 11 | * Once you have built the plugin's example app, you can run these tests from the command 12 | * line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or 13 | * you can run them directly from IDEs that support JUnit such as Android Studio. 14 | */ 15 | 16 | internal class JitsiMeetPluginTest { 17 | @Test 18 | fun onMethodCall_getPlatformVersion_returnsExpectedValue() { 19 | val plugin = JitsiMeetPlugin() 20 | 21 | val call = MethodCall("getPlatformVersion", null) 22 | val mockResult: MethodChannel.Result = Mockito.mock(MethodChannel.Result::class.java) 23 | plugin.onMethodCall(call, mockResult) 24 | 25 | Mockito.verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE) 26 | } 27 | 28 | @Test 29 | fun onMethodCall_join_returnsExpectedValue() { 30 | val plugin = JitsiMeetPlugin() 31 | 32 | val call = MethodCall("join", null) 33 | val mockResult: MethodChannel.Result = Mockito.mock(MethodChannel.Result::class.java) 34 | plugin.onMethodCall(call, mockResult) 35 | 36 | Mockito.verify(mockResult).success("Succesfully joined room") 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .packages 31 | .pub-cache/ 32 | .pub/ 33 | /build/ 34 | 35 | # Symbolication related 36 | app.*.symbols 37 | 38 | # Obfuscation related 39 | app.*.map.json 40 | 41 | # Android Studio will place build artifacts here 42 | /android/app/debug 43 | /android/app/profile 44 | /android/app/release 45 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # jitsi_meet_flutter_sdk_example 2 | 3 | Demonstrates how to use the jitsi_meet_flutter_sdk 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://docs.flutter.dev/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) 13 | 14 | For help getting started with Flutter development, view the 15 | [online documentation](https://docs.flutter.dev/), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | namespace "org.jitsi.jitsi_meet_flutter_sdk_example" 30 | compileSdkVersion 34 31 | ndkVersion flutter.ndkVersion 32 | 33 | compileOptions { 34 | sourceCompatibility JavaVersion.VERSION_1_8 35 | targetCompatibility JavaVersion.VERSION_1_8 36 | } 37 | 38 | kotlinOptions { 39 | jvmTarget = '1.8' 40 | } 41 | 42 | sourceSets { 43 | main.java.srcDirs += 'src/main/kotlin' 44 | } 45 | 46 | defaultConfig { 47 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 48 | applicationId "org.jitsi.jitsi_meet_flutter_sdk_example" 49 | // You can update the following values to match your application needs. 50 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. 51 | minSdkVersion 24 52 | targetSdkVersion flutter.targetSdkVersion 53 | versionCode flutterVersionCode.toInteger() 54 | versionName flutterVersionName 55 | } 56 | 57 | buildTypes { 58 | release { 59 | // TODO: Add your own signing config for the release build. 60 | // Signing with the debug keys for now, so `flutter run --release` works. 61 | signingConfig signingConfigs.debug 62 | } 63 | } 64 | } 65 | 66 | flutter { 67 | source '../..' 68 | } 69 | 70 | dependencies { 71 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 72 | } 73 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 15 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/org/jitsi/jitsi_meet_flutter_sdk_example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package org.jitsi.jitsi_meet_flutter_sdk_example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jitsi/jitsi-meet-flutter-sdk/5a2d2be1d172952b92b01518c4801e778c47f4d5/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jitsi/jitsi-meet-flutter-sdk/5a2d2be1d172952b92b01518c4801e778c47f4d5/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jitsi/jitsi-meet-flutter-sdk/5a2d2be1d172952b92b01518c4801e778c47f4d5/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jitsi/jitsi-meet-flutter-sdk/5a2d2be1d172952b92b01518c4801e778c47f4d5/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jitsi/jitsi-meet-flutter-sdk/5a2d2be1d172952b92b01518c4801e778c47f4d5/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.9.0' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:8.0.1' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | tasks.register("clean", Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Aug 08 11:41:31 EEST 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /example/integration_test/plugin_integration_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter integration test. 2 | // 3 | // Since integration tests run in a full Flutter application, they can interact 4 | // with the host side of a plugin implementation, unlike Dart unit tests. 5 | // 6 | // For more information about Flutter integration tests, please see 7 | // https://docs.flutter.dev/cookbook/testing/integration/introduction 8 | 9 | import 'package:flutter_test/flutter_test.dart'; 10 | import 'package:integration_test/integration_test.dart'; 11 | 12 | import 'package:jitsi_meet_flutter_sdk/jitsi_meet_flutter_sdk.dart'; 13 | 14 | void main() { 15 | IntegrationTestWidgetsFlutterBinding.ensureInitialized(); 16 | 17 | testWidgets('getPlatformVersion test', (WidgetTester tester) async { 18 | final JitsiMeet plugin = JitsiMeet(); 19 | final String? version = await plugin.getPlatformVersion(); 20 | // The version string depends on the host platform running the test, so 21 | // just assert that some non-empty string is returned. 22 | expect(version?.isNotEmpty, true); 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '15.1' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | target 'RunnerTests' do 36 | inherit! :search_paths 37 | end 38 | end 39 | 40 | post_install do |installer| 41 | installer.pods_project.targets.each do |target| 42 | flutter_additional_ios_build_settings(target) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - Giphy (2.2.12): 4 | - libwebp 5 | - integration_test (0.0.1): 6 | - Flutter 7 | - jitsi_meet_flutter_sdk (11.1.1): 8 | - Flutter 9 | - JitsiMeetSDK (= 11.2.0) 10 | - JitsiMeetSDK (11.2.0): 11 | - Giphy (= 2.2.12) 12 | - JitsiWebRTC (~> 124.0) 13 | - JitsiWebRTC (124.0.2) 14 | - libwebp (1.5.0): 15 | - libwebp/demux (= 1.5.0) 16 | - libwebp/mux (= 1.5.0) 17 | - libwebp/sharpyuv (= 1.5.0) 18 | - libwebp/webp (= 1.5.0) 19 | - libwebp/demux (1.5.0): 20 | - libwebp/webp 21 | - libwebp/mux (1.5.0): 22 | - libwebp/demux 23 | - libwebp/sharpyuv (1.5.0) 24 | - libwebp/webp (1.5.0): 25 | - libwebp/sharpyuv 26 | 27 | DEPENDENCIES: 28 | - Flutter (from `Flutter`) 29 | - integration_test (from `.symlinks/plugins/integration_test/ios`) 30 | - jitsi_meet_flutter_sdk (from `.symlinks/plugins/jitsi_meet_flutter_sdk/ios`) 31 | 32 | SPEC REPOS: 33 | trunk: 34 | - Giphy 35 | - JitsiMeetSDK 36 | - JitsiWebRTC 37 | - libwebp 38 | 39 | EXTERNAL SOURCES: 40 | Flutter: 41 | :path: Flutter 42 | integration_test: 43 | :path: ".symlinks/plugins/integration_test/ios" 44 | jitsi_meet_flutter_sdk: 45 | :path: ".symlinks/plugins/jitsi_meet_flutter_sdk/ios" 46 | 47 | SPEC CHECKSUMS: 48 | Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 49 | Giphy: 83628960ed04e1c3428ff1b4fb2b027f65e82f50 50 | integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e 51 | jitsi_meet_flutter_sdk: e0be5c00174a69660aebfdcfd49bf48a87eed8d2 52 | JitsiMeetSDK: 414bcd948c2fd082a4078bc16813cb36936d868a 53 | JitsiWebRTC: b47805ab5668be38e7ee60e2258f49badfe8e1d0 54 | libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8 55 | 56 | PODFILE CHECKSUM: a33f59345450d290a8dbcf342843f42525e4989f 57 | 58 | COCOAPODS: 1.16.2 59 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @main 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jitsi/jitsi-meet-flutter-sdk/5a2d2be1d172952b92b01518c4801e778c47f4d5/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jitsi/jitsi-meet-flutter-sdk/5a2d2be1d172952b92b01518c4801e778c47f4d5/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jitsi/jitsi-meet-flutter-sdk/5a2d2be1d172952b92b01518c4801e778c47f4d5/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jitsi/jitsi-meet-flutter-sdk/5a2d2be1d172952b92b01518c4801e778c47f4d5/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jitsi/jitsi-meet-flutter-sdk/5a2d2be1d172952b92b01518c4801e778c47f4d5/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jitsi/jitsi-meet-flutter-sdk/5a2d2be1d172952b92b01518c4801e778c47f4d5/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jitsi/jitsi-meet-flutter-sdk/5a2d2be1d172952b92b01518c4801e778c47f4d5/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jitsi/jitsi-meet-flutter-sdk/5a2d2be1d172952b92b01518c4801e778c47f4d5/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jitsi/jitsi-meet-flutter-sdk/5a2d2be1d172952b92b01518c4801e778c47f4d5/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jitsi/jitsi-meet-flutter-sdk/5a2d2be1d172952b92b01518c4801e778c47f4d5/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jitsi/jitsi-meet-flutter-sdk/5a2d2be1d172952b92b01518c4801e778c47f4d5/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jitsi/jitsi-meet-flutter-sdk/5a2d2be1d172952b92b01518c4801e778c47f4d5/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jitsi/jitsi-meet-flutter-sdk/5a2d2be1d172952b92b01518c4801e778c47f4d5/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jitsi/jitsi-meet-flutter-sdk/5a2d2be1d172952b92b01518c4801e778c47f4d5/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jitsi/jitsi-meet-flutter-sdk/5a2d2be1d172952b92b01518c4801e778c47f4d5/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jitsi/jitsi-meet-flutter-sdk/5a2d2be1d172952b92b01518c4801e778c47f4d5/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jitsi/jitsi-meet-flutter-sdk/5a2d2be1d172952b92b01518c4801e778c47f4d5/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jitsi/jitsi-meet-flutter-sdk/5a2d2be1d172952b92b01518c4801e778c47f4d5/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Jitsi Meet Flutter Sdk 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | jitsi_meet_flutter_sdk_example 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSAllowsArbitraryLoads 30 | 31 | NSExceptionDomains 32 | 33 | localhost 34 | 35 | NSTemporaryExceptionAllowsInsecureHTTPLoads 36 | 37 | 38 | 39 | 40 | NSCameraUsageDescription 41 | The camera is needed for making calls 42 | NSMicrophoneUsageDescription 43 | The microphone is needed for making call 44 | NSLocalNetworkUsageDescription 45 | Local network access is needed for P2P connections. 46 | UIBackgroundModes 47 | 48 | audio 49 | fetch 50 | voip 51 | 52 | UILaunchStoryboardName 53 | LaunchScreen 54 | UIRequiredDeviceCapabilities 55 | 56 | armv7 57 | 58 | UIMainStoryboardFile 59 | Main 60 | UISupportedInterfaceOrientations 61 | 62 | UIInterfaceOrientationPortrait 63 | UIInterfaceOrientationLandscapeLeft 64 | UIInterfaceOrientationLandscapeRight 65 | 66 | UISupportedInterfaceOrientations~ipad 67 | 68 | UIInterfaceOrientationPortrait 69 | UIInterfaceOrientationPortraitUpsideDown 70 | UIInterfaceOrientationLandscapeLeft 71 | UIInterfaceOrientationLandscapeRight 72 | 73 | UIViewControllerBasedStatusBarAppearance 74 | 75 | CADisableMinimumFrameDurationOnPhone 76 | 77 | UIApplicationSupportsIndirectInputEvents 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | @testable import jitsi_meet_flutter_sdk 6 | 7 | // This demonstrates a simple unit test of the Swift portion of this plugin's implementation. 8 | // 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | 11 | class RunnerTests: XCTestCase { 12 | 13 | func testGetPlatformVersion() { 14 | let plugin = JitsiMeePlugin() 15 | 16 | let call = FlutterMethodCall(methodName: "getPlatformVersion", arguments: []) 17 | 18 | let resultExpectation = expectation(description: "result block must be called.") 19 | plugin.handle(call) { result in 20 | XCTAssertEqual(result as! String, "iOS " + UIDevice.current.systemVersion) 21 | resultExpectation.fulfill() 22 | } 23 | waitForExpectations(timeout: 1) 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:jitsi_meet_flutter_sdk/jitsi_meet_flutter_sdk.dart'; 4 | 5 | void main() { 6 | runApp(const MyApp()); 7 | } 8 | 9 | class MyApp extends StatefulWidget { 10 | const MyApp({super.key}); 11 | 12 | @override 13 | State createState() => _MyAppState(); 14 | } 15 | 16 | class _MyAppState extends State { 17 | bool audioMuted = true; 18 | bool videoMuted = true; 19 | bool screenShareOn = false; 20 | List participants = []; 21 | final _jitsiMeetPlugin = JitsiMeet(); 22 | 23 | join() async { 24 | var options = JitsiMeetConferenceOptions( 25 | room: "testgabigabi", 26 | configOverrides: { 27 | "startWithAudioMuted": true, 28 | "startWithVideoMuted": true, 29 | }, 30 | featureFlags: { 31 | FeatureFlags.addPeopleEnabled: true, 32 | FeatureFlags.welcomePageEnabled: true, 33 | FeatureFlags.preJoinPageEnabled: true, 34 | FeatureFlags.unsafeRoomWarningEnabled: true, 35 | FeatureFlags.resolution: FeatureFlagVideoResolutions.resolution720p, 36 | FeatureFlags.audioFocusDisabled: true, 37 | FeatureFlags.audioMuteButtonEnabled: true, 38 | FeatureFlags.audioOnlyButtonEnabled: true, 39 | FeatureFlags.calenderEnabled: true, 40 | FeatureFlags.callIntegrationEnabled: true, 41 | FeatureFlags.carModeEnabled: true, 42 | FeatureFlags.closeCaptionsEnabled: true, 43 | FeatureFlags.conferenceTimerEnabled: true, 44 | FeatureFlags.chatEnabled: true, 45 | FeatureFlags.filmstripEnabled: true, 46 | FeatureFlags.fullScreenEnabled: true, 47 | FeatureFlags.helpButtonEnabled: true, 48 | FeatureFlags.inviteEnabled: true, 49 | FeatureFlags.androidScreenSharingEnabled: true, 50 | FeatureFlags.speakerStatsEnabled: true, 51 | FeatureFlags.kickOutEnabled: true, 52 | FeatureFlags.liveStreamingEnabled: true, 53 | FeatureFlags.lobbyModeEnabled: true, 54 | FeatureFlags.meetingNameEnabled: true, 55 | FeatureFlags.meetingPasswordEnabled: true, 56 | FeatureFlags.notificationEnabled: true, 57 | FeatureFlags.overflowMenuEnabled: true, 58 | FeatureFlags.pipEnabled: true, 59 | FeatureFlags.pipWhileScreenSharingEnabled: true, 60 | FeatureFlags.preJoinPageHideDisplayName: true, 61 | FeatureFlags.raiseHandEnabled: true, 62 | FeatureFlags.reactionsEnabled: true, 63 | FeatureFlags.recordingEnabled: true, 64 | FeatureFlags.replaceParticipant: true, 65 | FeatureFlags.securityOptionEnabled: true, 66 | FeatureFlags.serverUrlChangeEnabled: true, 67 | FeatureFlags.settingsEnabled: true, 68 | FeatureFlags.tileViewEnabled: true, 69 | FeatureFlags.videoMuteEnabled: true, 70 | FeatureFlags.videoShareEnabled: true, 71 | FeatureFlags.toolboxEnabled: true, 72 | FeatureFlags.iosRecordingEnabled: true, 73 | FeatureFlags.iosScreenSharingEnabled: true, 74 | FeatureFlags.toolboxAlwaysVisible: true, 75 | }, 76 | userInfo: JitsiMeetUserInfo( 77 | displayName: "Gabi", 78 | email: "gabi.borlea.1@gmail.com", 79 | avatar: 80 | "https://avatars.githubusercontent.com/u/57035818?s=400&u=02572f10fe61bca6fc20426548f3920d53f79693&v=4"), 81 | ); 82 | 83 | var listener = JitsiMeetEventListener( 84 | conferenceJoined: (url) { 85 | debugPrint("conferenceJoined: url: $url"); 86 | }, 87 | conferenceTerminated: (url, error) { 88 | debugPrint("conferenceTerminated: url: $url, error: $error"); 89 | }, 90 | conferenceWillJoin: (url) { 91 | debugPrint("conferenceWillJoin: url: $url"); 92 | }, 93 | participantJoined: (email, name, role, participantId) { 94 | debugPrint( 95 | "participantJoined: email: $email, name: $name, role: $role, " 96 | "participantId: $participantId", 97 | ); 98 | participants.add(participantId!); 99 | }, 100 | participantLeft: (participantId) { 101 | debugPrint("participantLeft: participantId: $participantId"); 102 | }, 103 | audioMutedChanged: (muted) { 104 | debugPrint("audioMutedChanged: isMuted: $muted"); 105 | }, 106 | videoMutedChanged: (muted) { 107 | debugPrint("videoMutedChanged: isMuted: $muted"); 108 | }, 109 | endpointTextMessageReceived: (senderId, message) { 110 | debugPrint( 111 | "endpointTextMessageReceived: senderId: $senderId, message: $message"); 112 | }, 113 | screenShareToggled: (participantId, sharing) { 114 | debugPrint( 115 | "screenShareToggled: participantId: $participantId, " 116 | "isSharing: $sharing", 117 | ); 118 | }, 119 | chatMessageReceived: (senderId, message, isPrivate, timestamp) { 120 | debugPrint( 121 | "chatMessageReceived: senderId: $senderId, message: $message, " 122 | "isPrivate: $isPrivate, timestamp: $timestamp", 123 | ); 124 | }, 125 | chatToggled: (isOpen) => debugPrint("chatToggled: isOpen: $isOpen"), 126 | participantsInfoRetrieved: (participantsInfo) { 127 | debugPrint( 128 | "participantsInfoRetrieved: participantsInfo: $participantsInfo, "); 129 | }, 130 | readyToClose: () { 131 | debugPrint("readyToClose"); 132 | }, 133 | ); 134 | await _jitsiMeetPlugin.join(options, listener); 135 | } 136 | 137 | hangUp() async { 138 | await _jitsiMeetPlugin.hangUp(); 139 | } 140 | 141 | setAudioMuted(bool? muted) async { 142 | var a = await _jitsiMeetPlugin.setAudioMuted(muted!); 143 | debugPrint("$a"); 144 | setState(() { 145 | audioMuted = muted; 146 | }); 147 | } 148 | 149 | setVideoMuted(bool? muted) async { 150 | var a = await _jitsiMeetPlugin.setVideoMuted(muted!); 151 | debugPrint("$a"); 152 | setState(() { 153 | videoMuted = muted; 154 | }); 155 | } 156 | 157 | sendEndpointTextMessage() async { 158 | var a = await _jitsiMeetPlugin.sendEndpointTextMessage(message: "HEY"); 159 | debugPrint("$a"); 160 | 161 | for (var p in participants) { 162 | var b = 163 | await _jitsiMeetPlugin.sendEndpointTextMessage(to: p, message: "HEY"); 164 | debugPrint("$b"); 165 | } 166 | } 167 | 168 | toggleScreenShare(bool? enabled) async { 169 | await _jitsiMeetPlugin.toggleScreenShare(enabled!); 170 | 171 | setState(() { 172 | screenShareOn = enabled; 173 | }); 174 | } 175 | 176 | openChat() async { 177 | await _jitsiMeetPlugin.openChat(); 178 | } 179 | 180 | sendChatMessage() async { 181 | var a = await _jitsiMeetPlugin.sendChatMessage(message: "HEY1"); 182 | debugPrint("$a"); 183 | 184 | for (var p in participants) { 185 | a = await _jitsiMeetPlugin.sendChatMessage(to: p, message: "HEY2"); 186 | debugPrint("$a"); 187 | } 188 | } 189 | 190 | closeChat() async { 191 | await _jitsiMeetPlugin.closeChat(); 192 | } 193 | 194 | retrieveParticipantsInfo() async { 195 | var a = await _jitsiMeetPlugin.retrieveParticipantsInfo(); 196 | debugPrint("$a"); 197 | } 198 | 199 | @override 200 | Widget build(BuildContext context) { 201 | return MaterialApp( 202 | home: Scaffold( 203 | appBar: AppBar( 204 | title: const Text('Plugin example app'), 205 | ), 206 | body: Center( 207 | child: Column( 208 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 209 | children: [ 210 | TextButton( 211 | onPressed: join, 212 | child: const Text("Join"), 213 | ), 214 | TextButton(onPressed: hangUp, child: const Text("Hang Up")), 215 | Row(children: [ 216 | const Text("Set Audio Muted"), 217 | Checkbox( 218 | value: audioMuted, 219 | onChanged: setAudioMuted, 220 | ), 221 | ]), 222 | Row(children: [ 223 | const Text("Set Video Muted"), 224 | Checkbox( 225 | value: videoMuted, 226 | onChanged: setVideoMuted, 227 | ), 228 | ]), 229 | TextButton( 230 | onPressed: sendEndpointTextMessage, 231 | child: const Text("Send Hey Endpoint Message To All")), 232 | Row(children: [ 233 | const Text("Toggle Screen Share"), 234 | Checkbox( 235 | value: screenShareOn, 236 | onChanged: toggleScreenShare, 237 | ), 238 | ]), 239 | TextButton( 240 | onPressed: openChat, child: const Text("Open Chat")), 241 | TextButton( 242 | onPressed: sendChatMessage, 243 | child: const Text("Send Chat Message to All")), 244 | TextButton( 245 | onPressed: closeChat, child: const Text("Close Chat")), 246 | TextButton( 247 | onPressed: retrieveParticipantsInfo, 248 | child: const Text("Retrieve Participants Info")), 249 | ]), 250 | )), 251 | ); 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /example/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 | sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "2.12.0" 12 | boolean_selector: 13 | dependency: transitive 14 | description: 15 | name: boolean_selector 16 | sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "2.1.2" 20 | characters: 21 | dependency: transitive 22 | description: 23 | name: characters 24 | sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "1.4.0" 28 | clock: 29 | dependency: transitive 30 | description: 31 | name: clock 32 | sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "1.1.2" 36 | collection: 37 | dependency: transitive 38 | description: 39 | name: collection 40 | sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "1.19.1" 44 | cupertino_icons: 45 | dependency: "direct main" 46 | description: 47 | name: cupertino_icons 48 | sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "1.0.5" 52 | fake_async: 53 | dependency: transitive 54 | description: 55 | name: fake_async 56 | sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" 57 | url: "https://pub.dev" 58 | source: hosted 59 | version: "1.3.2" 60 | file: 61 | dependency: transitive 62 | description: 63 | name: file 64 | sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 65 | url: "https://pub.dev" 66 | source: hosted 67 | version: "7.0.1" 68 | flutter: 69 | dependency: "direct main" 70 | description: flutter 71 | source: sdk 72 | version: "0.0.0" 73 | flutter_driver: 74 | dependency: transitive 75 | description: flutter 76 | source: sdk 77 | version: "0.0.0" 78 | flutter_lints: 79 | dependency: "direct dev" 80 | description: 81 | name: flutter_lints 82 | sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" 83 | url: "https://pub.dev" 84 | source: hosted 85 | version: "2.0.2" 86 | flutter_test: 87 | dependency: "direct dev" 88 | description: flutter 89 | source: sdk 90 | version: "0.0.0" 91 | fuchsia_remote_debug_protocol: 92 | dependency: transitive 93 | description: flutter 94 | source: sdk 95 | version: "0.0.0" 96 | integration_test: 97 | dependency: "direct dev" 98 | description: flutter 99 | source: sdk 100 | version: "0.0.0" 101 | jitsi_meet_flutter_sdk: 102 | dependency: "direct main" 103 | description: 104 | path: ".." 105 | relative: true 106 | source: path 107 | version: "11.2.0" 108 | leak_tracker: 109 | dependency: transitive 110 | description: 111 | name: leak_tracker 112 | sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec 113 | url: "https://pub.dev" 114 | source: hosted 115 | version: "10.0.8" 116 | leak_tracker_flutter_testing: 117 | dependency: transitive 118 | description: 119 | name: leak_tracker_flutter_testing 120 | sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 121 | url: "https://pub.dev" 122 | source: hosted 123 | version: "3.0.9" 124 | leak_tracker_testing: 125 | dependency: transitive 126 | description: 127 | name: leak_tracker_testing 128 | sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" 129 | url: "https://pub.dev" 130 | source: hosted 131 | version: "3.0.1" 132 | lints: 133 | dependency: transitive 134 | description: 135 | name: lints 136 | sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" 137 | url: "https://pub.dev" 138 | source: hosted 139 | version: "2.1.1" 140 | matcher: 141 | dependency: transitive 142 | description: 143 | name: matcher 144 | sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 145 | url: "https://pub.dev" 146 | source: hosted 147 | version: "0.12.17" 148 | material_color_utilities: 149 | dependency: transitive 150 | description: 151 | name: material_color_utilities 152 | sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec 153 | url: "https://pub.dev" 154 | source: hosted 155 | version: "0.11.1" 156 | meta: 157 | dependency: transitive 158 | description: 159 | name: meta 160 | sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c 161 | url: "https://pub.dev" 162 | source: hosted 163 | version: "1.16.0" 164 | path: 165 | dependency: transitive 166 | description: 167 | name: path 168 | sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" 169 | url: "https://pub.dev" 170 | source: hosted 171 | version: "1.9.1" 172 | platform: 173 | dependency: transitive 174 | description: 175 | name: platform 176 | sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" 177 | url: "https://pub.dev" 178 | source: hosted 179 | version: "3.1.6" 180 | plugin_platform_interface: 181 | dependency: transitive 182 | description: 183 | name: plugin_platform_interface 184 | sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" 185 | url: "https://pub.dev" 186 | source: hosted 187 | version: "2.1.4" 188 | process: 189 | dependency: transitive 190 | description: 191 | name: process 192 | sha256: "107d8be718f120bbba9dcd1e95e3bd325b1b4a4f07db64154635ba03f2567a0d" 193 | url: "https://pub.dev" 194 | source: hosted 195 | version: "5.0.3" 196 | sky_engine: 197 | dependency: transitive 198 | description: flutter 199 | source: sdk 200 | version: "0.0.0" 201 | source_span: 202 | dependency: transitive 203 | description: 204 | name: source_span 205 | sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" 206 | url: "https://pub.dev" 207 | source: hosted 208 | version: "1.10.1" 209 | stack_trace: 210 | dependency: transitive 211 | description: 212 | name: stack_trace 213 | sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" 214 | url: "https://pub.dev" 215 | source: hosted 216 | version: "1.12.1" 217 | stream_channel: 218 | dependency: transitive 219 | description: 220 | name: stream_channel 221 | sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" 222 | url: "https://pub.dev" 223 | source: hosted 224 | version: "2.1.4" 225 | string_scanner: 226 | dependency: transitive 227 | description: 228 | name: string_scanner 229 | sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" 230 | url: "https://pub.dev" 231 | source: hosted 232 | version: "1.4.1" 233 | sync_http: 234 | dependency: transitive 235 | description: 236 | name: sync_http 237 | sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" 238 | url: "https://pub.dev" 239 | source: hosted 240 | version: "0.3.1" 241 | term_glyph: 242 | dependency: transitive 243 | description: 244 | name: term_glyph 245 | sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" 246 | url: "https://pub.dev" 247 | source: hosted 248 | version: "1.2.2" 249 | test_api: 250 | dependency: transitive 251 | description: 252 | name: test_api 253 | sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd 254 | url: "https://pub.dev" 255 | source: hosted 256 | version: "0.7.4" 257 | vector_math: 258 | dependency: transitive 259 | description: 260 | name: vector_math 261 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 262 | url: "https://pub.dev" 263 | source: hosted 264 | version: "2.1.4" 265 | vm_service: 266 | dependency: transitive 267 | description: 268 | name: vm_service 269 | sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" 270 | url: "https://pub.dev" 271 | source: hosted 272 | version: "14.3.1" 273 | webdriver: 274 | dependency: transitive 275 | description: 276 | name: webdriver 277 | sha256: "3d773670966f02a646319410766d3b5e1037efb7f07cc68f844d5e06cd4d61c8" 278 | url: "https://pub.dev" 279 | source: hosted 280 | version: "3.0.4" 281 | sdks: 282 | dart: ">=3.7.0-0 <4.0.0" 283 | flutter: ">=3.18.0-18.0.pre.54" 284 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: jitsi_meet_flutter_sdk_example 2 | description: Demonstrates how to use the jitsi_meet_flutter_sdk plugi. 3 | # The following line prevents the package from being accidentally published to 4 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 5 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 6 | 7 | environment: 8 | sdk: '>=3.0.5 <4.0.0' 9 | 10 | # Dependencies specify other packages that your package needs in order to work. 11 | # To automatically upgrade your package dependencies to the latest versions 12 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 13 | # dependencies can be manually updated by changing the version numbers below to 14 | # the latest version available on pub.dev. To see which dependencies have newer 15 | # versions available, run `flutter pub outdated`. 16 | dependencies: 17 | flutter: 18 | sdk: flutter 19 | 20 | jitsi_meet_flutter_sdk: 21 | # When depending on this package from a real application you should use: 22 | # jitsi_meet_flutter_sdk: ^x.y.z 23 | # See https://dart.dev/tools/pub/dependencies#version-constraints 24 | # The example app is bundled with the plugin so we use a path dependency on 25 | # the parent directory to use the current plugin's version. 26 | path: ../ 27 | 28 | # The following adds the Cupertino Icons font to your application. 29 | # Use with the CupertinoIcons class for iOS style icons. 30 | cupertino_icons: ^1.0.2 31 | 32 | dev_dependencies: 33 | integration_test: 34 | sdk: flutter 35 | flutter_test: 36 | sdk: flutter 37 | 38 | # The "flutter_lints" package below contains a set of recommended lints to 39 | # encourage good coding practices. The lint set provided by the package is 40 | # activated in the `analysis_options.yaml` file located at the root of your 41 | # package. See that file for information about deactivating specific lint 42 | # rules and activating additional ones. 43 | flutter_lints: ^2.0.0 44 | 45 | # For information on the generic Dart part of this file, see the 46 | # following page: https://dart.dev/tools/pub/pubspec 47 | 48 | # The following section is specific to Flutter packages. 49 | flutter: 50 | 51 | # The following line ensures that the Material Icons font is 52 | # included with your application, so that you can use the icons in 53 | # the material Icons class. 54 | uses-material-design: true 55 | 56 | # To add assets to your application, add an assets section, like this: 57 | # assets: 58 | # - images/a_dot_burr.jpeg 59 | # - images/a_dot_ham.jpeg 60 | 61 | # An image asset can refer to one or more resolution-specific "variants", see 62 | # https://flutter.dev/assets-and-images/#resolution-aware 63 | 64 | # For details regarding adding assets from package dependencies, see 65 | # https://flutter.dev/assets-and-images/#from-packages 66 | 67 | # To add custom fonts to your application, add a fonts section here, 68 | # in this "flutter" section. Each entry in this list should have a 69 | # "family" key with the font family name, and a "fonts" key with a 70 | # list giving the asset and other descriptors for the font. For 71 | # example: 72 | # fonts: 73 | # - family: Schyler 74 | # fonts: 75 | # - asset: fonts/Schyler-Regular.ttf 76 | # - asset: fonts/Schyler-Italic.ttf 77 | # style: italic 78 | # - family: Trajan Pro 79 | # fonts: 80 | # - asset: fonts/TrajanPro.ttf 81 | # - asset: fonts/TrajanPro_Bold.ttf 82 | # weight: 700 83 | # 84 | # For details regarding fonts from package dependencies, 85 | # see https://flutter.dev/custom-fonts/#from-packages 86 | -------------------------------------------------------------------------------- /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 in the flutter_test package. 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:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:jitsi_meet_flutter_sdk_example/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Verify Platform version', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(const MyApp()); 17 | 18 | // Verify that platform version is retrieved. 19 | expect( 20 | find.byWidgetPredicate( 21 | (Widget widget) => 22 | widget is Text && widget.data!.startsWith('Running on:'), 23 | ), 24 | findsOneWidget, 25 | ); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/Generated.xcconfig 37 | /Flutter/ephemeral/ 38 | /Flutter/flutter_export_environment.sh -------------------------------------------------------------------------------- /ios/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jitsi/jitsi-meet-flutter-sdk/5a2d2be1d172952b92b01518c4801e778c47f4d5/ios/Assets/.gitkeep -------------------------------------------------------------------------------- /ios/Classes/JitsiMeetPlugin.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import JitsiMeetSDK 4 | 5 | public class JitsiMeetPlugin: NSObject, FlutterPlugin, FlutterStreamHandler { 6 | var flutterViewController: UIViewController 7 | var jitsiMeetViewController: JitsiMeetViewController? 8 | var eventSink: FlutterEventSink? 9 | 10 | init(flutterViewController: UIViewController) { 11 | self.flutterViewController = flutterViewController 12 | } 13 | 14 | public static func register(with registrar: FlutterPluginRegistrar) { 15 | let channel = FlutterMethodChannel(name: "jitsi_meet_flutter_sdk", binaryMessenger: registrar.messenger()) 16 | let flutterViewController: UIViewController = (UIApplication.shared.delegate?.window??.rootViewController)! 17 | let instance = JitsiMeetPlugin(flutterViewController: flutterViewController) 18 | registrar.addMethodCallDelegate(instance, channel: channel) 19 | 20 | let eventChannel = FlutterEventChannel(name: "jitsi_meet_flutter_sdk_events", binaryMessenger: registrar.messenger()) 21 | eventChannel.setStreamHandler(instance) 22 | } 23 | 24 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 25 | switch call.method { 26 | case "getPlatformVersion": 27 | result("iOS " + UIDevice.current.systemVersion) 28 | return 29 | case "join": 30 | join(call, result: result) 31 | return 32 | case "hangUp": 33 | hangUp(call, result: result) 34 | return 35 | case "setAudioMuted": 36 | setAudioMuted(call, result: result) 37 | return 38 | case "setVideoMuted": 39 | setVideoMuted(call, result: result) 40 | return 41 | case "sendEndpointTextMessage": 42 | sendEndpointTextMessage(call, result: result) 43 | return 44 | case "toggleScreenShare": 45 | toggleScreenShare(call, result: result) 46 | return 47 | case "openChat": 48 | openChat(call, result: result) 49 | return 50 | case "sendChatMessage": 51 | sendChatMessage(call, result: result) 52 | return 53 | case "closeChat": 54 | closeChat(call, result: result) 55 | return 56 | case "retrieveParticipantsInfo": 57 | retrieveParticipantsInfo(call, result: result) 58 | return 59 | case "enterPiP": 60 | enterPiP(call, result: result) 61 | return 62 | default: 63 | result(FlutterMethodNotImplemented) 64 | } 65 | } 66 | 67 | private func join(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 68 | let arguments = call.arguments as! [String: Any] 69 | let serverURL = arguments["serverURL"] as? String 70 | let room = arguments["room"] as? String 71 | let token = arguments["token"] as? String 72 | let configOverrides = arguments["configOverrides"] as? Dictionary 73 | let featureFlags = arguments["featureFlags"] as? Dictionary 74 | let rawUserInfo = arguments["userInfo"] as! [String: Any] 75 | let displayName = rawUserInfo["displayName"] as? String 76 | let email = rawUserInfo["email"] as? String 77 | var avatar: URL? = nil 78 | if rawUserInfo["avatar"] as? String != nil { 79 | avatar = URL(string: rawUserInfo["avatar"] as! String) 80 | } 81 | var userInfo: JitsiMeetUserInfo? = nil 82 | if (displayName != nil || email != nil || avatar != nil) { 83 | userInfo = JitsiMeetUserInfo(displayName: displayName, andEmail: email, andAvatar: avatar) 84 | } 85 | 86 | let options = JitsiMeetConferenceOptions.fromBuilder { (builder) in 87 | if (serverURL != nil) { 88 | builder.serverURL = URL(string: serverURL as! String) 89 | } 90 | if (room != nil) { 91 | builder.room = room; 92 | } 93 | if (token != nil) { 94 | builder.token = token; 95 | } 96 | configOverrides?.forEach { key, value in 97 | builder.setConfigOverride(key, withValue: value); 98 | } 99 | featureFlags?.forEach { key, value in 100 | builder.setFeatureFlag(key, withValue: value); 101 | } 102 | if (userInfo != nil) { 103 | builder.userInfo = userInfo 104 | } 105 | } 106 | 107 | jitsiMeetViewController = JitsiMeetViewController.init(options: options, eventSink: eventSink!) 108 | jitsiMeetViewController!.modalPresentationStyle = .overFullScreen 109 | flutterViewController.present(jitsiMeetViewController!, animated: true) 110 | result("Successfully joined meeting \(room)") 111 | } 112 | 113 | private func hangUp(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 114 | jitsiMeetViewController?.jitsiMeetView?.hangUp() 115 | result("Successfully hung up") 116 | } 117 | 118 | private func setAudioMuted(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 119 | let arguments = call.arguments as! [String: Any] 120 | let muted = arguments["muted"] as! Bool 121 | jitsiMeetViewController?.jitsiMeetView?.setAudioMuted(muted) 122 | result("Successfully set audio \(muted)") 123 | } 124 | 125 | private func setVideoMuted(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 126 | let arguments = call.arguments as! [String: Any] 127 | let muted = arguments["muted"] as! Bool 128 | jitsiMeetViewController?.jitsiMeetView?.setVideoMuted(muted) 129 | result("Successfully set video \(muted)") 130 | } 131 | 132 | private func sendEndpointTextMessage(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 133 | let arguments = call.arguments as! [String: Any] 134 | let to = arguments["to"] as? String 135 | let message: String = arguments["message"] as! String 136 | jitsiMeetViewController?.jitsiMeetView?.sendEndpointTextMessage(message, to) 137 | result("Successfully send endpoint text message \(to)") 138 | } 139 | 140 | private func toggleScreenShare(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 141 | let arguments = call.arguments as! [String: Any] 142 | let enabled = arguments["enabled"] as! Bool 143 | jitsiMeetViewController?.jitsiMeetView?.toggleScreenShare(enabled) 144 | result("Successfully toggled screen share \(enabled)") 145 | } 146 | 147 | private func openChat(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 148 | let arguments = call.arguments as! [String: Any] 149 | let to = arguments["to"] as? String 150 | jitsiMeetViewController?.jitsiMeetView?.openChat(to) 151 | result("Successfully opened chat \(to)") 152 | } 153 | 154 | private func sendChatMessage(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 155 | let arguments = call.arguments as! [String: Any] 156 | let to = arguments["to"] as? String 157 | let message: String = arguments["message"] as! String 158 | jitsiMeetViewController?.jitsiMeetView?.sendChatMessage(message, to) 159 | result("Successfully sent chat message \(to)") 160 | } 161 | 162 | private func closeChat(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 163 | jitsiMeetViewController?.jitsiMeetView?.closeChat() 164 | result("Successfully closed chat") 165 | } 166 | 167 | private func retrieveParticipantsInfo(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 168 | jitsiMeetViewController?.jitsiMeetView?.retrieveParticipantsInfo({ (data:[Any]?) in self.eventSink!(["event": "participantsInfoRetrieved", "data": data])}) 169 | result("Successfully retrieved participants info") 170 | } 171 | 172 | private func enterPiP(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 173 | jitsiMeetViewController?.enterPicture(inPicture: [:]) 174 | result("Successfully entered Picture in Picture") 175 | } 176 | 177 | public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { 178 | eventSink = events 179 | return nil 180 | } 181 | 182 | public func onCancel(withArguments arguments: Any?) -> FlutterError? { 183 | return nil 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /ios/Classes/JitsiMeetViewController.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import JitsiMeetSDK 4 | 5 | class JitsiMeetViewController: UIViewController { 6 | fileprivate var pipViewCoordinator: PiPViewCoordinator? 7 | fileprivate var wrapperJitsiMeetView: UIView? 8 | var jitsiMeetView: JitsiMeetView? 9 | 10 | let options: JitsiMeetConferenceOptions 11 | let eventSink: FlutterEventSink 12 | 13 | init(options: JitsiMeetConferenceOptions, eventSink: @escaping FlutterEventSink) { 14 | self.options = options; 15 | self.eventSink = eventSink; 16 | super.init(nibName: nil, bundle: nil) 17 | } 18 | 19 | required init?(coder aDecoder: NSCoder) { 20 | fatalError("init(coder:) is not supported") 21 | } 22 | 23 | override func viewDidLoad() { 24 | super.viewDidLoad() 25 | openJitsiMeet(); 26 | } 27 | 28 | func openJitsiMeet() { 29 | cleanUp() 30 | 31 | jitsiMeetView = JitsiMeetView() 32 | let wrapperJitsiMeetView = WrapperView() 33 | wrapperJitsiMeetView.backgroundColor = .black 34 | self.wrapperJitsiMeetView = wrapperJitsiMeetView 35 | 36 | wrapperJitsiMeetView.addSubview(jitsiMeetView!) 37 | 38 | jitsiMeetView!.autoresizingMask = [.flexibleWidth, .flexibleHeight] 39 | 40 | jitsiMeetView!.delegate = self 41 | jitsiMeetView!.join(options) 42 | 43 | pipViewCoordinator = PiPViewCoordinator(withView: wrapperJitsiMeetView) 44 | pipViewCoordinator?.configureAsStickyView(withParentView: view) 45 | 46 | wrapperJitsiMeetView.alpha = 0 47 | pipViewCoordinator?.show() 48 | } 49 | 50 | override func viewWillTransition(to size: CGSize, 51 | with coordinator: UIViewControllerTransitionCoordinator) { 52 | super.viewWillTransition(to: size, with: coordinator) 53 | 54 | let rect = CGRect(origin: CGPoint.zero, size: size) 55 | pipViewCoordinator?.resetBounds(bounds: rect) 56 | } 57 | 58 | fileprivate func cleanUp() { 59 | jitsiMeetView?.removeFromSuperview() 60 | wrapperJitsiMeetView?.removeFromSuperview() 61 | jitsiMeetView = nil 62 | wrapperJitsiMeetView = nil 63 | pipViewCoordinator = nil 64 | } 65 | } 66 | 67 | extension JitsiMeetViewController: JitsiMeetViewDelegate { 68 | func conferenceJoined(_ data: [AnyHashable : Any]) { 69 | self.eventSink(["event": "conferenceJoined", "data": data]) 70 | } 71 | 72 | func conferenceTerminated(_ data: [AnyHashable: Any]) { 73 | self.eventSink(["event": "conferenceTerminated", "data": data]) 74 | } 75 | 76 | func conferenceWillJoin(_ data: [AnyHashable : Any]) { 77 | self.eventSink(["event": "conferenceWillJoin", "data": data]) 78 | } 79 | 80 | func participantJoined(_ data: [AnyHashable : Any]) { 81 | self.eventSink(["event": "participantJoined", "data": data]) 82 | } 83 | 84 | func participantLeft(_ data: [AnyHashable : Any]) { 85 | self.eventSink(["event": "participantLeft", "data": data]) 86 | } 87 | 88 | func audioMutedChanged(_ data: [AnyHashable : Any]) { 89 | self.eventSink(["event": "audioMutedChanged", "data": data]) 90 | } 91 | 92 | func videoMutedChanged(_ data: [AnyHashable : Any]) { 93 | self.eventSink(["event": "videoMutedChanged", "data": data]) 94 | } 95 | 96 | func endpointTextMessageReceived(_ data: [AnyHashable : Any]) { 97 | self.eventSink(["event": "endpointTextMessageReceived", "data": data]) 98 | } 99 | 100 | func screenShareToggled(_ data: [AnyHashable : Any]) { 101 | self.eventSink(["event": "screenShareToggled", "data": data]) 102 | } 103 | 104 | func chatMessageReceived(_ data: [AnyHashable : Any]) { 105 | self.eventSink(["event": "chatMessageReceived", "data": data]) 106 | } 107 | 108 | func chatToggled(_ data: [AnyHashable : Any]) { 109 | self.eventSink(["event": "chatToggled", "data": data]) 110 | } 111 | 112 | func participantsInfoRetrieved(_ data: [AnyHashable : Any]) { 113 | self.eventSink(["event": "participantsInfoRetrieved", "data": data]) 114 | } 115 | 116 | func customButtonPressed(_ data: [AnyHashable : Any]) { 117 | self.eventSink(["event": "customButtonPressed", "data": data]) 118 | } 119 | 120 | func ready(toClose data: [AnyHashable : Any]) { 121 | self.eventSink(["event": "readyToClose"]) 122 | DispatchQueue.main.async { 123 | self.pipViewCoordinator?.hide { _ in 124 | self.cleanUp() 125 | self.dismiss(animated: true, completion: nil) 126 | } 127 | } 128 | } 129 | 130 | func enterPicture(inPicture data: [AnyHashable: Any]) { 131 | DispatchQueue.main.async { 132 | self.pipViewCoordinator?.enterPictureInPicture() 133 | } 134 | } 135 | } 136 | 137 | class WrapperView: UIView { 138 | override func touchesBegan(_ touches: Set, with event: UIEvent?) { 139 | } 140 | 141 | override func touchesMoved(_ touches: Set, with event: UIEvent?) { 142 | } 143 | 144 | override func touchesEnded(_ touches: Set, with event: UIEvent?) { 145 | } 146 | 147 | override func touchesCancelled(_ touches: Set, with event: UIEvent?) { 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /ios/jitsi_meet_flutter_sdk.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. 3 | # Run `pod lib lint jitsi_meet_flutter_sdk.podspec` to validate before publishing. 4 | # 5 | Pod::Spec.new do |s| 6 | s.name = 'jitsi_meet_flutter_sdk' 7 | s.version = '11.2.0' 8 | s.summary = 'Jitsi Meet Flutter SDK' 9 | s.description = <<-DESC 10 | Jitsi Meet Flutter SDK 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 'Flutter' 18 | s.dependency 'JitsiMeetSDK', '11.2.0' 19 | s.platform = :ios, '15.1' 20 | 21 | # Flutter.framework does not contain a i386 slice. 22 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } 23 | s.swift_version = '5.0' 24 | end 25 | -------------------------------------------------------------------------------- /lib/jitsi_meet_flutter_sdk.dart: -------------------------------------------------------------------------------- 1 | export 'src/jitsi_meet.dart' show JitsiMeet; 2 | export 'src/jitsi_meet_conference_options.dart' show JitsiMeetConferenceOptions; 3 | export 'src/jitsi_meet_event_listener.dart' show JitsiMeetEventListener; 4 | export 'src/jitsi_meet_user_info.dart' show JitsiMeetUserInfo; 5 | export 'src/features_flag/feature_flags.dart' show FeatureFlags; 6 | export 'src/features_flag/feature_flag_video_resolutions.dart' 7 | show FeatureFlagVideoResolutions; 8 | -------------------------------------------------------------------------------- /lib/src/features_flag/feature_flag_video_resolutions.dart: -------------------------------------------------------------------------------- 1 | class FeatureFlagVideoResolutions{ 2 | FeatureFlagVideoResolutions._(); 3 | 4 | /// VARs [resolution180p] : Video resolution at 180p 5 | static const resolution180p = 180; 6 | 7 | /// VARs [resolution360p] : Video resolution at 360p 8 | static const resolution360p = 360; 9 | 10 | /// VARs [resolution480p] : Video resolution at 480p 11 | static const resolution480p = 480; 12 | 13 | /// VARs [resolution720p] : Video resolution at 720p 14 | static const resolution720p = 720; 15 | } -------------------------------------------------------------------------------- /lib/src/features_flag/feature_flags.dart: -------------------------------------------------------------------------------- 1 | class FeatureFlags{ 2 | FeatureFlags._(); 3 | 4 | /// FeatureFlags [welcomePageEnabled] : Flag indicating if the welcome page should be enabled. 5 | /// Default: disabled (false). 6 | static const String welcomePageEnabled = "welcomepage.enabled"; 7 | 8 | /// FeatureFlags [audioFocusDisabled] : Flag indicating if the SDK should not require the audio focus. 9 | /// Used by apps that do not use Jitsi audio. 10 | /// Default: disabled (false). 11 | static const String audioFocusDisabled = "audio-focus.disabled"; 12 | 13 | /// FeatureFlags [addPeopleEnabled] : Flag indicating if add-people functionality should be enabled. 14 | /// Default: enabled (true). 15 | static const String addPeopleEnabled = "add-people.enabled"; 16 | 17 | /// FeatureFlags [audioMuteButtonEnabled] : Flag indicating if the audio mute button should be displayed. 18 | /// Default: enabled (true). 19 | static const String audioMuteButtonEnabled = "audio-mute.enabled"; 20 | 21 | /// FeatureFlags [audioOnlyButtonEnabled] : Flag indicating that the Audio only button in the overflow menu is enabled. 22 | /// Default: enabled (true). 23 | static const String audioOnlyButtonEnabled = "audio-only.enabled"; 24 | 25 | /// FeatureFlags [calenderEnabled] : Flag indicating if calendar integration should be enabled. 26 | /// Default: enabled (true) on Android, auto-detected on iOS. 27 | static const String calenderEnabled = "calendar.enabled"; 28 | 29 | /// FeatureFlags [callIntegrationEnabled] : Flag indicating if call integration (CallKit on iOS, ConnectionService on Android) should be enabled. 30 | /// Default: enabled (true). 31 | static const String callIntegrationEnabled = "call-integration.enabled"; 32 | 33 | /// FeatureFlags [carModeEnabled] : Flag indicating if car mode should be enabled. 34 | /// Default: enabled (true). 35 | static const String carModeEnabled = "car-mode.enabled"; 36 | 37 | /// FeatureFlags [closeCaptionsEnabled] : Flag indicating if close captions should be enabled. 38 | /// Default: enabled (true). 39 | static const String closeCaptionsEnabled = "close-captions.enabled"; 40 | 41 | /// FeatureFlags [conferenceTimerEnabled] : Flag indicating if conference timer should be enabled. 42 | /// Default: enabled (true). 43 | static const String conferenceTimerEnabled = "conference-timer.enabled"; 44 | 45 | /// FeatureFlags [chatEnabled] : Flag indicating if chat should be enabled. 46 | /// Default: enabled (true). 47 | static const String chatEnabled = "chat.enabled"; 48 | 49 | /// FeatureFlags [filmstripEnabled] : Flag indicating if the filmstrip should be enabled. 50 | /// Default: enabled (true). 51 | static const String filmstripEnabled = "filmstrip.enabled"; 52 | 53 | /// FeatureFlags [fullScreenEnabled] : Flag indicating if fullscreen (immersive) mode should be enabled. 54 | /// Default: enabled (true). 55 | static const String fullScreenEnabled = "fullscreen.enabled"; 56 | 57 | /// FeatureFlags [helpButtonEnabled] : Flag indicating if the Help button should be enabled. 58 | /// Default: enabled (true). 59 | static const String helpButtonEnabled = "help.enabled"; 60 | 61 | /// FeatureFlags [inviteEnabled] : Flag indicating if invite functionality should be enabled. 62 | /// Default: enabled (true). 63 | static const String inviteEnabled = "invite.enabled"; 64 | 65 | /// FeatureFlags [androidScreenSharingEnabled] : Flag indicating if screen sharing should be enabled in android. 66 | /// Default: enabled (true). 67 | static const String androidScreenSharingEnabled = "android.screensharing.enabled"; 68 | 69 | /// FeatureFlags [speakerStatsEnabled] : Flag indicating if speaker statistics should be enabled. 70 | /// Default: enabled (true). 71 | static const String speakerStatsEnabled = "speakerstats.enabled"; 72 | 73 | /// FeatureFlags [kickOutEnabled] : Flag indicating if kickout is enabled. 74 | /// Default: enabled (true). 75 | static const String kickOutEnabled = "kick-out.enabled"; 76 | 77 | /// FeatureFlags [liveStreamingEnabled] : Flag indicating if live-streaming should be enabled. 78 | /// Default: auto-detected. 79 | static const String liveStreamingEnabled = "live-streaming.enabled"; 80 | 81 | /// FeatureFlags [lobbyModeEnabled] : Flag indicating if lobby mode button should be enabled. 82 | /// Default: enabled. 83 | static const String lobbyModeEnabled = "lobby-mode.enabled"; 84 | 85 | /// FeatureFlags [meetingNameEnabled] : Flag indicating if displaying the meeting name should be enabled. 86 | /// Default: enabled (true). 87 | static const String meetingNameEnabled = "meeting-name.enabled"; 88 | 89 | /// FeatureFlags [meetingPasswordEnabled] : Flag indicating if the meeting password button should be enabled. 90 | /// Note that this flag just decides on the button, if a meeting has a password set, the password dialog will still show up. 91 | /// Default: enabled (true). 92 | static const String meetingPasswordEnabled = "meeting-password.enabled"; 93 | 94 | /// FeatureFlags [notificationEnabled] : Flag indicating if the notifications should be enabled. 95 | /// Default: enabled (true). 96 | static const String notificationEnabled = "notifications.enabled"; 97 | 98 | /// FeatureFlags [overflowMenuEnabled] : Flag indicating if the audio overflow menu button should be displayed. 99 | /// Default: enabled (true). 100 | static const String overflowMenuEnabled = "overflow-menu.enabled"; 101 | 102 | /// FeatureFlags [pipEnabled] : Flag indicating if Picture-in-Picture should be enabled. 103 | /// Default: auto-detected. 104 | static const String pipEnabled = "pip.enabled"; 105 | 106 | /// FeatureFlags [pipWhileScreenSharingEnabled] : Flag indicating if Picture-in-Picture button should be shown while screen sharing. 107 | /// Default: disabled (false). 108 | static const String pipWhileScreenSharingEnabled = "pip-while-screen-sharing.enabled"; 109 | 110 | /// FeatureFlags [preJoinPageEnabled] : Flag indicating if the prejoin page should be enabled. 111 | /// Default: enabled (true). 112 | static const String preJoinPageEnabled = "prejoinpage.enabled"; 113 | 114 | /// FeatureFlags [preJoinPageHideDisplayName] :Flag indicating if the participant name editing field should be displayed on the prejoin page. 115 | /// Default: disabled (false). 116 | static const String preJoinPageHideDisplayName = "prejoinpage.hideDisplayName"; 117 | 118 | /// FeatureFlags [raiseHandEnabled] :Flag indicating if raise hand feature should be enabled. 119 | /// Default: enabled. 120 | static const String raiseHandEnabled = "raise-hand.enabled"; 121 | 122 | /// FeatureFlags [reactionsEnabled] : Flag indicating if the reactions feature should be enabled. 123 | /// Default: enabled (true). 124 | static const String reactionsEnabled = "reactions.enabled"; 125 | 126 | /// FeatureFlags [recordingEnabled] : Flag indicating if recording should be enabled. 127 | /// Default: auto-detected. 128 | static const String recordingEnabled = "recording.enabled"; 129 | 130 | /// FeatureFlags [replaceParticipant] : Flag indicating if the user should join the conference with the replaceParticipant functionality. 131 | /// Default: (false). 132 | static const String replaceParticipant = "replace.participant"; 133 | 134 | /// FeatureFlags [securityOptionEnabled] : Flag indicating if the security options button should be enabled. 135 | /// Default: enabled (true). 136 | static const String securityOptionEnabled = "security-options.enabled"; 137 | 138 | /// FeatureFlags [serverUrlChangeEnabled] : Flag indicating if server URL change is enabled. 139 | /// Default: enabled (true). 140 | static const String serverUrlChangeEnabled = "server-url-change.enabled"; 141 | 142 | /// FeatureFlags [settingsEnabled] : Flag indicating if settings should be enabled. 143 | /// Default: enabled (true). 144 | static const String settingsEnabled = "settings.enabled"; 145 | 146 | /// FeatureFlags [tileViewEnabled] : Flag indicating if tile view feature should be enabled. 147 | /// Default: enabled. 148 | static const String tileViewEnabled = "tile-view.enabled"; 149 | 150 | /// FeatureFlags [videoMuteEnabled] : Flag indicating if the video mute button should be displayed. 151 | /// Default: enabled (true). 152 | static const String videoMuteEnabled = "video-mute.enabled"; 153 | 154 | /// FeatureFlags [videoShareEnabled] : Flag indicating if the video share button should be enabled 155 | /// Default: enabled (true). 156 | static const String videoShareEnabled = "video-share.enabled"; 157 | 158 | /// FeatureFlags [toolboxEnabled] : Flag indicating if the toolbox should be enabled 159 | /// Default: enabled. 160 | static const String toolboxEnabled = "toolbox.enabled"; 161 | 162 | /// FeatureFlags [resolution] : Flag indicating the local and (maximum) remote video resolution. Overrides the server configuration. 163 | /// Default: (unset). 164 | static const String resolution = "resolution"; 165 | 166 | /// FeatureFlags [unsafeRoomWarningEnabled] : Flag indicating if the unsafe room warning should be enabled. 167 | /// Default: disabled (false). 168 | static const String unsafeRoomWarningEnabled = "unsaferoomwarning.enabled"; 169 | 170 | /// FeatureFlags [iosRecordingEnabled] : Flag indicating if recording should be enabled in iOS. 171 | /// Default: disabled (false). 172 | static const String iosRecordingEnabled = "ios.recording.enabled"; 173 | 174 | /// FeatureFlags [iosScreenSharingEnabled] : Flag indicating if screen sharing should be enabled in iOS. 175 | /// Default: disabled (false). 176 | static const String iosScreenSharingEnabled = "ios.screensharing.enabled"; 177 | 178 | /// FeatureFlags [toolboxAlwaysVisible] : Flag indicating if the toolbox should be always be visible 179 | /// Default: disabled (false). 180 | static const String toolboxAlwaysVisible = "toolbox.alwaysVisible"; 181 | 182 | } -------------------------------------------------------------------------------- /lib/src/jitsi_meet.dart: -------------------------------------------------------------------------------- 1 | import 'jitsi_meet_conference_options.dart'; 2 | import 'jitsi_meet_event_listener.dart'; 3 | import 'jitsi_meet_platform_interface.dart'; 4 | import 'method_response.dart'; 5 | 6 | /// The entry point for the sdk. It is used to launch the meeting screen, 7 | /// to send and receive all the events. 8 | class JitsiMeet { 9 | Future getPlatformVersion() { 10 | return JitsiMeetPlatform.instance.getPlatformVersion(); 11 | } 12 | 13 | /// Joins a meeting with the given meeting [options] and 14 | /// optionally a [listener] is given for listening to events triggered by the native sdks. 15 | Future join(JitsiMeetConferenceOptions options, 16 | [JitsiMeetEventListener? listener]) async { 17 | return await JitsiMeetPlatform.instance 18 | .join(options, listener ?? JitsiMeetEventListener()); 19 | } 20 | 21 | /// The localParticipant leaves the current meeting. 22 | Future hangUp() async { 23 | return await JitsiMeetPlatform.instance.hangUp(); 24 | } 25 | 26 | /// Sets the state of the localParticipant audio [muted] according to the muted parameter. 27 | Future setAudioMuted(bool muted) async { 28 | return await JitsiMeetPlatform.instance.setAudioMuted(muted); 29 | } 30 | 31 | /// Sets the state of the localParticipant video [muted] according to the muted parameter. 32 | Future setVideoMuted(bool muted) async { 33 | return await JitsiMeetPlatform.instance.setVideoMuted(muted); 34 | } 35 | 36 | /// Sends a message via the data channel [to] one particular participant or to all of them. 37 | /// If the [to] param is empty, the [message] will be sent to all the participants in the conference. 38 | /// 39 | /// In order to get the participantId for the [to] parameter, the [JitsiMeetEventListener.participantsJoined] 40 | /// event should be listened for, which have as a parameter the participantId and this should be stored somehow. 41 | Future sendEndpointTextMessage( 42 | {String? to, required String message}) async { 43 | return await JitsiMeetPlatform.instance 44 | .sendEndpointTextMessage(to: to, message: message); 45 | } 46 | 47 | /// Sets the state of the localParticipant screen sharing according to the [enabled] parameter. 48 | Future toggleScreenShare(bool enabled) async { 49 | return await JitsiMeetPlatform.instance.toggleScreenShare(enabled); 50 | } 51 | 52 | /// Opens the chat dialog. If [to] contains a valid participantId, the private chat with that 53 | /// particular participant will be opened. 54 | Future openChat([String? to]) async { 55 | return await JitsiMeetPlatform.instance.openChat(to); 56 | } 57 | 58 | /// Sends a chat message via [to] one particular participant or to all of them. 59 | /// If the [to] param is empty, the [message] will be sent to all the participants in the conference. 60 | /// 61 | /// In order to get the participantId for the [to] parameter, the [JitsiMeetEventListener.participantsJoined] 62 | /// event should be listened for, which have as a parameter the participantId and this should be stored somehow. 63 | Future sendChatMessage( 64 | {String? to, required String message}) async { 65 | return await JitsiMeetPlatform.instance 66 | .sendChatMessage(to: to, message: message); 67 | } 68 | 69 | /// Closes the chat dialog. 70 | Future closeChat() async { 71 | return await JitsiMeetPlatform.instance.closeChat(); 72 | } 73 | 74 | /// Sends and event that will trigger the [JitsiMeetEventListener.participantsInfoRetrieved] event 75 | /// which will contain participants information. 76 | Future retrieveParticipantsInfo() async { 77 | return await JitsiMeetPlatform.instance.retrieveParticipantsInfo(); 78 | } 79 | 80 | /// Enters Picture in Picture mode. 81 | Future enterPiP() async { 82 | return await JitsiMeetPlatform.instance.enterPiP(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /lib/src/jitsi_meet_conference_options.dart: -------------------------------------------------------------------------------- 1 | import 'jitsi_meet_user_info.dart'; 2 | 3 | /// This object encapsulates all the options that can be tweaked when joining a conference. 4 | class JitsiMeetConferenceOptions { 5 | /// Server where the conference should take place. 6 | final String? serverURL; 7 | 8 | /// Room name. 9 | final String room; 10 | 11 | /// JWT token used for authentication. 12 | final String? token; 13 | 14 | /// Config overrides See: https://github.com/jitsi/jitsi-meet/blob/master/config.js. 15 | late final Map? configOverrides; 16 | 17 | /// Feature flags. See: https://github.com/jitsi/jitsi-meet/blob/master/react/features/base/flags/constants.ts. 18 | final Map? featureFlags; 19 | 20 | /// Information about the local user. It will be used in absence of a token. 21 | final JitsiMeetUserInfo? userInfo; 22 | 23 | JitsiMeetConferenceOptions( 24 | {this.serverURL, 25 | required this.room, 26 | this.token, 27 | this.configOverrides, 28 | this.featureFlags, 29 | this.userInfo}); 30 | } 31 | -------------------------------------------------------------------------------- /lib/src/jitsi_meet_event_listener.dart: -------------------------------------------------------------------------------- 1 | class JitsiMeetEventListener { 2 | /// Called when a conference was joined. 3 | /// 4 | /// [url] : the conference URL 5 | final Function(String url)? conferenceJoined; 6 | 7 | /// Called when the active conference ends, be it because of user choice or because of a failure. 8 | /// 9 | /// [url] : the conference URL 10 | /// [error] : missing if the conference finished gracefully, otherwise contains the error message 11 | final Function(String url, Object? error)? conferenceTerminated; 12 | 13 | /// Called before a conference is joined. 14 | /// 15 | /// [url] : the conference URL 16 | final Function(String url)? conferenceWillJoin; 17 | 18 | /// Called when a participant has joined the conference. 19 | /// 20 | /// [email] : the email of the participant. It may not be set if the remote participant didn't set one. 21 | /// [name] : the name of the participant. 22 | /// [role] : the role of the participant. 23 | /// [participantId] : the id of the participant. 24 | final Function( 25 | String? email, String? name, String? role, String? participantId)? 26 | participantJoined; 27 | 28 | /// Called when a participant has left the conference. 29 | /// 30 | /// [participantId] : the id of the participant that left. 31 | final Function(String? participantId)? participantLeft; 32 | 33 | /// Called when the local participant's audio is muted or unmuted. 34 | /// 35 | /// [muted] : a boolean indicating whether the audio is muted or not. 36 | final Function(bool muted)? audioMutedChanged; 37 | 38 | /// Called when the local participant's video is muted or unmuted. 39 | /// 40 | /// [muted] : a boolean indicating whether the video is muted or not. 41 | final Function(bool muted)? videoMutedChanged; 42 | 43 | /// Called when an endpoint text message is received. 44 | /// 45 | /// [senderId] : the id of the participant that sent the message. 46 | /// [message] : the content of the message. 47 | final Function(String senderId, String message)? endpointTextMessageReceived; 48 | 49 | /// Called when a participant starts or stops sharing his screen. 50 | /// 51 | /// [participantId] : the id of the participant 52 | /// [sharing] : the state of screen share 53 | final Function(String participantId, bool sharing)? screenShareToggled; 54 | 55 | /// Called when a chat text message is received. 56 | /// 57 | /// [senderId] : the id of the participant that sent the message. 58 | /// [message] : the content of the message. 59 | /// [isPrivate] : `true` if the message is private, `false` otherwise. 60 | /// [timestamp] : the (optional) timestamp of the message. 61 | final Function( 62 | String senderId, String message, bool isPrivate, String? timestamp)? 63 | chatMessageReceived; 64 | 65 | /// Called when the chat dialog is opened or closed. 66 | /// 67 | /// [isOpen] : `true` if the chat dialog is open, `false` otherwise. 68 | final Function(bool isOpen)? chatToggled; 69 | 70 | /// Called when `retrieveParticipantsInfo` action is called. 71 | /// 72 | /// [participantsInfo] : a list of participants information as a string. 73 | final Function(String participantsInfo)? participantsInfoRetrieved; 74 | 75 | /// Called when the SDK is ready to be closed. No meeting is happening at this point. 76 | final Function()? readyToClose; 77 | 78 | /// Called when a custom overflow menu button is pressed. 79 | /// 80 | /// [buttonId] : the id of the button that was pressed. 81 | final Function(String buttonId)? customButtonPressed; 82 | 83 | JitsiMeetEventListener({ 84 | this.conferenceJoined, 85 | this.conferenceTerminated, 86 | this.conferenceWillJoin, 87 | this.participantJoined, 88 | this.participantLeft, 89 | this.audioMutedChanged, 90 | this.videoMutedChanged, 91 | this.endpointTextMessageReceived, 92 | this.screenShareToggled, 93 | this.participantsInfoRetrieved, 94 | this.chatMessageReceived, 95 | this.chatToggled, 96 | this.readyToClose, 97 | this.customButtonPressed, 98 | }); 99 | } 100 | -------------------------------------------------------------------------------- /lib/src/jitsi_meet_method_channel.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:jitsi_meet_flutter_sdk/src/method_response.dart'; 6 | 7 | import 'jitsi_meet_conference_options.dart'; 8 | import 'jitsi_meet_event_listener.dart'; 9 | import 'jitsi_meet_platform_interface.dart'; 10 | 11 | /// An implementation of [JitsiMeetPlatform] that uses method channels. 12 | class MethodChannelJitsiMeet extends JitsiMeetPlatform { 13 | /// The method channel used to interact with the native platform. 14 | @visibleForTesting 15 | final methodChannel = const MethodChannel('jitsi_meet_flutter_sdk'); 16 | @visibleForTesting 17 | final eventChannel = const EventChannel('jitsi_meet_flutter_sdk_events'); 18 | 19 | bool _eventChannelIsInitialized = false; 20 | JitsiMeetEventListener? _listener; 21 | 22 | @override 23 | Future getPlatformVersion() async { 24 | final version = 25 | await methodChannel.invokeMethod('getPlatformVersion'); 26 | return version; 27 | } 28 | 29 | /// Joins a meeting with the given meeting [options] and 30 | /// optionally a [listener] is given for listening to events triggered by the native sdks. 31 | @override 32 | Future join(JitsiMeetConferenceOptions options, 33 | JitsiMeetEventListener? listener) async { 34 | _listener = listener; 35 | if (!_eventChannelIsInitialized) { 36 | _initialize(); 37 | } 38 | 39 | Map parsedOptions = { 40 | 'serverURL': options.serverURL, 41 | 'room': options.room, 42 | 'token': options.token, 43 | 'userInfo': { 44 | 'displayName': options.userInfo?.displayName, 45 | 'email': options.userInfo?.email, 46 | 'avatar': options.userInfo?.avatar, 47 | }, 48 | 'featureFlags': options.featureFlags, 49 | 'configOverrides': options.configOverrides 50 | }; 51 | return await methodChannel 52 | .invokeMethod('join', parsedOptions) 53 | .then((message) { 54 | return MethodResponse(isSuccess: true, message: message); 55 | }).catchError((error) { 56 | return MethodResponse( 57 | isSuccess: false, 58 | message: error.toString(), 59 | error: error, 60 | ); 61 | }); 62 | } 63 | 64 | /// The localParticipant leaves the current meeting. 65 | @override 66 | Future hangUp() async { 67 | return await methodChannel.invokeMethod('hangUp').then((message) { 68 | return MethodResponse(isSuccess: true, message: message); 69 | }).catchError((error) { 70 | return MethodResponse( 71 | isSuccess: false, 72 | message: error.toString(), 73 | error: error, 74 | ); 75 | }); 76 | } 77 | 78 | /// Sets the state of the localParticipant audio [muted] according to the muted parameter. 79 | @override 80 | Future setAudioMuted(bool muted) async { 81 | return await methodChannel.invokeMethod( 82 | 'setAudioMuted', {'muted': muted}).then((message) { 83 | return MethodResponse(isSuccess: true, message: message); 84 | }).catchError((error) { 85 | return MethodResponse( 86 | isSuccess: false, 87 | message: error.toString(), 88 | error: error, 89 | ); 90 | }); 91 | } 92 | 93 | /// Sets the state of the localParticipant video [muted] according to the muted parameter. 94 | @override 95 | Future setVideoMuted(bool muted) async { 96 | return await methodChannel.invokeMethod( 97 | 'setVideoMuted', {'muted': muted}).then((message) { 98 | return MethodResponse(isSuccess: true, message: message); 99 | }).catchError((error) { 100 | return MethodResponse( 101 | isSuccess: false, 102 | message: error.toString(), 103 | error: error, 104 | ); 105 | }); 106 | } 107 | 108 | /// Sends a message via the data channel [to] one particular participant or to all of them. 109 | /// If the [to] param is empty, the [message] will be sent to all the participants in the conference. 110 | /// 111 | /// In order to get the participantId for the [to] parameter, the [JitsiMeetEventListener.participantsJoined] 112 | /// event should be listened for, which have as a parameter the participantId and this should be stored somehow. 113 | @override 114 | Future sendEndpointTextMessage( 115 | {String? to, required String message}) async { 116 | return await methodChannel.invokeMethod('sendEndpointTextMessage', 117 | {'to': to ?? '', 'message': message}).then((message) { 118 | return MethodResponse(isSuccess: true, message: message); 119 | }).catchError((error) { 120 | return MethodResponse( 121 | isSuccess: false, 122 | message: error.toString(), 123 | error: error, 124 | ); 125 | }); 126 | } 127 | 128 | /// Sets the state of the localParticipant screen sharing according to the [enabled] parameter. 129 | @override 130 | Future toggleScreenShare(bool enabled) async { 131 | return await methodChannel.invokeMethod( 132 | 'toggleScreenShare', {'enabled': enabled}).then((message) { 133 | return MethodResponse(isSuccess: true, message: message); 134 | }).catchError((error) { 135 | return MethodResponse( 136 | isSuccess: false, 137 | message: error.toString(), 138 | error: error, 139 | ); 140 | }); 141 | } 142 | 143 | /// Opens the chat dialog. If [to] contains a valid participantId, the private chat with that 144 | /// particular participant will be opened. 145 | @override 146 | Future openChat([String? to]) async { 147 | return await methodChannel.invokeMethod('openChat', { 148 | 'to': to ?? '', 149 | }).then((message) { 150 | return MethodResponse(isSuccess: true, message: message); 151 | }).catchError((error) { 152 | return MethodResponse( 153 | isSuccess: false, 154 | message: error.toString(), 155 | error: error, 156 | ); 157 | }); 158 | } 159 | 160 | /// Sends a chat message via [to] one particular participant or to all of them. 161 | /// If the [to] param is empty, the [message] will be sent to all the participants in the conference. 162 | /// 163 | /// In order to get the participantId for the [to] parameter, the [JitsiMeetEventListener.participantsJoined] 164 | /// event should be listened for, which have as a parameter the participantId and this should be stored somehow. 165 | @override 166 | Future sendChatMessage( 167 | {String? to, required String message}) async { 168 | return await methodChannel.invokeMethod('sendChatMessage', 169 | {'to': to ?? '', 'message': message}).then((message) { 170 | return MethodResponse(isSuccess: true, message: message); 171 | }).catchError((error) { 172 | return MethodResponse( 173 | isSuccess: false, 174 | message: error.toString(), 175 | error: error, 176 | ); 177 | }); 178 | } 179 | 180 | /// Closes the chat dialog. 181 | @override 182 | Future closeChat() async { 183 | return await methodChannel 184 | .invokeMethod('closeChat') 185 | .then((message) { 186 | return MethodResponse(isSuccess: true, message: message); 187 | }).catchError((error) { 188 | return MethodResponse( 189 | isSuccess: false, 190 | message: error.toString(), 191 | error: error, 192 | ); 193 | }); 194 | } 195 | 196 | /// Sends and event that will trigger the [JitsiMeetEventListener.participantsInfoRetrieved] event 197 | /// which will contain participants information. 198 | @override 199 | Future retrieveParticipantsInfo() async { 200 | return await methodChannel 201 | .invokeMethod('retrieveParticipantsInfo') 202 | .then((message) { 203 | return MethodResponse(isSuccess: true, message: message); 204 | }).catchError((error) { 205 | return MethodResponse( 206 | isSuccess: false, 207 | message: error.toString(), 208 | error: error, 209 | ); 210 | }); 211 | } 212 | 213 | /// Enters Picture-in-Picture mode. 214 | @override 215 | Future enterPiP() async { 216 | return await methodChannel 217 | .invokeMethod('enterPiP') 218 | .then( 219 | (message) => MethodResponse( 220 | isSuccess: true, 221 | message: message, 222 | ), 223 | ) 224 | .catchError( 225 | (error) => MethodResponse( 226 | isSuccess: false, 227 | message: error.toString(), 228 | error: error, 229 | ), 230 | ); 231 | } 232 | 233 | void _initialize() { 234 | eventChannel.receiveBroadcastStream().listen((message) { 235 | final data = message['data']; 236 | switch (message['event']) { 237 | case "conferenceJoined": 238 | _listener?.conferenceJoined?.call(data["url"]); 239 | break; 240 | 241 | case "conferenceTerminated": 242 | _listener?.conferenceTerminated?.call(data["url"], data["error"]); 243 | break; 244 | 245 | case "conferenceWillJoin": 246 | _listener?.conferenceWillJoin?.call(data["url"]); 247 | break; 248 | 249 | case "participantJoined": 250 | _listener?.participantJoined?.call( 251 | data["email"], 252 | data["name"], 253 | data["role"], 254 | data["participantId"], 255 | ); 256 | break; 257 | 258 | case "participantLeft": 259 | _listener?.participantLeft?.call(data["participantId"]); 260 | break; 261 | 262 | case "audioMutedChanged": 263 | _listener?.audioMutedChanged?.call(parseBool(data["muted"])); 264 | break; 265 | 266 | case "videoMutedChanged": 267 | _listener?.videoMutedChanged?.call(parseBool(data["muted"])); 268 | break; 269 | 270 | case "endpointTextMessageReceived": 271 | _listener?.endpointTextMessageReceived 272 | ?.call(data["senderId"], data["message"]); 273 | break; 274 | 275 | case "screenShareToggled": 276 | _listener?.screenShareToggled 277 | ?.call(data["participantId"], parseBool(data["sharing"])); 278 | break; 279 | 280 | case "chatMessageReceived": 281 | _listener?.chatMessageReceived?.call( 282 | data["senderId"], 283 | data["message"], 284 | parseBool(data["isPrivate"]), 285 | data["timestamp"], 286 | ); 287 | break; 288 | 289 | case "chatToggled": 290 | _listener?.chatToggled?.call(parseBool(data["isOpen"])); 291 | break; 292 | 293 | case "participantsInfoRetrieved": 294 | String participantsInfo = ""; 295 | if (Platform.isAndroid) { 296 | participantsInfo = data["participantsInfo"]; 297 | } else if (Platform.isIOS) { 298 | participantsInfo = data.toString(); 299 | } 300 | _listener?.participantsInfoRetrieved?.call( 301 | participantsInfo, 302 | ); 303 | break; 304 | 305 | case "readyToClose": 306 | _listener?.readyToClose?.call(); 307 | break; 308 | 309 | case "customButtonPressed": 310 | _listener?.customButtonPressed?.call(data["id"]); 311 | break; 312 | } 313 | }).onError((error) { 314 | debugPrint("Error receiving data from the event channel: $error"); 315 | }); 316 | _eventChannelIsInitialized = true; 317 | } 318 | } 319 | 320 | bool parseBool(dynamic value) { 321 | if (value is bool) return value; 322 | if (value is String) return value == 'true'; 323 | if (value is num) return value != 0; 324 | throw ArgumentError('Unsupported type: $value'); 325 | } 326 | -------------------------------------------------------------------------------- /lib/src/jitsi_meet_platform_interface.dart: -------------------------------------------------------------------------------- 1 | import 'package:plugin_platform_interface/plugin_platform_interface.dart'; 2 | 3 | import 'jitsi_meet_conference_options.dart'; 4 | import 'jitsi_meet_event_listener.dart'; 5 | import 'jitsi_meet_method_channel.dart'; 6 | import 'method_response.dart'; 7 | 8 | abstract class JitsiMeetPlatform extends PlatformInterface { 9 | /// Constructs a JitsiMeetPlatform. 10 | JitsiMeetPlatform() : super(token: _token); 11 | 12 | static final Object _token = Object(); 13 | 14 | static JitsiMeetPlatform _instance = MethodChannelJitsiMeet(); 15 | 16 | /// The default instance of [JitsiMeetPlatform] to use. 17 | /// 18 | /// Defaults to [MethodChannelJitsiMeet]. 19 | static JitsiMeetPlatform get instance => _instance; 20 | 21 | /// Platform-specific implementations should set this with their own 22 | /// platform-specific class that extends [JitsiMeetPlatform] when 23 | /// they register themselves. 24 | static set instance(JitsiMeetPlatform instance) { 25 | PlatformInterface.verifyToken(instance, _token); 26 | _instance = instance; 27 | } 28 | 29 | Future getPlatformVersion() { 30 | throw UnimplementedError('platformVersion() has not been implemented.'); 31 | } 32 | 33 | /// Joins a meeting with the given meeting [options] and 34 | /// optionally a [listener] is given for listening to events triggered by the native sdks. 35 | Future join( 36 | JitsiMeetConferenceOptions options, JitsiMeetEventListener? listener) { 37 | throw UnimplementedError('join() has not been implemented.'); 38 | } 39 | 40 | /// The localParticipant leaves the current meeting. 41 | Future hangUp() { 42 | throw UnimplementedError('hangUp() has not been implemented.'); 43 | } 44 | 45 | /// Sets the state of the localParticipant audio [muted] according to the muted parameter. 46 | Future setAudioMuted(bool muted) { 47 | throw UnimplementedError('setAudioMuted() has not been implemented.'); 48 | } 49 | 50 | /// Sets the state of the localParticipant video [muted] according to the muted parameter. 51 | Future setVideoMuted(bool muted) { 52 | throw UnimplementedError('setVideoMuted() has not been implemented.'); 53 | } 54 | 55 | /// Sends a message via the data channel [to] one particular participant or to all of them. 56 | /// If the [to] param is empty, the [message] will be sent to all the participants in the conference. 57 | /// 58 | /// In order to get the participantId for the [to] parameter, the [JitsiMeetEventListener.participantsJoined] 59 | /// event should be listened for, which have as a parameter the participantId and this should be stored somehow. 60 | Future sendEndpointTextMessage( 61 | {String? to, required String message}) async { 62 | throw UnimplementedError( 63 | 'sendEndpointTextMessage() has not been implemented.'); 64 | } 65 | 66 | /// Sets the state of the localParticipant screen sharing according to the [enabled] parameter. 67 | Future toggleScreenShare(bool enabled) async { 68 | throw UnimplementedError( 69 | 'sendEndpointTextMessage() has not been implemented.'); 70 | } 71 | 72 | /// Opens the chat dialog. If [to] contains a valid participantId, the private chat with that 73 | /// particular participant will be opened. 74 | Future openChat([String? to]) async { 75 | throw UnimplementedError('openChat() has not been implemented.'); 76 | } 77 | 78 | /// Sends a chat message via [to] one particular participant or to all of them. 79 | /// If the [to] param is empty, the [message] will be sent to all the participants in the conference. 80 | /// 81 | /// In order to get the participantId for the [to] parameter, the [JitsiMeetEventListener.participantsJoined] 82 | /// event should be listened for, which have as a parameter the participantId and this should be stored somehow. 83 | Future sendChatMessage( 84 | {String? to, required String message}) async { 85 | throw UnimplementedError('sendChatMessage() has not been implemented.'); 86 | } 87 | 88 | /// Closes the chat dialog. 89 | Future closeChat() async { 90 | throw UnimplementedError('openChat() has not been implemented.'); 91 | } 92 | 93 | /// Sends and event that will trigger the [JitsiMeetEventListener.participantsInfoRetrieved] event 94 | /// which will contain participants information. 95 | Future retrieveParticipantsInfo() async { 96 | throw UnimplementedError( 97 | 'retrieveParticipantsInfo() has not been implemented.'); 98 | } 99 | 100 | /// Enters Picture-in-Picture mode. 101 | Future enterPiP() async { 102 | throw UnimplementedError('enterPiP() has not been implemented.'); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /lib/src/jitsi_meet_user_info.dart: -------------------------------------------------------------------------------- 1 | /// Information about the local user. It will be used in absence of a token. 2 | class JitsiMeetUserInfo { 3 | /// User display name. 4 | final String? displayName; 5 | 6 | /// User email. 7 | final String? email; 8 | 9 | /// URL for the user avatar. 10 | final String? avatar; 11 | 12 | JitsiMeetUserInfo({this.displayName, this.email, this.avatar}); 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/method_response.dart: -------------------------------------------------------------------------------- 1 | class MethodResponse { 2 | final bool isSuccess; 3 | final String? message; 4 | final dynamic error; 5 | 6 | MethodResponse({ 7 | required this.isSuccess, 8 | this.message, 9 | this.error, 10 | }); 11 | 12 | @override 13 | String toString() { 14 | return 'MethodResponse{isSuccess: $isSuccess, ' 15 | 'message: $message, error: $error}'; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: jitsi_meet_flutter_sdk 2 | description: A flutter plugin that serves as a Jitsi Meet flutter SDK which provides the same user experience as the Jitsi Meet app. 3 | version: 11.2.0 4 | homepage: https://jitsi.org/ 5 | repository: https://github.com/jitsi/jitsi-meet-flutter-sdk 6 | 7 | environment: 8 | sdk: '>=3.0.5 <4.0.0' 9 | flutter: ">=3.3.0" 10 | 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | plugin_platform_interface: ^2.0.2 15 | 16 | dev_dependencies: 17 | flutter_test: 18 | sdk: flutter 19 | flutter_lints: ^2.0.0 20 | 21 | flutter: 22 | plugin: 23 | platforms: 24 | android: 25 | package: org.jitsi.jitsi_meet_flutter_sdk 26 | pluginClass: JitsiMeetPlugin 27 | ios: 28 | pluginClass: JitsiMeetPlugin 29 | -------------------------------------------------------------------------------- /test/jitsi_meet_method_channel_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | import 'package:jitsi_meet_flutter_sdk/src/jitsi_meet_method_channel.dart'; 4 | 5 | void main() { 6 | TestWidgetsFlutterBinding.ensureInitialized(); 7 | 8 | MethodChannelJitsiMeet platform = MethodChannelJitsiMeet(); 9 | const MethodChannel channel = MethodChannel('jitsi_meet_flutter_sdk'); 10 | 11 | setUp(() { 12 | TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger 13 | .setMockMethodCallHandler( 14 | channel, 15 | (MethodCall methodCall) async { 16 | return '42'; 17 | }, 18 | ); 19 | }); 20 | 21 | tearDown(() { 22 | TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger 23 | .setMockMethodCallHandler(channel, null); 24 | }); 25 | 26 | test('getPlatformVersion', () async { 27 | expect(await platform.getPlatformVersion(), '42'); 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /test/jitsi_meet_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | import 'package:jitsi_meet_flutter_sdk/src/jitsi_meet.dart'; 3 | import 'package:jitsi_meet_flutter_sdk/src/jitsi_meet_conference_options.dart'; 4 | import 'package:jitsi_meet_flutter_sdk/src/jitsi_meet_event_listener.dart'; 5 | import 'package:jitsi_meet_flutter_sdk/src/jitsi_meet_method_channel.dart'; 6 | import 'package:jitsi_meet_flutter_sdk/src/jitsi_meet_platform_interface.dart'; 7 | import 'package:jitsi_meet_flutter_sdk/src/method_response.dart'; 8 | import 'package:plugin_platform_interface/plugin_platform_interface.dart'; 9 | 10 | class MockJitsiMeetPlatform 11 | with MockPlatformInterfaceMixin 12 | implements JitsiMeetPlatform { 13 | @override 14 | Future getPlatformVersion() => Future.value('42'); 15 | 16 | @override 17 | Future join( 18 | JitsiMeetConferenceOptions options, JitsiMeetEventListener? listener) { 19 | // TODO: implement join 20 | throw UnimplementedError(); 21 | } 22 | 23 | @override 24 | Future hangUp() { 25 | // TODO: implement hangUp 26 | throw UnimplementedError(); 27 | } 28 | 29 | @override 30 | Future setAudioMuted(bool muted) { 31 | // TODO: implement setAudioMuted 32 | throw UnimplementedError(); 33 | } 34 | 35 | @override 36 | Future setVideoMuted(bool muted) { 37 | // TODO: implement setVideoMuted 38 | throw UnimplementedError(); 39 | } 40 | 41 | @override 42 | Future sendEndpointTextMessage( 43 | {String? to, required String message}) { 44 | // TODO: implement sendEndpointTextMessage 45 | throw UnimplementedError(); 46 | } 47 | 48 | @override 49 | Future toggleScreenShare(bool enabled) { 50 | // TODO: implement toggleScreenShare 51 | throw UnimplementedError(); 52 | } 53 | 54 | @override 55 | Future openChat([String? to]) { 56 | // TODO: implement openChat 57 | throw UnimplementedError(); 58 | } 59 | 60 | @override 61 | Future sendChatMessage( 62 | {String? to, required String message}) { 63 | // TODO: implement sendChatMessage 64 | throw UnimplementedError(); 65 | } 66 | 67 | @override 68 | Future closeChat() { 69 | // TODO: implement closeChat 70 | throw UnimplementedError(); 71 | } 72 | 73 | @override 74 | Future retrieveParticipantsInfo() { 75 | // TODO: implement retrieveParticipantsInfo 76 | throw UnimplementedError(); 77 | } 78 | 79 | @override 80 | Future enterPiP() { 81 | throw UnimplementedError(); 82 | } 83 | } 84 | 85 | void main() { 86 | final JitsiMeetPlatform initialPlatform = JitsiMeetPlatform.instance; 87 | 88 | test('$MethodChannelJitsiMeet is the default instance', () { 89 | expect(initialPlatform, isInstanceOf()); 90 | }); 91 | 92 | test('getPlatformVersion', () async { 93 | JitsiMeet jitsiMeetPlugin = JitsiMeet(); 94 | MockJitsiMeetPlatform fakePlatform = MockJitsiMeetPlatform(); 95 | JitsiMeetPlatform.instance = fakePlatform; 96 | 97 | expect(await jitsiMeetPlugin.getPlatformVersion(), '42'); 98 | }); 99 | } 100 | -------------------------------------------------------------------------------- /update-native-sdks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | version=$(curl -s https://raw.githubusercontent.com/jitsi/jitsi-meet-release-notes/master/CHANGELOG-MOBILE-SDKS.md | grep -E '# \[[0-9]+\.[0-9]+\.[0-9]+\]' | head -1 | cut -d']' -f1 | cut -d'[' -f2) 4 | gradle_repo="org.jitsi.react:jitsi-meet-sdk" 5 | pod_repo="JitsiMeetSDK" 6 | version_regex="(\d+\.)?(\d+\.)?(\*|\d+)" 7 | perl -i -pe"s/$gradle_repo:$version_regex/$gradle_repo:$version/" android/build.gradle 8 | perl -i -pe"s/($pod_repo', )'$version_regex'/\1'$version'/" ios/jitsi_meet_flutter_sdk.podspec 9 | 10 | cd example/ios 11 | 12 | pod --silent update JitsiMeetSDK 13 | 14 | cd ../.. 15 | 16 | git checkout main 17 | git pull origin main --rebase 18 | git add android/build.gradle example/ios/Podfile.lock ios/jitsi_meet_flutter_sdk.podspec 19 | git commit -m "chore(deps): update native sdks to $version" 20 | git push origin main 21 | -------------------------------------------------------------------------------- /update-version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ $# -ne 1 ]]; then 4 | echo "Please specify a version" 5 | exit 1 6 | fi 7 | 8 | version=$1 9 | echo -e "## $version \n" > temp 10 | git fetch 11 | git checkout main 12 | git pull origin main --rebase 13 | 14 | latest_relase=$(git describe --tags --abbrev=0) 15 | git log $latest_relase..HEAD --no-merges --oneline --pretty=format:'* %s [%h](https://github.com/jitsi/jitsi-meet-flutter-sdk/commit/%H).' >> temp 16 | 17 | echo -e "\n" >> temp; 18 | 19 | cat CHANGELOG.md >> temp; 20 | 21 | cat temp > CHANGELOG.md; 22 | 23 | rm temp; 24 | badge_url="https:\/\/img.shields.io\/badge\/pub-v"; 25 | badge_color="blue"; 26 | version_regex="(\d+\.)?(\d+\.)?(\*|\d+)" 27 | perl -i -pe"s/$badge_url$version_regex-$badge_color/$badge_url$version-$badge_color/" README.md 28 | 29 | package_name="jitsi_meet_flutter_sdk" 30 | perl -i -pe"s/$package_name: \^$version_regex/$package_name: \^$version/" README.md 31 | 32 | perl -i -pe"s/version: $version_regex/version: $version/" pubspec.yaml 33 | 34 | cd example 35 | flutter pub get 36 | cd .. 37 | 38 | git add CHANGELOG.md pubspec.yaml README.md example/pubspec.lock 39 | git commit -m "v$version" 40 | git push origin main --------------------------------------------------------------------------------