Compare commits

..

No commits in common. "3585941ccb8e96b75c2b9f7ce08f00f975ef6bcd" and "61dbf9290997d4db57c5f9d1bd3ed20e3e3a9a0b" have entirely different histories.

10 changed files with 325 additions and 483 deletions

View File

@ -2,7 +2,6 @@ import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:livekit_client/livekit_client.dart'; import 'package:livekit_client/livekit_client.dart';
import 'package:livekit_noise_filter/livekit_noise_filter.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:surface/providers/sn_network.dart'; import 'package:surface/providers/sn_network.dart';
@ -132,14 +131,10 @@ class ChatCallProvider extends ChangeNotifier {
void initRoom() { void initRoom() {
initHardware(); initHardware();
final timeout = const Duration(seconds: 60);
_room = Room( _room = Room(
roomOptions: RoomOptions( roomOptions: const RoomOptions(
dynacast: true, dynacast: true,
adaptiveStream: true, adaptiveStream: true,
defaultAudioCaptureOptions: AudioCaptureOptions(
processor: LiveKitNoiseFilter(),
),
defaultAudioPublishOptions: AudioPublishOptions( defaultAudioPublishOptions: AudioPublishOptions(
name: 'call_voice', name: 'call_voice',
stream: 'call_stream', stream: 'call_stream',
@ -159,16 +154,6 @@ class ChatCallProvider extends ChangeNotifier {
params: VideoParametersPresets.h1080_169, params: VideoParametersPresets.h1080_169,
), ),
), ),
connectOptions: ConnectOptions(
autoSubscribe: true,
timeouts: Timeouts(
connection: timeout,
debounce: timeout,
publish: timeout,
peerConnection: timeout,
iceRestart: timeout,
),
),
); );
_listener = _room.createListener(); _listener = _room.createListener();
WakelockPlus.enable(); WakelockPlus.enable();

View File

@ -32,7 +32,7 @@ class _CallRoomScreenState extends State<CallRoomScreen> {
} }
} }
Widget _buildMeetLayout() { Widget _buildListLayout() {
final call = context.read<ChatCallProvider>(); final call = context.read<ChatCallProvider>();
return Stack( return Stack(
children: [ children: [
@ -41,7 +41,9 @@ class _CallRoomScreenState extends State<CallRoomScreen> {
Theme.of(context).colorScheme.surfaceContainer.withOpacity(0.75), Theme.of(context).colorScheme.surfaceContainer.withOpacity(0.75),
child: call.focusTrack != null child: call.focusTrack != null
? InteractiveParticipantWidget( ? InteractiveParticipantWidget(
isFixedAvatar: false,
participant: call.focusTrack!, participant: call.focusTrack!,
onTap: () {},
) )
: const SizedBox.shrink(), : const SizedBox.shrink(),
), ),
@ -60,18 +62,23 @@ class _CallRoomScreenState extends State<CallRoomScreen> {
return Container(); return Container();
} }
return SizedBox( return Padding(
height: 128, padding: const EdgeInsets.only(top: 8, left: 8),
width: 128, child: ClipRRect(
child: InteractiveParticipantWidget( borderRadius: const BorderRadius.all(Radius.circular(8)),
participant: track, child: InteractiveParticipantWidget(
avatarSize: 32, isFixedAvatar: true,
onTap: () { width: 120,
if (track.participant.sid != height: 120,
call.focusTrack?.participant.sid) { color: Theme.of(context).cardColor,
call.setFocusTrack(track); participant: track,
} onTap: () {
}, if (track.participant.sid !=
call.focusTrack?.participant.sid) {
call.setFocusTrack(track);
}
},
),
), ),
); );
}, },
@ -82,26 +89,50 @@ class _CallRoomScreenState extends State<CallRoomScreen> {
); );
} }
Widget _buildListLayout() { Widget _buildGridLayout() {
final call = context.read<ChatCallProvider>(); final call = context.read<ChatCallProvider>();
return LayoutBuilder( return LayoutBuilder(builder: (context, constraints) {
builder: (context, constraints) { double screenWidth = constraints.maxWidth;
return ListView.builder( double screenHeight = constraints.maxHeight;
padding: EdgeInsets.zero,
itemCount: math.max(0, call.participantTracks.length), int columns = (math.sqrt(call.participantTracks.length)).ceil();
itemBuilder: (BuildContext context, int index) { int rows = (call.participantTracks.length / columns).ceil();
final track = call.participantTracks[index];
return InteractiveParticipantWidget( double tileWidth = screenWidth / columns;
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), double tileHeight = screenHeight / rows;
isList: true,
avatarSize: 24, return StyledWidget(GridView.builder(
participant: track, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
); crossAxisCount: columns,
}, childAspectRatio: tileWidth / tileHeight,
); crossAxisSpacing: 8,
}, mainAxisSpacing: 8,
); ),
itemCount: math.max(0, call.participantTracks.length),
itemBuilder: (BuildContext context, int index) {
final track = call.participantTracks[index];
return Card(
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: InteractiveParticipantWidget(
color: Theme.of(context)
.colorScheme
.surfaceContainerHigh
.withOpacity(0.75),
participant: track,
onTap: () {
if (track.participant.sid !=
call.focusTrack?.participant.sid) {
call.setFocusTrack(track);
}
},
),
),
);
},
)).padding(all: 8);
});
} }
@override @override
@ -145,129 +176,131 @@ class _CallRoomScreenState extends State<CallRoomScreen> {
]), ]),
), ),
), ),
body: Column( body: GestureDetector(
children: [ behavior: HitTestBehavior.translucent,
SizedBox( child: Column(
width: MediaQuery.of(context).size.width, children: [
height: 64,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Builder(builder: (context) {
final call = context.read<ChatCallProvider>();
final connectionQuality =
call.room.localParticipant?.connectionQuality ??
livekit.ConnectionQuality.unknown;
return Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
call.channel?.name ?? 'unknown'.tr(),
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
const Gap(6),
Text(call.lastDuration.toString())
],
),
Row(
children: [
Text(
{
livekit.ConnectionState.disconnected:
'callStatusDisconnected'.tr(),
livekit.ConnectionState.connected:
'callStatusConnected'.tr(),
livekit.ConnectionState.connecting:
'callStatusConnecting'.tr(),
livekit.ConnectionState.reconnecting:
'callStatusReconnecting'.tr(),
}[call.room.connectionState]!,
),
const Gap(6),
if (connectionQuality !=
livekit.ConnectionQuality.unknown)
Icon(
{
livekit.ConnectionQuality.excellent:
Icons.signal_cellular_alt,
livekit.ConnectionQuality.good:
Icons.signal_cellular_alt_2_bar,
livekit.ConnectionQuality.poor:
Icons.signal_cellular_alt_1_bar,
}[connectionQuality],
color: {
livekit.ConnectionQuality.excellent:
Colors.green,
livekit.ConnectionQuality.good:
Colors.orange,
livekit.ConnectionQuality.poor:
Colors.red,
}[connectionQuality],
size: 16,
)
else
const SizedBox(
width: 12,
height: 12,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2,
padding: EdgeInsets.zero,
),
).padding(all: 3),
],
),
],
),
);
}),
Row(
children: [
IconButton(
icon: _layoutMode == 0
? const Icon(Icons.view_list)
: const Icon(Icons.grid_view),
onPressed: () {
_switchLayout();
},
),
],
),
],
).padding(left: 20, right: 16),
),
Expanded(
child: Material(
color: Theme.of(context).colorScheme.surfaceContainerLow,
child: Builder(
builder: (context) {
switch (_layoutMode) {
case 1:
return _buildListLayout();
default:
return _buildMeetLayout();
}
},
),
),
),
if (call.room.localParticipant != null)
SizedBox( SizedBox(
width: MediaQuery.of(context).size.width, width: MediaQuery.of(context).size.width,
child: ControlsWidget( height: 64,
call.room, child: Row(
call.room.localParticipant!, mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Builder(builder: (context) {
final call = context.read<ChatCallProvider>();
final connectionQuality =
call.room.localParticipant?.connectionQuality ??
livekit.ConnectionQuality.unknown;
return Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
call.channel?.name ?? 'unknown'.tr(),
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
const Gap(6),
Text(call.lastDuration.toString())
],
),
Row(
children: [
Text(
{
livekit.ConnectionState.disconnected:
'callStatusDisconnected'.tr(),
livekit.ConnectionState.connected:
'callStatusConnected'.tr(),
livekit.ConnectionState.connecting:
'callStatusConnecting'.tr(),
livekit.ConnectionState.reconnecting:
'callStatusReconnecting'.tr(),
}[call.room.connectionState]!,
),
const Gap(6),
if (connectionQuality !=
livekit.ConnectionQuality.unknown)
Icon(
{
livekit.ConnectionQuality.excellent:
Icons.signal_cellular_alt,
livekit.ConnectionQuality.good:
Icons.signal_cellular_alt_2_bar,
livekit.ConnectionQuality.poor:
Icons.signal_cellular_alt_1_bar,
}[connectionQuality],
color: {
livekit.ConnectionQuality.excellent:
Colors.green,
livekit.ConnectionQuality.good:
Colors.orange,
livekit.ConnectionQuality.poor:
Colors.red,
}[connectionQuality],
size: 16,
)
else
const SizedBox(
width: 12,
height: 12,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2,
),
).padding(all: 3),
],
),
],
),
);
}),
Row(
children: [
IconButton(
icon: _layoutMode == 0
? const Icon(Icons.view_list)
: const Icon(Icons.grid_view),
onPressed: () {
_switchLayout();
},
),
],
),
],
).padding(left: 20, right: 16),
),
Expanded(
child: Material(
color: Theme.of(context).colorScheme.surfaceContainerLow,
child: Builder(
builder: (context) {
switch (_layoutMode) {
case 1:
return _buildGridLayout();
default:
return _buildListLayout();
}
},
),
), ),
), ),
Gap(MediaQuery.of(context).padding.bottom), if (call.room.localParticipant != null)
], SizedBox(
width: MediaQuery.of(context).size.width,
child: ControlsWidget(
call.room,
call.room.localParticipant!,
),
),
],
),
onTap: () {},
), ),
); );
}); });

