From fd537440fb7daa1cce038dd4652e434394b3ba2d Mon Sep 17 00:00:00 2001 From: Pushpam <93931528+Decoder07@users.noreply.github.com> Date: Wed, 17 Jan 2024 12:53:41 +0530 Subject: [PATCH] FLUT-139: Auto-Hide Top & Bottom Bars after 5 seconds (#1692) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added auto hide functionality for header and footer * 🤖 Automated Format and Fix * Fixed conflicts --------- Co-authored-by: Decoder07 Co-authored-by: ygit --- .../src/meeting/meeting_grid_component.dart | 124 ++++++++++++++++ ...ting_navigation_visibility_controller.dart | 26 ++++ .../lib/src/meeting/meeting_page.dart | 137 +----------------- .../meeting_modes/one_to_one_mode.dart | 2 - 4 files changed, 158 insertions(+), 131 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_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..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'; @@ -63,6 +60,7 @@ class _MeetingPageState extends State { checkAudioState(); _enableForegroundService(); _visibilityController = MeetingNavigationVisibilityController(); + _visibilityController!.startTimerToHideButtons(); } void checkAudioState() async { @@ -185,130 +183,11 @@ class _MeetingPageState extends State { 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, - ), - )); - }); - }), + ChangeNotifierProvider.value( + value: _visibilityController, + child: MeetingGridComponent( + visibilityController: + _visibilityController)), Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -396,7 +275,7 @@ class _MeetingPageState extends State { child: Stack( children: [ ///This renders the video component - ///[HMSVideoView] is only rendered if video is ON + ///[HMSTextureView] is only rendered if video is ON /// ///else we render the [HMSCircularAvatar] Selector< 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);