From 57f938aa090c282e65eb6c6dd24621f68462242b Mon Sep 17 00:00:00 2001 From: jammal Date: Sun, 18 Oct 2020 14:47:33 +0300 Subject: [PATCH] fix ios issues --- ios/Podfile | 91 ++ ios/Runner/Base.lproj/Main.storyboard | 13 +- ios/Runner/Info.plist | 2 + lib/pages/conference/conference_page.dart | 776 +++++++-------- lib/pages/conference/conference_room.dart | 1042 ++++++++++----------- lib/pages/landing/landing_page.dart | 58 +- lib/pages/livecare/incoming_call.dart | 14 +- pubspec.yaml | 2 +- 8 files changed, 1047 insertions(+), 951 deletions(-) create mode 100644 ios/Podfile diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 00000000..c2702b3e --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,91 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '11.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +# pod 'FBSDKCoreKit' +# pod 'FBSDKLoginKit' + +def parse_KV_file(file, separator='=') + file_abs_path = File.expand_path(file) + if !File.exists? file_abs_path + return []; + end + generated_key_values = {} + skip_line_start_symbols = ["#", "/"] + File.foreach(file_abs_path) do |line| + next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } + plugin = line.split(pattern=separator) + if plugin.length == 2 + podname = plugin[0].strip() + path = plugin[1].strip() + podpath = File.expand_path("#{path}", file_abs_path) + generated_key_values[podname] = podpath + else + puts "Invalid plugin specification: #{line}" + end + end + generated_key_values +end + +target 'Runner' do + use_frameworks! + use_modular_headers! + + # Flutter Pod + + copied_flutter_dir = File.join(__dir__, 'Flutter') + copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework') + copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec') + unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path) + # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet. + # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration. + # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist. + + generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig') + unless File.exist?(generated_xcode_build_settings_path) + raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path) + cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR']; + + unless File.exist?(copied_framework_path) + FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir) + end + unless File.exist?(copied_podspec_path) + FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir) + end + end + + # Keep pod path relative so it can be checked into Podfile.lock. + pod 'Flutter', :path => 'Flutter' + + # Plugin Pods + + # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock + # referring to absolute paths on developers' machines. + system('rm -rf .symlinks') + system('mkdir -p .symlinks/plugins') + plugin_pods = parse_KV_file('../.flutter-plugins') + plugin_pods.each do |name, path| + symlink = File.join('.symlinks', 'plugins', name) + File.symlink(path, symlink) + pod name, :path => File.join(symlink, 'ios') + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings['ENABLE_BITCODE'] = 'NO' + end + end +end + diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard index f3c28516..00abc512 100644 --- a/ios/Runner/Base.lproj/Main.storyboard +++ b/ios/Runner/Base.lproj/Main.storyboard @@ -1,8 +1,10 @@ - - + + + - + + @@ -14,13 +16,14 @@ - + - + + diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index da3b2626..b807d9c6 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -22,6 +22,8 @@ $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS + io.flutter.embedded_views_preview + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile diff --git a/lib/pages/conference/conference_page.dart b/lib/pages/conference/conference_page.dart index aec5d418..7848d4f7 100644 --- a/lib/pages/conference/conference_page.dart +++ b/lib/pages/conference/conference_page.dart @@ -1,388 +1,388 @@ -// import 'dart:async'; -// -// import 'package:diplomaticquarterapp/models/LiveCare/room_model.dart'; -// import 'package:diplomaticquarterapp/pages/conference/conference_button_bar.dart'; -// import 'package:diplomaticquarterapp/pages/conference/conference_room.dart'; -// import 'package:diplomaticquarterapp/pages/conference/draggable_publisher.dart'; -// import 'package:diplomaticquarterapp/pages/conference/participant_widget.dart'; -// import 'package:diplomaticquarterapp/pages/conference/widgets/noise_box.dart'; -// import 'package:diplomaticquarterapp/pages/conference/widgets/platform_alert_dialog.dart'; -// import 'package:flutter/material.dart'; -// import 'package:flutter/services.dart'; -// import 'package:wakelock/wakelock.dart'; -// -// class ConferencePage extends StatefulWidget { -// final RoomModel roomModel; -// -// const ConferencePage({Key key, this.roomModel}) : super(key: key); -// -// @override -// _ConferencePageState createState() => _ConferencePageState(); -// } -// -// class _ConferencePageState extends State { -// final StreamController _onButtonBarVisibleStreamController = StreamController.broadcast(); -// final StreamController _onButtonBarHeightStreamController = StreamController.broadcast(); -// ConferenceRoom _conferenceRoom; -// StreamSubscription _onConferenceRoomException; -// -// @override -// void initState() { -// super.initState(); -// _lockInPortrait(); -// // _connectToRoom(); -// _wakeLock(true); -// } -// -// // void _connectToRoom() async { -// // try { -// // final conferenceRoom = ConferenceRoom( -// // name: widget.roomModel.name, -// // token: widget.roomModel.token, -// // identity: widget.roomModel.identity, -// // ); -// // await conferenceRoom.connect(); -// // setState(() { -// // _conferenceRoom = conferenceRoom; -// // _onConferenceRoomException = _conferenceRoom.onException.listen((err) async { -// // await PlatformAlertDialog( -// // title: err is PlatformException ? err.message : 'An error occured', -// // content: err is PlatformException ? err.details : err.toString(), -// // defaultActionText: 'OK', -// // ).show(context); -// // }); -// // _conferenceRoom.addListener(_conferenceRoomUpdated); -// // }); -// // } catch (err) { -// // print(err); -// // await PlatformAlertDialog( -// // title: err is PlatformException ? err.message : 'An error occured', -// // content: err is PlatformException ? err.details : err.toString(), -// // defaultActionText: 'OK', -// // ).show(context); -// // -// // Navigator.of(context).pop(); -// // } -// // } -// -// Future _lockInPortrait() async { -// await SystemChrome.setPreferredOrientations([ -// DeviceOrientation.portraitUp, -// DeviceOrientation.portraitDown, -// ]); -// } -// -// @override -// void dispose() { -// _freePortraitLock(); -// _wakeLock(false); -// _disposeStreamsAndSubscriptions(); -// if (_conferenceRoom != null) _conferenceRoom.removeListener(_conferenceRoomUpdated); -// super.dispose(); -// } -// -// Future _freePortraitLock() async { -// await SystemChrome.setPreferredOrientations([ -// DeviceOrientation.landscapeRight, -// DeviceOrientation.landscapeLeft, -// DeviceOrientation.portraitUp, -// DeviceOrientation.portraitDown, -// ]); -// } -// -// Future _disposeStreamsAndSubscriptions() async { -// if (_onButtonBarVisibleStreamController != null) await _onButtonBarVisibleStreamController.close(); -// if (_onButtonBarHeightStreamController != null) await _onButtonBarHeightStreamController.close(); -// if (_onConferenceRoomException != null) await _onConferenceRoomException.cancel(); -// } -// -// @override -// Widget build(BuildContext context) { -// return WillPopScope( -// onWillPop: () async => false, -// child: Scaffold( -// backgroundColor: Colors.black, -// body: _conferenceRoom == null ? showProgress() : buildLayout(), -// ), -// ); -// } -// -// LayoutBuilder buildLayout() { -// return LayoutBuilder( -// builder: (BuildContext context, BoxConstraints constraints) { -// return Stack( -// children: [ -// _buildParticipants(context, constraints.biggest, _conferenceRoom), -// ConferenceButtonBar( -// audioEnabled: _conferenceRoom.onAudioEnabled, -// videoEnabled: _conferenceRoom.onVideoEnabled, -// onAudioEnabled: _conferenceRoom.toggleAudioEnabled, -// onVideoEnabled: _conferenceRoom.toggleVideoEnabled, -// onHangup: _onHangup, -// onSwitchCamera: _conferenceRoom.switchCamera, -// onPersonAdd: _onPersonAdd, -// onPersonRemove: _onPersonRemove, -// onHeight: _onHeightBar, -// onShow: _onShowBar, -// onHide: _onHideBar, -// ), -// ], -// ); -// }, -// ); -// } -// -// Widget showProgress() { -// return Column( -// mainAxisAlignment: MainAxisAlignment.center, -// crossAxisAlignment: CrossAxisAlignment.center, -// children: [ -// Center(child: CircularProgressIndicator()), -// SizedBox( -// height: 10, -// ), -// Text( -// 'Connecting to the call...', -// style: TextStyle(color: Colors.white), -// ), -// ], -// ); -// } -// -// Future _onHangup() async { -// print('onHangup'); -// await _conferenceRoom.disconnect(); -// Navigator.of(context).pop(); -// } -// -// void _onPersonAdd() { -// print('onPersonAdd'); -// try { -// _conferenceRoom.addDummy( -// child: Stack( -// children: [ -// const Placeholder(), -// Center( -// child: Text( -// (_conferenceRoom.participants.length + 1).toString(), -// style: const TextStyle( -// shadows: [ -// Shadow( -// blurRadius: 3.0, -// color: Color.fromARGB(255, 0, 0, 0), -// ), -// Shadow( -// blurRadius: 8.0, -// color: Color.fromARGB(255, 255, 255, 255), -// ), -// ], -// fontSize: 80, -// ), -// ), -// ), -// ], -// ), -// ); -// } on PlatformException catch (err) { -// PlatformAlertDialog( -// title: err.message, -// content: err.details, -// defaultActionText: 'OK', -// ).show(context); -// } -// } -// -// void _onPersonRemove() { -// print('onPersonRemove'); -// _conferenceRoom.removeDummy(); -// } -// -// Widget _buildParticipants(BuildContext context, Size size, ConferenceRoom conferenceRoom) { -// final children = []; -// final length = conferenceRoom.participants.length; -// -// if (length <= 2) { -// _buildOverlayLayout(context, size, children); -// return Stack(children: children); -// } -// -// void buildInCols(bool removeLocalBeforeChunking, bool moveLastOfEachRowToNextRow, int columns) { -// _buildLayoutInGrid( -// context, -// size, -// children, -// removeLocalBeforeChunking: removeLocalBeforeChunking, -// moveLastOfEachRowToNextRow: moveLastOfEachRowToNextRow, -// columns: columns, -// ); -// } -// -// // if (length <= 3) { -// // buildInCols(true, false, 1); -// // } else if (length == 5) { -// // buildInCols(false, true, 2); -// // } else if (length <= 6 || length == 8) { -// // buildInCols(false, false, 2); -// // } else if (length == 7 || length == 9) { -// // buildInCols(true, false, 2); -// // } else if (length == 10) { -// // buildInCols(false, true, 3); -// // } else if (length == 13 || length == 16) { -// // buildInCols(true, false, 3); -// // } else if (length <= 18) { -// // buildInCols(false, false, 3); -// // } -// -// return Column( -// children: children, -// ); -// } -// -// void _buildOverlayLayout(BuildContext context, Size size, List children) { -// final participants = _conferenceRoom.participants; -// if (participants.length == 1) { -// children.add(_buildNoiseBox()); -// } else { -// final remoteParticipant = participants.firstWhere((ParticipantWidget participant) => participant.isRemote, orElse: () => null); -// if (remoteParticipant != null) { -// children.add(remoteParticipant); -// } -// } -// -// final localParticipant = participants.firstWhere((ParticipantWidget participant) => !participant.isRemote, orElse: () => null); -// if (localParticipant != null) { -// children.add(DraggablePublisher( -// key: Key('publisher'), -// child: localParticipant, -// availableScreenSize: size, -// onButtonBarVisible: _onButtonBarVisibleStreamController.stream, -// onButtonBarHeight: _onButtonBarHeightStreamController.stream, -// )); -// } -// } -// -// void _buildLayoutInGrid( -// BuildContext context, -// Size size, -// List children, { -// bool removeLocalBeforeChunking = false, -// bool moveLastOfEachRowToNextRow = false, -// int columns = 2, -// }) { -// final participants = _conferenceRoom.participants; -// ParticipantWidget localParticipant; -// if (removeLocalBeforeChunking) { -// localParticipant = participants.firstWhere((ParticipantWidget participant) => !participant.isRemote, orElse: () => null); -// if (localParticipant != null) { -// participants.remove(localParticipant); -// } -// } -// final chunkedParticipants = chunk(array: participants, size: columns); -// if (localParticipant != null) { -// chunkedParticipants.last.add(localParticipant); -// participants.add(localParticipant); -// } -// -// if (moveLastOfEachRowToNextRow) { -// for (var i = 0; i < chunkedParticipants.length - 1; i++) { -// var participant = chunkedParticipants[i].removeLast(); -// chunkedParticipants[i + 1].insert(0, participant); -// } -// } -// -// for (final participantChunk in chunkedParticipants) { -// final rowChildren = []; -// for (final participant in participantChunk) { -// rowChildren.add( -// Container( -// width: size.width / participantChunk.length, -// height: size.height / chunkedParticipants.length, -// child: participant, -// ), -// ); -// } -// children.add( -// Container( -// height: size.height / chunkedParticipants.length, -// child: Row( -// mainAxisAlignment: MainAxisAlignment.spaceEvenly, -// children: rowChildren, -// ), -// ), -// ); -// } -// } -// -// NoiseBox _buildNoiseBox() { -// return NoiseBox( -// density: NoiseBoxDensity.xLow, -// backgroundColor: Colors.grey.shade900, -// child: Center( -// child: Container( -// color: Colors.black54, -// width: double.infinity, -// height: 40, -// child: Center( -// child: Text( -// 'Waiting for another participant to connect to the call...', -// key: Key('text-wait'), -// textAlign: TextAlign.center, -// style: TextStyle(color: Colors.white), -// ), -// ), -// ), -// ), -// ); -// } -// -// List> chunk({@required List array, @required int size}) { -// final result = >[]; -// if (array.isEmpty || size <= 0) { -// return result; -// } -// var first = 0; -// var last = size; -// final totalLoop = array.length % size == 0 ? array.length ~/ size : array.length ~/ size + 1; -// for (var i = 0; i < totalLoop; i++) { -// if (last > array.length) { -// result.add(array.sublist(first, array.length)); -// } else { -// result.add(array.sublist(first, last)); -// } -// first = last; -// last = last + size; -// } -// return result; -// } -// -// void _onHeightBar(double height) { -// _onButtonBarHeightStreamController.add(height); -// } -// -// void _onShowBar() { -// setState(() { -// SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.bottom, SystemUiOverlay.top]); -// }); -// _onButtonBarVisibleStreamController.add(true); -// } -// -// void _onHideBar() { -// setState(() { -// SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.bottom]); -// }); -// _onButtonBarVisibleStreamController.add(false); -// } -// -// Future _wakeLock(bool enable) async { -// try { -// return await (enable ? Wakelock.enable() : Wakelock.disable()); -// } catch (err) { -// print('Unable to change the Wakelock and set it to $enable'); -// print(err); -// } -// } -// -// void _conferenceRoomUpdated() { -// setState(() {}); -// } -// } +import 'dart:async'; + +import 'package:diplomaticquarterapp/models/LiveCare/room_model.dart'; +import 'package:diplomaticquarterapp/pages/conference/conference_button_bar.dart'; +import 'package:diplomaticquarterapp/pages/conference/conference_room.dart'; +import 'package:diplomaticquarterapp/pages/conference/draggable_publisher.dart'; +import 'package:diplomaticquarterapp/pages/conference/participant_widget.dart'; +import 'package:diplomaticquarterapp/pages/conference/widgets/noise_box.dart'; +import 'package:diplomaticquarterapp/pages/conference/widgets/platform_alert_dialog.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:wakelock/wakelock.dart'; + +class ConferencePage extends StatefulWidget { + final RoomModel roomModel; + + const ConferencePage({Key key, this.roomModel}) : super(key: key); + + @override + _ConferencePageState createState() => _ConferencePageState(); +} + +class _ConferencePageState extends State { + final StreamController _onButtonBarVisibleStreamController = StreamController.broadcast(); + final StreamController _onButtonBarHeightStreamController = StreamController.broadcast(); + ConferenceRoom _conferenceRoom; + StreamSubscription _onConferenceRoomException; + + @override + void initState() { + super.initState(); + _lockInPortrait(); + _connectToRoom(); + _wakeLock(true); + } + + void _connectToRoom() async { + try { + final conferenceRoom = ConferenceRoom( + name: widget.roomModel.name, + token: widget.roomModel.token, + identity: widget.roomModel.identity, + ); + await conferenceRoom.connect(); + setState(() { + _conferenceRoom = conferenceRoom; + _onConferenceRoomException = _conferenceRoom.onException.listen((err) async { + await PlatformAlertDialog( + title: err is PlatformException ? err.message : 'An error occured', + content: err is PlatformException ? err.details : err.toString(), + defaultActionText: 'OK', + ).show(context); + }); + _conferenceRoom.addListener(_conferenceRoomUpdated); + }); + } catch (err) { + print(err); + await PlatformAlertDialog( + title: err is PlatformException ? err.message : 'An error occured', + content: err is PlatformException ? err.details : err.toString(), + defaultActionText: 'OK', + ).show(context); + + Navigator.of(context).pop(); + } + } + + Future _lockInPortrait() async { + await SystemChrome.setPreferredOrientations([ + DeviceOrientation.portraitUp, + DeviceOrientation.portraitDown, + ]); + } + + @override + void dispose() { + _freePortraitLock(); + _wakeLock(false); + _disposeStreamsAndSubscriptions(); + if (_conferenceRoom != null) _conferenceRoom.removeListener(_conferenceRoomUpdated); + super.dispose(); + } + + Future _freePortraitLock() async { + await SystemChrome.setPreferredOrientations([ + DeviceOrientation.landscapeRight, + DeviceOrientation.landscapeLeft, + DeviceOrientation.portraitUp, + DeviceOrientation.portraitDown, + ]); + } + + Future _disposeStreamsAndSubscriptions() async { + if (_onButtonBarVisibleStreamController != null) await _onButtonBarVisibleStreamController.close(); + if (_onButtonBarHeightStreamController != null) await _onButtonBarHeightStreamController.close(); + if (_onConferenceRoomException != null) await _onConferenceRoomException.cancel(); + } + + @override + Widget build(BuildContext context) { + return WillPopScope( + onWillPop: () async => false, + child: Scaffold( + backgroundColor: Colors.black, + body: _conferenceRoom == null ? showProgress() : buildLayout(), + ), + ); + } + + LayoutBuilder buildLayout() { + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Stack( + children: [ + _buildParticipants(context, constraints.biggest, _conferenceRoom), + ConferenceButtonBar( + audioEnabled: _conferenceRoom.onAudioEnabled, + videoEnabled: _conferenceRoom.onVideoEnabled, + onAudioEnabled: _conferenceRoom.toggleAudioEnabled, + onVideoEnabled: _conferenceRoom.toggleVideoEnabled, + onHangup: _onHangup, + onSwitchCamera: _conferenceRoom.switchCamera, + onPersonAdd: _onPersonAdd, + onPersonRemove: _onPersonRemove, + onHeight: _onHeightBar, + onShow: _onShowBar, + onHide: _onHideBar, + ), + ], + ); + }, + ); + } + + Widget showProgress() { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Center(child: CircularProgressIndicator()), + SizedBox( + height: 10, + ), + Text( + 'Connecting to the call...', + style: TextStyle(color: Colors.white), + ), + ], + ); + } + + Future _onHangup() async { + print('onHangup'); + await _conferenceRoom.disconnect(); + Navigator.of(context).pop(); + } + + void _onPersonAdd() { + print('onPersonAdd'); + try { + _conferenceRoom.addDummy( + child: Stack( + children: [ + const Placeholder(), + Center( + child: Text( + (_conferenceRoom.participants.length + 1).toString(), + style: const TextStyle( + shadows: [ + Shadow( + blurRadius: 3.0, + color: Color.fromARGB(255, 0, 0, 0), + ), + Shadow( + blurRadius: 8.0, + color: Color.fromARGB(255, 255, 255, 255), + ), + ], + fontSize: 80, + ), + ), + ), + ], + ), + ); + } on PlatformException catch (err) { + PlatformAlertDialog( + title: err.message, + content: err.details, + defaultActionText: 'OK', + ).show(context); + } + } + + void _onPersonRemove() { + print('onPersonRemove'); + _conferenceRoom.removeDummy(); + } + + Widget _buildParticipants(BuildContext context, Size size, ConferenceRoom conferenceRoom) { + final children = []; + final length = conferenceRoom.participants.length; + + if (length <= 2) { + _buildOverlayLayout(context, size, children); + return Stack(children: children); + } + + void buildInCols(bool removeLocalBeforeChunking, bool moveLastOfEachRowToNextRow, int columns) { + _buildLayoutInGrid( + context, + size, + children, + removeLocalBeforeChunking: removeLocalBeforeChunking, + moveLastOfEachRowToNextRow: moveLastOfEachRowToNextRow, + columns: columns, + ); + } + +// if (length <= 3) { +// buildInCols(true, false, 1); +// } else if (length == 5) { +// buildInCols(false, true, 2); +// } else if (length <= 6 || length == 8) { +// buildInCols(false, false, 2); +// } else if (length == 7 || length == 9) { +// buildInCols(true, false, 2); +// } else if (length == 10) { +// buildInCols(false, true, 3); +// } else if (length == 13 || length == 16) { +// buildInCols(true, false, 3); +// } else if (length <= 18) { +// buildInCols(false, false, 3); +// } + + return Column( + children: children, + ); + } + + void _buildOverlayLayout(BuildContext context, Size size, List children) { + final participants = _conferenceRoom.participants; + if (participants.length == 1) { + children.add(_buildNoiseBox()); + } else { + final remoteParticipant = participants.firstWhere((ParticipantWidget participant) => participant.isRemote, orElse: () => null); + if (remoteParticipant != null) { + children.add(remoteParticipant); + } + } + + final localParticipant = participants.firstWhere((ParticipantWidget participant) => !participant.isRemote, orElse: () => null); + if (localParticipant != null) { + children.add(DraggablePublisher( + key: Key('publisher'), + child: localParticipant, + availableScreenSize: size, + onButtonBarVisible: _onButtonBarVisibleStreamController.stream, + onButtonBarHeight: _onButtonBarHeightStreamController.stream, + )); + } + } + + void _buildLayoutInGrid( + BuildContext context, + Size size, + List children, { + bool removeLocalBeforeChunking = false, + bool moveLastOfEachRowToNextRow = false, + int columns = 2, + }) { + final participants = _conferenceRoom.participants; + ParticipantWidget localParticipant; + if (removeLocalBeforeChunking) { + localParticipant = participants.firstWhere((ParticipantWidget participant) => !participant.isRemote, orElse: () => null); + if (localParticipant != null) { + participants.remove(localParticipant); + } + } + final chunkedParticipants = chunk(array: participants, size: columns); + if (localParticipant != null) { + chunkedParticipants.last.add(localParticipant); + participants.add(localParticipant); + } + + if (moveLastOfEachRowToNextRow) { + for (var i = 0; i < chunkedParticipants.length - 1; i++) { + var participant = chunkedParticipants[i].removeLast(); + chunkedParticipants[i + 1].insert(0, participant); + } + } + + for (final participantChunk in chunkedParticipants) { + final rowChildren = []; + for (final participant in participantChunk) { + rowChildren.add( + Container( + width: size.width / participantChunk.length, + height: size.height / chunkedParticipants.length, + child: participant, + ), + ); + } + children.add( + Container( + height: size.height / chunkedParticipants.length, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: rowChildren, + ), + ), + ); + } + } + + NoiseBox _buildNoiseBox() { + return NoiseBox( + density: NoiseBoxDensity.xLow, + backgroundColor: Colors.grey.shade900, + child: Center( + child: Container( + color: Colors.black54, + width: double.infinity, + height: 40, + child: Center( + child: Text( + 'Waiting for another participant to connect to the call...', + key: Key('text-wait'), + textAlign: TextAlign.center, + style: TextStyle(color: Colors.white), + ), + ), + ), + ), + ); + } + + List> chunk({@required List array, @required int size}) { + final result = >[]; + if (array.isEmpty || size <= 0) { + return result; + } + var first = 0; + var last = size; + final totalLoop = array.length % size == 0 ? array.length ~/ size : array.length ~/ size + 1; + for (var i = 0; i < totalLoop; i++) { + if (last > array.length) { + result.add(array.sublist(first, array.length)); + } else { + result.add(array.sublist(first, last)); + } + first = last; + last = last + size; + } + return result; + } + + void _onHeightBar(double height) { + _onButtonBarHeightStreamController.add(height); + } + + void _onShowBar() { + setState(() { + SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.bottom, SystemUiOverlay.top]); + }); + _onButtonBarVisibleStreamController.add(true); + } + + void _onHideBar() { + setState(() { + SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.bottom]); + }); + _onButtonBarVisibleStreamController.add(false); + } + + Future _wakeLock(bool enable) async { + try { + return await (enable ? Wakelock.enable() : Wakelock.disable()); + } catch (err) { + print('Unable to change the Wakelock and set it to $enable'); + print(err); + } + } + + void _conferenceRoomUpdated() { + setState(() {}); + } +} diff --git a/lib/pages/conference/conference_room.dart b/lib/pages/conference/conference_room.dart index a88c3bdb..9c8c1c9a 100644 --- a/lib/pages/conference/conference_room.dart +++ b/lib/pages/conference/conference_room.dart @@ -4,527 +4,527 @@ import 'dart:typed_data'; import 'package:diplomaticquarterapp/pages/conference/participant_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -// import 'package:twilio_programmable_video/twilio_programmable_video.dart'; +import 'package:twilio_programmable_video/twilio_programmable_video.dart'; class ConferenceRoom with ChangeNotifier { - // final String name; - // final String token; - // final String identity; - // - // final StreamController _onAudioEnabledStreamController = StreamController.broadcast(); - // Stream onAudioEnabled; - // final StreamController _onVideoEnabledStreamController = StreamController.broadcast(); - // Stream onVideoEnabled; - // final StreamController _onExceptionStreamController = StreamController.broadcast(); - // Stream onException; - // - // final Completer _completer = Completer(); - // - // final List _participants = []; - // final List _participantBuffer = []; - // final List _streamSubscriptions = []; - // final List _dataTracks = []; - // final List _messages = []; - // - // CameraCapturer _cameraCapturer; - // Room _room; - // Timer _timer; - // - // ConferenceRoom({ - // @required this.name, - // @required this.token, - // @required this.identity, - // }) { - // onAudioEnabled = _onAudioEnabledStreamController.stream; - // onVideoEnabled = _onVideoEnabledStreamController.stream; - // onException = _onExceptionStreamController.stream; - // } - // - // List get participants { - // return [..._participants]; - // } - // - // Future connect() async { - // print('ConferenceRoom.connect()'); - // try { - // await TwilioProgrammableVideo.debug(dart: true, native: true); - // await TwilioProgrammableVideo.setSpeakerphoneOn(true); - // - // _cameraCapturer = CameraCapturer(CameraSource.FRONT_CAMERA); - // var connectOptions = ConnectOptions( - // token, - // roomName: name, - // preferredAudioCodecs: [OpusCodec()], - // audioTracks: [LocalAudioTrack(true)], - // dataTracks: [LocalDataTrack()], - // videoTracks: [LocalVideoTrack(true, _cameraCapturer)], - // enableDominantSpeaker: true, - // ); - // - // _room = await TwilioProgrammableVideo.connect(connectOptions); - // - // _streamSubscriptions.add(_room.onConnected.listen(_onConnected)); - // _streamSubscriptions.add(_room.onConnectFailure.listen(_onConnectFailure)); - // - // return _completer.future; - // } catch (err) { - // print(err); - // rethrow; - // } - // } - // - // Future disconnect() async { - // print('ConferenceRoom.disconnect()'); - // if (_timer != null) { - // _timer.cancel(); - // } - // await _room.disconnect(); - // } - // - // @override - // void dispose() { - // print('ConferenceRoom.dispose()'); - // _disposeStreamsAndSubscriptions(); - // super.dispose(); - // } - // - // Future _disposeStreamsAndSubscriptions() async { - // await _onAudioEnabledStreamController.close(); - // await _onVideoEnabledStreamController.close(); - // await _onExceptionStreamController.close(); - // for (var streamSubscription in _streamSubscriptions) { - // await streamSubscription.cancel(); - // } - // } - // - // Future sendMessage(String message) async { - // final tracks = _room.localParticipant.localDataTracks; - // final localDataTrack = tracks.isEmpty ? null : tracks[0].localDataTrack; - // if (localDataTrack == null || _messages.isNotEmpty) { - // print('ConferenceRoom.sendMessage => Track is not available yet, buffering message.'); - // _messages.add(message); - // return; - // } - // await localDataTrack.send(message); - // } - // - // Future sendBufferMessage(ByteBuffer message) async { - // final tracks = _room.localParticipant.localDataTracks; - // final localDataTrack = tracks.isEmpty ? null : tracks[0].localDataTrack; - // if (localDataTrack == null) { - // return; - // } - // await localDataTrack.sendBuffer(message); - // } - // - // Future toggleVideoEnabled() async { - // final tracks = _room.localParticipant.localVideoTracks; - // final localVideoTrack = tracks.isEmpty ? null : tracks[0].localVideoTrack; - // if (localVideoTrack == null) { - // print('ConferenceRoom.toggleVideoEnabled() => Track is not available yet!'); - // return; - // } - // await localVideoTrack.enable(!localVideoTrack.isEnabled); - // - // var index = _participants.indexWhere((ParticipantWidget participant) => !participant.isRemote); - // if (index < 0) { - // return; - // } - // var participant = _participants[index]; - // _participants.replaceRange( - // index, - // index + 1, - // [ - // participant.copyWith(videoEnabled: localVideoTrack.isEnabled), - // ], - // ); - // print('ConferenceRoom.toggleVideoEnabled() => ${localVideoTrack.isEnabled}'); - // _onVideoEnabledStreamController.add(localVideoTrack.isEnabled); - // notifyListeners(); - // } - // - // Future toggleAudioEnabled() async { - // final tracks = _room.localParticipant.localAudioTracks; - // final localAudioTrack = tracks.isEmpty ? null : tracks[0].localAudioTrack; - // if (localAudioTrack == null) { - // print('ConferenceRoom.toggleAudioEnabled() => Track is not available yet!'); - // return; - // } - // await localAudioTrack.enable(!localAudioTrack.isEnabled); - // - // var index = _participants.indexWhere((ParticipantWidget participant) => !participant.isRemote); - // if (index < 0) { - // return; - // } - // var participant = _participants[index]; - // _participants.replaceRange( - // index, - // index + 1, - // [ - // participant.copyWith(audioEnabled: localAudioTrack.isEnabled), - // ], - // ); - // print('ConferenceRoom.toggleAudioEnabled() => ${localAudioTrack.isEnabled}'); - // _onAudioEnabledStreamController.add(localAudioTrack.isEnabled); - // notifyListeners(); - // } - // - // Future switchCamera() async { - // print('ConferenceRoom.switchCamera()'); - // try { - // await _cameraCapturer.switchCamera(); - // } on FormatException catch (e) { - // print( - // 'ConferenceRoom.switchCamera() failed because of FormatException with message: ${e.message}', - // ); - // } - // } - // - // void addDummy({Widget child}) { - // print('ConferenceRoom.addDummy()'); - // if (_participants.length >= 18) { - // throw PlatformException( - // code: 'ConferenceRoom.maximumReached', - // message: 'Maximum reached', - // details: 'Currently the lay-out can only render a maximum of 18 participants', - // ); - // } - // _participants.insert( - // 0, - // ParticipantWidget( - // id: (_participants.length + 1).toString(), - // child: child, - // isRemote: true, - // audioEnabled: true, - // videoEnabled: true, - // isDummy: true, - // ), - // ); - // notifyListeners(); - // } - // - // void removeDummy() { - // print('ConferenceRoom.removeDummy()'); - // var dummy = _participants.firstWhere((participant) => participant.isDummy, orElse: () => null); - // if (dummy != null) { - // _participants.remove(dummy); - // notifyListeners(); - // } - // } - // - // void _onConnected(Room room) { - // print('ConferenceRoom._onConnected => state: ${room.state}'); - // - // // When connected for the first time, add remote participant listeners - // _streamSubscriptions.add(_room.onParticipantConnected.listen(_onParticipantConnected)); - // _streamSubscriptions.add(_room.onParticipantDisconnected.listen(_onParticipantDisconnected)); - // _streamSubscriptions.add(_room.onDominantSpeakerChange.listen(_onDominantSpeakerChanged)); - // // Only add ourselves when connected for the first time too. - // _participants.add( - // _buildParticipant( - // child: room.localParticipant.localVideoTracks[0].localVideoTrack.widget(), - // id: identity, - // audioEnabled: true, - // videoEnabled: true, - // ), - // ); - // for (final remoteParticipant in room.remoteParticipants) { - // var participant = _participants.firstWhere((participant) => participant.id == remoteParticipant.sid, orElse: () => null); - // if (participant == null) { - // print('Adding participant that was already present in the room ${remoteParticipant.sid}, before I connected'); - // _addRemoteParticipantListeners(remoteParticipant); - // } - // } - // // We have to listen for the [onDataTrackPublished] event on the [LocalParticipant] in - // // order to be able to use the [send] method. - // _streamSubscriptions.add(room.localParticipant.onDataTrackPublished.listen(_onLocalDataTrackPublished)); - // notifyListeners(); - // _completer.complete(room); - // - // _timer = Timer.periodic(const Duration(minutes: 1), (_) { - // // Let's see if we can send some data over the DataTrack API - // sendMessage('And another minute has passed since I connected...'); - // // Also try the ByteBuffer way of sending data - // final list = 'This data has been sent over the ByteBuffer channel of the DataTrack API'.codeUnits; - // var bytes = Uint8List.fromList(list); - // sendBufferMessage(bytes.buffer); - // }); - // } - // - // void _onLocalDataTrackPublished(LocalDataTrackPublishedEvent event) { - // // Send buffered messages, if any... - // while (_messages.isNotEmpty) { - // var message = _messages.removeAt(0); - // print('Sending buffered message: $message'); - // event.localDataTrackPublication.localDataTrack.send(message); - // } - // } - // - // void _onConnectFailure(RoomConnectFailureEvent event) { - // print('ConferenceRoom._onConnectFailure: ${event.exception}'); - // _completer.completeError(event.exception); - // } - // - // void _onDominantSpeakerChanged(DominantSpeakerChangedEvent event) { - // print('ConferenceRoom._onDominantSpeakerChanged: ${event.remoteParticipant.identity}'); - // var oldDominantParticipant = _participants.firstWhere((p) => p.isDominant, orElse: () => null); - // if (oldDominantParticipant != null) { - // var oldDominantParticipantIndex = _participants.indexOf(oldDominantParticipant); - // _participants.replaceRange(oldDominantParticipantIndex, oldDominantParticipantIndex + 1, [oldDominantParticipant.copyWith(isDominant: false)]); - // } - // - // var newDominantParticipant = _participants.firstWhere((p) => p.id == event.remoteParticipant.sid); - // var newDominantParticipantIndex = _participants.indexOf(newDominantParticipant); - // _participants.replaceRange(newDominantParticipantIndex, newDominantParticipantIndex + 1, [newDominantParticipant.copyWith(isDominant: true)]); - // notifyListeners(); - // } - // - // void _onParticipantConnected(RoomParticipantConnectedEvent event) { - // print('ConferenceRoom._onParticipantConnected, ${event.remoteParticipant.sid}'); - // _addRemoteParticipantListeners(event.remoteParticipant); - // } - // - // void _onParticipantDisconnected(RoomParticipantDisconnectedEvent event) { - // print('ConferenceRoom._onParticipantDisconnected: ${event.remoteParticipant.sid}'); - // _participants.removeWhere((ParticipantWidget p) => p.id == event.remoteParticipant.sid); - // notifyListeners(); - // } - // - // ParticipantWidget _buildParticipant({ - // @required Widget child, - // @required String id, - // @required bool audioEnabled, - // @required bool videoEnabled, - // RemoteParticipant remoteParticipant, - // }) { - // return ParticipantWidget( - // id: remoteParticipant?.sid, - // isRemote: remoteParticipant != null, - // child: child, - // audioEnabled: audioEnabled, - // videoEnabled: videoEnabled, - // ); - // } - // - // void _addRemoteParticipantListeners(RemoteParticipant remoteParticipant) { - // print('ConferenceRoom._addRemoteParticipantListeners() => Adding listeners to remoteParticipant ${remoteParticipant.sid}'); - // _streamSubscriptions.add(remoteParticipant.onAudioTrackDisabled.listen(_onAudioTrackDisabled)); - // _streamSubscriptions.add(remoteParticipant.onAudioTrackEnabled.listen(_onAudioTrackEnabled)); - // _streamSubscriptions.add(remoteParticipant.onAudioTrackPublished.listen(_onAudioTrackPublished)); - // _streamSubscriptions.add(remoteParticipant.onAudioTrackSubscribed.listen(_onAudioTrackSubscribed)); - // _streamSubscriptions.add(remoteParticipant.onAudioTrackSubscriptionFailed.listen(_onAudioTrackSubscriptionFailed)); - // _streamSubscriptions.add(remoteParticipant.onAudioTrackUnpublished.listen(_onAudioTrackUnpublished)); - // _streamSubscriptions.add(remoteParticipant.onAudioTrackUnsubscribed.listen(_onAudioTrackUnsubscribed)); - // - // _streamSubscriptions.add(remoteParticipant.onDataTrackPublished.listen(_onDataTrackPublished)); - // _streamSubscriptions.add(remoteParticipant.onDataTrackSubscribed.listen(_onDataTrackSubscribed)); - // _streamSubscriptions.add(remoteParticipant.onDataTrackSubscriptionFailed.listen(_onDataTrackSubscriptionFailed)); - // _streamSubscriptions.add(remoteParticipant.onDataTrackUnpublished.listen(_onDataTrackUnpublished)); - // _streamSubscriptions.add(remoteParticipant.onDataTrackUnsubscribed.listen(_onDataTrackUnsubscribed)); - // - // _streamSubscriptions.add(remoteParticipant.onVideoTrackDisabled.listen(_onVideoTrackDisabled)); - // _streamSubscriptions.add(remoteParticipant.onVideoTrackEnabled.listen(_onVideoTrackEnabled)); - // _streamSubscriptions.add(remoteParticipant.onVideoTrackPublished.listen(_onVideoTrackPublished)); - // _streamSubscriptions.add(remoteParticipant.onVideoTrackSubscribed.listen(_onVideoTrackSubscribed)); - // _streamSubscriptions.add(remoteParticipant.onVideoTrackSubscriptionFailed.listen(_onVideoTrackSubscriptionFailed)); - // _streamSubscriptions.add(remoteParticipant.onVideoTrackUnpublished.listen(_onVideoTrackUnpublished)); - // _streamSubscriptions.add(remoteParticipant.onVideoTrackUnsubscribed.listen(_onVideoTrackUnsubscribed)); - // } - // - // void _onAudioTrackDisabled(RemoteAudioTrackEvent event) { - // print('ConferenceRoom._onAudioTrackDisabled(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}, isEnabled: ${event.remoteAudioTrackPublication.isTrackEnabled}'); - // _setRemoteAudioEnabled(event); - // } - // - // void _onAudioTrackEnabled(RemoteAudioTrackEvent event) { - // print('ConferenceRoom._onAudioTrackEnabled(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}, isEnabled: ${event.remoteAudioTrackPublication.isTrackEnabled}'); - // _setRemoteAudioEnabled(event); - // } - // - // void _onAudioTrackPublished(RemoteAudioTrackEvent event) { - // print('ConferenceRoom._onAudioTrackPublished(), ${event.remoteParticipant.sid}}'); - // } - // - // void _onAudioTrackSubscribed(RemoteAudioTrackSubscriptionEvent event) { - // print('ConferenceRoom._onAudioTrackSubscribed(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}'); - // _addOrUpdateParticipant(event); - // } - // - // void _onAudioTrackSubscriptionFailed(RemoteAudioTrackSubscriptionFailedEvent event) { - // print('ConferenceRoom._onAudioTrackSubscriptionFailed(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}'); - // _onExceptionStreamController.add( - // PlatformException( - // code: 'ConferenceRoom.audioTrackSubscriptionFailed', - // message: 'AudioTrack Subscription Failed', - // details: event.exception.toString(), - // ), - // ); - // } - // - // void _onAudioTrackUnpublished(RemoteAudioTrackEvent event) { - // print('ConferenceRoom._onAudioTrackUnpublished(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}'); - // } - // - // void _onAudioTrackUnsubscribed(RemoteAudioTrackSubscriptionEvent event) { - // print('ConferenceRoom._onAudioTrackUnsubscribed(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrack.sid}'); - // } - // - // void _onDataTrackPublished(RemoteDataTrackEvent event) { - // print('ConferenceRoom._onDataTrackPublished(), ${event.remoteParticipant.sid}}'); - // } - // - // void _onDataTrackSubscribed(RemoteDataTrackSubscriptionEvent event) { - // print('ConferenceRoom._onDataTrackSubscribed(), ${event.remoteParticipant.sid}, ${event.remoteDataTrackPublication.trackSid}'); - // final dataTrack = event.remoteDataTrackPublication.remoteDataTrack; - // _dataTracks.add(dataTrack); - // _streamSubscriptions.add(dataTrack.onMessage.listen(_onMessage)); - // _streamSubscriptions.add(dataTrack.onBufferMessage.listen(_onBufferMessage)); - // } - // - // void _onDataTrackSubscriptionFailed(RemoteDataTrackSubscriptionFailedEvent event) { - // print('ConferenceRoom._onDataTrackSubscriptionFailed(), ${event.remoteParticipant.sid}, ${event.remoteDataTrackPublication.trackSid}'); - // _onExceptionStreamController.add( - // PlatformException( - // code: 'ConferenceRoom.dataTrackSubscriptionFailed', - // message: 'DataTrack Subscription Failed', - // details: event.exception.toString(), - // ), - // ); - // } - // - // void _onDataTrackUnpublished(RemoteDataTrackEvent event) { - // print('ConferenceRoom._onDataTrackUnpublished(), ${event.remoteParticipant.sid}, ${event.remoteDataTrackPublication.trackSid}'); - // } - // - // void _onDataTrackUnsubscribed(RemoteDataTrackSubscriptionEvent event) { - // print('ConferenceRoom._onDataTrackUnsubscribed(), ${event.remoteParticipant.sid}, ${event.remoteDataTrack.sid}'); - // } - // - // void _onVideoTrackDisabled(RemoteVideoTrackEvent event) { - // print('ConferenceRoom._onVideoTrackDisabled(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}, isEnabled: ${event.remoteVideoTrackPublication.isTrackEnabled}'); - // _setRemoteVideoEnabled(event); - // } - // - // void _onVideoTrackEnabled(RemoteVideoTrackEvent event) { - // print('ConferenceRoom._onVideoTrackEnabled(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}, isEnabled: ${event.remoteVideoTrackPublication.isTrackEnabled}'); - // _setRemoteVideoEnabled(event); - // } - // - // void _onVideoTrackPublished(RemoteVideoTrackEvent event) { - // print('ConferenceRoom._onVideoTrackPublished(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}'); - // } - // - // void _onVideoTrackSubscribed(RemoteVideoTrackSubscriptionEvent event) { - // print('ConferenceRoom._onVideoTrackSubscribed(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrack.sid}'); - // _addOrUpdateParticipant(event); - // } - // - // void _onVideoTrackSubscriptionFailed(RemoteVideoTrackSubscriptionFailedEvent event) { - // print('ConferenceRoom._onVideoTrackSubscriptionFailed(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}'); - // _onExceptionStreamController.add( - // PlatformException( - // code: 'ConferenceRoom.videoTrackSubscriptionFailed', - // message: 'VideoTrack Subscription Failed', - // details: event.exception.toString(), - // ), - // ); - // } - // - // void _onVideoTrackUnpublished(RemoteVideoTrackEvent event) { - // print('ConferenceRoom._onVideoTrackUnpublished(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}'); - // } - // - // void _onVideoTrackUnsubscribed(RemoteVideoTrackSubscriptionEvent event) { - // print('ConferenceRoom._onVideoTrackUnsubscribed(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrack.sid}'); - // } - // - // void _onMessage(RemoteDataTrackStringMessageEvent event) { - // print('onMessage => ${event.remoteDataTrack.sid}, ${event.message}'); - // } - // - // void _onBufferMessage(RemoteDataTrackBufferMessageEvent event) { - // print('onBufferMessage => ${event.remoteDataTrack.sid}, ${String.fromCharCodes(event.message.asUint8List())}'); - // } - // - // void _setRemoteAudioEnabled(RemoteAudioTrackEvent event) { - // if (event.remoteAudioTrackPublication == null) { - // return; - // } - // var index = _participants.indexWhere((ParticipantWidget participant) => participant.id == event.remoteParticipant.sid); - // if (index < 0) { - // return; - // } - // var participant = _participants[index]; - // _participants.replaceRange( - // index, - // index + 1, - // [ - // participant.copyWith(audioEnabled: event.remoteAudioTrackPublication.isTrackEnabled), - // ], - // ); - // notifyListeners(); - // } - // - // void _setRemoteVideoEnabled(RemoteVideoTrackEvent event) { - // if (event.remoteVideoTrackPublication == null) { - // return; - // } - // var index = _participants.indexWhere((ParticipantWidget participant) => participant.id == event.remoteParticipant.sid); - // if (index < 0) { - // return; - // } - // var participant = _participants[index]; - // _participants.replaceRange( - // index, - // index + 1, - // [ - // participant.copyWith(videoEnabled: event.remoteVideoTrackPublication.isTrackEnabled), - // ], - // ); - // notifyListeners(); - // } - // - // void _addOrUpdateParticipant(RemoteParticipantEvent event) { - // print('ConferenceRoom._addOrUpdateParticipant(), ${event.remoteParticipant.sid}'); - // final participant = _participants.firstWhere( - // (ParticipantWidget participant) => participant.id == event.remoteParticipant.sid, - // orElse: () => null, - // ); - // if (participant != null) { - // print('Participant found: ${participant.id}, updating A/V enabled values'); - // _setRemoteVideoEnabled(event); - // _setRemoteAudioEnabled(event); - // } else { - // final bufferedParticipant = _participantBuffer.firstWhere( - // (ParticipantBuffer participant) => participant.id == event.remoteParticipant.sid, - // orElse: () => null, - // ); - // if (bufferedParticipant != null) { - // _participantBuffer.remove(bufferedParticipant); - // } else if (event is RemoteAudioTrackEvent) { - // print('Audio subscription came first, waiting for the video subscription...'); - // _participantBuffer.add( - // ParticipantBuffer( - // id: event.remoteParticipant.sid, - // audioEnabled: event.remoteAudioTrackPublication?.remoteAudioTrack?.isEnabled ?? true, - // ), - // ); - // return; - // } - // if (event is RemoteVideoTrackSubscriptionEvent) { - // print('New participant, adding: ${event.remoteParticipant.sid}'); - // _participants.insert( - // 0, - // _buildParticipant( - // child: event.remoteVideoTrack.widget(), - // id: event.remoteParticipant.sid, - // remoteParticipant: event.remoteParticipant, - // audioEnabled: bufferedParticipant?.audioEnabled ?? true, - // videoEnabled: event.remoteVideoTrackPublication?.remoteVideoTrack?.isEnabled ?? true, - // ), - // ); - // } - // notifyListeners(); - // } - // } + final String name; + final String token; + final String identity; + + final StreamController _onAudioEnabledStreamController = StreamController.broadcast(); + Stream onAudioEnabled; + final StreamController _onVideoEnabledStreamController = StreamController.broadcast(); + Stream onVideoEnabled; + final StreamController _onExceptionStreamController = StreamController.broadcast(); + Stream onException; + + final Completer _completer = Completer(); + + final List _participants = []; + final List _participantBuffer = []; + final List _streamSubscriptions = []; + final List _dataTracks = []; + final List _messages = []; + + CameraCapturer _cameraCapturer; + Room _room; + Timer _timer; + + ConferenceRoom({ + @required this.name, + @required this.token, + @required this.identity, + }) { + onAudioEnabled = _onAudioEnabledStreamController.stream; + onVideoEnabled = _onVideoEnabledStreamController.stream; + onException = _onExceptionStreamController.stream; + } + + List get participants { + return [..._participants]; + } + + Future connect() async { + print('ConferenceRoom.connect()'); + try { + await TwilioProgrammableVideo.debug(dart: true, native: true); + await TwilioProgrammableVideo.setSpeakerphoneOn(true); + + _cameraCapturer = CameraCapturer(CameraSource.FRONT_CAMERA); + var connectOptions = ConnectOptions( + token, + roomName: name, + preferredAudioCodecs: [OpusCodec()], + audioTracks: [LocalAudioTrack(true)], + dataTracks: [LocalDataTrack()], + videoTracks: [LocalVideoTrack(true, _cameraCapturer)], + enableDominantSpeaker: true, + ); + + _room = await TwilioProgrammableVideo.connect(connectOptions); + + _streamSubscriptions.add(_room.onConnected.listen(_onConnected)); + _streamSubscriptions.add(_room.onConnectFailure.listen(_onConnectFailure)); + + return _completer.future; + } catch (err) { + print(err); + rethrow; + } + } + + Future disconnect() async { + print('ConferenceRoom.disconnect()'); + if (_timer != null) { + _timer.cancel(); + } + await _room.disconnect(); + } + + @override + void dispose() { + print('ConferenceRoom.dispose()'); + _disposeStreamsAndSubscriptions(); + super.dispose(); + } + + Future _disposeStreamsAndSubscriptions() async { + await _onAudioEnabledStreamController.close(); + await _onVideoEnabledStreamController.close(); + await _onExceptionStreamController.close(); + for (var streamSubscription in _streamSubscriptions) { + await streamSubscription.cancel(); + } + } + + Future sendMessage(String message) async { + final tracks = _room.localParticipant.localDataTracks; + final localDataTrack = tracks.isEmpty ? null : tracks[0].localDataTrack; + if (localDataTrack == null || _messages.isNotEmpty) { + print('ConferenceRoom.sendMessage => Track is not available yet, buffering message.'); + _messages.add(message); + return; + } + await localDataTrack.send(message); + } + + Future sendBufferMessage(ByteBuffer message) async { + final tracks = _room.localParticipant.localDataTracks; + final localDataTrack = tracks.isEmpty ? null : tracks[0].localDataTrack; + if (localDataTrack == null) { + return; + } + await localDataTrack.sendBuffer(message); + } + + Future toggleVideoEnabled() async { + final tracks = _room.localParticipant.localVideoTracks; + final localVideoTrack = tracks.isEmpty ? null : tracks[0].localVideoTrack; + if (localVideoTrack == null) { + print('ConferenceRoom.toggleVideoEnabled() => Track is not available yet!'); + return; + } + await localVideoTrack.enable(!localVideoTrack.isEnabled); + + var index = _participants.indexWhere((ParticipantWidget participant) => !participant.isRemote); + if (index < 0) { + return; + } + var participant = _participants[index]; + _participants.replaceRange( + index, + index + 1, + [ + participant.copyWith(videoEnabled: localVideoTrack.isEnabled), + ], + ); + print('ConferenceRoom.toggleVideoEnabled() => ${localVideoTrack.isEnabled}'); + _onVideoEnabledStreamController.add(localVideoTrack.isEnabled); + notifyListeners(); + } + + Future toggleAudioEnabled() async { + final tracks = _room.localParticipant.localAudioTracks; + final localAudioTrack = tracks.isEmpty ? null : tracks[0].localAudioTrack; + if (localAudioTrack == null) { + print('ConferenceRoom.toggleAudioEnabled() => Track is not available yet!'); + return; + } + await localAudioTrack.enable(!localAudioTrack.isEnabled); + + var index = _participants.indexWhere((ParticipantWidget participant) => !participant.isRemote); + if (index < 0) { + return; + } + var participant = _participants[index]; + _participants.replaceRange( + index, + index + 1, + [ + participant.copyWith(audioEnabled: localAudioTrack.isEnabled), + ], + ); + print('ConferenceRoom.toggleAudioEnabled() => ${localAudioTrack.isEnabled}'); + _onAudioEnabledStreamController.add(localAudioTrack.isEnabled); + notifyListeners(); + } + + Future switchCamera() async { + print('ConferenceRoom.switchCamera()'); + try { + await _cameraCapturer.switchCamera(); + } on FormatException catch (e) { + print( + 'ConferenceRoom.switchCamera() failed because of FormatException with message: ${e.message}', + ); + } + } + + void addDummy({Widget child}) { + print('ConferenceRoom.addDummy()'); + if (_participants.length >= 18) { + throw PlatformException( + code: 'ConferenceRoom.maximumReached', + message: 'Maximum reached', + details: 'Currently the lay-out can only render a maximum of 18 participants', + ); + } + _participants.insert( + 0, + ParticipantWidget( + id: (_participants.length + 1).toString(), + child: child, + isRemote: true, + audioEnabled: true, + videoEnabled: true, + isDummy: true, + ), + ); + notifyListeners(); + } + + void removeDummy() { + print('ConferenceRoom.removeDummy()'); + var dummy = _participants.firstWhere((participant) => participant.isDummy, orElse: () => null); + if (dummy != null) { + _participants.remove(dummy); + notifyListeners(); + } + } + + void _onConnected(Room room) { + print('ConferenceRoom._onConnected => state: ${room.state}'); + + // When connected for the first time, add remote participant listeners + _streamSubscriptions.add(_room.onParticipantConnected.listen(_onParticipantConnected)); + _streamSubscriptions.add(_room.onParticipantDisconnected.listen(_onParticipantDisconnected)); + _streamSubscriptions.add(_room.onDominantSpeakerChange.listen(_onDominantSpeakerChanged)); + // Only add ourselves when connected for the first time too. + _participants.add( + _buildParticipant( + child: room.localParticipant.localVideoTracks[0].localVideoTrack.widget(), + id: identity, + audioEnabled: true, + videoEnabled: true, + ), + ); + for (final remoteParticipant in room.remoteParticipants) { + var participant = _participants.firstWhere((participant) => participant.id == remoteParticipant.sid, orElse: () => null); + if (participant == null) { + print('Adding participant that was already present in the room ${remoteParticipant.sid}, before I connected'); + _addRemoteParticipantListeners(remoteParticipant); + } + } + // We have to listen for the [onDataTrackPublished] event on the [LocalParticipant] in + // order to be able to use the [send] method. + _streamSubscriptions.add(room.localParticipant.onDataTrackPublished.listen(_onLocalDataTrackPublished)); + notifyListeners(); + _completer.complete(room); + + _timer = Timer.periodic(const Duration(minutes: 1), (_) { + // Let's see if we can send some data over the DataTrack API + sendMessage('And another minute has passed since I connected...'); + // Also try the ByteBuffer way of sending data + final list = 'This data has been sent over the ByteBuffer channel of the DataTrack API'.codeUnits; + var bytes = Uint8List.fromList(list); + sendBufferMessage(bytes.buffer); + }); + } + + void _onLocalDataTrackPublished(LocalDataTrackPublishedEvent event) { + // Send buffered messages, if any... + while (_messages.isNotEmpty) { + var message = _messages.removeAt(0); + print('Sending buffered message: $message'); + event.localDataTrackPublication.localDataTrack.send(message); + } + } + + void _onConnectFailure(RoomConnectFailureEvent event) { + print('ConferenceRoom._onConnectFailure: ${event.exception}'); + _completer.completeError(event.exception); + } + + void _onDominantSpeakerChanged(DominantSpeakerChangedEvent event) { + print('ConferenceRoom._onDominantSpeakerChanged: ${event.remoteParticipant.identity}'); + var oldDominantParticipant = _participants.firstWhere((p) => p.isDominant, orElse: () => null); + if (oldDominantParticipant != null) { + var oldDominantParticipantIndex = _participants.indexOf(oldDominantParticipant); + _participants.replaceRange(oldDominantParticipantIndex, oldDominantParticipantIndex + 1, [oldDominantParticipant.copyWith(isDominant: false)]); + } + + var newDominantParticipant = _participants.firstWhere((p) => p.id == event.remoteParticipant.sid); + var newDominantParticipantIndex = _participants.indexOf(newDominantParticipant); + _participants.replaceRange(newDominantParticipantIndex, newDominantParticipantIndex + 1, [newDominantParticipant.copyWith(isDominant: true)]); + notifyListeners(); + } + + void _onParticipantConnected(RoomParticipantConnectedEvent event) { + print('ConferenceRoom._onParticipantConnected, ${event.remoteParticipant.sid}'); + _addRemoteParticipantListeners(event.remoteParticipant); + } + + void _onParticipantDisconnected(RoomParticipantDisconnectedEvent event) { + print('ConferenceRoom._onParticipantDisconnected: ${event.remoteParticipant.sid}'); + _participants.removeWhere((ParticipantWidget p) => p.id == event.remoteParticipant.sid); + notifyListeners(); + } + + ParticipantWidget _buildParticipant({ + @required Widget child, + @required String id, + @required bool audioEnabled, + @required bool videoEnabled, + RemoteParticipant remoteParticipant, + }) { + return ParticipantWidget( + id: remoteParticipant?.sid, + isRemote: remoteParticipant != null, + child: child, + audioEnabled: audioEnabled, + videoEnabled: videoEnabled, + ); + } + + void _addRemoteParticipantListeners(RemoteParticipant remoteParticipant) { + print('ConferenceRoom._addRemoteParticipantListeners() => Adding listeners to remoteParticipant ${remoteParticipant.sid}'); + _streamSubscriptions.add(remoteParticipant.onAudioTrackDisabled.listen(_onAudioTrackDisabled)); + _streamSubscriptions.add(remoteParticipant.onAudioTrackEnabled.listen(_onAudioTrackEnabled)); + _streamSubscriptions.add(remoteParticipant.onAudioTrackPublished.listen(_onAudioTrackPublished)); + _streamSubscriptions.add(remoteParticipant.onAudioTrackSubscribed.listen(_onAudioTrackSubscribed)); + _streamSubscriptions.add(remoteParticipant.onAudioTrackSubscriptionFailed.listen(_onAudioTrackSubscriptionFailed)); + _streamSubscriptions.add(remoteParticipant.onAudioTrackUnpublished.listen(_onAudioTrackUnpublished)); + _streamSubscriptions.add(remoteParticipant.onAudioTrackUnsubscribed.listen(_onAudioTrackUnsubscribed)); + + _streamSubscriptions.add(remoteParticipant.onDataTrackPublished.listen(_onDataTrackPublished)); + _streamSubscriptions.add(remoteParticipant.onDataTrackSubscribed.listen(_onDataTrackSubscribed)); + _streamSubscriptions.add(remoteParticipant.onDataTrackSubscriptionFailed.listen(_onDataTrackSubscriptionFailed)); + _streamSubscriptions.add(remoteParticipant.onDataTrackUnpublished.listen(_onDataTrackUnpublished)); + _streamSubscriptions.add(remoteParticipant.onDataTrackUnsubscribed.listen(_onDataTrackUnsubscribed)); + + _streamSubscriptions.add(remoteParticipant.onVideoTrackDisabled.listen(_onVideoTrackDisabled)); + _streamSubscriptions.add(remoteParticipant.onVideoTrackEnabled.listen(_onVideoTrackEnabled)); + _streamSubscriptions.add(remoteParticipant.onVideoTrackPublished.listen(_onVideoTrackPublished)); + _streamSubscriptions.add(remoteParticipant.onVideoTrackSubscribed.listen(_onVideoTrackSubscribed)); + _streamSubscriptions.add(remoteParticipant.onVideoTrackSubscriptionFailed.listen(_onVideoTrackSubscriptionFailed)); + _streamSubscriptions.add(remoteParticipant.onVideoTrackUnpublished.listen(_onVideoTrackUnpublished)); + _streamSubscriptions.add(remoteParticipant.onVideoTrackUnsubscribed.listen(_onVideoTrackUnsubscribed)); + } + + void _onAudioTrackDisabled(RemoteAudioTrackEvent event) { + print('ConferenceRoom._onAudioTrackDisabled(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}, isEnabled: ${event.remoteAudioTrackPublication.isTrackEnabled}'); + _setRemoteAudioEnabled(event); + } + + void _onAudioTrackEnabled(RemoteAudioTrackEvent event) { + print('ConferenceRoom._onAudioTrackEnabled(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}, isEnabled: ${event.remoteAudioTrackPublication.isTrackEnabled}'); + _setRemoteAudioEnabled(event); + } + + void _onAudioTrackPublished(RemoteAudioTrackEvent event) { + print('ConferenceRoom._onAudioTrackPublished(), ${event.remoteParticipant.sid}}'); + } + + void _onAudioTrackSubscribed(RemoteAudioTrackSubscriptionEvent event) { + print('ConferenceRoom._onAudioTrackSubscribed(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}'); + _addOrUpdateParticipant(event); + } + + void _onAudioTrackSubscriptionFailed(RemoteAudioTrackSubscriptionFailedEvent event) { + print('ConferenceRoom._onAudioTrackSubscriptionFailed(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}'); + _onExceptionStreamController.add( + PlatformException( + code: 'ConferenceRoom.audioTrackSubscriptionFailed', + message: 'AudioTrack Subscription Failed', + details: event.exception.toString(), + ), + ); + } + + void _onAudioTrackUnpublished(RemoteAudioTrackEvent event) { + print('ConferenceRoom._onAudioTrackUnpublished(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}'); + } + + void _onAudioTrackUnsubscribed(RemoteAudioTrackSubscriptionEvent event) { + print('ConferenceRoom._onAudioTrackUnsubscribed(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrack.sid}'); + } + + void _onDataTrackPublished(RemoteDataTrackEvent event) { + print('ConferenceRoom._onDataTrackPublished(), ${event.remoteParticipant.sid}}'); + } + + void _onDataTrackSubscribed(RemoteDataTrackSubscriptionEvent event) { + print('ConferenceRoom._onDataTrackSubscribed(), ${event.remoteParticipant.sid}, ${event.remoteDataTrackPublication.trackSid}'); + final dataTrack = event.remoteDataTrackPublication.remoteDataTrack; + _dataTracks.add(dataTrack); + _streamSubscriptions.add(dataTrack.onMessage.listen(_onMessage)); + _streamSubscriptions.add(dataTrack.onBufferMessage.listen(_onBufferMessage)); + } + + void _onDataTrackSubscriptionFailed(RemoteDataTrackSubscriptionFailedEvent event) { + print('ConferenceRoom._onDataTrackSubscriptionFailed(), ${event.remoteParticipant.sid}, ${event.remoteDataTrackPublication.trackSid}'); + _onExceptionStreamController.add( + PlatformException( + code: 'ConferenceRoom.dataTrackSubscriptionFailed', + message: 'DataTrack Subscription Failed', + details: event.exception.toString(), + ), + ); + } + + void _onDataTrackUnpublished(RemoteDataTrackEvent event) { + print('ConferenceRoom._onDataTrackUnpublished(), ${event.remoteParticipant.sid}, ${event.remoteDataTrackPublication.trackSid}'); + } + + void _onDataTrackUnsubscribed(RemoteDataTrackSubscriptionEvent event) { + print('ConferenceRoom._onDataTrackUnsubscribed(), ${event.remoteParticipant.sid}, ${event.remoteDataTrack.sid}'); + } + + void _onVideoTrackDisabled(RemoteVideoTrackEvent event) { + print('ConferenceRoom._onVideoTrackDisabled(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}, isEnabled: ${event.remoteVideoTrackPublication.isTrackEnabled}'); + _setRemoteVideoEnabled(event); + } + + void _onVideoTrackEnabled(RemoteVideoTrackEvent event) { + print('ConferenceRoom._onVideoTrackEnabled(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}, isEnabled: ${event.remoteVideoTrackPublication.isTrackEnabled}'); + _setRemoteVideoEnabled(event); + } + + void _onVideoTrackPublished(RemoteVideoTrackEvent event) { + print('ConferenceRoom._onVideoTrackPublished(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}'); + } + + void _onVideoTrackSubscribed(RemoteVideoTrackSubscriptionEvent event) { + print('ConferenceRoom._onVideoTrackSubscribed(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrack.sid}'); + _addOrUpdateParticipant(event); + } + + void _onVideoTrackSubscriptionFailed(RemoteVideoTrackSubscriptionFailedEvent event) { + print('ConferenceRoom._onVideoTrackSubscriptionFailed(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}'); + _onExceptionStreamController.add( + PlatformException( + code: 'ConferenceRoom.videoTrackSubscriptionFailed', + message: 'VideoTrack Subscription Failed', + details: event.exception.toString(), + ), + ); + } + + void _onVideoTrackUnpublished(RemoteVideoTrackEvent event) { + print('ConferenceRoom._onVideoTrackUnpublished(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}'); + } + + void _onVideoTrackUnsubscribed(RemoteVideoTrackSubscriptionEvent event) { + print('ConferenceRoom._onVideoTrackUnsubscribed(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrack.sid}'); + } + + void _onMessage(RemoteDataTrackStringMessageEvent event) { + print('onMessage => ${event.remoteDataTrack.sid}, ${event.message}'); + } + + void _onBufferMessage(RemoteDataTrackBufferMessageEvent event) { + print('onBufferMessage => ${event.remoteDataTrack.sid}, ${String.fromCharCodes(event.message.asUint8List())}'); + } + + void _setRemoteAudioEnabled(RemoteAudioTrackEvent event) { + if (event.remoteAudioTrackPublication == null) { + return; + } + var index = _participants.indexWhere((ParticipantWidget participant) => participant.id == event.remoteParticipant.sid); + if (index < 0) { + return; + } + var participant = _participants[index]; + _participants.replaceRange( + index, + index + 1, + [ + participant.copyWith(audioEnabled: event.remoteAudioTrackPublication.isTrackEnabled), + ], + ); + notifyListeners(); + } + + void _setRemoteVideoEnabled(RemoteVideoTrackEvent event) { + if (event.remoteVideoTrackPublication == null) { + return; + } + var index = _participants.indexWhere((ParticipantWidget participant) => participant.id == event.remoteParticipant.sid); + if (index < 0) { + return; + } + var participant = _participants[index]; + _participants.replaceRange( + index, + index + 1, + [ + participant.copyWith(videoEnabled: event.remoteVideoTrackPublication.isTrackEnabled), + ], + ); + notifyListeners(); + } + + void _addOrUpdateParticipant(RemoteParticipantEvent event) { + print('ConferenceRoom._addOrUpdateParticipant(), ${event.remoteParticipant.sid}'); + final participant = _participants.firstWhere( + (ParticipantWidget participant) => participant.id == event.remoteParticipant.sid, + orElse: () => null, + ); + if (participant != null) { + print('Participant found: ${participant.id}, updating A/V enabled values'); + _setRemoteVideoEnabled(event); + _setRemoteAudioEnabled(event); + } else { + final bufferedParticipant = _participantBuffer.firstWhere( + (ParticipantBuffer participant) => participant.id == event.remoteParticipant.sid, + orElse: () => null, + ); + if (bufferedParticipant != null) { + _participantBuffer.remove(bufferedParticipant); + } else if (event is RemoteAudioTrackEvent) { + print('Audio subscription came first, waiting for the video subscription...'); + _participantBuffer.add( + ParticipantBuffer( + id: event.remoteParticipant.sid, + audioEnabled: event.remoteAudioTrackPublication?.remoteAudioTrack?.isEnabled ?? true, + ), + ); + return; + } + if (event is RemoteVideoTrackSubscriptionEvent) { + print('New participant, adding: ${event.remoteParticipant.sid}'); + _participants.insert( + 0, + _buildParticipant( + child: event.remoteVideoTrack.widget(), + id: event.remoteParticipant.sid, + remoteParticipant: event.remoteParticipant, + audioEnabled: bufferedParticipant?.audioEnabled ?? true, + videoEnabled: event.remoteVideoTrackPublication?.remoteVideoTrack?.isEnabled ?? true, + ), + ); + } + notifyListeners(); + } + } } diff --git a/lib/pages/landing/landing_page.dart b/lib/pages/landing/landing_page.dart index 638e07ab..ff21d73e 100644 --- a/lib/pages/landing/landing_page.dart +++ b/lib/pages/landing/landing_page.dart @@ -78,35 +78,35 @@ class _LandingPageState extends State with WidgetsBindingObserver { print(route.settings.name); } - // setState(() { - // print("didChangeAppLifecycleState"); - // print('state = $state'); - // AppGlobal.context = context; - // if (state == AppLifecycleState.resumed) { - // print(LandingPage.isOpenCallPage); - // if (LandingPage.isOpenCallPage) { - // if (!isPageNavigated) { - // isPageNavigated = true; - // Navigator.push( - // context, - // MaterialPageRoute( - // builder: (context) => IncomingCall( - // incomingCallData: LandingPage.incomingCallData))) - // .then((value) { - // isPageNavigated = false; - // }); - // } - // } - // } - // - // if (state == AppLifecycleState.paused) { - // isPageNavigated = false; - // } - // - // if (state == AppLifecycleState.inactive) { - // isPageNavigated = false; - // } - // }); + setState(() { + print("didChangeAppLifecycleState"); + print('state = $state'); + AppGlobal.context = context; + if (state == AppLifecycleState.resumed) { + print(LandingPage.isOpenCallPage); + if (LandingPage.isOpenCallPage) { + if (!isPageNavigated) { + isPageNavigated = true; + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => IncomingCall( + incomingCallData: LandingPage.incomingCallData))) + .then((value) { + isPageNavigated = false; + }); + } + } + } + + if (state == AppLifecycleState.paused) { + isPageNavigated = false; + } + + if (state == AppLifecycleState.inactive) { + isPageNavigated = false; + } + }); } @override diff --git a/lib/pages/livecare/incoming_call.dart b/lib/pages/livecare/incoming_call.dart index 0f9208ea..de80b4ae 100644 --- a/lib/pages/livecare/incoming_call.dart +++ b/lib/pages/livecare/incoming_call.dart @@ -213,13 +213,13 @@ class _IncomingCallState extends State token: widget.incomingCallData.sessionId, identity: widget.incomingCallData.identity); - // await Navigator.of(context).push( - // MaterialPageRoute( - // fullscreenDialog: true, - // builder: (BuildContext context) => - // ConferencePage(roomModel: roomModel), - // ), - // ); + await Navigator.of(context).push( + MaterialPageRoute( + fullscreenDialog: true, + builder: (BuildContext context) => + ConferencePage(roomModel: roomModel), + ), + ); } catch (err) { print(err); await PlatformExceptionAlertDialog( diff --git a/pubspec.yaml b/pubspec.yaml index 0f46e89f..f7e1e67a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -144,7 +144,7 @@ dependencies: # recase: ^3.0.0 wakelock: ^0.1.4 after_layout: ^1.0.7 - #twilio_programmable_video: ^0.5.0+3 + twilio_programmable_video: ^0.5.0+3 flutter_tts: ^1.2.6 speech_to_text: