mirror of
https://gitlab.com/comunic/comunicmobile
synced 2024-10-23 15:03:22 +00:00
221 lines
6.0 KiB
Dart
221 lines
6.0 KiB
Dart
import 'package:comunic/helpers/calls_helper.dart';
|
|
import 'package:comunic/helpers/conversations_helper.dart';
|
|
import 'package:comunic/helpers/events_helper.dart';
|
|
import 'package:comunic/helpers/users_helper.dart';
|
|
import 'package:comunic/lists/call_members_list.dart';
|
|
import 'package:comunic/lists/users_list.dart';
|
|
import 'package:comunic/models/call_config.dart';
|
|
import 'package:comunic/models/call_member.dart';
|
|
import 'package:comunic/models/conversation.dart';
|
|
import 'package:comunic/ui/routes/main_route.dart';
|
|
import 'package:comunic/ui/widgets/safe_state.dart';
|
|
import 'package:comunic/utils/account_utils.dart';
|
|
import 'package:comunic/utils/intl_utils.dart';
|
|
import 'package:comunic/utils/ui_utils.dart';
|
|
import 'package:flutter/material.dart';
|
|
|
|
/// Call screen
|
|
///
|
|
/// @author Pierre Hubert
|
|
|
|
class CallScreen extends StatefulWidget {
|
|
final int convID;
|
|
|
|
const CallScreen({Key key, @required this.convID})
|
|
: assert(convID != null),
|
|
assert(convID > 0),
|
|
super(key: key);
|
|
|
|
@override
|
|
_CallScreenState createState() => _CallScreenState();
|
|
}
|
|
|
|
class _CallScreenState extends SafeState<CallScreen> {
|
|
// Widget properties
|
|
int get convID => widget.convID;
|
|
|
|
// State properties
|
|
Conversation _conversation;
|
|
String _convName;
|
|
CallConfig _conf;
|
|
var _error = false;
|
|
CallMembersList _membersList;
|
|
UsersList _usersList;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_initCall();
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
super.dispose();
|
|
_endCall();
|
|
}
|
|
|
|
void _initCall() async {
|
|
try {
|
|
setState(() => _error = false);
|
|
|
|
// First, load information about the conversation
|
|
_conversation = await ConversationsHelper().getSingleOrThrow(convID);
|
|
_convName =
|
|
await ConversationsHelper.getConversationNameAsync(_conversation);
|
|
assert(_convName != null);
|
|
|
|
setState(() {});
|
|
|
|
// Join the call
|
|
await CallsHelper.join(convID);
|
|
|
|
// Get call configuration
|
|
_conf = await CallsHelper.getConfig();
|
|
|
|
// Get current members of the call
|
|
final membersList = await CallsHelper.getMembers(convID);
|
|
membersList.removeUser(userID());
|
|
_usersList = await UsersHelper().getListWithThrow(membersList.usersID);
|
|
_membersList = membersList;
|
|
|
|
setState(() {});
|
|
|
|
// Register to events
|
|
this.listenChangeState<UserJoinedCallEvent>((e) {
|
|
// TODO : get user information if required
|
|
if (e.callID == convID) _membersList.add(CallMember(userID: e.userID));
|
|
});
|
|
|
|
this.listen<UserLeftCallEvent>((e) {
|
|
if (e.callID == convID) _removeMember(e.userID);
|
|
});
|
|
|
|
this.listen<CallPeerReadyEvent>((e) {
|
|
if (e.callID == convID) _memberReady(e.peerID);
|
|
});
|
|
|
|
this.listen<CallPeerInterruptedStreamingEvent>((e) {
|
|
if (e.callID == convID) _removeRemotePeerConnection(e.peerID);
|
|
});
|
|
|
|
this.listen<CallClosedEvent>((e) {
|
|
if (e.callID == convID) _leaveCall(needConfirm: false);
|
|
});
|
|
} catch (e, stack) {
|
|
print("Could not initialize call! $e\n$stack");
|
|
setState(() => _error = true);
|
|
}
|
|
}
|
|
|
|
/// Do clean up operations when call screen is destroyed
|
|
void _endCall() async {
|
|
try {
|
|
// Leave the call
|
|
await CallsHelper.leave(convID);
|
|
} catch (e, stack) {
|
|
print("Could not end call properly! $e\n$stack");
|
|
}
|
|
}
|
|
|
|
/// Make us leave the call
|
|
void _leaveCall({bool needConfirm = true}) async {
|
|
if (needConfirm &&
|
|
!await showConfirmDialog(
|
|
context: context,
|
|
message: tr("Do you really want to leave this call ?"))) return;
|
|
|
|
MainController.of(context).popPage();
|
|
}
|
|
|
|
/// Call this when a user started to stream media
|
|
void _memberReady(int memberID) {
|
|
_membersList.getUser(memberID).status = MemberStatus.READY;
|
|
|
|
setState(() {});
|
|
}
|
|
|
|
/// Call this when a user has interrupted streaming
|
|
void _removeRemotePeerConnection(int memberID) {
|
|
_membersList.getUser(memberID).status = MemberStatus.JOINED;
|
|
setState(() {});
|
|
}
|
|
|
|
/// Call this when a members has completely left the call
|
|
void _removeMember(int memberID) {
|
|
_removeRemotePeerConnection(memberID);
|
|
|
|
_membersList.removeUser(memberID);
|
|
setState(() {});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
leading: IconButton(
|
|
icon: Icon(Icons.arrow_back),
|
|
onPressed: () => _leaveCall(),
|
|
),
|
|
title:
|
|
_convName == null ? CircularProgressIndicator() : Text(_convName),
|
|
),
|
|
body: _buildBody(),
|
|
);
|
|
}
|
|
|
|
/// Build widget body
|
|
Widget _buildBody() {
|
|
// Handle errors
|
|
if (_error)
|
|
return buildErrorCard(tr("Could not initialize call!"), actions: [
|
|
MaterialButton(
|
|
onPressed: () => _initCall(),
|
|
child: Text(tr("Try again").toUpperCase()),
|
|
)
|
|
]);
|
|
|
|
// Check if are not ready to show call UI
|
|
if (_membersList == null) return buildCenteredProgressBar();
|
|
|
|
return Column(
|
|
children: <Widget>[_buildMembersArea(), Spacer(), _buildFooterArea()],
|
|
);
|
|
}
|
|
|
|
/// Build members area
|
|
Widget _buildMembersArea() {
|
|
return Center(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(8.0),
|
|
child: RichText(
|
|
text: TextSpan(
|
|
children: _membersList
|
|
.map((f) => TextSpan(
|
|
text: _usersList.getUser(f.userID).displayName + " ",
|
|
style: TextStyle(
|
|
color: f.status == MemberStatus.JOINED
|
|
? null
|
|
: Colors.green)))
|
|
.toList())),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Footer area
|
|
Widget _buildFooterArea() {
|
|
return Material(
|
|
color: Colors.black,
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: <Widget>[
|
|
// Hang up call
|
|
IconButton(
|
|
icon: Icon(Icons.phone, color: Colors.red),
|
|
onPressed: () => _leaveCall(),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|