diff --git a/.gitignore b/.gitignore index 0c8565dec..0c4dd3865 100644 --- a/.gitignore +++ b/.gitignore @@ -1,36 +1,56 @@ -sample apps/hms-callkit-app/.dart_tool/flutter_build/ -.dart_tool/package_config.json -.dart_tool/package_config_subset -.dart_tool/version -android/.gradle/6.8.3/gc.properties -android/.gradle/6.8.3/fileChanges/last-build.bin -android/.gradle/6.8.3/fileHashes/fileHashes.lock -android/.gradle/buildOutputCleanup/buildOutputCleanup.lock -android/.gradle/buildOutputCleanup/cache.properties -android/.gradle/checksums/checksums.lock -android/.gradle/checksums/sha1-checksums.bin -android/.gradle/configuration-cache/gc.properties -android/.gradle/vcs-1/gc.properties -android/.idea/.name -android/.idea/gradle.xml -android/.idea/misc.xml -android/.idea/workspace.xml -example/.flutter-plugins -example/.flutter-plugins-dependencies -example/.dart_tool/package_config.json -example/.dart_tool/package_config_subset -example/.dart_tool/flutter_build/ -example/android/.gradle/ -example/android/.idea/ -example/ios/.symlinks/ -example/ios/Pods/ -sample apps/flutter-quickstart-app/.dart_tool/ -sample apps/hms-callkit-app/.dart_tool/ -sample apps/flutter-quickstart-app/build/ios/Pods.build/ -packages/hms_room_kit/.dart_tool +## HMSSDK +packages/hmssdk_flutter/example/android/.gradle/6.8.3/gc.properties +packages/hmssdk_flutter/example/android/.gradle/6.8.3/fileChanges/last-build.bin +packages/hmssdk_flutter/example/android/.gradle/6.8.3/fileHashes/fileHashes.lock +packages/hmssdk_flutter/example/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +packages/hmssdk_flutter/example/android/.gradle/buildOutputCleanup/cache.properties +packages/hmssdk_flutter/example/android/.gradle/checksums/checksums.lock +packages/hmssdk_flutter/example/android/.gradle/checksums/sha1-checksums.bin +packages/hmssdk_flutter/example/android/.gradle/configuration-cache/gc.properties +packages/hmssdk_flutter/example/android/.gradle/vcs-1/gc.properties +packages/hmssdk_flutter/example/android/.idea/.name +packages/hmssdk_flutter/example/android/local.properties +packages/hmssdk_flutter/example/android/.idea/gradle.xml +packages/hmssdk_flutter/example/android/.idea/misc.xml +packages/hmssdk_flutter/example/android/.idea/workspace.xml +packages/hmssdk_flutter/example/.flutter-plugins +packages/hmssdk_flutter/example/.flutter-plugins-dependencies +packages/hmssdk_flutter/example/.dart_tool/package_config.json +packages/hmssdk_flutter/example/.dart_tool/package_config_subset +packages/hmssdk_flutter/example/.dart_tool/flutter_build/ +packages/hmssdk_flutter/example/android/.gradle/ +packages/hmssdk_flutter/example/android/.idea/ +packages/hmssdk_flutter/example/ios/.symlinks/ +packages/hmssdk_flutter/example/ios/Pods/ packages/hmssdk_flutter/.dart_tool packages/hmssdk_flutter/example/.dart_tool packages/hmssdk_flutter/example/android/fastlane packages/hmssdk_flutter/example/build packages/hmssdk_flutter/example/ios/fastlane -packages/hmssdk_flutter/example/android/app/google-services.json \ No newline at end of file +packages/hmssdk_flutter/example/ios/flutter-hms-4aea6d38fd2a.json +packages/hmssdk_flutter/example/android/app/google-services.json +packages/hmssdk_flutter/example/android/flutter-hms-4aea6d38fd2a.json +packages/hmssdk_flutter/build/ + +##HMS ROOM KIT +packages/hms_room_kit/.dart_tool +packages/hms_room_kit/.DS_Store +packages/hms_room_kit/.flutter-plugins +packages/hms_room_kit/.flutter-plugins-dependencies +packages/hms_room_kit/android/local.properties +packages/hms_room_kit/ios/Flutter/flutter_export_environment.sh +packages/hms_room_kit/ios/Flutter/Generated.xcconfig + +##Sample apps +sample apps/flutter-quickstart-app/.dart_tool/ +sample apps/hms-callkit-app/.dart_tool/ +sample apps/flutter-quickstart-app/build/ios/Pods.build/ +sample apps/hms-callkit-app/.dart_tool/flutter_build/ + +##GIT Files +.dart_tool/package_config.json +.dart_tool/package_config_subset +.dart_tool/version +.DS_Store +packages/.DS_Store +Runner.ipa diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index 9c3868223..541bca47f 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -1,18 +1,18 @@ version: 0.1 cli: - version: 1.16.1 + version: 1.16.2 plugins: sources: - id: trunk - ref: v1.2.4 + ref: v1.2.5 uri: https://github.com/trunk-io/plugins lint: enabled: - actionlint@1.6.26 - - checkov@2.4.9 - - osv-scanner@1.4.0 + - checkov@2.5.7 + - osv-scanner@1.4.1 - trivy@0.45.1 - - trufflehog@3.57.0 + - trufflehog@3.59.0 - oxipng@8.0.0 - yamllint@1.32.0 - markdownlint@0.37.0 diff --git a/packages/hms_room_kit/CHANGELOG.md b/packages/hms_room_kit/CHANGELOG.md index e89076961..80b91571f 100644 --- a/packages/hms_room_kit/CHANGELOG.md +++ b/packages/hms_room_kit/CHANGELOG.md @@ -5,6 +5,21 @@ | hms_room_kit | [![Pub Version](https://img.shields.io/pub/v/hms_room_kit)](https://pub.dev/packages/hms_room_kit) | | hmssdk_flutter | [![Pub Version](https://img.shields.io/pub/v/hmssdk_flutter)](https://pub.dev/packages/hmssdk_flutter) | +## 1.0.3 - 2023-10-16 + +### 🚀 Added + +- Large Room Support + - Enhanced Participants list to accommodate up to 20,000 peers in a room. + +- Added recording state indicator: showcasing "initializing" and "running" states. +- Added `Lower Hand` Capability in Participant list + +### Fixed + +- Fixed `userid` bug, where `userid` was not getting passed to SDK +- Fixed UI bugs + ## 1.0.2 - 2023-09-22 ### 🚀 Added diff --git a/packages/hms_room_kit/example/ios/Podfile.lock b/packages/hms_room_kit/example/ios/Podfile.lock index b1a42a426..5014e3d77 100644 --- a/packages/hms_room_kit/example/ios/Podfile.lock +++ b/packages/hms_room_kit/example/ios/Podfile.lock @@ -6,14 +6,14 @@ PODS: - HMSBroadcastExtensionSDK (0.0.9) - HMSHLSPlayerSDK (0.0.2): - HMSAnalyticsSDK (= 0.0.2) - - HMSSDK (0.9.12): + - HMSSDK (1.1.0): - HMSAnalyticsSDK (= 0.0.2) - HMSWebRTC (= 1.0.5116) - - hmssdk_flutter (1.8.0): + - hmssdk_flutter (1.9.0): - Flutter - HMSBroadcastExtensionSDK (= 0.0.9) - HMSHLSPlayerSDK (= 0.0.2) - - HMSSDK (= 0.9.12) + - HMSSDK (= 1.1.0) - HMSWebRTC (1.0.5116) - path_provider_foundation (0.0.1): - Flutter @@ -70,8 +70,8 @@ SPEC CHECKSUMS: HMSAnalyticsSDK: 4d2a88a729b1eb42f3d25f217c28937ec318a5b7 HMSBroadcastExtensionSDK: d80fe325f6c928bd8e5176290b5a4b7ae15d6fbb HMSHLSPlayerSDK: 6a54ad4d12f3dc2270d1ecd24019d71282a4f6a3 - HMSSDK: 65c1445ad2aac04deb127fe7611f65b8e7447752 - hmssdk_flutter: 806162401c1c9dd1fca07d913b9ee5f1e1d36930 + HMSSDK: 49e3ac665ceb8904d41787ddf99742e8d7d6529a + hmssdk_flutter: 414edcdff09f1587f53b04e9b93b39e6174a61cd HMSWebRTC: ae54e9dd91b869051b283b43b14f57d43b7bf8e1 path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6 @@ -81,4 +81,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: cc1f88378b4bfcf93a6ce00d2c587857c6008d3b -COCOAPODS: 1.12.1 +COCOAPODS: 1.13.0 diff --git a/packages/hms_room_kit/example/pubspec.lock b/packages/hms_room_kit/example/pubspec.lock index a7d284686..cd05b0146 100644 --- a/packages/hms_room_kit/example/pubspec.lock +++ b/packages/hms_room_kit/example/pubspec.lock @@ -222,15 +222,14 @@ packages: path: ".." relative: true source: path - version: "1.0.2" + version: "1.0.3" hmssdk_flutter: dependency: transitive description: - name: hmssdk_flutter - sha256: fb6cdad295b0c76be408beb9cef0fe3fab9aa25c775779943a68fab4e675628b - url: "https://pub.dev" - source: hosted - version: "1.8.0" + path: "../../hmssdk_flutter" + relative: true + source: path + version: "1.9.0" http: dependency: transitive description: diff --git a/packages/hms_room_kit/example/pubspec.yaml b/packages/hms_room_kit/example/pubspec.yaml index cea24629d..598433295 100644 --- a/packages/hms_room_kit/example/pubspec.yaml +++ b/packages/hms_room_kit/example/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.1 +version: 1.0.3 environment: sdk: ">=2.19.6 <3.0.0" diff --git a/packages/hms_room_kit/lib/src/assets/icons/arrow.svg b/packages/hms_room_kit/lib/src/assets/icons/arrow.svg deleted file mode 100644 index 40cd50ff7..000000000 --- a/packages/hms_room_kit/lib/src/assets/icons/arrow.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/hms_room_kit/lib/src/assets/icons/left_arrow.svg b/packages/hms_room_kit/lib/src/assets/icons/left_arrow.svg new file mode 100644 index 000000000..43ddf899a --- /dev/null +++ b/packages/hms_room_kit/lib/src/assets/icons/left_arrow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/hms_room_kit/lib/src/assets/icons/lower_hand.svg b/packages/hms_room_kit/lib/src/assets/icons/lower_hand.svg new file mode 100644 index 000000000..4df2a263b --- /dev/null +++ b/packages/hms_room_kit/lib/src/assets/icons/lower_hand.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/hms_room_kit/lib/src/assets/icons/right_arrow.svg b/packages/hms_room_kit/lib/src/assets/icons/right_arrow.svg new file mode 100644 index 000000000..4814fb7c6 --- /dev/null +++ b/packages/hms_room_kit/lib/src/assets/icons/right_arrow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/hms_room_kit/lib/src/hls_viewer/hls_viewer_bottom_navigation_bar.dart b/packages/hms_room_kit/lib/src/hls_viewer/hls_viewer_bottom_navigation_bar.dart index 733d45e9a..da98a8a7e 100644 --- a/packages/hms_room_kit/lib/src/hls_viewer/hls_viewer_bottom_navigation_bar.dart +++ b/packages/hms_room_kit/lib/src/hls_viewer/hls_viewer_bottom_navigation_bar.dart @@ -95,7 +95,7 @@ class HLSViewerBottomNavigationBar extends StatelessWidget { onTap: () => { context .read() - .changeMetadata(), + .toggleLocalPeerHandRaise(), }, enabledBorderColor: HMSThemeColors .backgroundDim diff --git a/packages/hms_room_kit/lib/src/hls_viewer/hls_viewer_header.dart b/packages/hms_room_kit/lib/src/hls_viewer/hls_viewer_header.dart index e2d2e6295..6f9e42d6b 100644 --- a/packages/hms_room_kit/lib/src/hls_viewer/hls_viewer_header.dart +++ b/packages/hms_room_kit/lib/src/hls_viewer/hls_viewer_header.dart @@ -1,8 +1,7 @@ ///Dart imports import 'dart:io'; -///Package imports -import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:hms_room_kit/src/common/utility_functions.dart'; import 'package:provider/provider.dart'; @@ -85,12 +84,14 @@ class HLSViewerHeader extends StatelessWidget { ///We render the recording icon based on the recording status ///If the recording is started we show the recording icon ///If the recording is not started we show nothing - Selector>( - selector: (_, meetingStore) => Tuple3( - meetingStore.recordingType["browser"] ?? false, - meetingStore.recordingType["server"] ?? false, - meetingStore.recordingType["hls"] ?? false, - ), + /// + ///If recording initialising state is true we show the loader + Selector>( + selector: (_, meetingStore) => Tuple4( + meetingStore.recordingType["browser"] ?? false, + meetingStore.recordingType["server"] ?? false, + meetingStore.recordingType["hls"] ?? false, + meetingStore.isRecordingInInitialisingState), builder: (_, data, __) { return (data.item1 || data.item2 || data.item3) ? SvgPicture.asset( @@ -101,7 +102,15 @@ class HLSViewerHeader extends StatelessWidget { HMSThemeColors.alertErrorDefault, BlendMode.srcIn), ) - : Container(); + : data.item4 + ? SizedBox( + height: 24, + width: 24, + child: CircularProgressIndicator( + strokeWidth: 1, + color: HMSThemeColors.onSurfaceHighEmphasis, + )) + : Container(); }), const SizedBox( width: 8, @@ -113,7 +122,7 @@ class HLSViewerHeader extends StatelessWidget { Selector>( selector: (_, meetingStore) => Tuple2( meetingStore.streamingType['hls'] ?? false, - meetingStore.peers.length), + meetingStore.peersInRoom), builder: (_, data, __) { return data.item1 ? Container( diff --git a/packages/hms_room_kit/lib/src/hmssdk_interactor.dart b/packages/hms_room_kit/lib/src/hmssdk_interactor.dart index 19c8b9107..558007313 100644 --- a/packages/hms_room_kit/lib/src/hmssdk_interactor.dart +++ b/packages/hms_room_kit/lib/src/hmssdk_interactor.dart @@ -392,4 +392,25 @@ class HMSSDKInteractor { Future cancelPreview() async { return await hmsSDK.cancelPreview(); } + + Future getPeerListIterator( + {PeerListIteratorOptions? peerListIteratorOptions}) async { + return await hmsSDK.getPeerListIterator( + peerListIteratorOptions: peerListIteratorOptions); + } + + void lowerLocalPeerHand({HMSActionResultListener? hmsActionResultListener}) { + hmsSDK.lowerLocalPeerHand(hmsActionResultListener: hmsActionResultListener); + } + + void raiseLocalPeerHand({HMSActionResultListener? hmsActionResultListener}) { + hmsSDK.raiseLocalPeerHand(hmsActionResultListener: hmsActionResultListener); + } + + void lowerRemotePeerHand( + {required HMSPeer forPeer, + HMSActionResultListener? hmsActionResultListener}) { + hmsSDK.lowerRemotePeerHand( + forPeer: forPeer, hmsActionResultListener: hmsActionResultListener); + } } diff --git a/packages/hms_room_kit/lib/src/layout_api/hms_room_layout.dart b/packages/hms_room_kit/lib/src/layout_api/hms_room_layout.dart index 7c697787b..6394e0201 100644 --- a/packages/hms_room_kit/lib/src/layout_api/hms_room_layout.dart +++ b/packages/hms_room_kit/lib/src/layout_api/hms_room_layout.dart @@ -216,6 +216,7 @@ class HMSRoomLayout { static Chat? chatData; static bool isParticipantsListEnabled = true; static bool isBRBEnabled = true; + static List? offStageRoles = []; static Future getRoomLayout( {required HMSSDKInteractor hmsSDKInteractor, @@ -234,14 +235,20 @@ class HMSRoomLayout { if (roleName != null) { int? roleIndex = data?.indexWhere((layoutData) => layoutData.role == roleName); + + ///Check if that role theme is present + ///If not we assign the theme at 0th index if (roleIndex != null && roleIndex != -1) { HMSThemeColors.applyLayoutColors(data?[roleIndex].themes?[0].palette); roleLayoutData = data?[roleIndex]; - return; + } else { + HMSThemeColors.applyLayoutColors(data?[0].themes?[0].palette); + roleLayoutData = data?[0]; } + } else { + HMSThemeColors.applyLayoutColors(data?[0].themes?[0].palette); + roleLayoutData = data?[0]; } - HMSThemeColors.applyLayoutColors(data?[0].themes?[0].palette); - roleLayoutData = data?[0]; peerType = roleLayoutData?.screens?.conferencing?.hlsLiveStreaming != null ? PeerRoleType.hlsViewer : PeerRoleType.conferencing; @@ -254,6 +261,8 @@ class HMSRoomLayout { isBRBEnabled = roleLayoutData?.screens?.conferencing?.defaultConf?.elements?.brb != null; + offStageRoles = roleLayoutData?.screens?.conferencing?.defaultConf + ?.elements?.onStageExp?.offStageRoles; } else { chatData = roleLayoutData ?.screens?.conferencing?.hlsLiveStreaming?.elements?.chat; @@ -263,6 +272,8 @@ class HMSRoomLayout { isBRBEnabled = roleLayoutData ?.screens?.conferencing?.hlsLiveStreaming?.elements?.brb != null; + offStageRoles = roleLayoutData?.screens?.conferencing?.hlsLiveStreaming + ?.elements?.onStageExp?.offStageRoles; } } diff --git a/packages/hms_room_kit/lib/src/meeting/meeting_header.dart b/packages/hms_room_kit/lib/src/meeting/meeting_header.dart index 1e53e22e4..57e994d19 100644 --- a/packages/hms_room_kit/lib/src/meeting/meeting_header.dart +++ b/packages/hms_room_kit/lib/src/meeting/meeting_header.dart @@ -60,6 +60,8 @@ class _MeetingHeaderState extends State { ///else we render an empty Container /// ///For hls streaming status we use the streamingType map from the [MeetingStore] + /// + ///If recording initialising state is true we show the loader Selector( selector: (_, meetingStore) => meetingStore.streamingType['hls'] ?? false, @@ -91,12 +93,12 @@ class _MeetingHeaderState extends State { ///else we render an empty Container /// ///For recording status we use the recordingType map from the [MeetingStore] - Selector>( - selector: (_, meetingStore) => Tuple3( - meetingStore.recordingType["browser"] ?? false, - meetingStore.recordingType["server"] ?? false, - meetingStore.recordingType["hls"] ?? false, - ), + Selector>( + selector: (_, meetingStore) => Tuple4( + meetingStore.recordingType["browser"] ?? false, + meetingStore.recordingType["server"] ?? false, + meetingStore.recordingType["hls"] ?? false, + meetingStore.isRecordingInInitialisingState), builder: (_, data, __) { return (data.item1 || data.item2 || data.item3) ? SvgPicture.asset( @@ -107,7 +109,15 @@ class _MeetingHeaderState extends State { HMSThemeColors.alertErrorDefault, BlendMode.srcIn), ) - : Container(); + : data.item4 + ? SizedBox( + height: 24, + width: 24, + child: CircularProgressIndicator( + strokeWidth: 2, + color: HMSThemeColors.onSurfaceHighEmphasis, + )) + : Container(); }), const SizedBox( width: 8, @@ -119,7 +129,7 @@ class _MeetingHeaderState extends State { Selector>( selector: (_, meetingStore) => Tuple2( meetingStore.streamingType['hls'] ?? false, - meetingStore.peers.length), + meetingStore.peersInRoom), builder: (_, data, __) { return data.item1 ? Container( diff --git a/packages/hms_room_kit/lib/src/meeting/meeting_page.dart b/packages/hms_room_kit/lib/src/meeting/meeting_page.dart index 05536865d..8ed2bbd2d 100644 --- a/packages/hms_room_kit/lib/src/meeting/meeting_page.dart +++ b/packages/hms_room_kit/lib/src/meeting/meeting_page.dart @@ -1,15 +1,10 @@ //Dart imports import 'dart:io'; +import 'dart:math'; ///Package imports import 'package:flutter/material.dart'; import 'package:flutter_foreground_task/flutter_foreground_task.dart'; -import 'package:hms_room_kit/src/preview_for_role/preview_for_role_bottom_sheet.dart'; -import 'package:hms_room_kit/src/preview_for_role/preview_for_role_header.dart'; -import 'package:hms_room_kit/src/widgets/common_widgets/hms_circular_avatar.dart'; -import 'package:hms_room_kit/src/widgets/common_widgets/hms_left_room_screen.dart'; -import 'package:hms_room_kit/src/widgets/meeting_modes/custom_one_to_one_grid.dart'; -import 'package:hms_room_kit/src/widgets/toasts/hms_recording_error_toast.dart'; import 'package:provider/provider.dart'; import 'package:tuple/tuple.dart'; import 'package:hmssdk_flutter/hmssdk_flutter.dart'; @@ -32,6 +27,12 @@ import 'package:hms_room_kit/src/widgets/app_dialogs/audio_device_change_dialog. import 'package:hms_room_kit/src/widgets/meeting_modes/one_to_one_mode.dart'; import 'package:hms_room_kit/src/meeting/meeting_store.dart'; import 'package:hms_room_kit/src/meeting/pip_view.dart'; +import 'package:hms_room_kit/src/preview_for_role/preview_for_role_bottom_sheet.dart'; +import 'package:hms_room_kit/src/preview_for_role/preview_for_role_header.dart'; +import 'package:hms_room_kit/src/widgets/common_widgets/hms_circular_avatar.dart'; +import 'package:hms_room_kit/src/widgets/common_widgets/hms_left_room_screen.dart'; +import 'package:hms_room_kit/src/widgets/meeting_modes/custom_one_to_one_grid.dart'; +import 'package:hms_room_kit/src/widgets/toasts/hms_recording_error_toast.dart'; ///[MeetingPage] is the main page of the meeting ///It takes the following parameters: @@ -497,8 +498,7 @@ class _MeetingPageState extends State { int>>( selector: (_, meetingStore) => Tuple2( - meetingStore.toasts - .toList(), + meetingStore.toasts, meetingStore .toasts.length), builder: (_, toastsItem, __) { @@ -507,6 +507,12 @@ class _MeetingPageState extends State { } return Stack( children: toastsItem.item1 + .sublist( + 0, + min( + 3, + toastsItem + .item2)) .asMap() .entries .map((toasts) { diff --git a/packages/hms_room_kit/lib/src/meeting/meeting_store.dart b/packages/hms_room_kit/lib/src/meeting/meeting_store.dart index 36245329f..151fc3255 100644 --- a/packages/hms_room_kit/lib/src/meeting/meeting_store.dart +++ b/packages/hms_room_kit/lib/src/meeting/meeting_store.dart @@ -216,6 +216,21 @@ class MeetingStore extends ChangeNotifier int currentScreenSharePage = 0; + ///PeerList iterators + ///This is a map with key as role and value as the iterator for that role + Map peerListIterators = {}; + + ///This checks whether to refresh peerlist or not + ///This is used in case when we are looking at a specific viewer role peerlist + ///i.e we are using the View All button functionalities. + bool disablePeerListRefresh = false; + + ///This stores the number of peers in the room + int peersInRoom = 0; + + ///Check whether recording is in intialising state + bool isRecordingInInitialisingState = false; + Future join(String userName, String roomCode, {HMSConfig? roomConfig}) async { //If roomConfig is null then only we call the methods to get the authToken @@ -224,7 +239,9 @@ class MeetingStore extends ChangeNotifier if (roomConfig == null) { //We use this to get the auth token from room code dynamic tokenData = await _hmsSDKInteractor.getAuthTokenByRoomCode( - roomCode: Constant.roomCode, endPoint: Constant.tokenEndPoint); + userId: Constant.prebuiltOptions?.userId, + roomCode: Constant.roomCode, + endPoint: Constant.tokenEndPoint); ///If the tokenData is String then we set the authToken in the roomConfig ///and then we join the room @@ -253,6 +270,13 @@ class MeetingStore extends ChangeNotifier return null; } + ///This method reapplies the theme layout based on the role name + void resetLayout(String roleName) { + HMSRoomLayout.resetLayout(roleName); + setMeetingModeUsingLayoutApi(); + notifyListeners(); + } + ///This method is used to set the meeting mode using the layout api void setMeetingModeUsingLayoutApi() { if (HMSRoomLayout.peerType == PeerRoleType.conferencing) { @@ -499,6 +523,8 @@ class MeetingStore extends ChangeNotifier meetingUrl: meetingUrl, toRecord: toRecord, rtmpUrls: rtmpUrls); _hmsSDKInteractor.startRtmpOrRecording(hmsRecordingConfig, this); + isRecordingInInitialisingState = true; + notifyListeners(); } void cancelPreview() async { @@ -508,7 +534,7 @@ class MeetingStore extends ChangeNotifier return; } if (isRaisedHand) { - changeMetadata(); + toggleLocalPeerHandRaise(); } if (currentRoleChangeRequest?.suggestedBy != null) { _hmsSDKInteractor.sendDirectMessage( @@ -567,25 +593,29 @@ class MeetingStore extends ChangeNotifier return await _hmsSDKInteractor.getPeer(peerId: peerId); } - void changeMetadata() { - isRaisedHand = !isRaisedHand; - isBRB = false; - String value = isRaisedHand ? "true" : "false"; - _hmsSDKInteractor.changeMetadata( - metadata: - "{\"isHandRaised\":$value,\"isBRBOn\":false,\"prevRole\":\"$previousRole\"}", - hmsActionResultListener: this); + void toggleLocalPeerHandRaise() { + if (isRaisedHand) { + _hmsSDKInteractor.lowerLocalPeerHand(hmsActionResultListener: this); + } else { + _hmsSDKInteractor.raiseLocalPeerHand(hmsActionResultListener: this); + } + } + + void lowerRemotePeerHand(HMSPeer forPeer) { + _hmsSDKInteractor.lowerRemotePeerHand( + forPeer: forPeer, hmsActionResultListener: this); } bool isBRB = false; void changeMetadataBRB() { isBRB = !isBRB; - isRaisedHand = false; + if (isRaisedHand) { + _hmsSDKInteractor.lowerLocalPeerHand(hmsActionResultListener: this); + } String value = isBRB ? "true" : "false"; _hmsSDKInteractor.changeMetadata( - metadata: - "{\"isHandRaised\":false,\"isBRBOn\":$value,\"prevRole\":\"$previousRole\"}", + metadata: "{\"isBRBOn\":$value,\"prevRole\":\"$previousRole\"}", hmsActionResultListener: this); if (isMicOn) { toggleMicMuteState(); @@ -603,16 +633,14 @@ class MeetingStore extends ChangeNotifier if (localPeer != null) { previousRole = localPeer?.role.name; if (isRaisedHand) { - changeMetadata(); - } else { - ///Setting the previous role - String value = isRaisedHand ? "true" : "false"; - _hmsSDKInteractor.changeMetadata( - metadata: - "{\"isHandRaised\":$value,\"isBRBOn\":false,\"prevRole\":\"$previousRole\"}", - hmsActionResultListener: this); + toggleLocalPeerHandRaise(); } - HMSRoomLayout.resetLayout(hmsRoleChangeRequest.suggestedRole.name); + + ///Setting the previous role + _hmsSDKInteractor.changeMetadata( + metadata: "{\"isBRBOn\":false,\"prevRole\":\"$previousRole\"}", + hmsActionResultListener: this); + // resetLayout(hmsRoleChangeRequest.suggestedRole.name); currentRoleChangeRequest = null; notifyListeners(); } @@ -653,6 +681,56 @@ class MeetingStore extends ChangeNotifier } } + void nextPeersForRole(String role) async { + dynamic nextSetOfPeers = await peerListIterators[role]?.next(); + if (nextSetOfPeers is List && nextSetOfPeers.isNotEmpty) { + for (var peer in nextSetOfPeers) { + addPeer(peer); + } + } + } + + ///This method is used to disable the peer list refresh + void disableRefresh() { + disablePeerListRefresh = true; + } + + ///This method is used to enable the peer list refresh + void enableRefresh() { + disablePeerListRefresh = false; + } + + ///This method is used to refresh the peer list + void refreshPeerList() async { + ///If the peer list refresh is disabled then we return + if (disablePeerListRefresh) { + return; + } + log("Calling refresh PeerList Method $peerListIterators"); + peerListIterators.clear(); + List? offStageRoles = HMSRoomLayout.roleLayoutData?.screens + ?.conferencing?.defaultConf?.elements?.onStageExp?.offStageRoles; + offStageRoles?.forEach((role) async { + var peerListIterator = await _hmsSDKInteractor.getPeerListIterator( + peerListIteratorOptions: + PeerListIteratorOptions(limit: 10, byRoleName: role)); + if (peerListIterator != null && peerListIterator is HMSPeerListIterator) { + peerListIterators[role] = peerListIterator; + participantsInMeeting -= participantsInMeetingMap[role]?.length ?? 0; + participantsInMeetingMap[role]?.clear(); + dynamic nonRealTimePeers = await peerListIterator.next(); + if (nonRealTimePeers is List) { + log("Calling refresh PeerList Method $nonRealTimePeers"); + if (nonRealTimePeers.isNotEmpty) { + for (var peer in nonRealTimePeers) { + addPeer(peer); + } + } + } + } + }); + } + Future?> getPeers() async { return await _hmsSDKInteractor.getPeers(); } @@ -801,10 +879,13 @@ class MeetingStore extends ChangeNotifier @override void onRoomUpdate({required HMSRoom room, required HMSRoomUpdate update}) { log("onRoomUpdate-> room: ${room.toString()} update: ${update.name}"); + peersInRoom = room.peerCount; switch (update) { case HMSRoomUpdate.browserRecordingStateUpdated: recordingType["browser"] = room.hmsBrowserRecordingState?.running ?? false; + isRecordingInInitialisingState = + room.hmsBrowserRecordingState?.initialising ?? false; break; case HMSRoomUpdate.serverRecordingStateUpdated: recordingType["server"] = @@ -827,11 +908,8 @@ class MeetingStore extends ChangeNotifier ? "HLS Streaming Started" : "HLS Streaming Stopped"); break; - case HMSRoomUpdate.roomPeerCountUpdated: - hmsRoom = room; - return; default: - return; + break; } hmsRoom = room; notifyListeners(); @@ -932,19 +1010,16 @@ class MeetingStore extends ChangeNotifier void onMessage({required HMSMessage message}) { log("onMessage-> sender: ${message.sender} message: ${message.message} time: ${message.time}, type: ${message.type}"); switch (message.type) { - case "metadata": - break; - case "EMOJI_REACTION": - break; case "role_change_declined": toggleRequestDeclined(message.sender); break; - default: + case "chat": addMessage(message); isNewMessageReceived = true; Utilities.showNotification( "New message from ${message.sender?.name ?? ""}", "message"); notifyListeners(); + default: break; } } @@ -952,8 +1027,10 @@ class MeetingStore extends ChangeNotifier @override void onRoleChangeRequest({required HMSRoleChangeRequest roleChangeRequest}) { log("onRoleChangeRequest-> sender: ${roleChangeRequest.suggestedBy} role: ${roleChangeRequest.suggestedRole}"); - currentRoleChangeRequest = roleChangeRequest; - previewForRole(roleChangeRequest.suggestedRole.name); + if (currentRoleChangeRequest == null) { + currentRoleChangeRequest = roleChangeRequest; + previewForRole(roleChangeRequest.suggestedRole.name); + } } void setCurrentPage(int newPage) { @@ -1233,10 +1310,9 @@ class MeetingStore extends ChangeNotifier participantsInMeetingMap[peer.role.name] ?.removeWhere((oldPeer) => oldPeer.peer.peerId == peer.peerId); participantsInMeeting--; - if (peer.metadata?.contains("\"isHandRaised\":true") ?? false) { + if (peer.isHandRaised) { participantsInMeetingMap["Hand Raised"] ?.removeWhere((oldPeer) => oldPeer.peer.peerId == peer.peerId); - participantsInMeeting--; } notifyListeners(); } @@ -1250,10 +1326,9 @@ class MeetingStore extends ChangeNotifier ?.add(ParticipantsStore(peer: peer)); participantsInMeeting++; } - if (peer.metadata?.contains("\"isHandRaised\":true") ?? false) { + if (peer.isHandRaised) { participantsInMeetingMap["Hand Raised"] ?.add(ParticipantsStore(peer: peer)); - participantsInMeeting++; } notifyListeners(); } @@ -1278,7 +1353,7 @@ class MeetingStore extends ChangeNotifier if (index != null && index != -1) { if ((peerUpdate == HMSPeerUpdate.nameChanged)) { participantsInMeetingMap[peer.role.name]?[index].updatePeer(peer); - if ((peer.metadata?.contains("\"isHandRaised\":true") ?? false)) { + if (peer.isHandRaised) { int? peerIndex = participantsInMeetingMap["Hand Raised"] ?.indexWhere((element) => element.peer.peerId == peer.peerId); if (peerIndex != null && peerIndex != -1) { @@ -1286,21 +1361,18 @@ class MeetingStore extends ChangeNotifier .updatePeer(peer); } } - } else if (peerUpdate == HMSPeerUpdate.metadataChanged) { - if ((peer.metadata?.contains("\"isHandRaised\":true") ?? false)) { + } else if (peerUpdate == HMSPeerUpdate.handRaiseUpdated) { + if (peer.isHandRaised) { if (participantsInMeetingMap["Hand Raised"]?.indexWhere( (element) => element.peer.peerId == peer.peerId) == -1) { participantsInMeetingMap["Hand Raised"] ?.add(ParticipantsStore(peer: peer)); - participantsInMeeting++; } participantsInMeetingMap[peer.role.name]?[index].updatePeer(peer); - } else if ((peer.metadata?.contains("\"isHandRaised\":false") ?? - false)) { + } else { participantsInMeetingMap["Hand Raised"]?.removeWhere( (handDownPeer) => handDownPeer.peer.peerId == peer.peerId); - participantsInMeeting--; participantsInMeetingMap[peer.role.name]?[index].updatePeer(peer); } notifyListeners(); @@ -1363,7 +1435,7 @@ class MeetingStore extends ChangeNotifier case HMSPeerUpdate.roleUpdated: if (peer.isLocal) { getSpotlightPeer(); - HMSRoomLayout.resetLayout(peer.role.name); + resetLayout(peer.role.name); localPeer = peer; } if (HMSRoomLayout @@ -1411,10 +1483,21 @@ class MeetingStore extends ChangeNotifier if (index != -1) { PeerTrackNode peerTrackNode = peerTracks[index]; peerTrackNode.peer = peer; - if (peer.metadata?.contains("\"isHandRaised\":true") ?? false) { - Utilities.showNotification( - "${peer.name} raised hand", "hand-raise"); - } + peerTrackNode.notify(); + } + updatePeerAt(peer); + updatePeerMap(update, peer); + break; + + case HMSPeerUpdate.handRaiseUpdated: + if (peer.isLocal) { + localPeer = peer; + } + int index = peerTracks + .indexWhere((element) => element.uid == "${peer.peerId}mainVideo"); + if (index != -1) { + PeerTrackNode peerTrackNode = peerTracks[index]; + peerTrackNode.peer = peer; peerTrackNode.notify(); } else { if (HMSRoomLayout.peerType == PeerRoleType.conferencing) { @@ -1894,11 +1977,11 @@ class MeetingStore extends ChangeNotifier /// Method to toggle the role change toast /// void addRemoveToastsForRoleChange({required HMSPeer peer}) { - if (peer.metadata?.contains("\"isHandRaised\":true") ?? false) { + if (peer.isHandRaised) { toasts.add( HMSToastModel(peer, hmsToastType: HMSToastsType.roleChangeToast)); notifyListeners(); - } else if (peer.metadata?.contains("\"isHandRaised\":false") ?? false) { + } else { toasts.removeWhere((toast) => toast.hmsToastType == HMSToastsType.roleChangeToast && peer.peerId == toast.toastData.peerId); @@ -2034,6 +2117,18 @@ class MeetingStore extends ChangeNotifier case HMSActionResultListenerMethod.sendHLSTimedMetadata: Utilities.showToast("Metadata sent successfully"); break; + case HMSActionResultListenerMethod.lowerLocalPeerHand: + isRaisedHand = false; + isBRB = false; + notifyListeners(); + break; + case HMSActionResultListenerMethod.raiseLocalPeerHand: + isRaisedHand = true; + isBRB = false; + notifyListeners(); + break; + case HMSActionResultListenerMethod.lowerRemotePeerHand: + break; default: log("ActionResultListener onException-> method: ${methodType.toString()}Could not find a valid case while switching"); break; @@ -2068,6 +2163,7 @@ class MeetingStore extends ChangeNotifier case HMSActionResultListenerMethod.startRtmpOrRecording: toasts.add(HMSToastModel(hmsException, hmsToastType: HMSToastsType.errorToast)); + isRecordingInInitialisingState = false; notifyListeners(); break; case HMSActionResultListenerMethod.stopRtmpAndRecording: @@ -2110,6 +2206,12 @@ class MeetingStore extends ChangeNotifier case HMSActionResultListenerMethod.sendHLSTimedMetadata: // TODO: Handle this case. break; + case HMSActionResultListenerMethod.lowerLocalPeerHand: + break; + case HMSActionResultListenerMethod.raiseLocalPeerHand: + break; + case HMSActionResultListenerMethod.lowerRemotePeerHand: + break; default: log("ActionResultListener onException-> method: ${methodType.toString()} Could not find a valid case while switching"); break; @@ -2271,4 +2373,14 @@ class MeetingStore extends ChangeNotifier } notifyListeners(); } + + @override + void onPeerListUpdate( + {required List addedPeers, + required List removedPeers}) { + log("onPeerListUpdate -> addedPeers: $addedPeers removedPeers: $removedPeers"); + for (var peer in addedPeers) { + addPeer(peer); + } + } } diff --git a/packages/hms_room_kit/lib/src/preview/preview_page.dart b/packages/hms_room_kit/lib/src/preview/preview_page.dart index 49c4f3b20..64d0a8edd 100644 --- a/packages/hms_room_kit/lib/src/preview/preview_page.dart +++ b/packages/hms_room_kit/lib/src/preview/preview_page.dart @@ -41,13 +41,14 @@ class PreviewPage extends StatefulWidget { class _PreviewPageState extends State { late MeetingStore _meetingStore; - TextEditingController nameController = TextEditingController(); + late TextEditingController nameController; bool isJoiningRoom = false; bool isHLSStarting = false; @override void initState() { super.initState(); + nameController = TextEditingController(text: widget.name); } @override @@ -66,7 +67,7 @@ class _PreviewPageState extends State { ///This function joins the room only if the name is not empty void _joinMeeting(PreviewStore previewStore) async { - if (nameController.text.isNotEmpty) { + if (nameController.text.trim().isNotEmpty) { FocusManager.instance.primaryFocus?.unfocus(); setState(() { isJoiningRoom = true; @@ -76,8 +77,8 @@ class _PreviewPageState extends State { _setMeetingStore(previewStore); /// We join the room here - HMSException? ans = - await _meetingStore.join(nameController.text, Constant.roomCode); + HMSException? ans = await _meetingStore.join( + nameController.text.trim(), Constant.roomCode); ///If the room join fails we show the error dialog if (ans != null && mounted) { @@ -403,7 +404,9 @@ class _PreviewPageState extends State { PreviewJoinButton( isEmpty: nameController - .text.isEmpty, + .text + .trim() + .isEmpty, previewStore: previewStore, isJoining: diff --git a/packages/hms_room_kit/lib/src/preview/preview_store.dart b/packages/hms_room_kit/lib/src/preview/preview_store.dart index 7a38dd8ea..594723366 100644 --- a/packages/hms_room_kit/lib/src/preview/preview_store.dart +++ b/packages/hms_room_kit/lib/src/preview/preview_store.dart @@ -111,7 +111,9 @@ class PreviewStore extends ChangeNotifier {required String userName, required String roomCode}) async { //We use this to get the auth token from room code dynamic tokenData = await hmsSDKInteractor.getAuthTokenByRoomCode( - roomCode: roomCode, endPoint: Constant.tokenEndPoint); + userId: Constant.prebuiltOptions?.userId, + roomCode: roomCode, + endPoint: Constant.tokenEndPoint); if ((tokenData is String?) && tokenData != null) { roomConfig = HMSConfig( @@ -349,4 +351,9 @@ class PreviewStore extends ChangeNotifier @override void onUpdateSpeakers({required List updateSpeakers}) {} + + @override + void onPeerListUpdate( + {required List addedPeers, + required List removedPeers}) {} } diff --git a/packages/hms_room_kit/lib/src/preview_for_role/preview_for_role_header.dart b/packages/hms_room_kit/lib/src/preview_for_role/preview_for_role_header.dart index 5cf98a9e6..0b1571cf5 100644 --- a/packages/hms_room_kit/lib/src/preview_for_role/preview_for_role_header.dart +++ b/packages/hms_room_kit/lib/src/preview_for_role/preview_for_role_header.dart @@ -104,7 +104,7 @@ class _PreviewForRoleHeaderState extends State { Selector>( selector: (_, meetingStore) => Tuple2( meetingStore.streamingType['hls'] ?? false, - meetingStore.peers.length), + meetingStore.peersInRoom), builder: (_, data, __) { return data.item1 ? Container( diff --git a/packages/hms_room_kit/lib/src/screen_controller.dart b/packages/hms_room_kit/lib/src/screen_controller.dart index 83853aeae..74da385e9 100644 --- a/packages/hms_room_kit/lib/src/screen_controller.dart +++ b/packages/hms_room_kit/lib/src/screen_controller.dart @@ -110,7 +110,7 @@ class _ScreenControllerState extends State { await _hmsSDKInteractor.build(); _previewStore = PreviewStore(hmsSDKInteractor: _hmsSDKInteractor); HMSException? ans = await _previewStore.startPreview( - userName: "", roomCode: Constant.roomCode); + userName: widget.options?.userName ?? "", roomCode: Constant.roomCode); ///If preview fails then we show the error dialog ///with the error message and description @@ -156,7 +156,7 @@ class _ScreenControllerState extends State { value: _previewStore, child: PreviewPage( roomCode: Constant.roomCode, - name: "", + name: widget.options?.userName ?? "", options: widget.options, )) : PreviewPermissions( diff --git a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/app_utilities_bottom_sheet.dart b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/app_utilities_bottom_sheet.dart index 63725c3ef..c43e57643 100644 --- a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/app_utilities_bottom_sheet.dart +++ b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/app_utilities_bottom_sheet.dart @@ -2,20 +2,18 @@ import 'package:badges/badges.dart' as badge; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:provider/provider.dart'; + +///Project imports +import 'package:hms_room_kit/src/widgets/common_widgets/more_option_item.dart'; +import 'package:hms_room_kit/src/meeting/meeting_store.dart'; +import 'package:hms_room_kit/hms_room_kit.dart'; import 'package:hms_room_kit/src/layout_api/hms_room_layout.dart'; import 'package:hms_room_kit/src/widgets/bottom_sheets/end_service_bottom_sheet.dart'; import 'package:hms_room_kit/src/widgets/bottom_sheets/overlay_participants_bottom_sheet.dart'; import 'package:hms_room_kit/src/widgets/common_widgets/hms_cross_button.dart'; import 'package:hms_room_kit/src/widgets/common_widgets/hms_subheading_text.dart'; import 'package:hms_room_kit/src/widgets/tab_widgets/chat_participants_tab_bar.dart'; -import 'package:provider/provider.dart'; - -///Project imports -import 'package:hms_room_kit/src/layout_api/hms_theme_colors.dart'; -import 'package:hms_room_kit/src/widgets/common_widgets/more_option_item.dart'; -import 'package:hms_room_kit/src/common/constants.dart'; -import 'package:hms_room_kit/src/widgets/common_widgets/hms_title_text.dart'; -import 'package:hms_room_kit/src/meeting/meeting_store.dart'; ///This renders the app utilities bottom sheet for webRTC or broadcaster ///It contains the participants, screen share, brb, raise hand and recording @@ -100,17 +98,11 @@ class _AppUtilitiesBottomSheetState extends State { }, optionIcon: badge.Badge( badgeStyle: badge.BadgeStyle( - badgeColor: HMSThemeColors.surfaceDefault, - padding: EdgeInsets.all( - context.read().peers.length < 1000 - ? 5 - : 8)), + badgeColor: HMSThemeColors.surfaceDefault, + ), badgeContent: HMSTitleText( - text: context - .read() - .peers - .length - .toString(), + text: Utilities.formatNumber( + context.read().peersInRoom), textColor: HMSThemeColors.onSurfaceHighEmphasis, fontSize: 10, lineHeight: 16, @@ -118,11 +110,15 @@ class _AppUtilitiesBottomSheetState extends State { ), child: Padding( padding: EdgeInsets.symmetric( - horizontal: - (context.read().peers.length < - 1000 - ? 5 - : 10)), + horizontal: context + .read() + .peersInRoom < + 1000 + ? 15 + : context.read().peersInRoom < + 10000 + ? 20 + : 30), child: SvgPicture.asset( "packages/hms_room_kit/lib/src/assets/icons/participants.svg", height: 20, @@ -182,7 +178,7 @@ class _AppUtilitiesBottomSheetState extends State { , MoreOptionItem( onTap: () async { - context.read().changeMetadata(); + context.read().toggleLocalPeerHandRaise(); Navigator.pop(context); }, isActive: meetingStore.isRaisedHand, @@ -203,10 +199,15 @@ class _AppUtilitiesBottomSheetState extends State { ///start/stop browser recording /// ///The recording permission is checked using the role of the local peer + /// + ///If Streaming is already running we disable the recording option if (meetingStore.localPeer?.role.permissions.browserRecording ?? false) + + ///If streaming is on or in initialising state disable the button ((meetingStore.streamingType["hls"] ?? false) || - (meetingStore.streamingType["rtmp"] ?? false)) + (meetingStore.streamingType["rtmp"] ?? false) || + meetingStore.isRecordingInInitialisingState) ? MoreOptionItem( onTap: () {}, isActive: false, diff --git a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/hls_more_options.dart b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/hls_more_options.dart index fc457bf89..71cfa12b9 100644 --- a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/hls_more_options.dart +++ b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/hls_more_options.dart @@ -1,9 +1,6 @@ ///Package imports import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:hms_room_kit/src/layout_api/hms_room_layout.dart'; -import 'package:hms_room_kit/src/widgets/bottom_sheets/overlay_participants_bottom_sheet.dart'; -import 'package:hms_room_kit/src/widgets/tab_widgets/chat_participants_tab_bar.dart'; import 'package:provider/provider.dart'; import 'package:badges/badges.dart' as badge; @@ -13,6 +10,9 @@ import 'package:hms_room_kit/src/widgets/bottom_sheets/change_name_bottom_sheet. import 'package:hms_room_kit/src/meeting/meeting_store.dart'; import 'package:hms_room_kit/src/widgets/common_widgets/hms_cross_button.dart'; import 'package:hms_room_kit/src/widgets/common_widgets/more_option_item.dart'; +import 'package:hms_room_kit/src/layout_api/hms_room_layout.dart'; +import 'package:hms_room_kit/src/widgets/bottom_sheets/overlay_participants_bottom_sheet.dart'; +import 'package:hms_room_kit/src/widgets/tab_widgets/chat_participants_tab_bar.dart'; ///[HLSMoreOptionsBottomSheet] is a bottom sheet that is used to show more options in the meeting class HLSMoreOptionsBottomSheet extends StatefulWidget { @@ -96,17 +96,11 @@ class _HLSMoreOptionsBottomSheetBottomSheetState }, optionIcon: badge.Badge( badgeStyle: badge.BadgeStyle( - badgeColor: HMSThemeColors.surfaceDefault, - padding: EdgeInsets.all( - context.read().peers.length < 1000 - ? 5 - : 8)), + badgeColor: HMSThemeColors.surfaceDefault, + ), badgeContent: HMSTitleText( - text: context - .read() - .peers - .length - .toString(), + text: Utilities.formatNumber( + context.read().peersInRoom), textColor: HMSThemeColors.onSurfaceHighEmphasis, fontSize: 10, lineHeight: 16, @@ -114,11 +108,15 @@ class _HLSMoreOptionsBottomSheetBottomSheetState ), child: Padding( padding: EdgeInsets.symmetric( - horizontal: - (context.read().peers.length < - 1000 - ? 5 - : 10)), + horizontal: context + .read() + .peersInRoom < + 1000 + ? 15 + : context.read().peersInRoom < + 10000 + ? 20 + : 30), child: SvgPicture.asset( "packages/hms_room_kit/lib/src/assets/icons/participants.svg", height: 20, diff --git a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/more_settings_bottom_sheet.dart b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/more_settings_bottom_sheet.dart index 0e32fa6c5..4359f54bb 100644 --- a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/more_settings_bottom_sheet.dart +++ b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/more_settings_bottom_sheet.dart @@ -303,7 +303,7 @@ class _MoreSettingsBottomSheetState extends State { ListTile( horizontalTitleGap: 2, onTap: () async { - context.read().changeMetadata(); + context.read().toggleLocalPeerHandRaise(); Navigator.pop(context); }, contentPadding: EdgeInsets.zero, diff --git a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/overlay_participants_bottom_sheet.dart b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/overlay_participants_bottom_sheet.dart index 837145ea4..8f3ca83c5 100644 --- a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/overlay_participants_bottom_sheet.dart +++ b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/overlay_participants_bottom_sheet.dart @@ -22,11 +22,10 @@ class OverlayParticipantsBottomSheet extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Selector( - selector: (_, meetingStore) => - meetingStore.participantsInMeeting, - builder: (_, participantsInMeeting, __) { + selector: (_, meetingStore) => meetingStore.peersInRoom, + builder: (_, peersInRoom, __) { return HMSSubheadingText( - text: "Participants ($participantsInMeeting)", + text: "Participants ($peersInRoom)", textColor: HMSThemeColors.onSurfaceHighEmphasis, fontWeight: FontWeight.w600, ); diff --git a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/participants_bottom_sheet.dart b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/participants_bottom_sheet.dart index 568d29657..858278a11 100644 --- a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/participants_bottom_sheet.dart +++ b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/participants_bottom_sheet.dart @@ -1,11 +1,14 @@ -///Package imports +///Dart imports +import 'dart:async'; import 'dart:convert'; import 'dart:developer'; +///Package imports import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:provider/provider.dart'; import 'package:tuple/tuple.dart'; +import 'package:hmssdk_flutter/hmssdk_flutter.dart'; ///Project imports import 'package:hms_room_kit/src/widgets/toasts/hms_toasts_type.dart'; @@ -13,7 +16,7 @@ import 'package:hms_room_kit/src/layout_api/hms_room_layout.dart'; import 'package:hms_room_kit/src/layout_api/hms_theme_colors.dart'; import 'package:hms_room_kit/src/model/participant_store.dart'; import 'package:hms_room_kit/src/widgets/common_widgets/hms_subheading_text.dart'; -import 'package:hmssdk_flutter/hmssdk_flutter.dart'; +import 'package:hms_room_kit/src/widgets/bottom_sheets/participants_view_all_bottom_sheet.dart'; import 'package:hms_room_kit/src/model/peer_track_node.dart'; import 'package:hms_room_kit/src/widgets/common_widgets/hms_title_text.dart'; import 'package:hms_room_kit/src/meeting/meeting_store.dart'; @@ -29,6 +32,35 @@ class ParticipantsBottomSheet extends StatefulWidget { } class _ParticipantsBottomSheetState extends State { + Timer? timer; + + @override + void initState() { + super.initState(); + timer = Timer.periodic(const Duration(seconds: 5), + (Timer t) => context.read().refreshPeerList()); + } + + @override + void dispose() { + timer?.cancel(); + super.dispose(); + } + + void viewAll(String role) { + showModalBottomSheet( + isScrollControlled: true, + backgroundColor: HMSThemeColors.surfaceDim, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + context: context, + builder: (ctx) => ChangeNotifierProvider.value( + value: context.read(), + child: ParticipantsViewAllBottomSheet(role: role)), + ); + } + Widget _kebabMenu(HMSPeer peer) { final meetingStore = context.read(); PeerTrackNode? peerTrackNode; @@ -97,6 +129,7 @@ class _ParticipantsBottomSheetState extends State { return; } catch (e) { log(e.toString()); + return; } } } @@ -112,6 +145,11 @@ class _ParticipantsBottomSheetState extends State { break; case 2: + ///Here we lower the remote peer hand + meetingStore.lowerRemotePeerHand(peer); + break; + case 3: + ///Here we check whether the video track is null or not if (peerTrackNode?.track == null) { return; @@ -119,7 +157,7 @@ class _ParticipantsBottomSheetState extends State { meetingStore.changeTrackState( peerTrackNode!.track!, !peerTrackNode.track!.isMute); break; - case 3: + case 4: ///Here we check whether the audio track is null or not if (peerTrackNode?.audioTrack == null) { @@ -128,7 +166,7 @@ class _ParticipantsBottomSheetState extends State { meetingStore.changeTrackState(peerTrackNode!.audioTrack!, !peerTrackNode.audioTrack!.isMute); break; - case 4: + case 5: ///This is called when someone clicks on remove Participant meetingStore.removePeerFromRoom(peer); @@ -170,11 +208,34 @@ class _ParticipantsBottomSheetState extends State { ), ]), ), + if (peer.isHandRaised) + PopupMenuItem( + value: 2, + child: Row(children: [ + SvgPicture.asset( + "packages/hms_room_kit/lib/src/assets/icons/lower_hand.svg", + width: 18, + height: 18, + colorFilter: ColorFilter.mode( + HMSThemeColors.onSurfaceHighEmphasis, + BlendMode.srcIn)), + const SizedBox( + width: 8, + ), + HMSTitleText( + text: "Lower Hand", + textColor: HMSThemeColors.onSurfaceHighEmphasis, + fontSize: 14, + lineHeight: 20, + letterSpacing: 0.1, + ), + ]), + ), if (mutePermission && peerTrackNode != null && !peerTrackNode.peer.isLocal) PopupMenuItem( - value: 2, + value: 3, child: Row(children: [ SvgPicture.asset( peerTrackNode.track?.isMute ?? false @@ -203,7 +264,7 @@ class _ParticipantsBottomSheetState extends State { peerTrackNode != null && !peerTrackNode.peer.isLocal) PopupMenuItem( - value: 3, + value: 4, child: Row(children: [ SvgPicture.asset( peerTrackNode.audioTrack?.isMute ?? false @@ -230,7 +291,7 @@ class _ParticipantsBottomSheetState extends State { ), if (removePeerPermission) PopupMenuItem( - value: 4, + value: 5, child: Row(children: [ SvgPicture.asset( "packages/hms_room_kit/lib/src/assets/icons/peer_remove.svg", @@ -315,7 +376,7 @@ class _ParticipantsBottomSheetState extends State { .onSurfaceHighEmphasis, title: HMSSubheadingText( text: - "${data.item1.keys.elementAt(index)} (${data.item1[role]?.length})", + "${data.item1.keys.elementAt(index)} (${(HMSRoomLayout.offStageRoles?.contains(role) ?? false) ? context.read().peerListIterators[role]?.totalCount ?? 0 : data.item1[role]?.length}) ", textColor: HMSThemeColors .onSurfaceMediumEmphasis, letterSpacing: 0.1, @@ -325,7 +386,7 @@ class _ParticipantsBottomSheetState extends State { height: data.item1[role] == null ? 0 : (data.item1[role]!.length) * - 60, + 54, child: Center( child: ListView.builder( physics: @@ -422,8 +483,9 @@ class _ParticipantsBottomSheetState extends State { Selector< ParticipantsStore, bool>( - selector: (_, participantsStore) => (participantsStore.peer.metadata?.contains("\"isHandRaised\":true") ?? - false), + selector: (_, participantsStore) => (participantsStore + .peer + .isHandRaised), builder: (_, isHandRaised, __) { @@ -457,7 +519,68 @@ class _ParticipantsBottomSheetState extends State { ); }), ), - ) + ), + if ((HMSRoomLayout.offStageRoles + ?.contains(role) ?? + false) && + ((context + .read() + .peerListIterators[ + role] + ?.totalCount ?? + 0) > + 10)) + Column( + children: [ + Divider( + height: 5, + color: HMSThemeColors + .borderDefault, + ), + Padding( + padding: + const EdgeInsets.fromLTRB( + 16, 12, 16, 12), + child: GestureDetector( + onTap: () { + viewAll(role); + }, + child: Row( + mainAxisAlignment: + MainAxisAlignment.end, + children: [ + Row( + children: [ + HMSSubheadingText( + text: + "View All", + textColor: + HMSThemeColors + .onSurfaceHighEmphasis), + Padding( + padding: + const EdgeInsets + .only( + left: + 4.0), + child: SvgPicture.asset( + "packages/hms_room_kit/lib/src/assets/icons/right_arrow.svg", + width: 24, + height: 24, + colorFilter: ColorFilter.mode( + HMSThemeColors + .onSurfaceHighEmphasis, + BlendMode + .srcIn)), + ), + ], + ) + ], + ), + ), + ), + ], + ), ], ), ), diff --git a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/participants_view_all_bottom_sheet.dart b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/participants_view_all_bottom_sheet.dart new file mode 100644 index 000000000..2a0ecb79e --- /dev/null +++ b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/participants_view_all_bottom_sheet.dart @@ -0,0 +1,582 @@ +///Dart imports +import 'dart:convert'; +import 'dart:developer'; + +///Package imports +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:hmssdk_flutter/hmssdk_flutter.dart'; +import 'package:provider/provider.dart'; +import 'package:tuple/tuple.dart'; + +///Project imports +import 'package:hms_room_kit/hms_room_kit.dart'; +import 'package:hms_room_kit/src/layout_api/hms_room_layout.dart'; +import 'package:hms_room_kit/src/meeting/meeting_store.dart'; +import 'package:hms_room_kit/src/model/participant_store.dart'; +import 'package:hms_room_kit/src/model/peer_track_node.dart'; +import 'package:hms_room_kit/src/widgets/common_widgets/hms_cross_button.dart'; +import 'package:hms_room_kit/src/widgets/common_widgets/hms_subheading_text.dart'; +import 'package:hms_room_kit/src/widgets/toasts/hms_toasts_type.dart'; + +///[ParticipantsViewAllBottomSheet] is a bottom sheet that is used to show all the participants of viewer groups for large rooms +///This uses the [peerListIterator] next method to get the next set of peers +class ParticipantsViewAllBottomSheet extends StatefulWidget { + final String role; + + const ParticipantsViewAllBottomSheet({Key? key, required this.role}) + : super(key: key); + + @override + State createState() => + _ParticipantsViewAllBottomSheetState(); +} + +class _ParticipantsViewAllBottomSheetState + extends State { + final ScrollController _scrollController = ScrollController(); + + @override + void initState() { + super.initState(); + context.read().disableRefresh(); + _scrollController.addListener(() { + if (_scrollController.position.pixels == + _scrollController.position.maxScrollExtent) { + context.read().nextPeersForRole(widget.role); + } + }); + } + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } + + Widget _kebabMenu(HMSPeer peer) { + final meetingStore = context.read(); + PeerTrackNode? peerTrackNode; + try { + peerTrackNode = meetingStore.peerTracks + .firstWhere((element) => element.uid == "${peer.peerId}mainVideo"); + } catch (e) { + peerTrackNode = null; + } + + bool mutePermission = + meetingStore.localPeer?.role.permissions.mute ?? false; + bool removePeerPermission = + meetingStore.localPeer?.role.permissions.removeOthers ?? false; + bool changeRolePermission = + meetingStore.localPeer?.role.permissions.changeRole ?? false; + bool isOnStageRole = meetingStore.getOnStageRole()?.name == peer.role.name; + bool isOnStageExpPresent = HMSRoomLayout.peerType == PeerRoleType.hlsViewer + ? HMSRoomLayout.roleLayoutData?.screens?.conferencing?.hlsLiveStreaming + ?.elements?.onStageExp != + null + : HMSRoomLayout.roleLayoutData?.screens?.conferencing?.defaultConf + ?.elements?.onStageExp != + null; + bool isOffStageRole = meetingStore.isOffStageRole(peer.role.name); + + ///Here we check whether to show three dots or not + ///We show three dots if the peer is not local + ///and the local peer has any of the following permissions: + ///changeRole, removeOthers, mute/unmute others + return (!peer.isLocal && + (changeRolePermission || removePeerPermission || mutePermission)) + ? PopupMenuButton( + padding: EdgeInsets.zero, + position: PopupMenuPosition.under, + color: HMSThemeColors.surfaceDefault, + shape: + RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + onSelected: (int value) async { + ///Here we have defined the functions to be executed on clicking the options + switch (value) { + case 1: + + ///If the peer is onStage already we show the option to remove from stage + ///and the peer's role is changed to it's previous role + /// + ///If the peer is offStage we show the option to bring on stage + ///and the peer 's role is changed to offStageRole from layout api + ///We also update the peer metadata with the previous role + ///which will be used while removing the peer from stage + if (isOnStageRole) { + if (peer.metadata != null) { + String? peerMetadata = peer.metadata; + if (peerMetadata?.contains("prevRole") ?? false) { + String? previousRole = + jsonDecode(peer.metadata!)["prevRole"]; + if (previousRole != null) { + try { + HMSRole? offStageRole = meetingStore.roles + .firstWhere( + (element) => element.name == previousRole); + meetingStore.changeRoleOfPeer( + peer: peer, + roleName: offStageRole, + forceChange: true); + return; + } catch (e) { + log(e.toString()); + return; + } + } + } + } + } + HMSRole? onStageRole = meetingStore.getOnStageRole(); + if (onStageRole != null) { + meetingStore.changeRoleOfPeer( + peer: peer, roleName: onStageRole, forceChange: false); + meetingStore.removeToast(HMSToastsType.roleChangeToast, + data: peer); + } + break; + case 2: + + ///Here we lower the remote peer hand + meetingStore.lowerRemotePeerHand(peer); + break; + case 3: + + ///Here we check whether the video track is null or not + if (peerTrackNode?.track == null) { + return; + } + meetingStore.changeTrackState( + peerTrackNode!.track!, !peerTrackNode.track!.isMute); + break; + case 4: + + ///Here we check whether the audio track is null or not + if (peerTrackNode?.audioTrack == null) { + return; + } + meetingStore.changeTrackState(peerTrackNode!.audioTrack!, + !peerTrackNode.audioTrack!.isMute); + break; + case 5: + + ///This is called when someone clicks on remove Participant + meetingStore.removePeerFromRoom(peer); + break; + default: + break; + } + }, + child: Icon( + Icons.more_vert_rounded, + size: 20, + color: HMSThemeColors.onSurfaceHighEmphasis, + ), + itemBuilder: (context) => [ + if (changeRolePermission && + isOnStageExpPresent && + (isOffStageRole || isOnStageRole)) + PopupMenuItem( + value: 1, + child: Row(children: [ + SvgPicture.asset( + "packages/hms_room_kit/lib/src/assets/icons/change_role.svg", + width: 20, + height: 20, + colorFilter: ColorFilter.mode( + HMSThemeColors.onSurfaceHighEmphasis, + BlendMode.srcIn)), + const SizedBox( + width: 8, + ), + HMSTitleText( + text: isOnStageRole + ? "Remove from Stage" + : "Bring on Stage", + textColor: HMSThemeColors.onSurfaceHighEmphasis, + fontSize: 14, + lineHeight: 20, + letterSpacing: 0.1, + ), + ]), + ), + if (peer.isHandRaised) + PopupMenuItem( + value: 2, + child: Row(children: [ + SvgPicture.asset( + "packages/hms_room_kit/lib/src/assets/icons/lower_hand.svg", + width: 18, + height: 18, + colorFilter: ColorFilter.mode( + HMSThemeColors.onSurfaceHighEmphasis, + BlendMode.srcIn)), + const SizedBox( + width: 8, + ), + HMSTitleText( + text: "Lower Hand", + textColor: HMSThemeColors.onSurfaceHighEmphasis, + fontSize: 14, + lineHeight: 20, + letterSpacing: 0.1, + ), + ]), + ), + if (mutePermission && + peerTrackNode != null && + !peerTrackNode.peer.isLocal) + PopupMenuItem( + value: 3, + child: Row(children: [ + SvgPicture.asset( + peerTrackNode.track?.isMute ?? false + ? "packages/hms_room_kit/lib/src/assets/icons/cam_state_on.svg" + : "packages/hms_room_kit/lib/src/assets/icons/cam_state_off.svg", + colorFilter: ColorFilter.mode( + HMSThemeColors.onSurfaceHighEmphasis, + BlendMode.srcIn), + width: 20, + height: 20, + ), + const SizedBox( + width: 8, + ), + HMSTitleText( + text: + "${peerTrackNode.track?.isMute ?? false ? "Unmute" : "Mute"} Video", + textColor: HMSThemeColors.onSurfaceHighEmphasis, + fontSize: 14, + lineHeight: 20, + letterSpacing: 0.1, + ), + ]), + ), + if (mutePermission && + peerTrackNode != null && + !peerTrackNode.peer.isLocal) + PopupMenuItem( + value: 4, + child: Row(children: [ + SvgPicture.asset( + peerTrackNode.audioTrack?.isMute ?? false + ? "packages/hms_room_kit/lib/src/assets/icons/mic_state_on.svg" + : "packages/hms_room_kit/lib/src/assets/icons/mic_state_off.svg", + colorFilter: ColorFilter.mode( + HMSThemeColors.onSurfaceHighEmphasis, + BlendMode.srcIn), + width: 20, + height: 20, + ), + const SizedBox( + width: 8, + ), + HMSTitleText( + text: + "${peerTrackNode.audioTrack?.isMute ?? false ? "Unmute" : "Mute"} Audio", + textColor: HMSThemeColors.onSurfaceHighEmphasis, + fontSize: 14, + lineHeight: 20, + letterSpacing: 0.1, + ), + ]), + ), + if (removePeerPermission) + PopupMenuItem( + value: 5, + child: Row(children: [ + SvgPicture.asset( + "packages/hms_room_kit/lib/src/assets/icons/peer_remove.svg", + width: 20, + height: 20, + colorFilter: ColorFilter.mode( + HMSThemeColors.alertErrorDefault, + BlendMode.srcIn)), + const SizedBox( + width: 8, + ), + HMSTitleText( + text: "Remove Participant", + textColor: HMSThemeColors.alertErrorDefault, + fontSize: 14, + lineHeight: 20, + letterSpacing: 0.1, + ), + ]), + ), + ]) + : const SizedBox(); + } + + void resetData() { + context.read().enableRefresh(); + } + + @override + Widget build(BuildContext context) { + return WillPopScope( + onWillPop: () async { + resetData(); + return true; + }, + child: SafeArea( + child: FractionallySizedBox( + heightFactor: 0.6, + child: Padding( + padding: const EdgeInsets.only(top: 12.0, left: 16, right: 16), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + InkWell( + onTap: () { + resetData(); + Navigator.pop(context); + }, + child: SvgPicture.asset( + "packages/hms_room_kit/lib/src/assets/icons/left_arrow.svg", + width: 24, + height: 24, + colorFilter: ColorFilter.mode( + HMSThemeColors.onSurfaceHighEmphasis, + BlendMode.srcIn)), + ), + Padding( + padding: const EdgeInsets.only(left: 8.0), + child: HMSTitleText( + text: "Participants", + letterSpacing: 0.15, + textColor: HMSThemeColors.onSurfaceHighEmphasis, + ), + ), + ], + ), + HMSCrossButton(onPressed: () => resetData()), + ], + ), + Expanded( + child: + Selector>, int>>( + selector: (_, meetingStore) => Tuple2( + meetingStore.participantsInMeetingMap, + meetingStore.participantsInMeeting), + builder: (_, data, __) { + return ClipRRect( + borderRadius: BorderRadius.circular(8), + child: Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all( + Radius.circular(8)), + border: Border.fromBorderSide( + BorderSide( + color: HMSThemeColors.borderDefault, + width: 1), + )), + child: Column( + children: [ + Row( + children: [ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, vertical: 12), + child: HMSSubheadingText( + fontWeight: FontWeight.w600, + text: widget.role, + textColor: HMSThemeColors + .onSurfaceMediumEmphasis, + letterSpacing: 0.1, + ), + ), + ], + ), + Divider( + height: 5, + color: HMSThemeColors.borderDefault, + ), + Expanded( + child: ListView.builder( + controller: _scrollController, + + ///We apply the check for showing loader here + itemCount: (data.item1[widget.role] + ?.length ?? + 0) + + ((context + .read< + MeetingStore>() + .peerListIterators[ + widget.role] + ?.totalCount ?? + 0) > + (data.item1[widget.role] + ?.length ?? + 0) + ? 1 + : 0), + itemBuilder: (context, peerIndex) { + if (peerIndex == + data.item1[widget.role]?.length) { + return Padding( + padding: + const EdgeInsets.symmetric( + vertical: 2.0), + child: Center( + child: + CircularProgressIndicator( + strokeWidth: 1, + color: HMSThemeColors + .primaryDefault, + ), + ), + ); + } + ParticipantsStore currentPeer = data + .item1[widget.role]![peerIndex]; + return Padding( + padding: + const EdgeInsets.only(top: 8.0), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Padding( + padding: + const EdgeInsets.fromLTRB( + 16, 8, 16, 16), + child: + ListenableProvider.value( + value: currentPeer, + child: Row( + mainAxisAlignment: + MainAxisAlignment + .spaceBetween, + children: [ + Selector< + ParticipantsStore, + String>( + selector: (_, + participantsStore) => + participantsStore + .peer.name, + builder: (_, + peerName, __) { + return HMSTitleText( + text: peerName + + ((data.item1[widget.role]![peerIndex].peer.isLocal) + ? " (You)" + : ""), + fontSize: 14, + lineHeight: + 20, + letterSpacing: + 0.1, + textColor: + HMSThemeColors + .onSurfaceHighEmphasis); + }), + + ///This contains the network quality, hand raise icon and kebab menu + Row( + children: [ + Selector< + ParticipantsStore, + int>( + selector: (_, + participantsStore) => + (participantsStore + .peer + .networkQuality + ?.quality ?? + -1), + builder: (_, + networkQuality, + __) { + return networkQuality != + -1 && + networkQuality < + 3 + ? Padding( + padding: const EdgeInsets + .only( + right: 16.0), + child: + CircleAvatar( + radius: + 16, + backgroundColor: + HMSThemeColors.surfaceDefault, + child: + SvgPicture.asset( + "packages/hms_room_kit/lib/src/assets/icons/network_$networkQuality.svg", + height: 16, + width: 16, + ), + ), + ) + : Container(); + }), + Selector< + ParticipantsStore, + bool>( + selector: (_, + participantsStore) => + (participantsStore + .peer + .isHandRaised), + builder: (_, + isHandRaised, + __) { + return isHandRaised + ? Padding( + padding: const EdgeInsets + .only( + right: 16.0), + child: + CircleAvatar( + radius: + 16, + backgroundColor: + HMSThemeColors.surfaceDefault, + child: + SvgPicture.asset( + "packages/hms_room_kit/lib/src/assets/icons/hand_outline.svg", + height: 16, + width: 16, + colorFilter: ColorFilter.mode(HMSThemeColors.onSurfaceHighEmphasis, BlendMode.srcIn), + ), + ), + ) + : Container(); + }), + _kebabMenu( + currentPeer + .peer) + ], + ) + ], + ), + ), + ), + ], + ), + ); + }), + ), + ], + ), + ), + ); + }), + ) + ], + ), + ), + )), + ); + } +} diff --git a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/remote_peer_bottom_sheet.dart b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/remote_peer_bottom_sheet.dart index e0f134fe2..709b7a608 100644 --- a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/remote_peer_bottom_sheet.dart +++ b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/remote_peer_bottom_sheet.dart @@ -142,62 +142,67 @@ class _RemotePeerBottomSheetState extends State { // text: "Spotlight Tile for Everyone", // textColor: HMSThemeColors.onSurfaceHighEmphasis)), - ListTile( - horizontalTitleGap: 2, - onTap: () async { - Navigator.pop(context); - if (widget.peerTrackNode.audioTrack != null) { - widget.meetingStore.changeTrackState( - widget.peerTrackNode.audioTrack!, - !(widget.peerTrackNode.audioTrack!.isMute)); - } - }, - contentPadding: EdgeInsets.zero, - leading: SvgPicture.asset( - "packages/hms_room_kit/lib/src/assets/icons/${(widget.peerTrackNode.audioTrack?.isMute ?? true) ? "mic_state_on" : "mic_state_off"}.svg", - semanticsLabel: "fl_mic_toggle", - height: 20, - width: 20, - colorFilter: ColorFilter.mode( - widget.peerTrackNode.audioTrack == null + if (widget.meetingStore.localPeer?.role.permissions.mute ?? + false) + ListTile( + horizontalTitleGap: 2, + onTap: () async { + Navigator.pop(context); + if (widget.peerTrackNode.audioTrack != null) { + widget.meetingStore.changeTrackState( + widget.peerTrackNode.audioTrack!, + !(widget.peerTrackNode.audioTrack!.isMute)); + } + }, + contentPadding: EdgeInsets.zero, + leading: SvgPicture.asset( + "packages/hms_room_kit/lib/src/assets/icons/${(widget.peerTrackNode.audioTrack?.isMute ?? true) ? "mic_state_on" : "mic_state_off"}.svg", + semanticsLabel: "fl_mic_toggle", + height: 20, + width: 20, + colorFilter: ColorFilter.mode( + widget.peerTrackNode.audioTrack == null + ? HMSThemeColors.onSurfaceLowEmphasis + : HMSThemeColors.onSurfaceHighEmphasis, + BlendMode.srcIn), + ), + title: HMSSubheadingText( + text: + "${(widget.peerTrackNode.audioTrack?.isMute ?? true) ? "Unmute" : "Mute"} Audio", + textColor: widget.peerTrackNode.audioTrack == null ? HMSThemeColors.onSurfaceLowEmphasis - : HMSThemeColors.onSurfaceHighEmphasis, - BlendMode.srcIn), - ), - title: HMSSubheadingText( - text: - "${(widget.peerTrackNode.audioTrack?.isMute ?? true) ? "Unmute" : "Mute"} Audio", - textColor: widget.peerTrackNode.audioTrack == null - ? HMSThemeColors.onSurfaceLowEmphasis - : HMSThemeColors.onSurfaceHighEmphasis)), - ListTile( - horizontalTitleGap: 2, - onTap: () async { - Navigator.pop(context); - if (widget.peerTrackNode.track != null) { - widget.meetingStore.changeTrackState( - widget.peerTrackNode.track!, - !(widget.peerTrackNode.track!.isMute)); - } - }, - contentPadding: EdgeInsets.zero, - leading: SvgPicture.asset( - "packages/hms_room_kit/lib/src/assets/icons/${(widget.peerTrackNode.track?.isMute ?? true) ? "cam_state_on" : "cam_state_off"}.svg", - semanticsLabel: "fl_camera_toggle", - height: 20, - width: 20, - colorFilter: ColorFilter.mode( - widget.peerTrackNode.track == null + : HMSThemeColors.onSurfaceHighEmphasis)), + + if (widget.meetingStore.localPeer?.role.permissions.mute ?? + false) + ListTile( + horizontalTitleGap: 2, + onTap: () async { + Navigator.pop(context); + if (widget.peerTrackNode.track != null) { + widget.meetingStore.changeTrackState( + widget.peerTrackNode.track!, + !(widget.peerTrackNode.track!.isMute)); + } + }, + contentPadding: EdgeInsets.zero, + leading: SvgPicture.asset( + "packages/hms_room_kit/lib/src/assets/icons/${(widget.peerTrackNode.track?.isMute ?? true) ? "cam_state_on" : "cam_state_off"}.svg", + semanticsLabel: "fl_camera_toggle", + height: 20, + width: 20, + colorFilter: ColorFilter.mode( + widget.peerTrackNode.track == null + ? HMSThemeColors.onSurfaceLowEmphasis + : HMSThemeColors.onSurfaceHighEmphasis, + BlendMode.srcIn), + ), + title: HMSSubheadingText( + text: + "${(widget.peerTrackNode.track?.isMute ?? true) ? "Unmute" : "Mute"} Video", + textColor: widget.peerTrackNode.track == null ? HMSThemeColors.onSurfaceLowEmphasis - : HMSThemeColors.onSurfaceHighEmphasis, - BlendMode.srcIn), - ), - title: HMSSubheadingText( - text: - "${(widget.peerTrackNode.track?.isMute ?? true) ? "Unmute" : "Mute"} Video", - textColor: widget.peerTrackNode.track == null - ? HMSThemeColors.onSurfaceLowEmphasis - : HMSThemeColors.onSurfaceHighEmphasis)), + : HMSThemeColors.onSurfaceHighEmphasis)), // ListTile( // horizontalTitleGap: 2, // onTap: () async { @@ -216,25 +221,29 @@ class _RemotePeerBottomSheetState extends State { // title: HMSSubheadingText( // text: "Volume", // textColor: HMSThemeColors.onSurfaceHighEmphasis)), - ListTile( - horizontalTitleGap: 2, - onTap: () async { - Navigator.pop(context); - widget.meetingStore - .removePeerFromRoom(widget.peerTrackNode.peer); - }, - contentPadding: EdgeInsets.zero, - leading: SvgPicture.asset( - "packages/hms_room_kit/lib/src/assets/icons/peer_remove.svg", - semanticsLabel: "fl_remove_peer", - height: 20, - width: 20, - colorFilter: ColorFilter.mode( - HMSThemeColors.alertErrorDefault, BlendMode.srcIn), - ), - title: HMSSubheadingText( - text: "Remove Participant", - textColor: HMSThemeColors.alertErrorDefault)), + if (widget.meetingStore.localPeer?.role.permissions + .removeOthers ?? + false) + ListTile( + horizontalTitleGap: 2, + onTap: () async { + Navigator.pop(context); + widget.meetingStore + .removePeerFromRoom(widget.peerTrackNode.peer); + }, + contentPadding: EdgeInsets.zero, + leading: SvgPicture.asset( + "packages/hms_room_kit/lib/src/assets/icons/peer_remove.svg", + semanticsLabel: "fl_remove_peer", + height: 20, + width: 20, + colorFilter: ColorFilter.mode( + HMSThemeColors.alertErrorDefault, + BlendMode.srcIn), + ), + title: HMSSubheadingText( + text: "Remove Participant", + textColor: HMSThemeColors.alertErrorDefault)), ], ), ), diff --git a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/viewer_settings_bottom_sheet.dart b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/viewer_settings_bottom_sheet.dart deleted file mode 100644 index 538a11b46..000000000 --- a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/viewer_settings_bottom_sheet.dart +++ /dev/null @@ -1,317 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:hms_room_kit/src/widgets/common_widgets/hms_text_style.dart'; -import 'package:hms_room_kit/src/common/app_color.dart'; -import 'package:hms_room_kit/src/common/utility_components.dart'; -import 'package:hms_room_kit/src/widgets/app_dialogs/hls_aspect_ratio_option_dialog.dart'; -import 'package:hms_room_kit/src/widgets/bottom_sheets/participants_bottom_sheet.dart'; -import 'package:hms_room_kit/src/meeting/meeting_store.dart'; -import 'package:provider/provider.dart'; -import 'package:badges/badges.dart' as badge; - -import 'notification_settings_bottom_sheet.dart'; - -class ViewerSettingsBottomSheet extends StatefulWidget { - const ViewerSettingsBottomSheet({super.key}); - - @override - State createState() => - _ViewerSettingsBottomSheetState(); -} - -class _ViewerSettingsBottomSheetState extends State { - bool isStatsEnabled = true; - @override - void initState() { - super.initState(); - } - - @override - Widget build(BuildContext context) { - return FractionallySizedBox( - heightFactor: 0.5, - child: Padding( - padding: const EdgeInsets.only(top: 20.0, left: 15, right: 15), - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Text( - "More Options", - style: HMSTextStyle.setTextStyle( - fontSize: 16, - color: themeDefaultColor, - letterSpacing: 0.15, - fontWeight: FontWeight.w600), - ), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - IconButton( - icon: SvgPicture.asset( - "packages/hms_room_kit/lib/src/assets/icons/close_button.svg", - width: 40, - ), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - ) - ], - ), - Padding( - padding: const EdgeInsets.only(top: 15, bottom: 10), - child: Divider( - color: dividerColor, - height: 5, - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - InkWell( - onTap: () async { - Navigator.pop(context); - showModalBottomSheet( - isScrollControlled: true, - backgroundColor: themeBottomSheetColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20), - ), - context: context, - builder: (ctx) => ChangeNotifierProvider.value( - value: context.read(), - child: const ParticipantsBottomSheet()), - ); - }, - child: Container( - decoration: BoxDecoration( - color: moreSettingsButtonColor, - borderRadius: BorderRadius.circular(10)), - height: 100, - width: 150, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - badge.Badge( - badgeStyle: badge.BadgeStyle( - badgeColor: hmsdefaultColor, - padding: EdgeInsets.all( - context.read().peers.length < - 1000 - ? 5 - : 8)), - badgeContent: Text(context - .read() - .peers - .length - .toString()), - child: Padding( - padding: EdgeInsets.all( - (context.read().peers.length < - 1000 - ? 5 - : 10)), - child: SvgPicture.asset( - "packages/hms_room_kit/lib/src/assets/icons/participants.svg", - colorFilter: ColorFilter.mode( - themeDefaultColor, BlendMode.srcIn), - ), - ), - ), - const SizedBox(height: 10), - Text( - "Participants", - semanticsLabel: "participants_button", - style: HMSTextStyle.setTextStyle( - fontSize: 14, - color: themeDefaultColor, - letterSpacing: 0.25, - fontWeight: FontWeight.w600), - ) - ], - ), - ), - ), - InkWell( - onTap: () async { - Navigator.pop(context); - showDialog( - context: context, - builder: (_) => AspectRatioOptionDialog( - //Default mode takes the height and width of the device and - //sets up the player according to device height and width - availableAspectRatios: const [ - "Default", - "16:9", - "4:3", - "1:1", - "3:4", - "9:16" - ], - meetingStore: context.read(), - )); - }, - child: Container( - decoration: BoxDecoration( - color: moreSettingsButtonColor, - borderRadius: BorderRadius.circular(10)), - height: 100, - width: 150, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SvgPicture.asset( - "packages/hms_room_kit/lib/src/assets/icons/aspect_ratio.svg", - colorFilter: ColorFilter.mode( - themeDefaultColor, BlendMode.srcIn), - ), - const SizedBox( - height: 10, - ), - Text( - "Set Aspect Ratio", - semanticsLabel: "fl_aspect_ratio", - style: HMSTextStyle.setTextStyle( - fontSize: 14, - color: themeDefaultColor, - letterSpacing: 0.25, - fontWeight: FontWeight.w600), - ), - ], - ), - ), - ) - ], - ), - const SizedBox( - height: 10, - ), - ListTile( - horizontalTitleGap: 2, - onTap: () async { - FocusManager.instance.primaryFocus?.unfocus(); - String name = await UtilityComponents.showNameChangeDialog( - context: context, - placeholder: "Enter Name", - prefilledValue: - context.read().localPeer?.name ?? ""); - - if (name.isNotEmpty && mounted) { - context.read().changeName(name: name); - } - if (mounted) { - Navigator.pop(context); - } - }, - contentPadding: EdgeInsets.zero, - leading: SvgPicture.asset( - "packages/hms_room_kit/lib/src/assets/icons/pencil.svg", - fit: BoxFit.scaleDown, - ), - title: Text( - "Change Name", - style: HMSTextStyle.setTextStyle( - fontSize: 14, - color: themeDefaultColor, - letterSpacing: 0.25, - fontWeight: FontWeight.w600), - ), - ), - ListTile( - horizontalTitleGap: 2, - onTap: () async { - context.read().changeMetadata(); - Navigator.pop(context); - }, - contentPadding: EdgeInsets.zero, - leading: SvgPicture.asset( - "packages/hms_room_kit/lib/src/assets/icons/hand_outline.svg", - fit: BoxFit.scaleDown, - colorFilter: ColorFilter.mode( - context.read().isRaisedHand - ? errorColor - : themeDefaultColor, - BlendMode.srcIn), - ), - title: Text( - "Raise Hand", - semanticsLabel: "hand_raise_button", - style: HMSTextStyle.setTextStyle( - fontSize: 14, - color: context.read().isRaisedHand - ? errorColor - : themeDefaultColor, - letterSpacing: 0.25, - fontWeight: FontWeight.w600), - )), - ListTile( - horizontalTitleGap: 2, - onTap: () async { - Navigator.pop(context); - showModalBottomSheet( - isScrollControlled: true, - backgroundColor: themeBottomSheetColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20), - ), - context: context, - builder: (ctx) => - const NotificationSettingsBottomSheet()); - }, - contentPadding: EdgeInsets.zero, - leading: SvgPicture.asset( - "packages/hms_room_kit/lib/src/assets/icons/notification.svg", - fit: BoxFit.scaleDown, - colorFilter: - ColorFilter.mode(themeDefaultColor, BlendMode.srcIn), - ), - title: Text( - "Modify Notifications", - semanticsLabel: "fl_notification_setting", - style: HMSTextStyle.setTextStyle( - fontSize: 14, - color: themeDefaultColor, - letterSpacing: 0.25, - fontWeight: FontWeight.w600), - )), - if (Platform.isAndroid) - ListTile( - horizontalTitleGap: 2, - onTap: () async { - Navigator.pop(context); - // context.read().enterPipModeOnAndroid(); - }, - contentPadding: EdgeInsets.zero, - leading: SvgPicture.asset( - "packages/hms_room_kit/lib/src/assets/icons/screen_share.svg", - fit: BoxFit.scaleDown, - colorFilter: - ColorFilter.mode(themeDefaultColor, BlendMode.srcIn), - ), - title: Text( - "Enter Pip Mode", - semanticsLabel: "fl_pip_mode", - style: HMSTextStyle.setTextStyle( - fontSize: 14, - color: themeDefaultColor, - letterSpacing: 0.25, - fontWeight: FontWeight.w600), - )), - ], - ), - ), - ), - ); - } -} diff --git a/packages/hms_room_kit/lib/src/widgets/peer_widgets/hand_raise.dart b/packages/hms_room_kit/lib/src/widgets/peer_widgets/hand_raise.dart index 39a991484..bffabf294 100644 --- a/packages/hms_room_kit/lib/src/widgets/peer_widgets/hand_raise.dart +++ b/packages/hms_room_kit/lib/src/widgets/peer_widgets/hand_raise.dart @@ -13,9 +13,9 @@ class HandRaise extends StatelessWidget { @override Widget build(BuildContext context) { - return Selector( - builder: (_, metadata, __) { - return metadata?.contains("\"isHandRaised\":true") ?? false + return Selector( + builder: (_, isHandRaised, __) { + return isHandRaised ? Positioned( top: 5, left: 5, @@ -45,6 +45,6 @@ class HandRaise extends StatelessWidget { ) : Container(); }, - selector: (_, peerTrackNode) => peerTrackNode.peer.metadata); + selector: (_, peerTrackNode) => peerTrackNode.peer.isHandRaised); } } diff --git a/packages/hms_room_kit/lib/src/widgets/peer_widgets/more_option.dart b/packages/hms_room_kit/lib/src/widgets/peer_widgets/more_option.dart index 3bdce2dfe..41b70925c 100644 --- a/packages/hms_room_kit/lib/src/widgets/peer_widgets/more_option.dart +++ b/packages/hms_room_kit/lib/src/widgets/peer_widgets/more_option.dart @@ -22,53 +22,61 @@ class _MoreOptionState extends State { @override Widget build(BuildContext context) { MeetingStore meetingStore = context.read(); + bool mutePermission = + meetingStore.localPeer?.role.permissions.mute ?? false; + bool removeOthers = + meetingStore.localPeer?.role.permissions.removeOthers ?? false; - return Positioned( - bottom: 5, - right: 5, - child: GestureDetector( - onTap: () { - ///[peerTrackNode] is the peerTrackNode of the peer whose more option is clicked - ///We only show the modal bottom sheet if the peer is not the local peer - var peerTrackNode = context.read(); - if (peerTrackNode.peer.peerId != meetingStore.localPeer!.peerId) { - showModalBottomSheet( - isScrollControlled: true, - backgroundColor: HMSThemeColors.surfaceDim, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16)), + return (mutePermission || removeOthers) + ? Positioned( + bottom: 5, + right: 5, + child: GestureDetector( + onTap: () { + ///[peerTrackNode] is the peerTrackNode of the peer whose more option is clicked + ///We only show the modal bottom sheet if the peer is not the local peer + var peerTrackNode = context.read(); + if (peerTrackNode.peer.peerId != + meetingStore.localPeer!.peerId) { + showModalBottomSheet( + isScrollControlled: true, + backgroundColor: HMSThemeColors.surfaceDim, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(16), + topRight: Radius.circular(16)), + ), + context: context, + builder: (ctx) => ChangeNotifierProvider.value( + value: context.read(), + child: RemotePeerBottomSheet( + meetingStore: meetingStore, + peerTrackNode: peerTrackNode, + )), + ); + } + }, + child: Semantics( + label: + "fl_${context.read().peer.name}more_option", + child: Container( + height: 28, + width: 28, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + color: HMSThemeColors.backgroundDim.withOpacity(0.64), + ), + child: Center( + child: Icon( + Icons.more_vert, + color: HMSThemeColors.onSurfaceHighEmphasis, + size: 20, + ), + ), + ), ), - context: context, - builder: (ctx) => ChangeNotifierProvider.value( - value: context.read(), - child: RemotePeerBottomSheet( - meetingStore: meetingStore, - peerTrackNode: peerTrackNode, - )), - ); - } - }, - child: Semantics( - label: "fl_${context.read().peer.name}more_option", - child: Container( - height: 28, - width: 28, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(8), - color: HMSThemeColors.backgroundDim.withOpacity(0.64), ), - child: Center( - child: Icon( - Icons.more_vert, - color: HMSThemeColors.onSurfaceHighEmphasis, - size: 20, - ), - ), - ), - ), - ), - ); + ) + : Container(); } } diff --git a/packages/hms_room_kit/pubspec.lock b/packages/hms_room_kit/pubspec.lock index a10f942b1..550e656a5 100644 --- a/packages/hms_room_kit/pubspec.lock +++ b/packages/hms_room_kit/pubspec.lock @@ -212,10 +212,10 @@ packages: dependency: "direct main" description: name: hmssdk_flutter - sha256: fb6cdad295b0c76be408beb9cef0fe3fab9aa25c775779943a68fab4e675628b + sha256: e8b12fbdde193bc0bfbf0ef40086840c0b8d056778f01c53f4efa15849f35e38 url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.0" http: dependency: transitive description: diff --git a/packages/hms_room_kit/pubspec.yaml b/packages/hms_room_kit/pubspec.yaml index 9bbe262dd..198e557e3 100644 --- a/packages/hms_room_kit/pubspec.yaml +++ b/packages/hms_room_kit/pubspec.yaml @@ -1,6 +1,6 @@ name: hms_room_kit description: 100ms Room Kit provides simple & easy to use UI components to build Live Streaming & Video Conferencing experiences in your apps. -version: 1.0.2 +version: 1.0.3 homepage: https://www.100ms.live/ repository: https://github.com/100mslive/100ms-flutter issue_tracker: https://github.com/100mslive/100ms-flutter/issues @@ -14,7 +14,7 @@ dependencies: flutter: sdk: flutter - hmssdk_flutter: ^1.8.0 + hmssdk_flutter: ^1.9.0 intl: ^0.18.1 permission_handler: ^11.0.0 provider: ^6.0.5 diff --git a/packages/hmssdk_flutter/CHANGELOG.md b/packages/hmssdk_flutter/CHANGELOG.md index 1e50505cc..c22ac044e 100644 --- a/packages/hmssdk_flutter/CHANGELOG.md +++ b/packages/hmssdk_flutter/CHANGELOG.md @@ -5,6 +5,24 @@ | hms_room_kit | [![Pub Version](https://img.shields.io/pub/v/hms_room_kit)](https://pub.dev/packages/hms_room_kit) | | hmssdk_flutter | [![Pub Version](https://img.shields.io/pub/v/hmssdk_flutter)](https://pub.dev/packages/hmssdk_flutter) | +## 1.9.0 - 2023-10-16 + +### Added + +- Large Room Support + - Added first class "Hand Raise" apis: `raiseLocalPeerHand`, `lowerLocalPeerHand`, `lowerRemotePeerHand` + - Added `onPeerListUpdate` event on `HMSUpdateListener`, This requires overriding the `onPeerListUpdate` method in all implementations of HMSUpdateListener. + - Added "Peer List Iterator" APIs - `getPeerListIterator` + + For more details refer [here](https://www.100ms.live/docs/flutter/v2/how-to-guides/interact-with-room/peer/large-room) + +- Added `isHandRaised` property on `HMSPeer` class instance + +- Added `initialising` property on `HMSBrowserRecordingState` class instance + +Updated to Android SDK 2.7.7 & iOS SDK 1.1.0 + +**Full Changelog**: [1.8.0...1.9.0](https://github.com/100mslive/100ms-flutter/compare/1.8.0...1.9.0) ## 1.8.0 - 2023-09-15 diff --git a/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/HMSHLSVariantExtension.kt b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/HMSHLSVariantExtension.kt index ecd84e17c..823ea8296 100644 --- a/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/HMSHLSVariantExtension.kt +++ b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/HMSHLSVariantExtension.kt @@ -11,8 +11,9 @@ class HMSHLSVariantExtension { args["hls_stream_url"] = hmshlsVariant.hlsStreamUrl ?: "" args["meeting_url"] = hmshlsVariant.meetingUrl ?: "" args["metadata"] = hmshlsVariant.metadata ?: "" - args["started_at"] = - SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(hmshlsVariant.startedAt).toString() + hmshlsVariant.startedAt?.let { + args["started_at"] = SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(it).toString() + } return args } } diff --git a/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/HMSPeerExtension.kt b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/HMSPeerExtension.kt index 603057d0c..60dbae826 100644 --- a/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/HMSPeerExtension.kt +++ b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/HMSPeerExtension.kt @@ -15,6 +15,7 @@ class HMSPeerExtension { args["is_local"] = peer.isLocal args["role"] = HMSRoleExtension.toDictionary(peer.hmsRole) args["metadata"] = peer.metadata + args["is_hand_raised"] = peer.isHandRaised args["customer_user_id"] = peer.customerUserID args["audio_track"] = HMSTrackExtension.toDictionary(peer.audioTrack) args["video_track"] = HMSTrackExtension.toDictionary(peer.videoTrack) @@ -41,6 +42,7 @@ class HMSPeerExtension { HMSPeerUpdate.NETWORK_QUALITY_UPDATED -> "networkQualityUpdated" HMSPeerUpdate.BECAME_DOMINANT_SPEAKER -> "becameDominantSpeaker" HMSPeerUpdate.NO_DOMINANT_SPEAKER -> "noDominantSpeaker" + HMSPeerUpdate.HAND_RAISED_CHANGED -> "handRaiseUpdated" else -> "defaultUpdate" } } diff --git a/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/HMSRtmpStreamingState.kt b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/HMSRtmpStreamingState.kt index 4c017f19c..e901b30ad 100644 --- a/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/HMSRtmpStreamingState.kt +++ b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/HMSRtmpStreamingState.kt @@ -36,7 +36,7 @@ class HMSStreamingState { if (hmsBrowserRecordingState.running) { map["started_at"] = SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(hmsBrowserRecordingState.startedAt).toString() } - map["initialising"] = false + map["initialising"] = hmsBrowserRecordingState.initialising return map } diff --git a/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/HmssdkFlutterPlugin.kt b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/HmssdkFlutterPlugin.kt index 48cde7094..224b55a5d 100644 --- a/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/HmssdkFlutterPlugin.kt +++ b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/HmssdkFlutterPlugin.kt @@ -164,8 +164,8 @@ class HmssdkFlutterPlugin : } // MARK: Peer Actions - "change_metadata", "change_name" -> { - peerActions(call, result) + "change_metadata", "change_name", "raise_local_peer_hand", "lower_local_peer_hand", "lower_remote_peer_hand" -> { + HMSPeerAction.peerActions(call, result, hmssdk!!) } // MARK: Recording @@ -241,6 +241,9 @@ class HmssdkFlutterPlugin : "get_room_layout" -> { getRoomLayout(call, result) } + "get_peer_list_iterator", "peer_list_iterator_has_next", "peer_list_iterator_next" -> { + HMSPeerListIteratorAction.peerListIteratorAction(call, result, hmssdk!!) + } else -> { result.notImplemented() } @@ -322,25 +325,6 @@ class HmssdkFlutterPlugin : } } - // MARK: Peer Actions - private fun peerActions( - call: MethodCall, - result: Result, - ) { - when (call.method) { - "change_metadata" -> { - changeMetadata(call, result) - } - "change_name" -> { - changeName(call, result) - } - - else -> { - result.notImplemented() - } - } - } - // MARK: Logger private fun loggerActions( call: MethodCall, @@ -539,6 +523,7 @@ class HmssdkFlutterPlugin : private fun leave(result: Result) { hmssdk!!.leave(hmsActionResultListener = HMSCommonAction.getActionListener(result)) HMSPipAction.disposePIP(activity) + HMSPeerListIteratorAction.clearIteratorMap() removeAllKeyChangeListener() } @@ -884,6 +869,7 @@ class HmssdkFlutterPlugin : hmsActionResultListener = HMSCommonAction.getActionListener(result), ) HMSPipAction.disposePIP(activity) + HMSPeerListIteratorAction.clearIteratorMap() removeAllKeyChangeListener() } @@ -975,21 +961,6 @@ class HmssdkFlutterPlugin : result.success(true) } - private var hasChangedMetadata: Boolean = false - - private fun changeMetadata( - call: MethodCall, - result: Result, - ) { - hasChangedMetadata = !hasChangedMetadata - val metadata = call.argument("metadata") - - hmssdk!!.changeMetadata( - metadata!!, - hmsActionResultListener = HMSCommonAction.getActionListener(result), - ) - } - private val hmsUpdateListener = object : HMSUpdateListener { override fun onChangeTrackStateRequest(details: HMSChangeTrackStateRequest) { @@ -1137,6 +1108,7 @@ class HmssdkFlutterPlugin : if (HMSPipAction.isPIPActive(activity)) { activity.moveTaskToBack(true) HMSPipAction.disposePIP(activity) + HMSPeerListIteratorAction.clearIteratorMap() removeAllKeyChangeListener() } if (args["data"] != null) { @@ -1183,6 +1155,37 @@ class HmssdkFlutterPlugin : eventSink?.success(args) } } + + override fun peerListUpdated( + addedPeers: ArrayList?, + removedPeers: ArrayList?, + ) { + val args = HashMap() + args["event_name"] = "on_peer_list_update" + val parameters = HashMap() + val peersAdded = ArrayList?>() + val peersRemoved = ArrayList?>() + /** + * Here we add peers to the list after parsing the + * peer object + */ + addedPeers?.forEach { peer -> + peersAdded.add(HMSPeerExtension.toDictionary(peer)) + } + + removedPeers?.forEach { peer -> + peersRemoved.add(HMSPeerExtension.toDictionary(peer)) + } + + parameters["added_peers"] = peersAdded + parameters["removed_peers"] = peersRemoved + + args["data"] = parameters + + CoroutineScope(Dispatchers.Main).launch { + eventSink?.success(args) + } + } } private val hmsPreviewListener = @@ -1302,17 +1305,6 @@ class HmssdkFlutterPlugin : logsBuffer.clear() } - private fun changeName( - call: MethodCall, - result: Result, - ) { - val name = call.argument("name") - hmssdk!!.changeName( - name = name!!, - hmsActionResultListener = HMSCommonAction.getActionListener(result), - ) - } - fun onVideoViewError( methodName: String, error: String, diff --git a/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/PeerListIteratorExtension.kt b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/PeerListIteratorExtension.kt new file mode 100644 index 000000000..63fc4142a --- /dev/null +++ b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/PeerListIteratorExtension.kt @@ -0,0 +1,20 @@ +package live.hms.hmssdk_flutter + +import live.hms.video.sdk.models.PeerListIterator + +class PeerListIteratorExtension { + companion object { + fun toDictionary( + peerListIterator: PeerListIterator, + uid: String, + ): HashMap { + val map = HashMap() + + map["limit"] = peerListIterator.limit + map["total_count"] = peerListIterator.totalCount + map["uid"] = uid + + return map + } + } +} diff --git a/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/methods/HMSPeerAction.kt b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/methods/HMSPeerAction.kt new file mode 100644 index 000000000..03c6bd0c2 --- /dev/null +++ b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/methods/HMSPeerAction.kt @@ -0,0 +1,103 @@ +package live.hms.hmssdk_flutter.methods + +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import live.hms.hmssdk_flutter.HMSCommonAction +import live.hms.video.sdk.HMSSDK +import live.hms.video.sdk.models.HMSPeer + +class HMSPeerAction { + companion object { + // MARK: Peer Actions + fun peerActions( + call: MethodCall, + result: MethodChannel.Result, + hmssdk: HMSSDK, + ) { + when (call.method) { + "change_metadata" -> { + changeMetadata(call, result, hmssdk) + } + "change_name" -> { + changeName(call, result, hmssdk) + } + "raise_local_peer_hand" -> { + raiseLocalPeerHand(result, hmssdk) + } + "lower_local_peer_hand" -> { + lowerLocalPeerHand(result, hmssdk) + } + "lower_remote_peer_hand" -> { + lowerRemotePeerHand(call, result, hmssdk) + } + else -> { + result.notImplemented() + } + } + } + + private fun changeMetadata( + call: MethodCall, + result: MethodChannel.Result, + hmssdk: HMSSDK, + ) { + val metadata = call.argument("metadata") + + metadata?.let { + hmssdk.changeMetadata( + it, + hmsActionResultListener = HMSCommonAction.getActionListener(result), + ) + } + } + + private fun changeName( + call: MethodCall, + result: MethodChannel.Result, + hmssdk: HMSSDK, + ) { + val name = call.argument("name") + + name?.let { + hmssdk.changeName( + name = name, + hmsActionResultListener = HMSCommonAction.getActionListener(result), + ) + } + } + + private fun lowerLocalPeerHand( + result: MethodChannel.Result, + hmssdk: HMSSDK, + ) { + hmssdk.lowerLocalPeerHand(HMSCommonAction.getActionListener(result)) + } + + private fun raiseLocalPeerHand( + result: MethodChannel.Result, + hmssdk: HMSSDK, + ) { + hmssdk.raiseLocalPeerHand(HMSCommonAction.getActionListener(result)) + } + + private fun lowerRemotePeerHand( + call: MethodCall, + result: MethodChannel.Result, + hmssdk: HMSSDK, + ) { + val peerId = call.argument("peer_id") + peerId?.let { + var forPeer: HMSPeer? = null + hmssdk.getPeers().forEach { peer -> + if (peer.peerID == peerId) { + forPeer = peer + return@forEach + } + } + forPeer?.let { + hmssdk.lowerRemotePeerHand(it, HMSCommonAction.getActionListener(result)) + } + } + } + } +} diff --git a/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/methods/HMSPeerListIteratorAction.kt b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/methods/HMSPeerListIteratorAction.kt new file mode 100644 index 000000000..8bc585269 --- /dev/null +++ b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/methods/HMSPeerListIteratorAction.kt @@ -0,0 +1,161 @@ +package live.hms.hmssdk_flutter.methods + +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import live.hms.hmssdk_flutter.* +import live.hms.video.error.HMSException +import live.hms.video.sdk.HMSSDK +import live.hms.video.sdk.listeners.PeerListResultListener +import live.hms.video.sdk.models.HMSPeer +import live.hms.video.sdk.models.PeerListIterator +import live.hms.video.sdk.models.PeerListIteratorOptions + +class HMSPeerListIteratorAction { + companion object { + /** + * [peerListIterators] stores the iterators with unique id's. + * This is used whenever we call [peerListIteratorHasNext], or [peerListIteratorNext] + * we fetch the respective peerListIterator based on the uid passed from flutter channel + * and execute the method based on it. + */ + private var peerListIterators = HashMap() + + fun peerListIteratorAction( + call: MethodCall, + result: MethodChannel.Result, + hmssdk: HMSSDK, + ) { + when (call.method) { + "get_peer_list_iterator" -> { + getPeerListIterator(call, result, hmssdk) + } + "peer_list_iterator_has_next" -> { + peerListIteratorHasNext(call, result) + } + "peer_list_iterator_next" -> { + peerListIteratorNext(call, result) + } + } + } + + /** + * This method returns the peer list iterator based on the parameter we pass from flutter channel + */ + private fun getPeerListIterator( + call: MethodCall, + result: MethodChannel.Result, + hmssdk: HMSSDK, + ) { + val uid = call.argument("uid") + + uid?.let { id -> + val peerListOptionsMap = call.argument?>("peer_list_iterator_options") + + var peerListIteratorOptions: PeerListIteratorOptions? = null + + peerListOptionsMap?.let { + val limit = it["limit"] as Int? + + limit?.let { limitValue -> + peerListIteratorOptions = + PeerListIteratorOptions( + byRoleName = it["by_role_name"] as String?, + byPeerIds = it["by_peer_ids"] as ArrayList?, limit = limitValue, + ) + } ?: run { + HMSErrorLogger.returnHMSException("getPeerListIterator", "limit parameter is null while peerListIteratorOptions is non-null", "NULL Error", result) + } + } + + val peerListIterator = hmssdk.getPeerListIterator(peerListIteratorOptions) + + /** + * We store the iterator in [peerListIterators] map for later operations with key as [id] which unique + * id for the iterator + */ + peerListIterators[id] = peerListIterator + + result.success(HMSResultExtension.toDictionary(true, PeerListIteratorExtension.toDictionary(peerListIterator, id))) + } ?: run { + HMSErrorLogger.returnHMSException("getPeerListIterator", "uid is null", "NULL Error", result) + } + } + + /** + * Method to check whether iterator has next set of peers or not + */ + private fun peerListIteratorHasNext( + call: MethodCall, + result: MethodChannel.Result, + ) { + val uid = call.argument("uid") + + uid?.let { + /** + * Here we find the iterator with [uid] passed from flutter channel + * Since we need to perform the operation on that specific iterator + */ + val peerListIterator = peerListIterators[it] + + peerListIterator?.let { iterator -> + result.success(HMSResultExtension.toDictionary(true, iterator.hasNext())) + } ?: run { + HMSErrorLogger.returnHMSException("peerListIteratorHasNext", "No peerListIterator with given uid found", "NULL Error", result) + } + } ?: run { + HMSErrorLogger.returnHMSException("peerListIteratorHasNext", "uid is null", "NULL Error", result) + } + } + + /** + * Method to get a list of next set of peers in iterator, the number of peers returned is equal to + * the limit. + */ + private fun peerListIteratorNext( + call: MethodCall, + methodChannelResult: MethodChannel.Result, + ) { + val uid = call.argument("uid") + + uid?.let { + /** + * Here we find the iterator with [uid] passed from flutter channel + * Since we need to perform the operation on that specific iterator + */ + val peerListIterator = peerListIterators[it] + + peerListIterator?.let { iterator -> + iterator.next( + object : PeerListResultListener { + override fun onError(error: HMSException) { + methodChannelResult.success(HMSResultExtension.toDictionary(false, HMSExceptionExtension.toDictionary(error))) + } + + override fun onSuccess(result: ArrayList) { + val data = HashMap() + val peerList = ArrayList() + result.forEach { peer -> + peerList.add(HMSPeerExtension.toDictionary(peer)) + } + data["peers"] = peerList + data["total_count"] = iterator.totalCount + methodChannelResult.success(HMSResultExtension.toDictionary(true, data)) + } + }, + ) + } ?: run { + HMSErrorLogger.returnHMSException("peerListIteratorNext", "No peerListIterator with given uid found", "NULL Error", methodChannelResult) + } + } ?: run { + HMSErrorLogger.returnHMSException("peerListIteratorNext", "uid is null", "NULL Error", methodChannelResult) + } + } + + /** + * This method clears the iterator map on leave, end Room or onRemovedFromRoom method calls + */ + fun clearIteratorMap() { + peerListIterators.clear() + } + } +} diff --git a/packages/hmssdk_flutter/example/ExampleAppChangelog.txt b/packages/hmssdk_flutter/example/ExampleAppChangelog.txt index 081543974..f3ac83c9c 100644 --- a/packages/hmssdk_flutter/example/ExampleAppChangelog.txt +++ b/packages/hmssdk_flutter/example/ExampleAppChangelog.txt @@ -1,17 +1,21 @@ -Board: https://github.com/100mslive/100ms-flutter/projects/30 +Board: https://100ms.atlassian.net/jira/software/projects/FLUT/boards/34/ -- Setting the HLS Player aspect ratio as per video stream content -https://github.com/100mslive/100ms-flutter/pull/1556 +- Fixed userid bug in prebuilt +https://github.com/100mslive/100ms-flutter/pull/1601 -- Error Toasts as per Figma design -https://github.com/100mslive/100ms-flutter/issues/1489 +- Added first class api for hand raise and peer list updates +https://github.com/100mslive/100ms-flutter/pull/1600 -- Added layout honouring of Layout API config -https://github.com/100mslive/100ms-flutter/pull/1557 +- Added peer list iterator changes for large rooms +https://github.com/100mslive/100ms-flutter/pull/1602 -- Updated to Android SDK version to 2.7.3 and iOS SDK version to 0.9.11 +Bug fixes: -Room Kit: 1.0.1 -Core SDK: 1.8.0 -Android SDK: 2.7.6 -iOS SDK: 0.9.12 \ No newline at end of file +- Fix for local tile inset when inset is disabled after role changed + +- VoS joined from flutter cannot be removed from stage + +Room Kit: 1.0.3 +Core SDK: 1.9.0 +Android SDK: 2.7.7 +iOS SDK: 1.1.0 \ No newline at end of file diff --git a/packages/hmssdk_flutter/example/android/app/build.gradle b/packages/hmssdk_flutter/example/android/app/build.gradle index 1e7216cca..7e316683d 100644 --- a/packages/hmssdk_flutter/example/android/app/build.gradle +++ b/packages/hmssdk_flutter/example/android/app/build.gradle @@ -32,8 +32,8 @@ android { applicationId "live.hms.flutter" minSdkVersion 21 targetSdkVersion 33 - versionCode 361 - versionName "1.5.61" + versionCode 373 + versionName "1.5.73" } signingConfigs { diff --git a/packages/hmssdk_flutter/example/ios/Podfile.lock b/packages/hmssdk_flutter/example/ios/Podfile.lock index b2cfc57f3..4f433afa8 100644 --- a/packages/hmssdk_flutter/example/ios/Podfile.lock +++ b/packages/hmssdk_flutter/example/ios/Podfile.lock @@ -25,15 +25,15 @@ PODS: - Firebase/Performance (= 10.9.0) - firebase_core - Flutter - - FirebaseABTesting (10.15.0): + - FirebaseABTesting (10.16.0): - FirebaseCore (~> 10.0) - FirebaseCore (10.9.0): - FirebaseCoreInternal (~> 10.0) - GoogleUtilities/Environment (~> 7.8) - GoogleUtilities/Logger (~> 7.8) - - FirebaseCoreExtension (10.15.0): + - FirebaseCoreExtension (10.16.0): - FirebaseCore (~> 10.0) - - FirebaseCoreInternal (10.15.0): + - FirebaseCoreInternal (10.16.0): - "GoogleUtilities/NSData+zlib (~> 7.8)" - FirebaseCrashlytics (10.9.0): - FirebaseCore (~> 10.5) @@ -45,7 +45,7 @@ PODS: - PromisesObjC (~> 2.1) - FirebaseDynamicLinks (10.9.0): - FirebaseCore (~> 10.0) - - FirebaseInstallations (10.15.0): + - FirebaseInstallations (10.16.0): - FirebaseCore (~> 10.0) - GoogleUtilities/Environment (~> 7.8) - GoogleUtilities/UserDefaults (~> 7.8) @@ -60,13 +60,13 @@ PODS: - GoogleUtilities/ISASwizzler (~> 7.8) - GoogleUtilities/MethodSwizzler (~> 7.8) - nanopb (< 2.30910.0, >= 2.30908.0) - - FirebaseRemoteConfig (10.15.0): + - FirebaseRemoteConfig (10.16.0): - FirebaseABTesting (~> 10.0) - FirebaseCore (~> 10.0) - FirebaseInstallations (~> 10.0) - GoogleUtilities/Environment (~> 7.8) - "GoogleUtilities/NSData+zlib (~> 7.8)" - - FirebaseSessions (10.15.0): + - FirebaseSessions (10.16.0): - FirebaseCore (~> 10.5) - FirebaseCoreExtension (~> 10.0) - FirebaseInstallations (~> 10.0) @@ -95,14 +95,14 @@ PODS: - HMSBroadcastExtensionSDK (0.0.9) - HMSHLSPlayerSDK (0.0.2): - HMSAnalyticsSDK (= 0.0.2) - - HMSSDK (0.9.12): + - HMSSDK (1.1.0): - HMSAnalyticsSDK (= 0.0.2) - HMSWebRTC (= 1.0.5116) - - hmssdk_flutter (1.8.0): + - hmssdk_flutter (1.9.0): - Flutter - HMSBroadcastExtensionSDK (= 0.0.9) - HMSHLSPlayerSDK (= 0.0.2) - - HMSSDK (= 0.9.12) + - HMSSDK (= 1.1.0) - HMSWebRTC (1.0.5116) - MTBBarcodeScanner (5.0.11) - nanopb (2.30909.0): @@ -214,16 +214,16 @@ SPEC CHECKSUMS: firebase_crashlytics: 9b80d1944507cc07fa1c4455797f7d2eb7c8873f firebase_dynamic_links: db9f2ebcc3ea646e76a1d3ee37e9e57890ff0a83 firebase_performance: d11d1fd9591547f6b75f325aaadd6550eaf7e090 - FirebaseABTesting: 7fa3bca17f79ac433301d20d5cd33401f7738dca + FirebaseABTesting: 03f0a8b88cf618350527f2c6a2234e29b9c65064 FirebaseCore: b68d3616526ec02e4d155166bbafb8eca64af557 - FirebaseCoreExtension: d3f1ea3725fb41f56e8fbfb29eeaff54e7ffb8f6 - FirebaseCoreInternal: 2f4bee5ed00301b5e56da0849268797a2dd31fb4 + FirebaseCoreExtension: 2dbc745b337eb99d2026a7a309ae037bd873f45e + FirebaseCoreInternal: 26233f705cc4531236818a07ac84d20c333e505a FirebaseCrashlytics: b60329455285aff853e54139d8ddbfe1e5f2b9f9 FirebaseDynamicLinks: 8cb66c4f403aa6ddf86ff3bc3c383a652f344ce9 - FirebaseInstallations: cae95cab0f965ce05b805189de1d4c70b11c76fb + FirebaseInstallations: b822f91a61f7d1ba763e5ccc9d4f2e6f2ed3b3ee FirebasePerformance: eee2f5da94fd7e5d15487649f8fe10a90c87c174 - FirebaseRemoteConfig: 64b6ada098c649304114a817effd7e5f87229b11 - FirebaseSessions: ee59a7811bef4c15f65ef6472f3210faa293f9c8 + FirebaseRemoteConfig: 17ec974c6cac5cdc6cf8297062c2219851857f06 + FirebaseSessions: 96e7781e545929cde06dd91088ddbb0841391b43 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 flutter_foreground_task: 21ef182ab0a29a3005cc72cd70e5f45cb7f7f817 GoogleDataTransport: 54dee9d48d14580407f8f5fbf2f496e92437a2f2 @@ -231,8 +231,8 @@ SPEC CHECKSUMS: HMSAnalyticsSDK: 4d2a88a729b1eb42f3d25f217c28937ec318a5b7 HMSBroadcastExtensionSDK: d80fe325f6c928bd8e5176290b5a4b7ae15d6fbb HMSHLSPlayerSDK: 6a54ad4d12f3dc2270d1ecd24019d71282a4f6a3 - HMSSDK: 65c1445ad2aac04deb127fe7611f65b8e7447752 - hmssdk_flutter: 806162401c1c9dd1fca07d913b9ee5f1e1d36930 + HMSSDK: 49e3ac665ceb8904d41787ddf99742e8d7d6529a + hmssdk_flutter: 414edcdff09f1587f53b04e9b93b39e6174a61cd HMSWebRTC: ae54e9dd91b869051b283b43b14f57d43b7bf8e1 MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431 @@ -249,4 +249,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 919064996fff867cd85dbf9e7730ff45bac23884 -COCOAPODS: 1.12.1 +COCOAPODS: 1.13.0 diff --git a/packages/hmssdk_flutter/example/ios/Runner.xcodeproj/project.pbxproj b/packages/hmssdk_flutter/example/ios/Runner.xcodeproj/project.pbxproj index 604654b08..b4f14f7ae 100644 --- a/packages/hmssdk_flutter/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/hmssdk_flutter/example/ios/Runner.xcodeproj/project.pbxproj @@ -199,15 +199,15 @@ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( D769E267F13386F26EC49DC5 /* [CP] Check Pods Manifest.lock */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + EC1052BA2858A77D005EAB9E /* Embed App Extensions */, + 7B56423DDF1948A38AE2EA10 /* [CP] Embed Pods Frameworks */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, - 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 7B56423DDF1948A38AE2EA10 /* [CP] Embed Pods Frameworks */, CC19BBB77EA3E7F699588CF7 /* [firebase_crashlytics] Crashlytics Upload Symbols */, - EC1052BA2858A77D005EAB9E /* Embed App Extensions */, ); buildRules = ( ); @@ -312,7 +312,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n"; }; 402164D3899CD572C6304156 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; @@ -386,7 +386,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"$PODS_ROOT/FirebaseCrashlytics/upload-symbols\" --flutter-project \"$PROJECT_DIR/firebase_app_id_file.json\" "; + shellScript = "\"$PODS_ROOT/FirebaseCrashlytics/upload-symbols\" --flutter-project \"$PROJECT_DIR/firebase_app_id_file.json\" \n"; }; D769E267F13386F26EC49DC5 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; diff --git a/packages/hmssdk_flutter/example/ios/Runner/Info.plist b/packages/hmssdk_flutter/example/ios/Runner/Info.plist index 304eaacf5..208abe8c4 100644 --- a/packages/hmssdk_flutter/example/ios/Runner/Info.plist +++ b/packages/hmssdk_flutter/example/ios/Runner/Info.plist @@ -21,7 +21,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.5.62 + 1.5.73 CFBundleSignature ???? CFBundleURLTypes @@ -48,7 +48,7 @@ CFBundleVersion - 362 + 373 ITSAppUsesNonExemptEncryption LSApplicationCategoryType diff --git a/packages/hmssdk_flutter/example/lib/main.dart b/packages/hmssdk_flutter/example/lib/main.dart index 7c237aa56..7f7ae1c85 100644 --- a/packages/hmssdk_flutter/example/lib/main.dart +++ b/packages/hmssdk_flutter/example/lib/main.dart @@ -309,6 +309,8 @@ class _HomePageState extends State { roomCode: Constant.roomCode, options: HMSPrebuiltOptions( endPoints: endPoints, + userId: + "user_flutter", // pass your custom unique user identifier here iOSScreenshareConfig: HMSIOSScreenshareConfig( appGroup: "group.flutterhms", preferredExtension: diff --git a/packages/hmssdk_flutter/example/pubspec.lock b/packages/hmssdk_flutter/example/pubspec.lock index 34365429e..3274e9089 100644 --- a/packages/hmssdk_flutter/example/pubspec.lock +++ b/packages/hmssdk_flutter/example/pubspec.lock @@ -294,15 +294,14 @@ packages: path: "../../hms_room_kit" relative: true source: path - version: "1.0.2" + version: "1.0.3" hmssdk_flutter: dependency: transitive description: - name: hmssdk_flutter - sha256: fb6cdad295b0c76be408beb9cef0fe3fab9aa25c775779943a68fab4e675628b - url: "https://pub.dev" - source: hosted - version: "1.8.0" + path: ".." + relative: true + source: path + version: "1.9.0" http: dependency: transitive description: diff --git a/packages/hmssdk_flutter/example/pubspec.yaml b/packages/hmssdk_flutter/example/pubspec.yaml index 7fce87ec0..da0a18b7b 100644 --- a/packages/hmssdk_flutter/example/pubspec.yaml +++ b/packages/hmssdk_flutter/example/pubspec.yaml @@ -4,7 +4,7 @@ description: Demonstrates how to use the hmssdk_flutter plugin. # The following line prevents the package from being accidentally published to # pub.dev using `pub publish`. This is preferred for private packages. publish_to: "none" # Remove this line if you wish to publish to pub.dev -version: 1.8.0 +version: 1.9.0 environment: sdk: ">=2.16.0 <4.0.0" diff --git a/packages/hmssdk_flutter/ios/Classes/Actions/HMSPeerAction.swift b/packages/hmssdk_flutter/ios/Classes/Actions/HMSPeerAction.swift new file mode 100644 index 000000000..c1031131c --- /dev/null +++ b/packages/hmssdk_flutter/ios/Classes/Actions/HMSPeerAction.swift @@ -0,0 +1,110 @@ +// +// HMSPeerAction.swift +// hmssdk_flutter +// +// Created by Pushpam on 27/09/23. +// + +import Foundation +import HMSSDK + +class HMSPeerAction { + + static func peerActions(_ call: FlutterMethodCall, _ result: @escaping FlutterResult, _ hmsSDK: HMSSDK?) { + switch call.method { + case "change_metadata": + changeMetadata(call, result, hmsSDK) + + case "change_name": + changeName(call, result, hmsSDK) + + case "lower_remote_peer_hand": + lowerRemotePeerHand(call, result, hmsSDK) + + case "raise_local_peer_hand": + raiseLocalPeerHand(result, hmsSDK) + + case "lower_local_peer_hand": + lowerLocalPeerHand(result, hmsSDK) + + default: + result(FlutterMethodNotImplemented) + } + } + + static private func changeName(_ call: FlutterMethodCall, _ result: @escaping FlutterResult, _ hmsSDK: HMSSDK?) { + + let arguments = call.arguments as![AnyHashable: Any] + + guard let name = arguments["name"] as? String else { + result(HMSErrorExtension.getError("No name found in \(#function)")) + return + } + hmsSDK?.change(name: name) { _, error in + if let error = error { + result(HMSErrorExtension.toDictionary(error)) + } else { + result(nil) + } + + } + } + + static private func changeMetadata(_ call: FlutterMethodCall, _ result: @escaping FlutterResult, _ hmsSDK: HMSSDK?) { + + let arguments = call.arguments as! [AnyHashable: Any] + + guard let metadata = arguments["metadata"] as? String else { + result(HMSErrorExtension.getError("No metadata found in \(#function)")) + return + } + hmsSDK?.change(metadata: metadata) {_, error in + if let error = error { + result(HMSErrorExtension.toDictionary(error)) + return + } else { + result(nil) + } + } + } + + static private func lowerLocalPeerHand(_ result: @escaping FlutterResult, _ hmsSDK: HMSSDK?) { + hmsSDK?.lowerLocalPeerHand { + _, error in if let error = error { + result(HMSErrorExtension.toDictionary(error)) + } else { + result(nil) + } + } + } + + static private func raiseLocalPeerHand(_ result: @escaping FlutterResult, _ hmsSDK: HMSSDK?) { + hmsSDK?.raiseLocalPeerHand { + _, error in if let error = error { + result(HMSErrorExtension.toDictionary(error)) + } else { + result(nil) + } + } + } + + static private func lowerRemotePeerHand(_ call: FlutterMethodCall, _ result: @escaping FlutterResult, _ hmsSDK: HMSSDK? ) { + + let arguments = call.arguments as? [AnyHashable: Any] + + guard let peerId = arguments?["peer_id"] as? String, + let forPeer = HMSCommonAction.getPeer(by: peerId, hmsSDK: hmsSDK) + else { + HMSErrorLogger.returnArgumentsError("forPeer is null") + return + } + + hmsSDK?.lowerRemotePeerHand(forPeer) { _, error in + if let error = error { + result(HMSErrorExtension.toDictionary(error)) + } else { + result(nil) + } + } + } +} diff --git a/packages/hmssdk_flutter/ios/Classes/Actions/HMSPeerListIteratorAction.swift b/packages/hmssdk_flutter/ios/Classes/Actions/HMSPeerListIteratorAction.swift new file mode 100644 index 000000000..8ff02905d --- /dev/null +++ b/packages/hmssdk_flutter/ios/Classes/Actions/HMSPeerListIteratorAction.swift @@ -0,0 +1,166 @@ +// +// HMSPeerListIteratorAction.swift +// hmssdk_flutter +// +// Created by Pushpam on 04/10/23. +// + +import Foundation +import HMSSDK + +class HMSPeerListIteratorAction { + + /** + * [peerListIterators] stores the iterators with unique id's. + * This is used whenever we call [peerListIteratorHasNext], or [peerListIteratorNext] + * we fetch the respective peerListIterator based on the uid passed from flutter channel + * and execute the method based on it. + */ + private static var peerListIterators = [String: HMSPeerListIterator]() + + static func peerListIteratorAction(_ call: FlutterMethodCall, _ result: @escaping FlutterResult, _ hmsSDK: HMSSDK?) { + switch call.method { + + case "get_peer_list_iterator": + getPeerListIterator(call, result, hmsSDK) + + case "peer_list_iterator_has_next": + peerListIteratorHasNext(call, result) + + case "peer_list_iterator_next": + peerListIteratorNext(call, result) + + default: + result(FlutterMethodNotImplemented) + } + } + + /** + * This method returns the peer list iterator based on the parameter we pass from flutter channel + */ + private static func getPeerListIterator(_ call: FlutterMethodCall, _ result: @escaping FlutterResult, _ hmsSDK: HMSSDK?) { + + let arguments = call.arguments as? [AnyHashable: Any] + + guard let uid = arguments?["uid"] as? String + else { + HMSErrorLogger.returnHMSException(#function, "uid is null", "NULL Error", result) + return + } + + var peerListIteratorOptions: HMSPeerListIteratorOptions? + + let peerListOptionsMap = arguments?["peer_list_iterator_options"] as? [String: Any] + + guard let limit = peerListOptionsMap?["limit"] as? Int + else { + HMSErrorLogger.returnHMSException(#function, "limit parameter is null while peerListIteratorOptions is non-null", "NULL Error", result) + return + } + + peerListIteratorOptions = HMSPeerListIteratorOptions(filterByRoleName: peerListOptionsMap?["by_role_name"] as? String, filterByPeerIds: peerListOptionsMap?["by_peer_ids"] as? [String], limit: limit) + + var peerListIterator: HMSPeerListIterator? + + if peerListIteratorOptions != nil { + peerListIterator = hmsSDK?.getPeerListIterator(options: peerListIteratorOptions!) + } else { + peerListIterator = hmsSDK?.getPeerListIterator() + } + + if peerListIterator != nil { + + /** + * We store the iterator in [peerListIterators] map for later operations with key as [id] which unique + * id for the iterator + */ + peerListIterators[uid] = peerListIterator + + result(HMSResultExtension.toDictionary(true, HMSPeerListIteratorExtension.toDictionary(peerListIterator!, uid))) + } else { + HMSErrorLogger.returnHMSException(#function, "peerListIterator is null", "NULL Error", result) + return + } + + } + + /** + * Method to check whether iterator has next set of peers or not + */ + private static func peerListIteratorHasNext(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { + + let arguments = call.arguments as? [AnyHashable: Any] + + guard let uid = arguments?["uid"] as? String + else { + HMSErrorLogger.returnHMSException(#function, "uid is null", "NULL Error", result) + return + } + + /** + * Here we find the iterator with [uid] passed from flutter channel + * Since we need to perform the operation on that specific iterator + */ + guard let peerListIterator = peerListIterators[uid] + else { + HMSErrorLogger.returnHMSException(#function, "No peerListIterator with given uid found", "NULL Error", result) + return + } + + result(HMSResultExtension.toDictionary(true, peerListIterator.hasNext)) + + } + + /** + * Method to get a list of next set of peers in iterator, the number of peers returned is equal to + * the limit. + */ + private static func peerListIteratorNext(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { + + let arguments = call.arguments as? [AnyHashable: Any] + + guard let uid = arguments?["uid"] as? String + else { + HMSErrorLogger.returnHMSException(#function, "uid is null", "NULL Error", result) + return + } + + /** + * Here we find the iterator with [uid] passed from flutter channel + * Since we need to perform the operation on that specific iterator + */ + guard let peerListIterator = peerListIterators[uid] + else { + HMSErrorLogger.returnHMSException(#function, "No peerListIterator with given uid found", "NULL Error", result) + return + } + + peerListIterator.next(completion: { peers, error in + if let error = error { + result(HMSResultExtension.toDictionary(false, HMSErrorExtension.toDictionary(error))) + } else { + var data = [String: Any]() + var peersList = [Any]() + + peers?.forEach { + peersList.append(HMSPeerExtension.toDictionary($0)) + } + + data["peers"] = peersList + data["total_count"] = peerListIterator.totalCount + + result(HMSResultExtension.toDictionary(true, data)) + } + + }) + + } + + /** + * This method clears the iterator map on leave, end Room or onRemovedFromRoom method calls + */ + static func clearIteratorMap() { + peerListIterators.removeAll() + } + +} diff --git a/packages/hmssdk_flutter/ios/Classes/Models/HMSPeerExtension.swift b/packages/hmssdk_flutter/ios/Classes/Models/HMSPeerExtension.swift index c953bc867..556cb731d 100644 --- a/packages/hmssdk_flutter/ios/Classes/Models/HMSPeerExtension.swift +++ b/packages/hmssdk_flutter/ios/Classes/Models/HMSPeerExtension.swift @@ -16,6 +16,7 @@ class HMSPeerExtension { "peer_id": peer.peerID, "name": peer.name, "is_local": peer.isLocal, + "is_hand_raised": peer.isHandRaised, "customer_description": peer.metadata ?? "", "customer_user_id": peer.customerUserID ?? "", "joined_at": "\(peer.joinedAt)", @@ -65,6 +66,8 @@ class HMSPeerExtension { return "metadataChanged" case .networkQualityUpdated: return "networkQualityUpdated" + case .handRaiseUpdated: + return "handRaiseUpdated" @unknown default: return "defaultUpdate" } diff --git a/packages/hmssdk_flutter/ios/Classes/Models/HMSPeerListIteratorExtension.swift b/packages/hmssdk_flutter/ios/Classes/Models/HMSPeerListIteratorExtension.swift new file mode 100644 index 000000000..90fa1927f --- /dev/null +++ b/packages/hmssdk_flutter/ios/Classes/Models/HMSPeerListIteratorExtension.swift @@ -0,0 +1,23 @@ +// +// HMSPeerListIteratorExtension.swift +// hmssdk_flutter +// +// Created by Pushpam on 04/10/23. +// + +import Foundation +import HMSSDK + +class HMSPeerListIteratorExtension { + + static func toDictionary(_ peerListIterator: HMSPeerListIterator, _ uid: String) -> [String: Any] { + + var dict = [ + "uid": uid, + "limit": peerListIterator.options.limit, + "total_count": peerListIterator.totalCount + ] as [String: Any] + + return dict + } +} diff --git a/packages/hmssdk_flutter/ios/Classes/SwiftHmssdkFlutterPlugin.swift b/packages/hmssdk_flutter/ios/Classes/SwiftHmssdkFlutterPlugin.swift index fe18c06ad..c4a001ed0 100644 --- a/packages/hmssdk_flutter/ios/Classes/SwiftHmssdkFlutterPlugin.swift +++ b/packages/hmssdk_flutter/ios/Classes/SwiftHmssdkFlutterPlugin.swift @@ -202,8 +202,8 @@ public class SwiftHmssdkFlutterPlugin: NSObject, FlutterPlugin, HMSUpdateListene roleActions(call, result) // MARK: - Peer Action - case "change_metadata", "change_name": - peerActions(call, result) + case "change_metadata", "change_name", "raise_local_peer_hand", "lower_local_peer_hand", "lower_remote_peer_hand": + HMSPeerAction.peerActions(call, result, hmsSDK) // MARK: - RTMP @@ -292,6 +292,9 @@ public class SwiftHmssdkFlutterPlugin: NSObject, FlutterPlugin, HMSUpdateListene case "get_room_layout": getRoomLayout(call, result) + case "get_peer_list_iterator", "peer_list_iterator_has_next", "peer_list_iterator_next": + HMSPeerListIteratorAction.peerListIteratorAction(call, result, hmsSDK) + default: result(FlutterMethodNotImplemented) } @@ -364,20 +367,6 @@ public class SwiftHmssdkFlutterPlugin: NSObject, FlutterPlugin, HMSUpdateListene } } - // MARK: - Peer Actions - - private func peerActions(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - switch call.method { - case "change_metadata": - changeMetadata(call, result) - - case "change_name": - changeName(call, result) - default: - result(FlutterMethodNotImplemented) - } - } - // MARK: - Logging private func loggingActions(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { switch call.method { @@ -1057,50 +1046,6 @@ public class SwiftHmssdkFlutterPlugin: NSObject, FlutterPlugin, HMSUpdateListene } } } - - private var hasChangedMetadata = false - - private func changeMetadata(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - - let arguments = call.arguments as! [AnyHashable: Any] - - guard let metadata = arguments["metadata"] as? String else { - result(HMSErrorExtension.getError("No metadata found in \(#function)")) - return - } - - hmsSDK?.change(metadata: metadata) { [weak self] _, error in - if let error = error { - result(HMSErrorExtension.toDictionary(error)) - return - } else { - if let strongSelf = self { - strongSelf.hasChangedMetadata = !strongSelf.hasChangedMetadata - } - result(nil) - } - } - } - - private func changeName(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - - let arguments = call.arguments as![AnyHashable: Any] - - guard let name = arguments["name"] as? String else { - result(HMSErrorExtension.getError("No name found in \(#function)")) - return - } - - hmsSDK?.change(name: name) { _, error in - if let error = error { - result(HMSErrorExtension.toDictionary(error)) - } else { - result(nil) - } - - } - } - // MARK: - Logging private var logLevel = HMSLogLevel.off @@ -1417,6 +1362,28 @@ public class SwiftHmssdkFlutterPlugin: NSObject, FlutterPlugin, HMSUpdateListene eventSink?(data) } + public func onPeerListUpdate(added: [HMSPeer], removed: [HMSPeer]) { + var parameters = [String: Any]() + + var addedPeers = [Any]() + var removedPeers = [Any]() + + added.forEach { + addedPeers.append(HMSPeerExtension.toDictionary($0)) + } + + removed.forEach { + removedPeers.append(HMSPeerExtension.toDictionary($0)) + } + + parameters["added_peers"] = addedPeers + parameters["removed_peers"] = removedPeers + + let data = ["event_name": "on_peer_list_update", "data": parameters] as [String: Any] + + eventSink?(data) + } + // MARK: - RTC Stats Listeners public func on(localAudioStats: HMSLocalAudioStats, track: HMSAudioTrack, peer: HMSPeer) { @@ -1590,5 +1557,6 @@ public class SwiftHmssdkFlutterPlugin: NSObject, FlutterPlugin, HMSUpdateListene destroyPIPController() removeAllKeyChangeListener() removeHMSLogger() + HMSPeerListIteratorAction.clearIteratorMap() } } diff --git a/packages/hmssdk_flutter/lib/assets/sdk-versions.json b/packages/hmssdk_flutter/lib/assets/sdk-versions.json index 92d525d70..1f2bd762e 100644 --- a/packages/hmssdk_flutter/lib/assets/sdk-versions.json +++ b/packages/hmssdk_flutter/lib/assets/sdk-versions.json @@ -1,7 +1,7 @@ { - "flutter": "1.8.0", - "ios": "0.9.12", + "flutter": "1.9.0", + "ios": "1.1.0", "iOSBroadcastExtension": "0.0.9", "iOSHLSPlayerSDK": "0.0.2", - "android": "2.7.6" + "android": "2.7.7" } diff --git a/packages/hmssdk_flutter/lib/hmssdk_flutter.dart b/packages/hmssdk_flutter/lib/hmssdk_flutter.dart index 365d12f3c..a2ed8ad45 100644 --- a/packages/hmssdk_flutter/lib/hmssdk_flutter.dart +++ b/packages/hmssdk_flutter/lib/hmssdk_flutter.dart @@ -98,6 +98,8 @@ export 'src/model/hls_player/hms_hls_player_controller.dart'; export 'src/model/hls_player/hms_hls_player_stats.dart'; export 'src/model/hls_player/hms_hls_cue.dart'; export 'src/model/hls_player/hms_hls_timed_metadata.dart'; +export 'src/model/hms_peer_list_iterator.dart'; +export 'src/model/peer_list_iterator_options.dart'; //Views export 'src/ui/meeting/hms_video_view.dart'; diff --git a/packages/hmssdk_flutter/lib/src/common/platform_methods.dart b/packages/hmssdk_flutter/lib/src/common/platform_methods.dart index 0849cdbf7..1e62e30ef 100644 --- a/packages/hmssdk_flutter/lib/src/common/platform_methods.dart +++ b/packages/hmssdk_flutter/lib/src/common/platform_methods.dart @@ -187,7 +187,15 @@ enum PlatformMethod { toggleAlwaysScreenOn, getRoomLayout, previewForRole, - cancelPreview + cancelPreview, + + ///PeerListIteratorMethods + getPeerListIterator, + peerListIteratorHasNext, + peerListIteratorNext, + lowerLocalPeerHand, + lowerRemotePeerHand, + raiseLocalPeerHand } extension PlatformMethodValues on PlatformMethod { @@ -465,6 +473,21 @@ extension PlatformMethodValues on PlatformMethod { return "toggle_always_screen_on"; case PlatformMethod.getRoomLayout: return "get_room_layout"; + + ///Peer list iterator methods + case PlatformMethod.getPeerListIterator: + return "get_peer_list_iterator"; + case PlatformMethod.peerListIteratorHasNext: + return "peer_list_iterator_has_next"; + case PlatformMethod.peerListIteratorNext: + return "peer_list_iterator_next"; + + case PlatformMethod.lowerLocalPeerHand: + return "lower_local_peer_hand"; + case PlatformMethod.lowerRemotePeerHand: + return "lower_remote_peer_hand"; + case PlatformMethod.raiseLocalPeerHand: + return "raise_local_peer_hand"; default: return 'unknown'; } @@ -744,6 +767,21 @@ extension PlatformMethodValues on PlatformMethod { return PlatformMethod.toggleAlwaysScreenOn; case "get_room_layout": return PlatformMethod.getRoomLayout; + + ///Peer List Iterator methods + case "get_peer_list_iterator": + return PlatformMethod.getPeerListIterator; + case "peer_list_iterator_has_next": + return PlatformMethod.peerListIteratorHasNext; + case "peer_list_iterator_next": + return PlatformMethod.peerListIteratorNext; + + case "lower_local_peer_hand": + return PlatformMethod.lowerLocalPeerHand; + case "lower_remote_peer_hand": + return PlatformMethod.lowerRemotePeerHand; + case "raise_local_peer_hand": + return PlatformMethod.raiseLocalPeerHand; default: return PlatformMethod.unknown; } diff --git a/packages/hmssdk_flutter/lib/src/enum/hms_action_result_listener_method.dart b/packages/hmssdk_flutter/lib/src/enum/hms_action_result_listener_method.dart index 1817b2594..13c400c4b 100644 --- a/packages/hmssdk_flutter/lib/src/enum/hms_action_result_listener_method.dart +++ b/packages/hmssdk_flutter/lib/src/enum/hms_action_result_listener_method.dart @@ -23,5 +23,8 @@ enum HMSActionResultListenerMethod { changeRoleOfPeersWithRoles, setSessionMetadataForKey, sendHLSTimedMetadata, + lowerLocalPeerHand, + lowerRemotePeerHand, + raiseLocalPeerHand, unknown } diff --git a/packages/hmssdk_flutter/lib/src/enum/hms_peer_update.dart b/packages/hmssdk_flutter/lib/src/enum/hms_peer_update.dart index 32711e23e..230fb1b34 100644 --- a/packages/hmssdk_flutter/lib/src/enum/hms_peer_update.dart +++ b/packages/hmssdk_flutter/lib/src/enum/hms_peer_update.dart @@ -17,7 +17,10 @@ enum HMSPeerUpdate { defaultUpdate, ///Peer's network quality updates - networkQualityUpdated + networkQualityUpdated, + + ///Peer's hand raise updates + handRaiseUpdated } extension HMSPeerUpdateValues on HMSPeerUpdate { @@ -35,8 +38,8 @@ extension HMSPeerUpdateValues on HMSPeerUpdate { return HMSPeerUpdate.nameChanged; case 'networkQualityUpdated': return HMSPeerUpdate.networkQualityUpdated; - case 'defaultUpdate': - return HMSPeerUpdate.defaultUpdate; + case 'handRaiseUpdated': + return HMSPeerUpdate.handRaiseUpdated; default: return HMSPeerUpdate.defaultUpdate; } @@ -59,10 +62,14 @@ extension HMSPeerUpdateValues on HMSPeerUpdate { case HMSPeerUpdate.nameChanged: return 'nameChanged'; - case HMSPeerUpdate.defaultUpdate: - return 'defaultUpdate'; + case HMSPeerUpdate.handRaiseUpdated: + return 'handRaiseUpdated'; + case HMSPeerUpdate.networkQualityUpdated: return 'networkQualityUpdated'; + + default: + return 'defaultUpdate'; } } } diff --git a/packages/hmssdk_flutter/lib/src/enum/hms_update_listener_method.dart b/packages/hmssdk_flutter/lib/src/enum/hms_update_listener_method.dart index 1cac55dfb..e23c1603f 100644 --- a/packages/hmssdk_flutter/lib/src/enum/hms_update_listener_method.dart +++ b/packages/hmssdk_flutter/lib/src/enum/hms_update_listener_method.dart @@ -13,6 +13,7 @@ enum HMSUpdateListenerMethod { onRemovedFromRoom, onAudioDeviceChanged, onSessionStoreAvailable, + onPeerListUpdate, unknown } @@ -47,6 +48,8 @@ extension HMSUpdateListenerMethodValues on HMSUpdateListenerMethod { return HMSUpdateListenerMethod.onAudioDeviceChanged; case 'on_session_store_available': return HMSUpdateListenerMethod.onSessionStoreAvailable; + case 'on_peer_list_update': + return HMSUpdateListenerMethod.onPeerListUpdate; default: return HMSUpdateListenerMethod.unknown; } diff --git a/packages/hmssdk_flutter/lib/src/hmssdk.dart b/packages/hmssdk_flutter/lib/src/hmssdk.dart index 43819c728..fe28339d6 100644 --- a/packages/hmssdk_flutter/lib/src/hmssdk.dart +++ b/packages/hmssdk_flutter/lib/src/hmssdk.dart @@ -1380,6 +1380,100 @@ class HMSSDK { PlatformService.removeRTCStatsListener(listener); } + /// Method to get the peer list iterator in the room, only in case of large rooms + /// + /// Checkout the [HMSPeerListIterator] class for more details about the iterator + /// + /// **Parameters**: + /// + /// **peerListIteratorOptions** - [peerListIteratorOptions] is the options to get the iterator based on the filter set in [peerListIteratorOptions] parameter + Future getPeerListIterator( + {PeerListIteratorOptions? peerListIteratorOptions}) async { + var result = await PlatformService.invokeMethod( + PlatformMethod.getPeerListIterator, + arguments: { + "uid": DateTime.now().microsecondsSinceEpoch.toString(), + "peer_list_iterator_options": peerListIteratorOptions?.toMap() + }); + if (result["success"]) { + return HMSPeerListIterator.fromMap(result["data"]); + } else { + return HMSException.fromMap(result["data"]["error"]); + } + } + + ///Method to lower hand for local peer + /// + ///**Parameter**: + /// + ///**hmsActionResultListener** - [hmsActionResultListener] is a callback instance on which [HMSActionResultListener.onSuccess] and [HMSActionResultListener.onException] will be called. + /// + ///TODO: Add docs link + void lowerLocalPeerHand( + {HMSActionResultListener? hmsActionResultListener}) async { + final dynamic result = + await PlatformService.invokeMethod(PlatformMethod.lowerLocalPeerHand); + if (hmsActionResultListener != null) { + if (result == null) { + hmsActionResultListener.onSuccess( + methodType: HMSActionResultListenerMethod.lowerLocalPeerHand); + } else { + hmsActionResultListener.onException( + methodType: HMSActionResultListenerMethod.lowerLocalPeerHand, + hmsException: HMSException.fromMap(result["error"])); + } + } + } + + ///Method to raise hand for local peer + /// + ///**Parameter**: + /// + ///**hmsActionResultListener** - [hmsActionResultListener] is a callback instance on which [HMSActionResultListener.onSuccess] and [HMSActionResultListener.onException] will be called. + /// + ///TODO: Add docs link + void raiseLocalPeerHand( + {HMSActionResultListener? hmsActionResultListener}) async { + final dynamic result = + await PlatformService.invokeMethod(PlatformMethod.raiseLocalPeerHand); + if (hmsActionResultListener != null) { + if (result == null) { + hmsActionResultListener.onSuccess( + methodType: HMSActionResultListenerMethod.raiseLocalPeerHand); + } else { + hmsActionResultListener.onException( + methodType: HMSActionResultListenerMethod.raiseLocalPeerHand, + hmsException: HMSException.fromMap(result["error"])); + } + } + } + + ///Method to lower remote peer's hand + /// + ///**Parameter**: + /// + ///**forPeer** - [forPeer] the peer whose hand you wish to lower + ///**hmsActionResultListener** - [hmsActionResultListener] is a callback instance on which [HMSActionResultListener.onSuccess] and [HMSActionResultListener.onException] will be called. + /// + ///TODO: Add docs link + void lowerRemotePeerHand( + {required HMSPeer forPeer, + HMSActionResultListener? hmsActionResultListener}) async { + final dynamic result = await PlatformService.invokeMethod( + PlatformMethod.lowerRemotePeerHand, + arguments: {"peer_id": forPeer.peerId}); + if (hmsActionResultListener != null) { + if (result == null) { + hmsActionResultListener.onSuccess( + methodType: HMSActionResultListenerMethod.lowerRemotePeerHand); + } else { + hmsActionResultListener.onException( + methodType: HMSActionResultListenerMethod.lowerRemotePeerHand, + hmsException: HMSException.fromMap(result["error"])); + } + } + } + /// To modify local peer's audio & video tracks settings use the [hmsTrackSetting]. Only required for advanced use cases. HMSTrackSetting? hmsTrackSetting; diff --git a/packages/hmssdk_flutter/lib/src/model/hms_local_peer.dart b/packages/hmssdk_flutter/lib/src/model/hms_local_peer.dart index 9ee60cd33..0f7276c25 100644 --- a/packages/hmssdk_flutter/lib/src/model/hms_local_peer.dart +++ b/packages/hmssdk_flutter/lib/src/model/hms_local_peer.dart @@ -19,6 +19,7 @@ class HMSLocalPeer extends HMSPeer { required String name, required bool isLocal, required HMSRole role, + required bool isHandRaised, String? customerUserId, String? metadata, HMSLocalAudioTrack? audioTrack, @@ -31,6 +32,7 @@ class HMSLocalPeer extends HMSPeer { isLocal: isLocal, name: name, peerId: peerId, + isHandRaised: isHandRaised, customerUserId: customerUserId, metadata: metadata, role: role, @@ -46,6 +48,7 @@ class HMSLocalPeer extends HMSPeer { peerId: map['peer_id'], name: map['name'], isLocal: map['is_local'], + isHandRaised: map['is_hand_raised'], role: HMSRole.fromMap(map['role']), metadata: map['metadata'], customerUserId: map['customer_user_id'], diff --git a/packages/hmssdk_flutter/lib/src/model/hms_peer.dart b/packages/hmssdk_flutter/lib/src/model/hms_peer.dart index 9becd7d66..0fa5c7719 100644 --- a/packages/hmssdk_flutter/lib/src/model/hms_peer.dart +++ b/packages/hmssdk_flutter/lib/src/model/hms_peer.dart @@ -22,10 +22,8 @@ class HMSPeer { ///returns whether the peer is local or not. final bool isLocal; - @override - String toString() { - return 'HMSPeer{name: $name, isLocal: $isLocal}'; - } + ///returns whether peer's hand is raised or not + final bool isHandRaised; ///the current role of the peer in the room final HMSRole role; @@ -54,19 +52,21 @@ class HMSPeer { ///updatedAt is the time when the peer object was last updated final DateTime? updatedAt; - HMSPeer( - {required this.peerId, - required this.name, - required this.isLocal, - required this.role, - this.customerUserId, - this.metadata, - this.audioTrack, - this.videoTrack, - this.auxiliaryTracks, - this.networkQuality, - this.joinedAt, - this.updatedAt}); + HMSPeer({ + required this.peerId, + required this.name, + required this.isLocal, + required this.role, + required this.isHandRaised, + this.customerUserId, + this.metadata, + this.audioTrack, + this.videoTrack, + this.auxiliaryTracks, + this.networkQuality, + this.joinedAt, + this.updatedAt, + }); ///important to compare using [peerId] @override @@ -89,6 +89,7 @@ class HMSPeer { peerId: map['peer_id'], name: map['name'], isLocal: map['is_local'], + isHandRaised: map['is_hand_raised'], role: role, metadata: map['metadata'], customerUserId: map['customer_user_id'], @@ -108,6 +109,7 @@ class HMSPeer { peerId: map['peer_id'], name: map['name'], isLocal: map['is_local'], + isHandRaised: map['is_hand_raised'], role: role, metadata: map['metadata'], customerUserId: map['customer_user_id'], @@ -135,6 +137,11 @@ class HMSPeer { return peer; } + @override + String toString() { + return 'HMSPeer{name: $name, isLocal: $isLocal}'; + } + static List fromListOfMap(List peersMap) { List peers = peersMap.map((e) => HMSPeer.fromMap(e)).toList(); return peers; diff --git a/packages/hmssdk_flutter/lib/src/model/hms_peer_list_iterator.dart b/packages/hmssdk_flutter/lib/src/model/hms_peer_list_iterator.dart new file mode 100644 index 000000000..196e47568 --- /dev/null +++ b/packages/hmssdk_flutter/lib/src/model/hms_peer_list_iterator.dart @@ -0,0 +1,75 @@ +///Project imports +import 'package:hmssdk_flutter/hmssdk_flutter.dart'; +import 'package:hmssdk_flutter/src/service/platform_service.dart'; + +///100ms HMSPeerListIterator +/// +///[HMSPeerListIterator] contains the info about the peer list iterator +/// +///[limit] returns the maximum number of peers that can be returned in a single call to next. +/// +///[totalCount] returns the total number of peers present with the iterator based on the filters applied with [PeerListIteratorOptions] while making +///[getPeerListIterator] method call +/// +///[uid] returns the unique id of the peer list iterator +class HMSPeerListIterator { + ///maximum number of peers that can be returned in a single call to next + final int limit; + + ///total number of peers present with the iterator based on the filters applied with [PeerListIteratorOptions] while making + ///[getPeerListIterator] method call + int _totalCount; + + ///unique id of the peer list iterator + final String uid; + + int get totalCount => _totalCount; + + HMSPeerListIterator({ + required this.limit, + required this.uid, + required totalCount, + }) : _totalCount = totalCount; + + factory HMSPeerListIterator.fromMap(Map map) { + return HMSPeerListIterator( + limit: map['limit'] ?? 0, + totalCount: map['total_count'] ?? 0, + uid: map['uid'] ?? '', + ); + } + + ///This method is used to check if there are more peers to be returned + /// + ///returns [true] if there are more peers to be returned else [false] + Future hasNext() async { + var result = await PlatformService.invokeMethod( + PlatformMethod.peerListIteratorHasNext, + arguments: {"uid": uid}); + if (result["success"]) { + return result["data"]; + } else { + return HMSException.fromMap(result["data"]["error"]); + } + } + + ///This method is used to get the next set of peers + /// + ///returns a list of [HMSPeer] objects, the number of peers in a single call depends on the + ///[limit] parameter in [PeerListIteratorOptions] set during the [getPeerListIterator] method call + Future next() async { + var result = await PlatformService.invokeMethod( + PlatformMethod.peerListIteratorNext, + arguments: {"uid": uid}); + if (result["success"]) { + List peers = []; + _totalCount = result["data"]["total_count"]; + for (var peer in result["data"]["peers"]) { + peers.add(HMSPeer.fromMap(peer)); + } + return peers; + } else { + return HMSException.fromMap(result["data"]["error"]); + } + } +} diff --git a/packages/hmssdk_flutter/lib/src/model/hms_remote_peer.dart b/packages/hmssdk_flutter/lib/src/model/hms_remote_peer.dart index 09fcf540e..8f22b400b 100644 --- a/packages/hmssdk_flutter/lib/src/model/hms_remote_peer.dart +++ b/packages/hmssdk_flutter/lib/src/model/hms_remote_peer.dart @@ -24,6 +24,7 @@ class HMSRemotePeer extends HMSPeer { HMSRemotePeer( {required String peerId, required String name, + required bool isHandRaised, bool isLocal = false, required HMSRole role, String? customerUserId, @@ -38,6 +39,7 @@ class HMSRemotePeer extends HMSPeer { peerId: peerId, name: name, isLocal: isLocal, + isHandRaised: isHandRaised, role: role, customerUserId: customerUserId, metadata: metadata, @@ -66,6 +68,7 @@ class HMSRemotePeer extends HMSPeer { peerId: map['peer_id'], name: map['name'], isLocal: map['is_local'], + isHandRaised: map['is_hand_raised'], role: role, metadata: map['metadata'], customerUserId: map['customer_user_id'], diff --git a/packages/hmssdk_flutter/lib/src/model/hms_update_listener.dart b/packages/hmssdk_flutter/lib/src/model/hms_update_listener.dart index 9baf13a25..1e44ec9a4 100644 --- a/packages/hmssdk_flutter/lib/src/model/hms_update_listener.dart +++ b/packages/hmssdk_flutter/lib/src/model/hms_update_listener.dart @@ -105,4 +105,10 @@ abstract class HMSUpdateListener { /// - Parameters: /// - hmsSessionStore: An instance of HMSSessionStore which will be used to call session metadata methods void onSessionStoreAvailable({HMSSessionStore? hmsSessionStore}); + + ///Upon joining a room with existing peers, onPeerListUpdated will be called with the list of peers present + ///in the room instead of getting onPeerUpdate for each peer in the room. + ///Subsequent peer joins/leaves would be notified via both onPeerUpdate and onPeerListUpdated + void onPeerListUpdate( + {required List addedPeers, required List removedPeers}); } diff --git a/packages/hmssdk_flutter/lib/src/model/peer_list_iterator_options.dart b/packages/hmssdk_flutter/lib/src/model/peer_list_iterator_options.dart new file mode 100644 index 000000000..ed14bec77 --- /dev/null +++ b/packages/hmssdk_flutter/lib/src/model/peer_list_iterator_options.dart @@ -0,0 +1,31 @@ +/// This class is used to set the options for the [getPeerListIterator] method +/// +/// [byPeerIds] list of peerIds for which peer list is required +/// +/// [byRoleName] name of the role for which peer list is required +/// +/// [limit] maximum number of peers to be returned +class PeerListIteratorOptions { + /// list of peerIds for which peer list is required + final List? byPeerIds; + + /// name of the role for which peer list is required + final String? byRoleName; + + /// maximum number of peers to be returned + final int limit; + + PeerListIteratorOptions({ + required this.limit, + this.byPeerIds, + this.byRoleName, + }); + + Map toMap() { + return { + 'by_peer_ids': this.byPeerIds, + 'by_role_name': this.byRoleName, + 'limit': this.limit, + }; + } +} diff --git a/packages/hmssdk_flutter/lib/src/service/platform_service.dart b/packages/hmssdk_flutter/lib/src/service/platform_service.dart index f25b6a755..509cf3fb3 100644 --- a/packages/hmssdk_flutter/lib/src/service/platform_service.dart +++ b/packages/hmssdk_flutter/lib/src/service/platform_service.dart @@ -286,6 +286,26 @@ class PlatformService { notifyUpdateListeners(method, {}); break; + case HMSUpdateListenerMethod.onPeerListUpdate: + List addedPeers = []; + List removedPeers = []; + + if (data.containsKey("added_peers") && data["added_peers"] != null) { + for (var peer in data["added_peers"]) { + addedPeers.add(HMSPeer.fromMap(peer)); + } + } + + if (data.containsKey("removed_peers") && + data["removed_peers"] != null) { + for (var peer in data["removed_peers"]) { + removedPeers.add(HMSPeer.fromMap(peer)); + } + } + + notifyUpdateListeners(method, + {"added_peers": addedPeers, "removed_peers": removedPeers}); + break; case HMSUpdateListenerMethod.unknown: break; } @@ -661,6 +681,14 @@ class PlatformService { e.onSessionStoreAvailable(hmsSessionStore: HMSSessionStore())); break; + case HMSUpdateListenerMethod.onPeerListUpdate: + updateListeners.forEach((e) { + e.onPeerListUpdate( + addedPeers: arguments["added_peers"], + removedPeers: arguments["removed_peers"]); + }); + break; + case HMSUpdateListenerMethod.unknown: break; } diff --git a/packages/hmssdk_flutter/pubspec.yaml b/packages/hmssdk_flutter/pubspec.yaml index ceac0638c..f9515341f 100644 --- a/packages/hmssdk_flutter/pubspec.yaml +++ b/packages/hmssdk_flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: hmssdk_flutter description: Add Real Time Audio & Video calls, Interactive Live Streaming & Recording, Chat, HLS, RTMP, PiP, CallKit, VoIP, Video conferencing, Stream Player & WebRTC-based communications API -version: 1.8.0 +version: 1.9.0 homepage: https://www.100ms.live/ repository: https://github.com/100mslive/100ms-flutter issue_tracker: https://github.com/100mslive/100ms-flutter/issues diff --git a/sample apps/bloc/pubspec.lock b/sample apps/bloc/pubspec.lock index 3d387b3cc..0353333d2 100644 --- a/sample apps/bloc/pubspec.lock +++ b/sample apps/bloc/pubspec.lock @@ -45,10 +45,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.17.2" cupertino_icons: dependency: "direct main" description: @@ -260,18 +260,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" string_scanner: dependency: transitive description: @@ -292,10 +292,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.6.0" typed_data: dependency: transitive description: diff --git a/sample apps/flutter-audio-room-quickstart/pubspec.lock b/sample apps/flutter-audio-room-quickstart/pubspec.lock index 4cee29067..b9ad621c8 100644 --- a/sample apps/flutter-audio-room-quickstart/pubspec.lock +++ b/sample apps/flutter-audio-room-quickstart/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.17.2" cupertino_icons: dependency: "direct main" description: @@ -188,18 +188,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" string_scanner: dependency: transitive description: @@ -220,10 +220,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.6.0" vector_math: dependency: transitive description: diff --git a/sample apps/flutter-hls-quickstart/pubspec.lock b/sample apps/flutter-hls-quickstart/pubspec.lock index 011c49784..3cc98b632 100644 --- a/sample apps/flutter-hls-quickstart/pubspec.lock +++ b/sample apps/flutter-hls-quickstart/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.17.2" csslib: dependency: transitive description: @@ -209,18 +209,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" string_scanner: dependency: transitive description: @@ -241,10 +241,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.6.0" vector_math: dependency: transitive description: diff --git a/sample apps/flutter-quickstart-app/pubspec.lock b/sample apps/flutter-quickstart-app/pubspec.lock index e3a87a5f0..8f4622fce 100644 --- a/sample apps/flutter-quickstart-app/pubspec.lock +++ b/sample apps/flutter-quickstart-app/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.17.2" cupertino_icons: dependency: "direct main" description: @@ -188,18 +188,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" string_scanner: dependency: transitive description: @@ -220,10 +220,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.6.0" vector_math: dependency: transitive description: diff --git a/sample apps/getx/pubspec.lock b/sample apps/getx/pubspec.lock index 1137ef86a..d775b7e51 100644 --- a/sample apps/getx/pubspec.lock +++ b/sample apps/getx/pubspec.lock @@ -45,10 +45,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.17.2" cupertino_icons: dependency: "direct main" description: @@ -228,18 +228,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" string_scanner: dependency: transitive description: @@ -260,10 +260,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.6.0" typed_data: dependency: transitive description: diff --git a/sample apps/hms-callkit-app/pubspec.lock b/sample apps/hms-callkit-app/pubspec.lock index 47c7c686a..181a17dfa 100644 --- a/sample apps/hms-callkit-app/pubspec.lock +++ b/sample apps/hms-callkit-app/pubspec.lock @@ -69,10 +69,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.17.2" cross_file: dependency: transitive description: @@ -441,18 +441,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" string_scanner: dependency: transitive description: @@ -473,10 +473,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.6.0" typed_data: dependency: transitive description: diff --git a/sample apps/mobx/pubspec.lock b/sample apps/mobx/pubspec.lock index 812a91ed9..2fa543676 100644 --- a/sample apps/mobx/pubspec.lock +++ b/sample apps/mobx/pubspec.lock @@ -141,10 +141,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.17.2" convert: dependency: transitive description: @@ -516,18 +516,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" stream_transform: dependency: transitive description: @@ -556,10 +556,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.6.0" timing: dependency: transitive description: diff --git a/sample apps/riverpod/pubspec.lock b/sample apps/riverpod/pubspec.lock index e7ee6c55c..ecdac52f5 100644 --- a/sample apps/riverpod/pubspec.lock +++ b/sample apps/riverpod/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.17.2" cupertino_icons: dependency: "direct main" description: @@ -220,10 +220,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.11.0" state_notifier: dependency: transitive description: @@ -236,10 +236,10 @@ packages: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" string_scanner: dependency: transitive description: @@ -260,10 +260,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.6.0" typed_data: dependency: transitive description: