2024-04-28 15:05:59 +00:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:livekit_client/livekit_client.dart';
|
|
|
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
|
|
|
|
|
|
|
class ParticipantMenu extends StatefulWidget {
|
|
|
|
final RemoteParticipant participant;
|
|
|
|
final VideoTrack? videoTrack;
|
|
|
|
final bool isScreenShare;
|
|
|
|
final bool showStatsLayer;
|
|
|
|
|
|
|
|
const ParticipantMenu({
|
|
|
|
super.key,
|
|
|
|
required this.participant,
|
|
|
|
this.videoTrack,
|
|
|
|
this.isScreenShare = false,
|
|
|
|
this.showStatsLayer = false,
|
|
|
|
});
|
|
|
|
|
|
|
|
@override
|
|
|
|
State<ParticipantMenu> createState() => _ParticipantMenuState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _ParticipantMenuState extends State<ParticipantMenu> {
|
|
|
|
RemoteTrackPublication<RemoteVideoTrack>? get _videoPublication =>
|
2024-05-01 09:37:34 +00:00
|
|
|
widget.participant.videoTrackPublications
|
|
|
|
.where((element) => element.sid == widget.videoTrack?.sid)
|
|
|
|
.firstOrNull;
|
2024-04-28 15:05:59 +00:00
|
|
|
|
|
|
|
RemoteTrackPublication<RemoteAudioTrack>? get _firstAudioPublication =>
|
|
|
|
widget.participant.audioTrackPublications.firstOrNull;
|
|
|
|
|
|
|
|
void tookAction() {
|
|
|
|
if (Navigator.canPop(context)) {
|
|
|
|
Navigator.pop(context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
|
|
|
Container(
|
2024-05-01 09:37:34 +00:00
|
|
|
padding:
|
|
|
|
const EdgeInsets.only(left: 8, right: 8, top: 20, bottom: 12),
|
2024-04-28 15:05:59 +00:00
|
|
|
child: Padding(
|
|
|
|
padding: const EdgeInsets.symmetric(
|
|
|
|
horizontal: 8,
|
|
|
|
vertical: 12,
|
|
|
|
),
|
|
|
|
child: Text(
|
|
|
|
AppLocalizations.of(context)!.action,
|
|
|
|
style: Theme.of(context).textTheme.headlineSmall,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Expanded(
|
|
|
|
child: ListView(
|
|
|
|
children: [
|
|
|
|
if (_firstAudioPublication != null && !widget.isScreenShare)
|
|
|
|
ListTile(
|
|
|
|
leading: Icon(
|
|
|
|
Icons.volume_up,
|
|
|
|
color: {
|
2024-05-01 09:37:34 +00:00
|
|
|
TrackSubscriptionState.notAllowed:
|
|
|
|
Theme.of(context).colorScheme.error,
|
|
|
|
TrackSubscriptionState.unsubscribed: Theme.of(context)
|
|
|
|
.colorScheme
|
|
|
|
.onSurface
|
|
|
|
.withOpacity(0.6),
|
|
|
|
TrackSubscriptionState.subscribed:
|
|
|
|
Theme.of(context).colorScheme.primary,
|
2024-04-28 15:05:59 +00:00
|
|
|
}[_firstAudioPublication!.subscriptionState],
|
|
|
|
),
|
|
|
|
title: Text(
|
|
|
|
_firstAudioPublication!.subscribed
|
|
|
|
? AppLocalizations.of(context)!.chatCallMute
|
|
|
|
: AppLocalizations.of(context)!.chatCallUnMute,
|
|
|
|
),
|
|
|
|
onTap: () {
|
|
|
|
if (_firstAudioPublication!.subscribed) {
|
|
|
|
_firstAudioPublication!.unsubscribe();
|
|
|
|
} else {
|
|
|
|
_firstAudioPublication!.subscribe();
|
|
|
|
}
|
|
|
|
tookAction();
|
|
|
|
},
|
|
|
|
),
|
|
|
|
if (_videoPublication != null)
|
|
|
|
ListTile(
|
|
|
|
leading: Icon(
|
|
|
|
widget.isScreenShare ? Icons.monitor : Icons.videocam,
|
|
|
|
color: {
|
2024-05-01 09:37:34 +00:00
|
|
|
TrackSubscriptionState.notAllowed:
|
|
|
|
Theme.of(context).colorScheme.error,
|
|
|
|
TrackSubscriptionState.unsubscribed: Theme.of(context)
|
|
|
|
.colorScheme
|
|
|
|
.onSurface
|
|
|
|
.withOpacity(0.6),
|
|
|
|
TrackSubscriptionState.subscribed:
|
|
|
|
Theme.of(context).colorScheme.primary,
|
2024-04-28 15:05:59 +00:00
|
|
|
}[_videoPublication!.subscriptionState],
|
|
|
|
),
|
|
|
|
title: Text(
|
|
|
|
_videoPublication!.subscribed
|
|
|
|
? AppLocalizations.of(context)!.chatCallVideoOff
|
|
|
|
: AppLocalizations.of(context)!.chatCallVideoOn,
|
|
|
|
),
|
|
|
|
onTap: () {
|
|
|
|
if (_videoPublication!.subscribed) {
|
|
|
|
_videoPublication!.unsubscribe();
|
|
|
|
} else {
|
|
|
|
_videoPublication!.subscribe();
|
|
|
|
}
|
|
|
|
tookAction();
|
|
|
|
},
|
|
|
|
),
|
|
|
|
if (_videoPublication != null) const Divider(thickness: 0.3),
|
|
|
|
if (_videoPublication != null)
|
|
|
|
...[30, 15, 8].map(
|
|
|
|
(x) => ListTile(
|
|
|
|
leading: Icon(
|
2024-05-01 09:37:34 +00:00
|
|
|
_videoPublication?.fps == x
|
|
|
|
? Icons.check_box_outlined
|
|
|
|
: Icons.check_box_outline_blank,
|
2024-04-28 15:05:59 +00:00
|
|
|
),
|
|
|
|
title: Text('Set preferred frame-per-second to $x'),
|
|
|
|
onTap: () {
|
|
|
|
_videoPublication!.setVideoFPS(x);
|
|
|
|
tookAction();
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
if (_videoPublication != null) const Divider(thickness: 0.3),
|
|
|
|
if (_videoPublication != null)
|
|
|
|
...[
|
|
|
|
('High', VideoQuality.HIGH),
|
|
|
|
('Medium', VideoQuality.MEDIUM),
|
|
|
|
('Low', VideoQuality.LOW),
|
|
|
|
].map(
|
|
|
|
(x) => ListTile(
|
|
|
|
leading: Icon(
|
2024-05-01 09:37:34 +00:00
|
|
|
_videoPublication?.videoQuality == x.$2
|
|
|
|
? Icons.check_box_outlined
|
|
|
|
: Icons.check_box_outline_blank,
|
2024-04-28 15:05:59 +00:00
|
|
|
),
|
|
|
|
title: Text('Set preferred quality to ${x.$1}'),
|
|
|
|
onTap: () {
|
|
|
|
_videoPublication!.setVideoQuality(x.$2);
|
|
|
|
tookAction();
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|