View File

@ -54,15 +54,11 @@ class AccountImage extends StatelessWidget {
)) ))
.center(), .center(),
) )
: UniversalImage( : AutoResizeUniversalImage(
sn.getAttachmentUrl(url), sn.getAttachmentUrl(url),
filterQuality: filterQuality, filterQuality: filterQuality,
key: Key('attachment-${content.hashCode}'), key: Key('attachment-${content.hashCode}'),
fit: BoxFit.cover, fit: BoxFit.cover,
width: (radius != null ? radius! : 20) * 2,
height: (radius != null ? radius! : 20) * 2,
cacheWidth: (radius != null ? radius! : 20) * 2,
cacheHeight: (radius != null ? radius! : 20) * 2,
), ),
), ),
), ),

View File

@ -8,12 +8,12 @@ import 'package:surface/widgets/account/account_image.dart';
class NoContentWidget extends StatefulWidget { class NoContentWidget extends StatefulWidget {
final SnAccount? userinfo; final SnAccount? userinfo;
final bool isSpeaking; final bool isSpeaking;
final double? avatarSize; final bool isFixed;
const NoContentWidget({ const NoContentWidget({
super.key, super.key,
this.userinfo, this.userinfo,
this.avatarSize, this.isFixed = false,
required this.isSpeaking, required this.isSpeaking,
}); });
@ -45,35 +45,41 @@ class _NoContentWidgetState extends State<NoContentWidget>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final double radius = widget.avatarSize ?? final double radius = widget.isFixed
math.min( ? 32
MediaQuery.of(context).size.width * 0.1, : math.min(
MediaQuery.of(context).size.height * 0.1, MediaQuery.of(context).size.width * 0.1,
); MediaQuery.of(context).size.height * 0.1,
);
return Animate( return Container(
autoPlay: false, alignment: Alignment.center,
controller: _animationController, child: Center(
effects: [ child: Animate(
CustomEffect( autoPlay: false,
begin: widget.isSpeaking ? 2 : 0, controller: _animationController,
end: 8, effects: [
curve: Curves.easeInOut, CustomEffect(
duration: 1250.ms, begin: widget.isSpeaking ? 2 : 0,
builder: (context, value, child) => Container( end: 8,
decoration: BoxDecoration( curve: Curves.easeInOut,
borderRadius: BorderRadius.all(Radius.circular(radius + 8)), duration: 1250.ms,
border: value > 0 builder: (context, value, child) => Container(
? Border.all(color: Colors.green, width: value) decoration: BoxDecoration(
: null, borderRadius: BorderRadius.all(Radius.circular(radius + 8)),
), border: value > 0
child: child, ? Border.all(color: Colors.green, width: value)
: null,
),
child: child,
),
)
],
child: AccountImage(
content: widget.userinfo?.avatar,
radius: radius,
), ),
) ),
],
child: AccountImage(
content: widget.userinfo?.avatar,
radius: radius,
), ),
); );
} }

