From 19b1de73ee5cf98f289f83a97fc7aa937d23ab85 Mon Sep 17 00:00:00 2001 From: Decoder07 Date: Sat, 13 Jan 2024 23:41:26 +0530 Subject: [PATCH 1/3] Added auto hide functionality for header and footer --- ...ting_navigation_visibility_controller.dart | 26 + .../lib/src/meeting/meeting_page.dart | 803 +++++++++--------- .../meeting_modes/one_to_one_mode.dart | 2 - 3 files changed, 439 insertions(+), 392 deletions(-) diff --git a/packages/hms_room_kit/lib/src/meeting/meeting_navigation_visibility_controller.dart b/packages/hms_room_kit/lib/src/meeting/meeting_navigation_visibility_controller.dart index 194744c18..c0b5a1157 100644 --- a/packages/hms_room_kit/lib/src/meeting/meeting_navigation_visibility_controller.dart +++ b/packages/hms_room_kit/lib/src/meeting/meeting_navigation_visibility_controller.dart @@ -1,10 +1,36 @@ +///Dart imports +import 'dart:async'; + +///Package imports import 'package:flutter/widgets.dart'; class MeetingNavigationVisibilityController extends ChangeNotifier { bool showControls = true; + ///This variable stores whether the timer is active or not + /// + ///This is done to avoid multiple timers running at the same time + bool _isTimerActive = false; + + ///This method toggles the visibility of the buttons void toggleControlsVisibility() { showControls = !showControls; + + ///If the controls are now visible and + ///If the timer is not active, we start the timer + if (showControls && !_isTimerActive) { + startTimerToHideButtons(); + } notifyListeners(); } + + ///This method starts a timer for 5 seconds and then hides the buttons + void startTimerToHideButtons() { + _isTimerActive = true; + Timer(const Duration(seconds: 5), () { + showControls = false; + _isTimerActive = false; + notifyListeners(); + }); + } } 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 27bf239bc..b8e2ec658 100644 --- a/packages/hms_room_kit/lib/src/meeting/meeting_page.dart +++ b/packages/hms_room_kit/lib/src/meeting/meeting_page.dart @@ -63,6 +63,7 @@ class _MeetingPageState extends State { checkAudioState(); _enableForegroundService(); _visibilityController = MeetingNavigationVisibilityController(); + _visibilityController!.startTimerToHideButtons(); } void checkAudioState() async { @@ -183,408 +184,430 @@ class _MeetingPageState extends State { .height - MediaQuery.of(context).padding.top - MediaQuery.of(context).padding.bottom, - child: Stack( - children: [ - Selector< - MeetingStore, - Tuple6< - List, - bool, - int, - int, - MeetingMode, - PeerTrackNode?>>( - selector: (_, meetingStore) => Tuple6( - meetingStore.peerTracks, - meetingStore.isHLSLink, - meetingStore - .peerTracks.length, - meetingStore.screenShareCount, - meetingStore.meetingMode, - meetingStore - .peerTracks.isNotEmpty - ? meetingStore.peerTracks[ - meetingStore - .screenShareCount] - : null), - builder: (_, data, __) { - if (data.item3 == 0) { - return Center( - child: Column( - mainAxisSize: - MainAxisSize.min, - children: [ - CircularProgressIndicator( - strokeWidth: 2, - color: HMSThemeColors - .primaryDefault, - ), - const SizedBox( - height: 10, - ), - if (context - .read() - .peers - .isNotEmpty) - HMSTitleText( - text: - "Please wait for broadcaster to join", - textColor: - HMSThemeColors - .onSurfaceHighEmphasis) - ], - )); - } - return Selector< - MeetingStore, - Tuple2>( - selector: (_, - meetingStore) => - Tuple2( - meetingStore - .meetingMode, - meetingStore - .localPeer), - builder: (_, modeData, __) { - Size size = Size( - MediaQuery.of(context) - .size - .width, - MediaQuery.of(context) - .size - .height - - 122 - - MediaQuery.of( - context) - .padding - .bottom - - MediaQuery.of( - context) - .padding - .top); - return Positioned( - top: 55, - left: 0, - right: 0, - bottom: 68, - /*** - * The logic for gridview is as follows: - * - Default mode is Active Speaker mode which displays only 4 tiles on screen without scroll and updates the tile according to who is currently speaking - * - If there are only 2 peers in the room in which one is local peer then automatically the mode is switched to oneToOne mode - * - As the peer count increases the mode is switched back to active speaker view in case of default mode - * - Remaining as the mode from bottom sheet is selected corresponding grid layout is rendered - */ - child: - GestureDetector( - onTap: () => - _visibilityController - ?.toggleControlsVisibility(), - child: (modeData - .item1 == - MeetingMode - .activeSpeakerWithInset && - (context.read().localPeer?.audioTrack != - null || - context.read().localPeer?.videoTrack != - null)) - ? OneToOneMode( - bottomMargin: - 225, - peerTracks: - data - .item1, - screenShareCount: - data - .item4, - context: - context, - size: size) - : CustomOneToOneGrid( - isLocalInsetPresent: - false, - peerTracks: - data.item1, - ), - )); - }); - }), - Column( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Padding( - padding: - const EdgeInsets.only( - left: 15, - right: 15, - top: 5, - bottom: 2), - child: ChangeNotifierProvider.value( - value: - _visibilityController, - child: - const MeetingHeader())), - Padding( - padding: - const EdgeInsets.only( - bottom: 8.0), - child: ChangeNotifierProvider.value( - value: - _visibilityController, - child: - const MeetingBottomNavigationBar())), - ], - ), + child: ChangeNotifierProvider.value( + value: _visibilityController, + child: Stack( + children: [ + Selector< + MeetingStore, + Tuple6< + List, + bool, + int, + int, + MeetingMode, + PeerTrackNode?>>( + selector: (_, meetingStore) => + Tuple6( + meetingStore.peerTracks, + meetingStore.isHLSLink, + meetingStore + .peerTracks.length, + meetingStore + .screenShareCount, + meetingStore + .meetingMode, + meetingStore.peerTracks + .isNotEmpty + ? meetingStore + .peerTracks[ + meetingStore + .screenShareCount] + : null), + builder: (_, data, __) { + if (data.item3 == 0) { + return Center( + child: Column( + mainAxisSize: + MainAxisSize.min, + children: [ + CircularProgressIndicator( + strokeWidth: 2, + color: HMSThemeColors + .primaryDefault, + ), + const SizedBox( + height: 10, + ), + if (context + .read< + MeetingStore>() + .peers + .isNotEmpty) + HMSTitleText( + text: + "Please wait for broadcaster to join", + textColor: + HMSThemeColors + .onSurfaceHighEmphasis) + ], + )); + } + return Selector< + MeetingStore, + Tuple2>( + selector: (_, + meetingStore) => + Tuple2( + meetingStore + .meetingMode, + meetingStore + .localPeer), + builder: + (_, modeData, __) { - ///This gets rendered when the previewForRole method is called - ///This is used to show the preview for role component - Selector< - MeetingStore, - Tuple3< - HMSLocalVideoTrack?, - HMSLocalAudioTrack?, - HMSRoleChangeRequest?>>( - selector: (_, meetingStore) => Tuple3( - meetingStore - .previewForRoleVideoTrack, - meetingStore - .previewForRoleAudioTrack, - meetingStore - .currentRoleChangeRequest), - builder: (_, previewForRoleTracks, - __) { - ///If the preview for role tracks are not null - ///or role change request is not null - ///we show the preview for role component - ///else we show and empty Container - if (previewForRoleTracks.item1 != null || - previewForRoleTracks - .item2 != - null || - previewForRoleTracks - .item3 != - null) { - WidgetsBinding.instance - .addPostFrameCallback( - (timeStamp) { - ///For preview for role component we use the [showGeneralDialog] - showGeneralDialog( - context: context, - pageBuilder: - (ctx, _, __) { - return ListenableProvider - .value( - value: context.read< - MeetingStore>(), - child: Scaffold( - body: SafeArea( + ///This renders the video grid based on whether the controls are visible or not + return Selector< + MeetingNavigationVisibilityController, + bool>( + selector: (_, + meetingNavigationVisibilityController) => + meetingNavigationVisibilityController + .showControls, + builder: (_, + showControls, + __) { + return Center( child: - Container( - color: HMSThemeColors - .backgroundDim, - height: MediaQuery.of( - context) - .size - .height, - width: MediaQuery.of( - context) - .size - .width, + AnimatedContainer( + duration: const Duration( + milliseconds: + 200), + + ///If the controls are visible we reduce the + ///height of video grid by 140 else it covers the whole screen + height: showControls + ? MediaQuery.of(context).size.height - + MediaQuery.of(context) + .padding + .top - + MediaQuery.of(context) + .padding + .bottom - + 140 + : MediaQuery.of(context).size.height - + MediaQuery.of(context) + .padding + .top - + MediaQuery.of(context) + .padding + .bottom - + 20, + child: + GestureDetector( + onTap: () => + _visibilityController + ?.toggleControlsVisibility(), + child: (modeData.item1 == MeetingMode.activeSpeakerWithInset && + (context.read().localPeer?.audioTrack != null || + context.read().localPeer?.videoTrack != null)) + ? OneToOneMode( + + ///This is done to keep the inset tile + ///at correct position when controls are hidden + bottomMargin: showControls + ? 250 + : 130, + peerTracks: + data.item1, + screenShareCount: + data.item4, + context: + context, + ) + : CustomOneToOneGrid( + isLocalInsetPresent: + false, + peerTracks: + data.item1, + ), + ), + ), + ); + }); + }); + }), + Column( + mainAxisAlignment: + MainAxisAlignment + .spaceBetween, + children: [ + Padding( + padding: + const EdgeInsets.only( + left: 15, + right: 15, + top: 5, + bottom: 2), + child: ChangeNotifierProvider.value( + value: + _visibilityController, + child: + const MeetingHeader())), + Padding( + padding: + const EdgeInsets.only( + bottom: 8.0), + child: ChangeNotifierProvider.value( + value: + _visibilityController, + child: + const MeetingBottomNavigationBar())), + ], + ), + + ///This gets rendered when the previewForRole method is called + ///This is used to show the preview for role component + Selector< + MeetingStore, + Tuple3< + HMSLocalVideoTrack?, + HMSLocalAudioTrack?, + HMSRoleChangeRequest?>>( + selector: (_, meetingStore) => Tuple3( + meetingStore + .previewForRoleVideoTrack, + meetingStore + .previewForRoleAudioTrack, + meetingStore + .currentRoleChangeRequest), + builder: (_, + previewForRoleTracks, __) { + ///If the preview for role tracks are not null + ///or role change request is not null + ///we show the preview for role component + ///else we show and empty Container + if (previewForRoleTracks.item1 != null || + previewForRoleTracks + .item2 != + null || + previewForRoleTracks + .item3 != + null) { + WidgetsBinding.instance + .addPostFrameCallback( + (timeStamp) { + ///For preview for role component we use the [showGeneralDialog] + showGeneralDialog( + context: context, + pageBuilder: + (ctx, _, __) { + return ListenableProvider + .value( + value: context.read< + MeetingStore>(), + child: Scaffold( + body: SafeArea( + child: + Container( + color: HMSThemeColors + .backgroundDim, + height: MediaQuery.of( + context) + .size + .height, + width: MediaQuery.of( + context) + .size + .width, - ///We render the preview for role component - child: Stack( - children: [ - ///This renders the video component - ///[HMSVideoView] is only rendered if video is ON - /// - ///else we render the [HMSCircularAvatar] - Selector< - MeetingStore, - bool>( - selector: (_, meetingStore) => meetingStore - .isVideoOn, - builder: (_, - isVideoOn, - __) { - return Container( - height: MediaQuery.of(context).size.height, - width: MediaQuery.of(context).size.width, - color: HMSThemeColors.backgroundDim, - child: (isVideoOn && previewForRoleTracks.item1 != null) - ? Center( - child: HMSTextureView( - scaleType: ScaleType.SCALE_ASPECT_FILL, - track: previewForRoleTracks.item1!, - setMirror: true, + ///We render the preview for role component + child: + Stack( + children: [ + ///This renders the video component + ///[HMSVideoView] is only rendered if video is ON + /// + ///else we render the [HMSCircularAvatar] + Selector( + selector: (_, meetingStore) => meetingStore + .isVideoOn, + builder: (_, + isVideoOn, + __) { + return Container( + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + color: HMSThemeColors.backgroundDim, + child: (isVideoOn && previewForRoleTracks.item1 != null) + ? Center( + child: HMSTextureView( + scaleType: ScaleType.SCALE_ASPECT_FILL, + track: previewForRoleTracks.item1!, + setMirror: true, + ), + ) + : Center( + child: HMSCircularAvatar(name: context.read().localPeer?.name ?? ""), ), - ) - : Center( - child: HMSCircularAvatar(name: context.read().localPeer?.name ?? ""), - ), - ); - }), + ); + }), - ///This renders the preview for role header - const PreviewForRoleHeader(), + ///This renders the preview for role header + const PreviewForRoleHeader(), - ///This renders the preview for role bottom sheet - PreviewForRoleBottomSheet( - meetingStore: - context.read(), - roleChangeRequest: context - .read() - .currentRoleChangeRequest, - ) - ], + ///This renders the preview for role bottom sheet + PreviewForRoleBottomSheet( + meetingStore: + context.read(), + roleChangeRequest: context + .read() + .currentRoleChangeRequest, + ) + ], + ), ), ), ), - ), - ); - }); - }); - } - return Container(); - }), + ); + }); + }); + } + return Container(); + }), - Selector( - selector: (_, meetingStore) => - meetingStore - .hmsTrackChangeRequest, - builder: (_, - hmsTrackChangeRequest, __) { - if (hmsTrackChangeRequest != - null) { - HMSTrackChangeRequest - currentRequest = - hmsTrackChangeRequest; - context - .read() - .hmsTrackChangeRequest = - null; - WidgetsBinding.instance - .addPostFrameCallback( - (_) { - UtilityComponents - .showTrackChangeDialog( - context, - currentRequest); - }); - } - return const SizedBox(); - }), - Selector( - selector: (_, meetingStore) => - meetingStore - .showAudioDeviceChangePopup, - builder: (_, - showAudioDeviceChangePopup, - __) { - if (showAudioDeviceChangePopup) { - context - .read() - .showAudioDeviceChangePopup = - false; - WidgetsBinding.instance - .addPostFrameCallback( - (_) { - showDialog( - context: context, - builder: (_) => - AudioDeviceChangeDialog( - currentAudioDevice: context - .read< - MeetingStore>() - .currentAudioOutputDevice!, - audioDevicesList: context - .read< - MeetingStore>() - .availableAudioOutputDevices, - changeAudioDevice: - (audioDevice) { - context + Selector( + selector: (_, meetingStore) => + meetingStore + .hmsTrackChangeRequest, + builder: (_, + hmsTrackChangeRequest, __) { + if (hmsTrackChangeRequest != + null) { + HMSTrackChangeRequest + currentRequest = + hmsTrackChangeRequest; + context + .read() + .hmsTrackChangeRequest = + null; + WidgetsBinding.instance + .addPostFrameCallback( + (_) { + UtilityComponents + .showTrackChangeDialog( + context, + currentRequest); + }); + } + return const SizedBox(); + }), + Selector( + selector: (_, meetingStore) => + meetingStore + .showAudioDeviceChangePopup, + builder: (_, + showAudioDeviceChangePopup, + __) { + if (showAudioDeviceChangePopup) { + context + .read() + .showAudioDeviceChangePopup = + false; + WidgetsBinding.instance + .addPostFrameCallback( + (_) { + showDialog( + context: context, + builder: (_) => + AudioDeviceChangeDialog( + currentAudioDevice: context .read< MeetingStore>() - .switchAudioOutput( - audioDevice: - audioDevice); - }, - )); - }); - } - return const SizedBox(); - }), - Selector< - MeetingStore, - Tuple2, - int>>( - selector: (_, meetingStore) => - Tuple2( - meetingStore.toasts, - meetingStore - .toasts.length), - builder: (_, toastsItem, __) { - if (toastsItem.item1.isEmpty) { - return Container(); - } - return Stack( - children: toastsItem.item1 - .sublist( - 0, - min( - 3, - toastsItem - .item2)) - .asMap() - .entries - .map((toasts) { - return Positioned( - bottom: - 48.0 + 8 * toasts.key, - left: 5, - child: Transform.scale( - scale: _getToastScale( - toasts.key, - toastsItem.item2), - child: getToast( - toasts.value, - toasts.key, - toastsItem.item2), - )); - }).toList()); - }), - Selector( - selector: (_, meetingStore) => - meetingStore.reconnecting, - builder: (_, reconnecting, __) { - if (reconnecting) { - return UtilityComponents - .showReconnectingDialog( - context); - } - return const SizedBox(); - }), - if (failureErrors.item2 != null) - if (failureErrors.item2?.code?.errorCode == 1003 || - failureErrors.item2?.code - ?.errorCode == - 2000 || - failureErrors.item2?.code - ?.errorCode == - 4005 || - failureErrors.item2?.code - ?.errorCode == - 424) - UtilityComponents - .showFailureError( - failureErrors.item2!, - context, - () => context - .read() - .leave()) - ], + .currentAudioOutputDevice!, + audioDevicesList: context + .read< + MeetingStore>() + .availableAudioOutputDevices, + changeAudioDevice: + (audioDevice) { + context + .read< + MeetingStore>() + .switchAudioOutput( + audioDevice: + audioDevice); + }, + )); + }); + } + return const SizedBox(); + }), + Selector< + MeetingStore, + Tuple2, + int>>( + selector: (_, meetingStore) => + Tuple2( + meetingStore.toasts, + meetingStore + .toasts.length), + builder: (_, toastsItem, __) { + if (toastsItem + .item1.isEmpty) { + return Container(); + } + return Stack( + children: toastsItem.item1 + .sublist( + 0, + min( + 3, + toastsItem + .item2)) + .asMap() + .entries + .map((toasts) { + return Positioned( + bottom: 48.0 + + 8 * toasts.key, + left: 5, + child: Transform.scale( + scale: _getToastScale( + toasts.key, + toastsItem.item2), + child: getToast( + toasts.value, + toasts.key, + toastsItem.item2), + )); + }).toList()); + }), + Selector( + selector: (_, meetingStore) => + meetingStore.reconnecting, + builder: (_, reconnecting, __) { + if (reconnecting) { + return UtilityComponents + .showReconnectingDialog( + context); + } + return const SizedBox(); + }), + if (failureErrors.item2 != null) + if (failureErrors.item2?.code?.errorCode == 1003 || + failureErrors.item2?.code + ?.errorCode == + 2000 || + failureErrors.item2?.code + ?.errorCode == + 4005 || + failureErrors.item2?.code + ?.errorCode == + 424) + UtilityComponents + .showFailureError( + failureErrors.item2!, + context, + () => context + .read< + MeetingStore>() + .leave()) + ], + ), ), ), ), diff --git a/packages/hms_room_kit/lib/src/widgets/meeting_modes/one_to_one_mode.dart b/packages/hms_room_kit/lib/src/widgets/meeting_modes/one_to_one_mode.dart index 0c3f8b289..451a892f0 100644 --- a/packages/hms_room_kit/lib/src/widgets/meeting_modes/one_to_one_mode.dart +++ b/packages/hms_room_kit/lib/src/widgets/meeting_modes/one_to_one_mode.dart @@ -17,14 +17,12 @@ import 'package:hms_room_kit/src/widgets/peer_widgets/inset_collapsed_view.dart' class OneToOneMode extends StatefulWidget { final List peerTracks; final BuildContext context; - final Size size; final int screenShareCount; final double bottomMargin; const OneToOneMode( {Key? key, required this.peerTracks, required this.context, - required this.size, required this.screenShareCount, this.bottomMargin = 272}) : super(key: key); From 1f340cc40434f26ef8552a834e0b536dd673680b Mon Sep 17 00:00:00 2001 From: Decoder07 Date: Sat, 13 Jan 2024 18:14:15 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=A4=96=20Automated=20Format=20and=20F?= =?UTF-8?q?ix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/src/meeting/meeting_page.dart | 4 +-- packages/hmssdk_flutter/example/pubspec.lock | 26 +++++++++---------- packages/hmssdk_flutter/pubspec.lock | 26 +++++++++---------- 3 files changed, 27 insertions(+), 29 deletions(-) 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 b8e2ec658..eec890443 100644 --- a/packages/hms_room_kit/lib/src/meeting/meeting_page.dart +++ b/packages/hms_room_kit/lib/src/meeting/meeting_page.dart @@ -256,7 +256,6 @@ class _MeetingPageState extends State { .localPeer), builder: (_, modeData, __) { - ///This renders the video grid based on whether the controls are visible or not return Selector< MeetingNavigationVisibilityController, @@ -275,7 +274,7 @@ class _MeetingPageState extends State { milliseconds: 200), - ///If the controls are visible we reduce the + ///If the controls are visible we reduce the ///height of video grid by 140 else it covers the whole screen height: showControls ? MediaQuery.of(context).size.height - @@ -303,7 +302,6 @@ class _MeetingPageState extends State { (context.read().localPeer?.audioTrack != null || context.read().localPeer?.videoTrack != null)) ? OneToOneMode( - ///This is done to keep the inset tile ///at correct position when controls are hidden bottomMargin: showControls diff --git a/packages/hmssdk_flutter/example/pubspec.lock b/packages/hmssdk_flutter/example/pubspec.lock index cb0c5b7b7..098334077 100644 --- a/packages/hmssdk_flutter/example/pubspec.lock +++ b/packages/hmssdk_flutter/example/pubspec.lock @@ -69,10 +69,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" convert: dependency: transitive description: @@ -362,10 +362,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" mime: dependency: transitive description: @@ -647,18 +647,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -679,10 +679,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" tuple: dependency: transitive description: @@ -839,10 +839,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" win32: dependency: transitive description: @@ -868,5 +868,5 @@ packages: source: hosted version: "6.3.0" sdks: - dart: ">=3.1.0 <4.0.0" + dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=3.13.0" diff --git a/packages/hmssdk_flutter/pubspec.lock b/packages/hmssdk_flutter/pubspec.lock index c3cc2c33e..53939cd59 100644 --- a/packages/hmssdk_flutter/pubspec.lock +++ b/packages/hmssdk_flutter/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" fake_async: dependency: transitive description: @@ -79,10 +79,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" path: dependency: transitive description: @@ -108,18 +108,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -140,10 +140,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" vector_math: dependency: transitive description: @@ -156,10 +156,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=2.10.0" From 788dfc9f85b4735d97db98644f10da7e2f5e09e6 Mon Sep 17 00:00:00 2001 From: Decoder07 Date: Sun, 14 Jan 2024 00:00:26 +0530 Subject: [PATCH 3/3] Fixed conflicts --- .../src/meeting/meeting_grid_component.dart | 124 ++++ .../lib/src/meeting/meeting_page.dart | 688 +++++++----------- 2 files changed, 397 insertions(+), 415 deletions(-) create mode 100644 packages/hms_room_kit/lib/src/meeting/meeting_grid_component.dart diff --git a/packages/hms_room_kit/lib/src/meeting/meeting_grid_component.dart b/packages/hms_room_kit/lib/src/meeting/meeting_grid_component.dart new file mode 100644 index 000000000..9829b788d --- /dev/null +++ b/packages/hms_room_kit/lib/src/meeting/meeting_grid_component.dart @@ -0,0 +1,124 @@ +///Dart imports +import 'dart:io'; + +///Package imports +import 'package:flutter/material.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/enums/meeting_mode.dart'; +import 'package:hms_room_kit/src/meeting/meeting_navigation_visibility_controller.dart'; +import 'package:hms_room_kit/src/meeting/meeting_store.dart'; +import 'package:hms_room_kit/src/model/peer_track_node.dart'; +import 'package:hms_room_kit/src/widgets/meeting_modes/custom_one_to_one_grid.dart'; +import 'package:hms_room_kit/src/widgets/meeting_modes/one_to_one_mode.dart'; + +///[MeetingGridComponent] is a component that is used to show the video grid +class MeetingGridComponent extends StatelessWidget { + final MeetingNavigationVisibilityController? visibilityController; + + const MeetingGridComponent({super.key, required this.visibilityController}); + + @override + Widget build(BuildContext context) { + return Selector< + MeetingStore, + Tuple6, bool, int, int, MeetingMode, + PeerTrackNode?>>( + selector: (_, meetingStore) => Tuple6( + meetingStore.peerTracks, + meetingStore.isHLSLink, + meetingStore.peerTracks.length, + meetingStore.screenShareCount, + meetingStore.meetingMode, + meetingStore.peerTracks.isNotEmpty + ? meetingStore.peerTracks[meetingStore.screenShareCount] + : null), + builder: (_, data, __) { + if (data.item3 == 0) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + CircularProgressIndicator( + strokeWidth: 2, + color: HMSThemeColors.primaryDefault, + ), + const SizedBox( + height: 10, + ), + if (context.read().peers.isNotEmpty) + HMSTitleText( + text: "Please wait for broadcaster to join", + textColor: HMSThemeColors.onSurfaceHighEmphasis) + ], + )); + } + return Selector>( + selector: (_, meetingStore) => + Tuple2(meetingStore.meetingMode, meetingStore.localPeer), + builder: (_, modeData, __) { + ///This renders the video grid based on whether the controls are visible or not + return Selector( + selector: (_, meetingNavigationVisibilityController) => + meetingNavigationVisibilityController.showControls, + builder: (_, showControls, __) { + return Center( + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + + ///If the controls are visible we reduce the + ///height of video grid by 140 else it covers the whole screen + /// + ///Here we also check for the platform and reduce the height accordingly + height: showControls + ? MediaQuery.of(context).size.height - + MediaQuery.of(context).padding.top - + MediaQuery.of(context).padding.bottom - + (Platform.isAndroid + ? 160 + : Platform.isIOS + ? 230 + : 160) + : MediaQuery.of(context).size.height - + MediaQuery.of(context).padding.top - + MediaQuery.of(context).padding.bottom - + 20, + child: GestureDetector( + onTap: () => visibilityController + ?.toggleControlsVisibility(), + child: (modeData.item1 == + MeetingMode.activeSpeakerWithInset && + (context + .read() + .localPeer + ?.audioTrack != + null || + context + .read() + .localPeer + ?.videoTrack != + null)) + ? OneToOneMode( + ///This is done to keep the inset tile + ///at correct position when controls are hidden + bottomMargin: showControls ? 250 : 130, + peerTracks: data.item1, + screenShareCount: data.item4, + context: context, + ) + : CustomOneToOneGrid( + isLocalInsetPresent: false, + peerTracks: data.item1, + ), + ), + ), + ); + }); + }); + }); + } +} 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 eec890443..2410aa88e 100644 --- a/packages/hms_room_kit/lib/src/meeting/meeting_page.dart +++ b/packages/hms_room_kit/lib/src/meeting/meeting_page.dart @@ -11,10 +11,7 @@ import 'package:hmssdk_flutter/hmssdk_flutter.dart'; ///Project imports import 'package:hms_room_kit/hms_room_kit.dart'; -import 'package:hms_room_kit/src/enums/meeting_mode.dart'; -import 'package:hms_room_kit/src/model/peer_track_node.dart'; -import 'package:hms_room_kit/src/widgets/meeting_modes/custom_one_to_one_grid.dart'; -import 'package:hms_room_kit/src/widgets/meeting_modes/one_to_one_mode.dart'; +import 'package:hms_room_kit/src/meeting/meeting_grid_component.dart'; import 'package:hms_room_kit/src/meeting/meeting_navigation_visibility_controller.dart'; import 'package:hms_room_kit/src/meeting/meeting_bottom_navigation_bar.dart'; import 'package:hms_room_kit/src/meeting/meeting_header.dart'; @@ -184,428 +181,289 @@ class _MeetingPageState extends State { .height - MediaQuery.of(context).padding.top - MediaQuery.of(context).padding.bottom, - child: ChangeNotifierProvider.value( - value: _visibilityController, - child: Stack( - children: [ - Selector< - MeetingStore, - Tuple6< - List, - bool, - int, - int, - MeetingMode, - PeerTrackNode?>>( - selector: (_, meetingStore) => - Tuple6( - meetingStore.peerTracks, - meetingStore.isHLSLink, - meetingStore - .peerTracks.length, - meetingStore - .screenShareCount, - meetingStore - .meetingMode, - meetingStore.peerTracks - .isNotEmpty - ? meetingStore - .peerTracks[ - meetingStore - .screenShareCount] - : null), - builder: (_, data, __) { - if (data.item3 == 0) { - return Center( - child: Column( - mainAxisSize: - MainAxisSize.min, - children: [ - CircularProgressIndicator( - strokeWidth: 2, - color: HMSThemeColors - .primaryDefault, - ), - const SizedBox( - height: 10, - ), - if (context - .read< - MeetingStore>() - .peers - .isNotEmpty) - HMSTitleText( - text: - "Please wait for broadcaster to join", - textColor: - HMSThemeColors - .onSurfaceHighEmphasis) - ], - )); - } - return Selector< - MeetingStore, - Tuple2>( - selector: (_, - meetingStore) => - Tuple2( - meetingStore - .meetingMode, - meetingStore - .localPeer), - builder: - (_, modeData, __) { - ///This renders the video grid based on whether the controls are visible or not - return Selector< - MeetingNavigationVisibilityController, - bool>( - selector: (_, - meetingNavigationVisibilityController) => - meetingNavigationVisibilityController - .showControls, - builder: (_, - showControls, - __) { - return Center( - child: - AnimatedContainer( - duration: const Duration( - milliseconds: - 200), + child: Stack( + children: [ + ChangeNotifierProvider.value( + value: _visibilityController, + child: MeetingGridComponent( + visibilityController: + _visibilityController)), + Column( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: + const EdgeInsets.only( + left: 15, + right: 15, + top: 5, + bottom: 2), + child: ChangeNotifierProvider.value( + value: + _visibilityController, + child: + const MeetingHeader())), + Padding( + padding: + const EdgeInsets.only( + bottom: 8.0), + child: ChangeNotifierProvider.value( + value: + _visibilityController, + child: + const MeetingBottomNavigationBar())), + ], + ), - ///If the controls are visible we reduce the - ///height of video grid by 140 else it covers the whole screen - height: showControls - ? MediaQuery.of(context).size.height - - MediaQuery.of(context) - .padding - .top - - MediaQuery.of(context) - .padding - .bottom - - 140 - : MediaQuery.of(context).size.height - - MediaQuery.of(context) - .padding - .top - - MediaQuery.of(context) - .padding - .bottom - - 20, - child: - GestureDetector( - onTap: () => - _visibilityController - ?.toggleControlsVisibility(), - child: (modeData.item1 == MeetingMode.activeSpeakerWithInset && - (context.read().localPeer?.audioTrack != null || - context.read().localPeer?.videoTrack != null)) - ? OneToOneMode( - ///This is done to keep the inset tile - ///at correct position when controls are hidden - bottomMargin: showControls - ? 250 - : 130, - peerTracks: - data.item1, - screenShareCount: - data.item4, - context: - context, - ) - : CustomOneToOneGrid( - isLocalInsetPresent: - false, - peerTracks: - data.item1, - ), - ), - ), - ); - }); - }); - }), - Column( - mainAxisAlignment: - MainAxisAlignment - .spaceBetween, - children: [ - Padding( - padding: - const EdgeInsets.only( - left: 15, - right: 15, - top: 5, - bottom: 2), - child: ChangeNotifierProvider.value( - value: - _visibilityController, - child: - const MeetingHeader())), - Padding( - padding: - const EdgeInsets.only( - bottom: 8.0), - child: ChangeNotifierProvider.value( - value: - _visibilityController, - child: - const MeetingBottomNavigationBar())), - ], - ), - - ///This gets rendered when the previewForRole method is called - ///This is used to show the preview for role component - Selector< - MeetingStore, - Tuple3< - HMSLocalVideoTrack?, - HMSLocalAudioTrack?, - HMSRoleChangeRequest?>>( - selector: (_, meetingStore) => Tuple3( - meetingStore - .previewForRoleVideoTrack, - meetingStore - .previewForRoleAudioTrack, - meetingStore - .currentRoleChangeRequest), - builder: (_, - previewForRoleTracks, __) { - ///If the preview for role tracks are not null - ///or role change request is not null - ///we show the preview for role component - ///else we show and empty Container - if (previewForRoleTracks.item1 != null || - previewForRoleTracks - .item2 != - null || - previewForRoleTracks - .item3 != - null) { - WidgetsBinding.instance - .addPostFrameCallback( - (timeStamp) { - ///For preview for role component we use the [showGeneralDialog] - showGeneralDialog( - context: context, - pageBuilder: - (ctx, _, __) { - return ListenableProvider - .value( - value: context.read< - MeetingStore>(), - child: Scaffold( - body: SafeArea( - child: - Container( - color: HMSThemeColors - .backgroundDim, - height: MediaQuery.of( - context) - .size - .height, - width: MediaQuery.of( - context) - .size - .width, + ///This gets rendered when the previewForRole method is called + ///This is used to show the preview for role component + Selector< + MeetingStore, + Tuple3< + HMSLocalVideoTrack?, + HMSLocalAudioTrack?, + HMSRoleChangeRequest?>>( + selector: (_, meetingStore) => Tuple3( + meetingStore + .previewForRoleVideoTrack, + meetingStore + .previewForRoleAudioTrack, + meetingStore + .currentRoleChangeRequest), + builder: (_, previewForRoleTracks, + __) { + ///If the preview for role tracks are not null + ///or role change request is not null + ///we show the preview for role component + ///else we show and empty Container + if (previewForRoleTracks.item1 != null || + previewForRoleTracks + .item2 != + null || + previewForRoleTracks + .item3 != + null) { + WidgetsBinding.instance + .addPostFrameCallback( + (timeStamp) { + ///For preview for role component we use the [showGeneralDialog] + showGeneralDialog( + context: context, + pageBuilder: + (ctx, _, __) { + return ListenableProvider + .value( + value: context.read< + MeetingStore>(), + child: Scaffold( + body: SafeArea( + child: + Container( + color: HMSThemeColors + .backgroundDim, + height: MediaQuery.of( + context) + .size + .height, + width: MediaQuery.of( + context) + .size + .width, - ///We render the preview for role component - child: - Stack( - children: [ - ///This renders the video component - ///[HMSVideoView] is only rendered if video is ON - /// - ///else we render the [HMSCircularAvatar] - Selector( - selector: (_, meetingStore) => meetingStore - .isVideoOn, - builder: (_, - isVideoOn, - __) { - return Container( - height: MediaQuery.of(context).size.height, - width: MediaQuery.of(context).size.width, - color: HMSThemeColors.backgroundDim, - child: (isVideoOn && previewForRoleTracks.item1 != null) - ? Center( - child: HMSTextureView( - scaleType: ScaleType.SCALE_ASPECT_FILL, - track: previewForRoleTracks.item1!, - setMirror: true, - ), - ) - : Center( - child: HMSCircularAvatar(name: context.read().localPeer?.name ?? ""), + ///We render the preview for role component + child: Stack( + children: [ + ///This renders the video component + ///[HMSTextureView] is only rendered if video is ON + /// + ///else we render the [HMSCircularAvatar] + Selector< + MeetingStore, + bool>( + selector: (_, meetingStore) => meetingStore + .isVideoOn, + builder: (_, + isVideoOn, + __) { + return Container( + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + color: HMSThemeColors.backgroundDim, + child: (isVideoOn && previewForRoleTracks.item1 != null) + ? Center( + child: HMSTextureView( + scaleType: ScaleType.SCALE_ASPECT_FILL, + track: previewForRoleTracks.item1!, + setMirror: true, ), - ); - }), + ) + : Center( + child: HMSCircularAvatar(name: context.read().localPeer?.name ?? ""), + ), + ); + }), - ///This renders the preview for role header - const PreviewForRoleHeader(), + ///This renders the preview for role header + const PreviewForRoleHeader(), - ///This renders the preview for role bottom sheet - PreviewForRoleBottomSheet( - meetingStore: - context.read(), - roleChangeRequest: context - .read() - .currentRoleChangeRequest, - ) - ], - ), + ///This renders the preview for role bottom sheet + PreviewForRoleBottomSheet( + meetingStore: + context.read(), + roleChangeRequest: context + .read() + .currentRoleChangeRequest, + ) + ], ), ), ), - ); - }); - }); - } - return Container(); - }), + ), + ); + }); + }); + } + return Container(); + }), - Selector( - selector: (_, meetingStore) => - meetingStore - .hmsTrackChangeRequest, - builder: (_, - hmsTrackChangeRequest, __) { - if (hmsTrackChangeRequest != - null) { - HMSTrackChangeRequest - currentRequest = - hmsTrackChangeRequest; - context - .read() - .hmsTrackChangeRequest = - null; - WidgetsBinding.instance - .addPostFrameCallback( - (_) { - UtilityComponents - .showTrackChangeDialog( - context, - currentRequest); - }); - } - return const SizedBox(); - }), - Selector( - selector: (_, meetingStore) => - meetingStore - .showAudioDeviceChangePopup, - builder: (_, - showAudioDeviceChangePopup, - __) { - if (showAudioDeviceChangePopup) { - context - .read() - .showAudioDeviceChangePopup = - false; - WidgetsBinding.instance - .addPostFrameCallback( - (_) { - showDialog( - context: context, - builder: (_) => - AudioDeviceChangeDialog( - currentAudioDevice: context - .read< - MeetingStore>() - .currentAudioOutputDevice!, - audioDevicesList: context + Selector( + selector: (_, meetingStore) => + meetingStore + .hmsTrackChangeRequest, + builder: (_, + hmsTrackChangeRequest, __) { + if (hmsTrackChangeRequest != + null) { + HMSTrackChangeRequest + currentRequest = + hmsTrackChangeRequest; + context + .read() + .hmsTrackChangeRequest = + null; + WidgetsBinding.instance + .addPostFrameCallback( + (_) { + UtilityComponents + .showTrackChangeDialog( + context, + currentRequest); + }); + } + return const SizedBox(); + }), + Selector( + selector: (_, meetingStore) => + meetingStore + .showAudioDeviceChangePopup, + builder: (_, + showAudioDeviceChangePopup, + __) { + if (showAudioDeviceChangePopup) { + context + .read() + .showAudioDeviceChangePopup = + false; + WidgetsBinding.instance + .addPostFrameCallback( + (_) { + showDialog( + context: context, + builder: (_) => + AudioDeviceChangeDialog( + currentAudioDevice: context + .read< + MeetingStore>() + .currentAudioOutputDevice!, + audioDevicesList: context + .read< + MeetingStore>() + .availableAudioOutputDevices, + changeAudioDevice: + (audioDevice) { + context .read< MeetingStore>() - .availableAudioOutputDevices, - changeAudioDevice: - (audioDevice) { - context - .read< - MeetingStore>() - .switchAudioOutput( - audioDevice: - audioDevice); - }, - )); - }); - } - return const SizedBox(); - }), - Selector< - MeetingStore, - Tuple2, - int>>( - selector: (_, meetingStore) => - Tuple2( - meetingStore.toasts, - meetingStore - .toasts.length), - builder: (_, toastsItem, __) { - if (toastsItem - .item1.isEmpty) { - return Container(); - } - return Stack( - children: toastsItem.item1 - .sublist( - 0, - min( - 3, - toastsItem - .item2)) - .asMap() - .entries - .map((toasts) { - return Positioned( - bottom: 48.0 + - 8 * toasts.key, - left: 5, - child: Transform.scale( - scale: _getToastScale( - toasts.key, - toastsItem.item2), - child: getToast( - toasts.value, - toasts.key, - toastsItem.item2), - )); - }).toList()); - }), - Selector( - selector: (_, meetingStore) => - meetingStore.reconnecting, - builder: (_, reconnecting, __) { - if (reconnecting) { - return UtilityComponents - .showReconnectingDialog( - context); - } - return const SizedBox(); - }), - if (failureErrors.item2 != null) - if (failureErrors.item2?.code?.errorCode == 1003 || - failureErrors.item2?.code - ?.errorCode == - 2000 || - failureErrors.item2?.code - ?.errorCode == - 4005 || - failureErrors.item2?.code - ?.errorCode == - 424) - UtilityComponents - .showFailureError( - failureErrors.item2!, - context, - () => context - .read< - MeetingStore>() - .leave()) - ], - ), + .switchAudioOutput( + audioDevice: + audioDevice); + }, + )); + }); + } + return const SizedBox(); + }), + Selector< + MeetingStore, + Tuple2, + int>>( + selector: (_, meetingStore) => + Tuple2( + meetingStore.toasts, + meetingStore + .toasts.length), + builder: (_, toastsItem, __) { + if (toastsItem.item1.isEmpty) { + return Container(); + } + return Stack( + children: toastsItem.item1 + .sublist( + 0, + min( + 3, + toastsItem + .item2)) + .asMap() + .entries + .map((toasts) { + return Positioned( + bottom: + 48.0 + 8 * toasts.key, + left: 5, + child: Transform.scale( + scale: _getToastScale( + toasts.key, + toastsItem.item2), + child: getToast( + toasts.value, + toasts.key, + toastsItem.item2), + )); + }).toList()); + }), + Selector( + selector: (_, meetingStore) => + meetingStore.reconnecting, + builder: (_, reconnecting, __) { + if (reconnecting) { + return UtilityComponents + .showReconnectingDialog( + context); + } + return const SizedBox(); + }), + if (failureErrors.item2 != null) + if (failureErrors.item2?.code?.errorCode == 1003 || + failureErrors.item2?.code + ?.errorCode == + 2000 || + failureErrors.item2?.code + ?.errorCode == + 4005 || + failureErrors.item2?.code + ?.errorCode == + 424) + UtilityComponents + .showFailureError( + failureErrors.item2!, + context, + () => context + .read() + .leave()) + ], ), ), ),