diff --git a/lib/pods/chat/call.dart b/lib/pods/chat/call.dart index 8d3f10e4..b17af936 100644 --- a/lib/pods/chat/call.dart +++ b/lib/pods/chat/call.dart @@ -1,9 +1,11 @@ import 'dart:async'; import 'dart:io'; import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_webrtc/flutter_webrtc.dart'; +import 'package:island/widgets/alert.dart'; import 'package:island/widgets/chat/call_button.dart'; -import 'package:livekit_client/livekit_client.dart'; +import 'package:livekit_client/livekit_client.dart' as lk; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:island/pods/network.dart'; @@ -41,7 +43,7 @@ sealed class CallParticipantLive with _$CallParticipantLive { const factory CallParticipantLive({ required CallParticipant participant, - required Participant remoteParticipant, + required lk.Participant remoteParticipant, }) = _CallParticipantLive; bool get isSpeaking => remoteParticipant.isSpeaking; @@ -57,21 +59,21 @@ sealed class CallParticipantLive with _$CallParticipantLive { @Riverpod(keepAlive: true) class CallNotifier extends _$CallNotifier { - Room? _room; - LocalParticipant? _localParticipant; + lk.Room? _room; + lk.LocalParticipant? _localParticipant; List _participants = []; final Map _participantInfoByIdentity = {}; - EventsListener? _roomListener; + lk.EventsListener? _roomListener; List get participants => List.unmodifiable(_participants); - LocalParticipant? get localParticipant => _localParticipant; + lk.LocalParticipant? get localParticipant => _localParticipant; Map participantsVolumes = {}; Timer? _durationTimer; - Room? get room => _room; + lk.Room? get room => _room; @override CallState build() { @@ -91,10 +93,10 @@ class CallNotifier extends _$CallNotifier { _roomListener = _room!.createListener(); _room!.addListener(_onRoomChange); _roomListener! - ..on((e) { + ..on((e) { _refreshLiveParticipants(); }) - ..on((e) { + ..on((e) { _participants = []; state = state.copyWith(); }); @@ -188,7 +190,7 @@ class CallNotifier extends _$CallNotifier { // Add remote participants _participants.addAll( participants.map((p) { - RemoteParticipant? remote; + lk.RemoteParticipant? remote; for (final r in remotes) { if (r.identity == p.identity) { remote = r; @@ -216,7 +218,7 @@ class CallNotifier extends _$CallNotifier { return; } else if (_room != null) { if (!_room!.isDisposed && - _room!.connectionState != ConnectionState.disconnected) { + _room!.connectionState != lk.ConnectionState.disconnected) { throw Exception('Call already connected'); } } @@ -256,15 +258,15 @@ class CallNotifier extends _$CallNotifier { }); // Connect to LiveKit - _room = Room(); + _room = lk.Room(); await _room!.connect( endpoint, token, - connectOptions: ConnectOptions(autoSubscribe: true), - roomOptions: RoomOptions(adaptiveStream: true, dynacast: true), - fastConnectOptions: FastConnectOptions( - microphone: TrackOption(enabled: true), + connectOptions: lk.ConnectOptions(autoSubscribe: true), + roomOptions: lk.RoomOptions(adaptiveStream: true, dynacast: true), + fastConnectOptions: lk.FastConnectOptions( + microphone: lk.TrackOption(enabled: true), ), ); _localParticipant = _room!.localParticipant; @@ -273,14 +275,14 @@ class CallNotifier extends _$CallNotifier { _updateLiveParticipants(participants); if (!kIsWeb && (Platform.isIOS || Platform.isAndroid)) { - Hardware.instance.setSpeakerphoneOn(true); + lk.Hardware.instance.setSpeakerphoneOn(true); } // Listen for connection updates _room!.addListener(() { final wasConnected = state.isConnected; final isNowConnected = - _room!.connectionState == ConnectionState.connected; + _room!.connectionState == lk.ConnectionState.connected; state = state.copyWith( isConnected: isNowConnected, isMicrophoneEnabled: _localParticipant!.isMicrophoneEnabled(), @@ -334,18 +336,43 @@ class CallNotifier extends _$CallNotifier { } } - Future toggleScreenShare() async { + Future toggleScreenShare(BuildContext context) async { if (_localParticipant != null) { final target = !_localParticipant!.isScreenShareEnabled(); state = state.copyWith(isScreenSharing: target); - await _localParticipant!.setScreenShareEnabled(target); + + if (target && lk.lkPlatformIsDesktop()) { + try { + final source = await showDialog( + context: context, + builder: (context) => lk.ScreenSelectDialog(), + ); + if (source == null) { + return; + } + var track = await lk.LocalVideoTrack.createScreenShareTrack( + lk.ScreenShareCaptureOptions( + sourceId: source.id, + maxFrameRate: 30.0, + captureScreenAudio: true, + ), + ); + await _localParticipant!.publishVideoTrack(track); + } catch (err) { + showErrorAlert(err); + } + return; + } else { + await _localParticipant!.setScreenShareEnabled(target); + } + state = state.copyWith(); } } Future toggleSpeakerphone() async { state = state.copyWith(isSpeakerphone: !state.isSpeakerphone); - await Hardware.instance.setSpeakerphoneOn(state.isSpeakerphone); + await lk.Hardware.instance.setSpeakerphoneOn(state.isSpeakerphone); state = state.copyWith(); } diff --git a/lib/pods/chat/call.freezed.dart b/lib/pods/chat/call.freezed.dart index 4c9c65aa..f1254f9b 100644 --- a/lib/pods/chat/call.freezed.dart +++ b/lib/pods/chat/call.freezed.dart @@ -295,7 +295,7 @@ as String?, /// @nodoc mixin _$CallParticipantLive implements DiagnosticableTreeMixin { - CallParticipant get participant; Participant get remoteParticipant; + CallParticipant get participant; lk.Participant get remoteParticipant; /// Create a copy of CallParticipantLive /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -332,7 +332,7 @@ abstract mixin class $CallParticipantLiveCopyWith<$Res> { factory $CallParticipantLiveCopyWith(CallParticipantLive value, $Res Function(CallParticipantLive) _then) = _$CallParticipantLiveCopyWithImpl; @useResult $Res call({ - CallParticipant participant, Participant remoteParticipant + CallParticipant participant, lk.Participant remoteParticipant }); @@ -353,7 +353,7 @@ class _$CallParticipantLiveCopyWithImpl<$Res> return _then(_self.copyWith( participant: null == participant ? _self.participant : participant // ignore: cast_nullable_to_non_nullable as CallParticipant,remoteParticipant: null == remoteParticipant ? _self.remoteParticipant : remoteParticipant // ignore: cast_nullable_to_non_nullable -as Participant, +as lk.Participant, )); } /// Create a copy of CallParticipantLive @@ -444,7 +444,7 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult maybeWhen(TResult Function( CallParticipant participant, Participant remoteParticipant)? $default,{required TResult orElse(),}) {final _that = this; +@optionalTypeArgs TResult maybeWhen(TResult Function( CallParticipant participant, lk.Participant remoteParticipant)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { case _CallParticipantLive() when $default != null: return $default(_that.participant,_that.remoteParticipant);case _: @@ -465,7 +465,7 @@ return $default(_that.participant,_that.remoteParticipant);case _: /// } /// ``` -@optionalTypeArgs TResult when(TResult Function( CallParticipant participant, Participant remoteParticipant) $default,) {final _that = this; +@optionalTypeArgs TResult when(TResult Function( CallParticipant participant, lk.Participant remoteParticipant) $default,) {final _that = this; switch (_that) { case _CallParticipantLive(): return $default(_that.participant,_that.remoteParticipant);} @@ -482,7 +482,7 @@ return $default(_that.participant,_that.remoteParticipant);} /// } /// ``` -@optionalTypeArgs TResult? whenOrNull(TResult? Function( CallParticipant participant, Participant remoteParticipant)? $default,) {final _that = this; +@optionalTypeArgs TResult? whenOrNull(TResult? Function( CallParticipant participant, lk.Participant remoteParticipant)? $default,) {final _that = this; switch (_that) { case _CallParticipantLive() when $default != null: return $default(_that.participant,_that.remoteParticipant);case _: @@ -501,7 +501,7 @@ class _CallParticipantLive extends CallParticipantLive with DiagnosticableTreeMi @override final CallParticipant participant; -@override final Participant remoteParticipant; +@override final lk.Participant remoteParticipant; /// Create a copy of CallParticipantLive /// with the given fields replaced by the non-null parameter values. @@ -539,7 +539,7 @@ abstract mixin class _$CallParticipantLiveCopyWith<$Res> implements $CallPartici factory _$CallParticipantLiveCopyWith(_CallParticipantLive value, $Res Function(_CallParticipantLive) _then) = __$CallParticipantLiveCopyWithImpl; @override @useResult $Res call({ - CallParticipant participant, Participant remoteParticipant + CallParticipant participant, lk.Participant remoteParticipant }); @@ -560,7 +560,7 @@ class __$CallParticipantLiveCopyWithImpl<$Res> return _then(_CallParticipantLive( participant: null == participant ? _self.participant : participant // ignore: cast_nullable_to_non_nullable as CallParticipant,remoteParticipant: null == remoteParticipant ? _self.remoteParticipant : remoteParticipant // ignore: cast_nullable_to_non_nullable -as Participant, +as lk.Participant, )); } diff --git a/lib/pods/chat/call.g.dart b/lib/pods/chat/call.g.dart index bae843ea..5c4db434 100644 --- a/lib/pods/chat/call.g.dart +++ b/lib/pods/chat/call.g.dart @@ -6,7 +6,7 @@ part of 'call.dart'; // RiverpodGenerator // ************************************************************************** -String _$callNotifierHash() => r'd374402e51d331cf40724e51fd86bce3c5504776'; +String _$callNotifierHash() => r'438b516c8b786f47495a3435e22b400cd0ca2772'; /// See also [CallNotifier]. @ProviderFor(CallNotifier) diff --git a/lib/widgets/chat/call_overlay.dart b/lib/widgets/chat/call_overlay.dart index 4d0f0f16..2688c48a 100644 --- a/lib/widgets/chat/call_overlay.dart +++ b/lib/widgets/chat/call_overlay.dart @@ -42,7 +42,7 @@ class CallControlsBar extends HookConsumerWidget { callState.isScreenSharing ? Icons.stop_screen_share : Icons.screen_share, - onPressed: () => callNotifier.toggleScreenShare(), + onPressed: () => callNotifier.toggleScreenShare(context), backgroundColor: const Color(0xFF424242), ), _buildCircularButtonWithDropdown( @@ -383,7 +383,7 @@ class CallOverlayBar extends HookConsumerWidget { : Icons.screen_share, ), onPressed: () { - callNotifier.toggleScreenShare(); + callNotifier.toggleScreenShare(context); }, style: actionButtonStyle, ),