View File

@ -2,9 +2,7 @@ import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart'; import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:gap/gap.dart';
import 'package:livekit_client/livekit_client.dart'; import 'package:livekit_client/livekit_client.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:surface/types/account.dart'; import 'package:surface/types/account.dart';
import 'package:surface/types/chat.dart'; import 'package:surface/types/chat.dart';
import 'package:surface/widgets/chat/call/call_no_content.dart'; import 'package:surface/widgets/chat/call/call_no_content.dart';
@ -13,32 +11,23 @@ import 'package:surface/widgets/chat/call/call_participant_menu.dart';
import 'package:surface/widgets/chat/call/call_participant_stats.dart'; import 'package:surface/widgets/chat/call/call_participant_stats.dart';
abstract class ParticipantWidget extends StatefulWidget { abstract class ParticipantWidget extends StatefulWidget {
static ParticipantWidget widgetFor( static ParticipantWidget widgetFor(ParticipantTrack participantTrack,
ParticipantTrack participantTrack, { {bool isFixed = false, bool showStatsLayer = false}) {
double? avatarSize,
EdgeInsets? padding,
bool showStatsLayer = false,
bool isList = false,
}) {
if (participantTrack.participant is LocalParticipant) { if (participantTrack.participant is LocalParticipant) {
return LocalParticipantWidget( return LocalParticipantWidget(
participantTrack.participant as LocalParticipant, participantTrack.participant as LocalParticipant,
participantTrack.videoTrack, participantTrack.videoTrack,
avatarSize, isFixed,
participantTrack.isScreenShare, participantTrack.isScreenShare,
showStatsLayer, showStatsLayer,
isList,
padding,
); );
} else if (participantTrack.participant is RemoteParticipant) { } else if (participantTrack.participant is RemoteParticipant) {
return RemoteParticipantWidget( return RemoteParticipantWidget(
participantTrack.participant as RemoteParticipant, participantTrack.participant as RemoteParticipant,
participantTrack.videoTrack, participantTrack.videoTrack,
avatarSize, isFixed,
participantTrack.isScreenShare, participantTrack.isScreenShare,
showStatsLayer, showStatsLayer,
isList,
padding,
); );
} }
throw UnimplementedError('Unknown participant type'); throw UnimplementedError('Unknown participant type');
@ -47,10 +36,8 @@ abstract class ParticipantWidget extends StatefulWidget {
abstract final Participant participant; abstract final Participant participant;
abstract final VideoTrack? videoTrack; abstract final VideoTrack? videoTrack;
abstract final bool isScreenShare; abstract final bool isScreenShare;
abstract final double? avatarSize; abstract final bool isFixed;
abstract final bool showStatsLayer; abstract final bool showStatsLayer;
abstract final bool isList;
abstract final EdgeInsets? padding;
final VideoQuality quality; final VideoQuality quality;
const ParticipantWidget({ const ParticipantWidget({
@ -65,24 +52,18 @@ class LocalParticipantWidget extends ParticipantWidget {
@override @override
final VideoTrack? videoTrack; final VideoTrack? videoTrack;
@override @override
final double? avatarSize; final bool isFixed;
@override @override
final bool isScreenShare; final bool isScreenShare;
@override @override
final bool showStatsLayer; final bool showStatsLayer;
@override
final bool isList;
@override
final EdgeInsets? padding;
const LocalParticipantWidget( const LocalParticipantWidget(
this.participant, this.participant,
this.videoTrack, this.videoTrack,
this.avatarSize, this.isFixed,
this.isScreenShare, this.isScreenShare,
this.showStatsLayer, this.showStatsLayer, {
this.isList,
this.padding, {
super.key, super.key,
}); });
@ -96,24 +77,18 @@ class RemoteParticipantWidget extends ParticipantWidget {
@override @override
final VideoTrack? videoTrack; final VideoTrack? videoTrack;
@override @override
final double? avatarSize; final bool isFixed;
@override @override
final bool isScreenShare; final bool isScreenShare;
@override @override
final bool showStatsLayer; final bool showStatsLayer;
@override
final bool isList;
@override
final EdgeInsets? padding;
const RemoteParticipantWidget( const RemoteParticipantWidget(
this.participant, this.participant,
this.videoTrack, this.videoTrack,
this.avatarSize, this.isFixed,
this.isScreenShare, this.isScreenShare,
this.showStatsLayer, this.showStatsLayer, {
this.isList,
this.padding, {
super.key, super.key,
}); });
@ -161,82 +136,19 @@ abstract class _ParticipantWidgetState<T extends ParticipantWidget>
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext ctx) {
if (widget.isList) {
return Padding(
padding: widget.padding ?? EdgeInsets.zero,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
SizedBox(
width: (widget.avatarSize ?? 32) * 2,
height: (widget.avatarSize ?? 32) * 2,
child: Center(
child: NoContentWidget(
userinfo: _userinfoMetadata,
avatarSize: widget.avatarSize,
isSpeaking: widget.participant.isSpeaking,
),
),
),
const Gap(8),
Expanded(
child: SizedBox(
height: (widget.avatarSize ?? 32) * 2,
child: ParticipantInfoWidget(
isList: true,
title: widget.participant.name.isNotEmpty
? widget.participant.name
: widget.participant.identity,
audioAvailable: _firstAudioPublication?.muted == false &&
_firstAudioPublication?.subscribed == true,
connectionQuality: widget.participant.connectionQuality,
isScreenShare: widget.isScreenShare,
),
),
),
],
),
if (_activeVideoTrack != null && !_activeVideoTrack!.muted)
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: AspectRatio(
aspectRatio: 16 / 9,
child: Material(
borderRadius: const BorderRadius.all(Radius.circular(8)),
color: Theme.of(context)
.colorScheme
.surfaceContainer
.withOpacity(0.75),
child: VideoTrackRenderer(
_activeVideoTrack!,
fit: RTCVideoViewObjectFit.RTCVideoViewObjectFitContain,
),
),
).padding(top: 8),
),
],
),
);
}
return Stack( return Stack(
children: [ children: [
if (_activeVideoTrack != null && !_activeVideoTrack!.muted) _activeVideoTrack != null && !_activeVideoTrack!.muted
VideoTrackRenderer( ? VideoTrackRenderer(
_activeVideoTrack!, _activeVideoTrack!,
fit: RTCVideoViewObjectFit.RTCVideoViewObjectFitContain, fit: RTCVideoViewObjectFit.RTCVideoViewObjectFitContain,
) )
else : NoContentWidget(
Center( userinfo: _userinfoMetadata,
child: NoContentWidget( isFixed: widget.isFixed,
userinfo: _userinfoMetadata, isSpeaking: widget.participant.isSpeaking,
avatarSize: widget.avatarSize, ),
isSpeaking: widget.participant.isSpeaking,
),
),
if (widget.showStatsLayer) if (widget.showStatsLayer)
Positioned( Positioned(
top: 30, top: 30,
@ -287,51 +199,44 @@ class _RemoteParticipantWidgetState
} }
class InteractiveParticipantWidget extends StatelessWidget { class InteractiveParticipantWidget extends StatelessWidget {
final double? avatarSize; final double? width;
final bool isList; final double? height;
final Color? color;
final bool isFixedAvatar;
final ParticipantTrack participant; final ParticipantTrack participant;
final Function? onTap; final Function() onTap;
final EdgeInsets? padding;
const InteractiveParticipantWidget({ const InteractiveParticipantWidget({
super.key, super.key,
this.avatarSize, this.width,
this.isList = false, this.height,
this.padding, this.color,
this.isFixedAvatar = false,
required this.participant, required this.participant,
this.onTap, required this.onTap,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Material( return GestureDetector(
color: Colors.transparent, child: Container(
child: InkWell( width: width,
onTap: onTap != null height: height,
? () { color: color,
onTap?.call(); child: ParticipantWidget.widgetFor(participant, isFixed: isFixedAvatar),
}
: null,
onLongPress: () {
if (participant.participant is LocalParticipant) return;
showModalBottomSheet(
context: context,
builder: (context) => ParticipantMenu(
participant: participant.participant as RemoteParticipant,
videoTrack: participant.videoTrack,
isScreenShare: participant.isScreenShare,
),
);
},
child: Container(
child: ParticipantWidget.widgetFor(
participant,
avatarSize: avatarSize,
isList: isList,
padding: padding,
),
),
), ),
onTap: () => onTap(),
onLongPress: () {
if (participant.participant is LocalParticipant) return;
showModalBottomSheet(
context: context,
builder: (context) => ParticipantMenu(
participant: participant.participant as RemoteParticipant,
videoTrack: participant.videoTrack,
isScreenShare: participant.isScreenShare,
),
);
},
); );
} }
} }

View File

@ -9,7 +9,6 @@ class ParticipantInfoWidget extends StatelessWidget {
final bool audioAvailable; final bool audioAvailable;
final ConnectionQuality connectionQuality; final ConnectionQuality connectionQuality;
final bool isScreenShare; final bool isScreenShare;
final bool isList;
const ParticipantInfoWidget({ const ParticipantInfoWidget({
super.key, super.key,
@ -17,124 +16,64 @@ class ParticipantInfoWidget extends StatelessWidget {
this.audioAvailable = true, this.audioAvailable = true,
this.connectionQuality = ConnectionQuality.unknown, this.connectionQuality = ConnectionQuality.unknown,
this.isScreenShare = false, this.isScreenShare = false,
this.isList = false,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) => Container(
if (isList) { color: Theme.of(context).colorScheme.onSurface.withOpacity(0.75),
return Column( padding: const EdgeInsets.symmetric(
mainAxisAlignment: MainAxisAlignment.center, vertical: 7,
crossAxisAlignment: CrossAxisAlignment.start, horizontal: 10,
children: [ ),
if (title != null) child: Row(
Text( mainAxisAlignment: MainAxisAlignment.end,
title!, crossAxisAlignment: CrossAxisAlignment.center,
overflow: TextOverflow.ellipsis, children: [
style: const TextStyle( if (title != null)
color: Colors.white, Flexible(
fontWeight: FontWeight.bold, child: Text(
), title!,
).padding(left: 2), overflow: TextOverflow.ellipsis,
Row( style: const TextStyle(color: Colors.white),
children: [
isScreenShare
? const Icon(
Symbols.monitor,
color: Colors.white,
size: 16,
)
: Icon(
audioAvailable ? Symbols.mic : Symbols.mic_off,
color: audioAvailable ? Colors.white : Colors.red,
size: 16,
),
const Gap(3),
if (connectionQuality != ConnectionQuality.unknown)
Icon(
{
ConnectionQuality.excellent: Symbols.signal_cellular_alt,
ConnectionQuality.good: Symbols.signal_cellular_alt_2_bar,
ConnectionQuality.poor: Symbols.signal_cellular_alt_1_bar,
}[connectionQuality],
color: {
ConnectionQuality.excellent: Colors.green,
ConnectionQuality.good: Colors.orange,
ConnectionQuality.poor: Colors.red,
}[connectionQuality],
size: 16,
)
else
const SizedBox(
width: 12,
height: 12,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2,
),
).padding(all: 3),
],
)
],
);
}
return Container(
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.75),
padding: const EdgeInsets.symmetric(
vertical: 7,
horizontal: 10,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (title != null)
Flexible(
child: Text(
title!,
overflow: TextOverflow.ellipsis,
style: const TextStyle(color: Colors.white),
),
),
const Gap(5),
isScreenShare
? const Icon(
Symbols.monitor,
color: Colors.white,
size: 16,
)
: Icon(
audioAvailable ? Symbols.mic : Symbols.mic_off,
color: audioAvailable ? Colors.white : Colors.red,
size: 16,
), ),
const Gap(3),
if (connectionQuality != ConnectionQuality.unknown)
Icon(
{
ConnectionQuality.excellent: Symbols.signal_cellular_alt,
ConnectionQuality.good: Symbols.signal_cellular_alt_2_bar,
ConnectionQuality.poor: Symbols.signal_cellular_alt_1_bar,
}[connectionQuality],
color: {
ConnectionQuality.excellent: Colors.green,
ConnectionQuality.good: Colors.orange,
ConnectionQuality.poor: Colors.red,
}[connectionQuality],
size: 16,
)
else
const SizedBox(
width: 12,
height: 12,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2,
), ),
).padding(all: 3), const Gap(5),
], isScreenShare
), ? const Icon(
); Symbols.monitor,
} color: Colors.white,
size: 16,
)
: Icon(
audioAvailable ? Symbols.mic : Symbols.mic_off,
color: audioAvailable ? Colors.white : Colors.red,
size: 16,
),
const Gap(3),
if (connectionQuality != ConnectionQuality.unknown)
Icon(
{
ConnectionQuality.excellent: Symbols.signal_cellular_alt,
ConnectionQuality.good: Symbols.signal_cellular_alt_2_bar,
ConnectionQuality.poor: Symbols.signal_cellular_alt_1_bar,
}[connectionQuality],
color: {
ConnectionQuality.excellent: Colors.green,
ConnectionQuality.good: Colors.orange,
ConnectionQuality.poor: Colors.red,
}[connectionQuality],
size: 16,
)
else
const SizedBox(
width: 12,
height: 12,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2,
),
).padding(all: 3),
],
),
);
} }

View File

@ -24,7 +24,6 @@ import gal
import hotkey_manager_macos import hotkey_manager_macos
import in_app_review import in_app_review
import livekit_client import livekit_client
import livekit_noise_filter
import local_notifier import local_notifier
import media_kit_libs_macos_video import media_kit_libs_macos_video
import media_kit_video import media_kit_video
@ -61,7 +60,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
HotkeyManagerMacosPlugin.register(with: registry.registrar(forPlugin: "HotkeyManagerMacosPlugin")) HotkeyManagerMacosPlugin.register(with: registry.registrar(forPlugin: "HotkeyManagerMacosPlugin"))
InAppReviewPlugin.register(with: registry.registrar(forPlugin: "InAppReviewPlugin")) InAppReviewPlugin.register(with: registry.registrar(forPlugin: "InAppReviewPlugin"))
LiveKitPlugin.register(with: registry.registrar(forPlugin: "LiveKitPlugin")) LiveKitPlugin.register(with: registry.registrar(forPlugin: "LiveKitPlugin"))
LiveKitKrispNoiseFilterPlugin.register(with: registry.registrar(forPlugin: "LiveKitKrispNoiseFilterPlugin"))
LocalNotifierPlugin.register(with: registry.registrar(forPlugin: "LocalNotifierPlugin")) LocalNotifierPlugin.register(with: registry.registrar(forPlugin: "LocalNotifierPlugin"))
MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin")) MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin"))
MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin")) MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin"))

View File

@ -152,11 +152,6 @@ PODS:
- flutter_webrtc - flutter_webrtc
- FlutterMacOS - FlutterMacOS
- WebRTC-SDK (= 125.6422.06) - WebRTC-SDK (= 125.6422.06)
- livekit_noise_filter (0.0.1):
- flutter_webrtc
- FlutterMacOS
- LiveKitKrispNoiseFilter (= 0.0.7)
- LiveKitKrispNoiseFilter (0.0.7)
- local_notifier (0.1.0): - local_notifier (0.1.0):
- FlutterMacOS - FlutterMacOS
- media_kit_libs_macos_video (1.0.4): - media_kit_libs_macos_video (1.0.4):
@ -242,7 +237,6 @@ DEPENDENCIES:
- hotkey_manager_macos (from `Flutter/ephemeral/.symlinks/plugins/hotkey_manager_macos/macos`) - hotkey_manager_macos (from `Flutter/ephemeral/.symlinks/plugins/hotkey_manager_macos/macos`)
- in_app_review (from `Flutter/ephemeral/.symlinks/plugins/in_app_review/macos`) - in_app_review (from `Flutter/ephemeral/.symlinks/plugins/in_app_review/macos`)
- livekit_client (from `Flutter/ephemeral/.symlinks/plugins/livekit_client/macos`) - livekit_client (from `Flutter/ephemeral/.symlinks/plugins/livekit_client/macos`)
- livekit_noise_filter (from `Flutter/ephemeral/.symlinks/plugins/livekit_noise_filter/macos`)
- local_notifier (from `Flutter/ephemeral/.symlinks/plugins/local_notifier/macos`) - local_notifier (from `Flutter/ephemeral/.symlinks/plugins/local_notifier/macos`)
- media_kit_libs_macos_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_video/macos`) - media_kit_libs_macos_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_video/macos`)
- media_kit_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_video/macos`) - media_kit_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_video/macos`)
@ -271,7 +265,6 @@ SPEC REPOS:
- GoogleDataTransport - GoogleDataTransport
- GoogleUtilities - GoogleUtilities
- HotKey - HotKey
- LiveKitKrispNoiseFilter
- nanopb - nanopb
- OrderedSet - OrderedSet
- PromisesObjC - PromisesObjC
@ -322,8 +315,6 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/in_app_review/macos :path: Flutter/ephemeral/.symlinks/plugins/in_app_review/macos
livekit_client: livekit_client:
:path: Flutter/ephemeral/.symlinks/plugins/livekit_client/macos :path: Flutter/ephemeral/.symlinks/plugins/livekit_client/macos
livekit_noise_filter:
:path: Flutter/ephemeral/.symlinks/plugins/livekit_noise_filter/macos
local_notifier: local_notifier:
:path: Flutter/ephemeral/.symlinks/plugins/local_notifier/macos :path: Flutter/ephemeral/.symlinks/plugins/local_notifier/macos
media_kit_libs_macos_video: media_kit_libs_macos_video:
@ -387,8 +378,6 @@ SPEC CHECKSUMS:
hotkey_manager_macos: a4317849af96d2430fa89944d3c58977ca089fbe hotkey_manager_macos: a4317849af96d2430fa89944d3c58977ca089fbe
in_app_review: 0599bccaed5e02f6bed2b0d30d16f86b63ed8638 in_app_review: 0599bccaed5e02f6bed2b0d30d16f86b63ed8638
livekit_client: 35690bf9861be6325a6f7d11bb38d50c7c9fed80 livekit_client: 35690bf9861be6325a6f7d11bb38d50c7c9fed80
livekit_noise_filter: c5710c0871ef3621b48c0b44d3c3ff938ba414b2
LiveKitKrispNoiseFilter: efe418ceca28163ace0ff222bd2cc02384645d84
local_notifier: ebf072651e35ae5e47280ad52e2707375cb2ae4e local_notifier: ebf072651e35ae5e47280ad52e2707375cb2ae4e
media_kit_libs_macos_video: 85a23e549b5f480e72cae3e5634b5514bc692f65 media_kit_libs_macos_video: 85a23e549b5f480e72cae3e5634b5514bc692f65
media_kit_video: fa6564e3799a0a28bff39442334817088b7ca758 media_kit_video: fa6564e3799a0a28bff39442334817088b7ca758

View File

@ -1381,14 +1381,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.1" version: "2.4.1"
livekit_noise_filter:
dependency: "direct main"
description:
name: livekit_noise_filter
sha256: "398bfd1cc63ada9dee9fd7ea415e2fc1e51e091a6d217aad3649b882c35c7fcb"
url: "https://pub.dev"
source: hosted
version: "0.1.0"
local_notifier: local_notifier:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -144,7 +144,6 @@ dependencies:
latlong2: ^0.9.1 latlong2: ^0.9.1
crypto: ^3.0.6 crypto: ^3.0.6
audioplayers: ^6.4.0 audioplayers: ^6.4.0
livekit_noise_filter: ^0.1.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: