Various call UI/UX improvements
This commit is contained in:
parent
5ed8d28a27
commit
4ef50db3e5
|
@ -96,7 +96,7 @@ public abstract interface ConversationAdditionPopulator : ConversationItemPopula
|
||||||
|
|
||||||
public abstract interface VideoCallPlugin : Object {
|
public abstract interface VideoCallPlugin : Object {
|
||||||
|
|
||||||
public abstract bool supports(string media);
|
public abstract bool supports(string? media);
|
||||||
// Video widget
|
// Video widget
|
||||||
public abstract VideoCallWidget? create_widget(WidgetType type);
|
public abstract VideoCallWidget? create_widget(WidgetType type);
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ using Gee;
|
||||||
using Xmpp;
|
using Xmpp;
|
||||||
|
|
||||||
public class Dino.PeerState : Object {
|
public class Dino.PeerState : Object {
|
||||||
|
public signal void stream_created(string media);
|
||||||
public signal void counterpart_sends_video_updated(bool mute);
|
public signal void counterpart_sends_video_updated(bool mute);
|
||||||
public signal void info_received(Xep.JingleRtp.CallSessionInfo session_info);
|
public signal void info_received(Xep.JingleRtp.CallSessionInfo session_info);
|
||||||
|
|
||||||
|
@ -214,14 +215,14 @@ public class Dino.PeerState : Object {
|
||||||
// If video_content_parameter == null && !mute we're trying to mute a non-existant feed. It will be muted as soon as it is created.
|
// If video_content_parameter == null && !mute we're trying to mute a non-existant feed. It will be muted as soon as it is created.
|
||||||
}
|
}
|
||||||
|
|
||||||
public Xep.JingleRtp.Stream? get_video_stream(Call call) {
|
public Xep.JingleRtp.Stream? get_video_stream() {
|
||||||
if (video_content_parameter != null) {
|
if (video_content_parameter != null) {
|
||||||
return video_content_parameter.stream;
|
return video_content_parameter.stream;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Xep.JingleRtp.Stream? get_audio_stream(Call call) {
|
public Xep.JingleRtp.Stream? get_audio_stream() {
|
||||||
if (audio_content_parameter != null) {
|
if (audio_content_parameter != null) {
|
||||||
return audio_content_parameter.stream;
|
return audio_content_parameter.stream;
|
||||||
}
|
}
|
||||||
|
@ -235,8 +236,8 @@ public class Dino.PeerState : Object {
|
||||||
session.terminated.connect((stream, we_terminated, reason_name, reason_text) =>
|
session.terminated.connect((stream, we_terminated, reason_name, reason_text) =>
|
||||||
session_terminated(we_terminated, reason_name, reason_text)
|
session_terminated(we_terminated, reason_name, reason_text)
|
||||||
);
|
);
|
||||||
session.additional_content_add_incoming.connect((session,stream, content) =>
|
session.additional_content_add_incoming.connect((stream, content) =>
|
||||||
on_incoming_content_add(stream, session, content)
|
on_incoming_content_add(stream, content.session, content)
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach (Xep.Jingle.Content content in session.contents) {
|
foreach (Xep.Jingle.Content content in session.contents) {
|
||||||
|
@ -358,6 +359,8 @@ public class Dino.PeerState : Object {
|
||||||
} else if (media == "audio" && !we_should_send_audio) {
|
} else if (media == "audio" && !we_should_send_audio) {
|
||||||
mute_own_audio(true);
|
mute_own_audio(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stream_created(media);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void on_counterpart_mute_update(bool mute, string? media) {
|
private void on_counterpart_mute_update(bool mute, string? media) {
|
||||||
|
|
|
@ -9,6 +9,7 @@ public class Dino.CallState : Object {
|
||||||
public signal void peer_left(Jid jid, PeerState peer_state, string? reason_name, string? reason_text);
|
public signal void peer_left(Jid jid, PeerState peer_state, string? reason_name, string? reason_text);
|
||||||
|
|
||||||
public StreamInteractor stream_interactor;
|
public StreamInteractor stream_interactor;
|
||||||
|
public Plugins.VideoCallPlugin call_plugin = Dino.Application.get_default().plugin_registry.video_call_plugin;
|
||||||
public Call call;
|
public Call call;
|
||||||
public Xep.Muji.GroupCall? group_call { get; set; }
|
public Xep.Muji.GroupCall? group_call { get; set; }
|
||||||
public Jid? parent_muc { get; set; }
|
public Jid? parent_muc { get; set; }
|
||||||
|
@ -109,22 +110,25 @@ public class Dino.CallState : Object {
|
||||||
terminated(call.account.bare_jid, null, null);
|
terminated(call.account.bare_jid, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void end() {
|
public void end(string? reason_text = null) {
|
||||||
var peers_cpy = new ArrayList<PeerState>();
|
var peers_cpy = new ArrayList<PeerState>();
|
||||||
peers_cpy.add_all(peers.values);
|
peers_cpy.add_all(peers.values);
|
||||||
|
|
||||||
if (group_call != null) {
|
if (group_call != null) {
|
||||||
stream_interactor.get_module(MucManager.IDENTITY).part(call.account, group_call.muc_jid);
|
XmppStream stream = stream_interactor.get_stream(call.account);
|
||||||
|
if (stream != null) {
|
||||||
|
stream.get_module(Xep.Muc.Module.IDENTITY).exit(stream, group_call.muc_jid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (call.state == Call.State.IN_PROGRESS || call.state == Call.State.ESTABLISHING) {
|
if (call.state == Call.State.IN_PROGRESS || call.state == Call.State.ESTABLISHING) {
|
||||||
foreach (PeerState peer in peers_cpy) {
|
foreach (PeerState peer in peers_cpy) {
|
||||||
peer.end(Xep.Jingle.ReasonElement.SUCCESS);
|
peer.end(Xep.Jingle.ReasonElement.SUCCESS, reason_text);
|
||||||
}
|
}
|
||||||
call.state = Call.State.ENDED;
|
call.state = Call.State.ENDED;
|
||||||
} else if (call.state == Call.State.RINGING) {
|
} else if (call.state == Call.State.RINGING) {
|
||||||
foreach (PeerState peer in peers_cpy) {
|
foreach (PeerState peer in peers_cpy) {
|
||||||
peer.end(Xep.Jingle.ReasonElement.CANCEL);
|
peer.end(Xep.Jingle.ReasonElement.CANCEL, reason_text);
|
||||||
}
|
}
|
||||||
if (parent_muc != null && group_call != null) {
|
if (parent_muc != null && group_call != null) {
|
||||||
XmppStream stream = stream_interactor.get_stream(call.account);
|
XmppStream stream = stream_interactor.get_stream(call.account);
|
||||||
|
@ -138,7 +142,7 @@ public class Dino.CallState : Object {
|
||||||
|
|
||||||
call.end_time = new DateTime.now_utc();
|
call.end_time = new DateTime.now_utc();
|
||||||
|
|
||||||
terminated(call.account.bare_jid, null, null);
|
terminated(call.account.bare_jid, null, reason_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void mute_own_audio(bool mute) {
|
public void mute_own_audio(bool mute) {
|
||||||
|
@ -168,7 +172,7 @@ public class Dino.CallState : Object {
|
||||||
|
|
||||||
debug("[%s] Inviting to muji call %s", call.account.bare_jid.to_string(), invitee.to_string());
|
debug("[%s] Inviting to muji call %s", call.account.bare_jid.to_string(), invitee.to_string());
|
||||||
yield stream.get_module(Xep.Muc.Module.IDENTITY).change_affiliation(stream, group_call.muc_jid, invitee, null, "owner");
|
yield stream.get_module(Xep.Muc.Module.IDENTITY).change_affiliation(stream, group_call.muc_jid, invitee, null, "owner");
|
||||||
stream.get_module(Xep.MujiMeta.Module.IDENTITY).send_invite(stream, invitee, group_call.muc_jid, we_should_send_video);
|
stream.get_module(Xep.MujiMeta.Module.IDENTITY).send_invite(stream, invitee, group_call.muc_jid, we_should_send_video, message_type);
|
||||||
|
|
||||||
// If the peer hasn't accepted within a minute, retract the invite
|
// If the peer hasn't accepted within a minute, retract the invite
|
||||||
Timeout.add_seconds(60, () => {
|
Timeout.add_seconds(60, () => {
|
||||||
|
@ -183,13 +187,43 @@ public class Dino.CallState : Object {
|
||||||
|
|
||||||
if (!contains_peer) {
|
if (!contains_peer) {
|
||||||
debug("[%s] Retracting invite to %s from %s", call.account.bare_jid.to_string(), group_call.muc_jid.to_string(), invitee.to_string());
|
debug("[%s] Retracting invite to %s from %s", call.account.bare_jid.to_string(), group_call.muc_jid.to_string(), invitee.to_string());
|
||||||
stream.get_module(Xep.MujiMeta.Module.IDENTITY).send_invite_retract_to_peer(stream, invitee, group_call.muc_jid);
|
stream.get_module(Xep.MujiMeta.Module.IDENTITY).send_invite_retract_to_peer(stream, invitee, group_call.muc_jid, message_type);
|
||||||
stream.get_module(Xep.Muc.Module.IDENTITY).change_affiliation.begin(stream, group_call.muc_jid, invitee, null, "none");
|
stream.get_module(Xep.Muc.Module.IDENTITY).change_affiliation.begin(stream, group_call.muc_jid, invitee, null, "none");
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Plugins.MediaDevice? get_microphone_device() {
|
||||||
|
if (peers.is_empty) return null;
|
||||||
|
var audio_stream = peers.values.to_array()[0].get_audio_stream();
|
||||||
|
return call_plugin.get_device(audio_stream, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Plugins.MediaDevice? get_speaker_device() {
|
||||||
|
if (peers.is_empty) return null;
|
||||||
|
var audio_stream = peers.values.to_array()[0].get_audio_stream();
|
||||||
|
return call_plugin.get_device(audio_stream, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Plugins.MediaDevice? get_video_device() {
|
||||||
|
if (peers.is_empty) return null;
|
||||||
|
var video_stream = peers.values.to_array()[0].get_video_stream();
|
||||||
|
return call_plugin.get_device(video_stream, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set_audio_device(Plugins.MediaDevice? device) {
|
||||||
|
foreach (PeerState peer_state in peers.values) {
|
||||||
|
call_plugin.set_device(peer_state.get_audio_stream(), device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set_video_device(Plugins.MediaDevice? device) {
|
||||||
|
foreach (PeerState peer_state in peers.values) {
|
||||||
|
call_plugin.set_device(peer_state.get_video_stream(), device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal void rename_peer(Jid from_jid, Jid to_jid) {
|
internal void rename_peer(Jid from_jid, Jid to_jid) {
|
||||||
debug("[%s] Renaming %s to %s exists %s", call.account.bare_jid.to_string(), from_jid.to_string(), to_jid.to_string(), peers.has_key(from_jid).to_string());
|
debug("[%s] Renaming %s to %s exists %s", call.account.bare_jid.to_string(), from_jid.to_string(), to_jid.to_string(), peers.has_key(from_jid).to_string());
|
||||||
PeerState? peer_state = peers[from_jid];
|
PeerState? peer_state = peers[from_jid];
|
||||||
|
@ -226,23 +260,35 @@ public class Dino.CallState : Object {
|
||||||
this.bind_property("group-call", peer_state, "group-call", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
|
this.bind_property("group-call", peer_state, "group-call", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
|
||||||
|
|
||||||
peer_state.session_terminated.connect((we_terminated, reason_name, reason_text) => {
|
peer_state.session_terminated.connect((we_terminated, reason_name, reason_text) => {
|
||||||
|
debug("[%s] Peer left %s: %s %s (%i peers remaining)", call.account.bare_jid.to_string(), reason_text ?? "", reason_name ?? "", peer_state.jid.to_string(), peers.size);
|
||||||
peers.unset(peer_state.jid);
|
peers.unset(peer_state.jid);
|
||||||
debug("[%s] Peer left %s left %i", call.account.bare_jid.to_string(), peer_state.jid.to_string(), peers.size);
|
|
||||||
|
|
||||||
if (peers.is_empty) {
|
if (peers.is_empty) {
|
||||||
if (group_call != null) group_call.leave(stream_interactor.get_stream(call.account));
|
if (group_call != null) {
|
||||||
|
group_call.leave(stream_interactor.get_stream(call.account));
|
||||||
|
on_call_terminated(peer_state.jid, we_terminated, null, "All participants have left the group call");
|
||||||
|
} else {
|
||||||
on_call_terminated(peer_state.jid, we_terminated, reason_name, reason_text);
|
on_call_terminated(peer_state.jid, we_terminated, reason_name, reason_text);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
peer_left(peer_state.jid, peer_state, reason_name, reason_text);
|
peer_left(peer_state.jid, peer_state, reason_name, reason_text);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async bool can_convert_into_groupcall() {
|
||||||
|
if (peers.size == 0) return false;
|
||||||
|
Jid peer = peers.keys.to_array()[0];
|
||||||
|
bool peer_has_feature = yield stream_interactor.get_module(EntityInfo.IDENTITY).has_feature(call.account, peer, Xep.Muji.NS_URI);
|
||||||
|
bool can_initiate = stream_interactor.get_module(Calls.IDENTITY).can_initiate_groupcall(call.account);
|
||||||
|
return peer_has_feature && can_initiate;
|
||||||
|
}
|
||||||
|
|
||||||
public async void convert_into_group_call() {
|
public async void convert_into_group_call() {
|
||||||
XmppStream stream = stream_interactor.get_stream(call.account);
|
XmppStream stream = stream_interactor.get_stream(call.account);
|
||||||
if (stream == null) return;
|
if (stream == null) return;
|
||||||
|
|
||||||
Jid? muc_jid = null;
|
Jid? muc_jid = stream_interactor.get_module(MucManager.IDENTITY).default_muc_server[call.account];
|
||||||
if (muc_jid == null) {
|
if (muc_jid == null) {
|
||||||
warning("Failed to initiate group call: MUC server not known.");
|
warning("Failed to initiate group call: MUC server not known.");
|
||||||
return;
|
return;
|
||||||
|
@ -320,11 +366,18 @@ public class Dino.CallState : Object {
|
||||||
|
|
||||||
this.group_call.peer_left.connect((jid) => {
|
this.group_call.peer_left.connect((jid) => {
|
||||||
debug("[%s] Group call peer left: %s", call.account.bare_jid.to_string(), jid.to_string());
|
debug("[%s] Group call peer left: %s", call.account.bare_jid.to_string(), jid.to_string());
|
||||||
|
PeerState? peer_state = peers[jid];
|
||||||
if (!peers.has_key(jid)) return;
|
if (!peers.has_key(jid)) return;
|
||||||
// end() will in the end cause a `peer_left` signal and removal from `peers`
|
peer_left(jid, peer_state, Xep.Jingle.ReasonElement.CANCEL, "Peer left the MUJI MUC");
|
||||||
peers[jid].end(Xep.Jingle.ReasonElement.CANCEL, "Peer left the MUJI MUC");
|
peer_state.end(Xep.Jingle.ReasonElement.CANCEL, "Peer left the MUJI MUC");
|
||||||
|
peers.unset(jid);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (group_call.peers_to_connect_to.size > 3) {
|
||||||
|
end("Call too full - P2p calls don't work well with many participants");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Call all peers that are in the room already
|
// Call all peers that are in the room already
|
||||||
foreach (Jid peer_jid in group_call.peers_to_connect_to) {
|
foreach (Jid peer_jid in group_call.peers_to_connect_to) {
|
||||||
// Don't establish connection if we have one already (the person that invited us to the call)
|
// Don't establish connection if we have one already (the person that invited us to the call)
|
||||||
|
|
|
@ -67,38 +67,24 @@ namespace Dino {
|
||||||
return call_state;
|
return call_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async bool can_do_audio_calls_async(Conversation conversation) {
|
public bool can_we_do_calls(Account account) {
|
||||||
if (!can_do_audio_calls()) return false;
|
|
||||||
|
|
||||||
if (conversation.type_ == Conversation.Type.CHAT) {
|
|
||||||
return (yield get_call_resources(conversation.account, conversation.counterpart)).size > 0 || has_jmi_resources(conversation.counterpart);
|
|
||||||
} else {
|
|
||||||
return stream_interactor.get_module(MucManager.IDENTITY).is_private_room(conversation.account, conversation.counterpart);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool can_do_audio_calls() {
|
|
||||||
Plugins.VideoCallPlugin? plugin = Application.get_default().plugin_registry.video_call_plugin;
|
Plugins.VideoCallPlugin? plugin = Application.get_default().plugin_registry.video_call_plugin;
|
||||||
if (plugin == null) return false;
|
if (plugin == null) return false;
|
||||||
|
|
||||||
return plugin.supports("audio");
|
return plugin.supports(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async bool can_do_video_calls_async(Conversation conversation) {
|
public async bool can_conversation_do_calls(Conversation conversation) {
|
||||||
if (!can_do_video_calls()) return false;
|
|
||||||
|
|
||||||
if (conversation.type_ == Conversation.Type.CHAT) {
|
if (conversation.type_ == Conversation.Type.CHAT) {
|
||||||
return (yield get_call_resources(conversation.account, conversation.counterpart)).size > 0 || has_jmi_resources(conversation.counterpart);
|
return (yield get_call_resources(conversation.account, conversation.counterpart)).size > 0 || has_jmi_resources(conversation.counterpart);
|
||||||
} else {
|
} else {
|
||||||
return stream_interactor.get_module(MucManager.IDENTITY).is_private_room(conversation.account, conversation.counterpart);
|
bool is_private = stream_interactor.get_module(MucManager.IDENTITY).is_private_room(conversation.account, conversation.counterpart);
|
||||||
|
return is_private && can_initiate_groupcall(conversation.account);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool can_do_video_calls() {
|
public bool can_initiate_groupcall(Account account) {
|
||||||
Plugins.VideoCallPlugin? plugin = Application.get_default().plugin_registry.video_call_plugin;
|
return stream_interactor.get_module(MucManager.IDENTITY).default_muc_server[account] != null;
|
||||||
if (plugin == null) return false;
|
|
||||||
|
|
||||||
return plugin.supports("video");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Gee.List<Jid> get_call_resources(Account account, Jid counterpart) {
|
public async Gee.List<Jid> get_call_resources(Account account, Jid counterpart) {
|
||||||
|
@ -107,7 +93,10 @@ namespace Dino {
|
||||||
XmppStream? stream = stream_interactor.get_stream(account);
|
XmppStream? stream = stream_interactor.get_stream(account);
|
||||||
if (stream == null) return ret;
|
if (stream == null) return ret;
|
||||||
|
|
||||||
Gee.List<Jid>? full_jids = stream.get_flag(Presence.Flag.IDENTITY).get_resources(counterpart);
|
Presence.Flag? presence_flag = stream.get_flag(Presence.Flag.IDENTITY);
|
||||||
|
if (presence_flag == null) return ret;
|
||||||
|
|
||||||
|
Gee.List<Jid>? full_jids = presence_flag.get_resources(counterpart);
|
||||||
if (full_jids == null) return ret;
|
if (full_jids == null) return ret;
|
||||||
|
|
||||||
foreach (Jid full_jid in full_jids) {
|
foreach (Jid full_jid in full_jids) {
|
||||||
|
@ -148,11 +137,6 @@ namespace Dino {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void on_incoming_call(Account account, Xep.Jingle.Session session) {
|
private void on_incoming_call(Account account, Xep.Jingle.Session session) {
|
||||||
if (!can_do_audio_calls()) {
|
|
||||||
warning("Incoming call but no call support detected. Ignoring.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Jid? muji_muc = null;
|
Jid? muji_muc = null;
|
||||||
bool counterpart_wants_video = false;
|
bool counterpart_wants_video = false;
|
||||||
foreach (Xep.Jingle.Content content in session.contents) {
|
foreach (Xep.Jingle.Content content in session.contents) {
|
||||||
|
@ -268,7 +252,7 @@ namespace Dino {
|
||||||
if (!call.account.equals(account)) return;
|
if (!call.account.equals(account)) return;
|
||||||
|
|
||||||
// We already know the call; this is a reflection of our own invite
|
// We already know the call; this is a reflection of our own invite
|
||||||
if (call_states[call].parent_muc.equals_bare(inviter_jid)) return;
|
if (call_states[call].parent_muc != null && call_states[call].parent_muc.equals_bare(inviter_jid)) return;
|
||||||
|
|
||||||
if (call.counterparts.contains(inviter_jid) && call_states[call].accepted) {
|
if (call.counterparts.contains(inviter_jid) && call_states[call].accepted) {
|
||||||
// A call is converted into a group call.
|
// A call is converted into a group call.
|
||||||
|
@ -337,11 +321,6 @@ namespace Dino {
|
||||||
|
|
||||||
Xep.JingleMessageInitiation.Module mi_module = stream_interactor.module_manager.get_module(account, Xep.JingleMessageInitiation.Module.IDENTITY);
|
Xep.JingleMessageInitiation.Module mi_module = stream_interactor.module_manager.get_module(account, Xep.JingleMessageInitiation.Module.IDENTITY);
|
||||||
mi_module.session_proposed.connect((from, to, sid, descriptions) => {
|
mi_module.session_proposed.connect((from, to, sid, descriptions) => {
|
||||||
if (!can_do_audio_calls()) {
|
|
||||||
warning("Incoming call but no call support detected. Ignoring.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool audio_requested = descriptions.any_match((description) => description.ns_uri == Xep.JingleRtp.NS_URI && description.get_attribute("media") == "audio");
|
bool audio_requested = descriptions.any_match((description) => description.ns_uri == Xep.JingleRtp.NS_URI && description.get_attribute("media") == "audio");
|
||||||
bool video_requested = descriptions.any_match((description) => description.ns_uri == Xep.JingleRtp.NS_URI && description.get_attribute("media") == "video");
|
bool video_requested = descriptions.any_match((description) => description.ns_uri == Xep.JingleRtp.NS_URI && description.get_attribute("media") == "video");
|
||||||
if (!audio_requested && !video_requested) return;
|
if (!audio_requested && !video_requested) return;
|
||||||
|
|
|
@ -118,6 +118,7 @@ public class NotificationEvents : StreamInteractionModule, Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void on_call_incoming(Call call, CallState call_state, Conversation conversation, bool video) {
|
private async void on_call_incoming(Call call, CallState call_state, Conversation conversation, bool video) {
|
||||||
|
if (!stream_interactor.get_module(Calls.IDENTITY).can_we_do_calls(call.account)) return;
|
||||||
string conversation_display_name = get_conversation_display_name(stream_interactor, conversation, null);
|
string conversation_display_name = get_conversation_display_name(stream_interactor, conversation, null);
|
||||||
|
|
||||||
NotificationProvider notifier = yield notifier.wait_async();
|
NotificationProvider notifier = yield notifier.wait_async();
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace Dino.Ui {
|
||||||
public Revealer header_bar_revealer = new Revealer() { halign=Align.END, valign=Align.START, transition_type=RevealerTransitionType.CROSSFADE, transition_duration=200, visible=true };
|
public Revealer header_bar_revealer = new Revealer() { halign=Align.END, valign=Align.START, transition_type=RevealerTransitionType.CROSSFADE, transition_duration=200, visible=true };
|
||||||
public Box own_video_box = new Box(Orientation.HORIZONTAL, 0) { halign=Align.END, valign=Align.END, visible=true };
|
public Box own_video_box = new Box(Orientation.HORIZONTAL, 0) { halign=Align.END, valign=Align.END, visible=true };
|
||||||
public Revealer invite_button_revealer = new Revealer() { margin_top=50, margin_right=30, halign=Align.END, valign=Align.START, transition_type=RevealerTransitionType.CROSSFADE, transition_duration=200 };
|
public Revealer invite_button_revealer = new Revealer() { margin_top=50, margin_right=30, halign=Align.END, valign=Align.START, transition_type=RevealerTransitionType.CROSSFADE, transition_duration=200 };
|
||||||
public Button invite_button = new Button.from_icon_name("dino-account-plus") { relief=ReliefStyle.NONE, visible=false };
|
public Button invite_button = new Button.from_icon_name("dino-account-plus") { relief=ReliefStyle.NONE, visible=true };
|
||||||
private Widget? own_video = null;
|
private Widget? own_video = null;
|
||||||
private HashMap<string, ParticipantWidget> participant_widgets = new HashMap<string, ParticipantWidget>();
|
private HashMap<string, ParticipantWidget> participant_widgets = new HashMap<string, ParticipantWidget>();
|
||||||
private ArrayList<string> participants = new ArrayList<string>();
|
private ArrayList<string> participants = new ArrayList<string>();
|
||||||
|
@ -191,7 +191,11 @@ namespace Dino.Ui {
|
||||||
} else if (reason_name == Xmpp.Xep.Jingle.ReasonElement.DECLINE || reason_name == Xmpp.Xep.Jingle.ReasonElement.BUSY) {
|
} else if (reason_name == Xmpp.Xep.Jingle.ReasonElement.DECLINE || reason_name == Xmpp.Xep.Jingle.ReasonElement.BUSY) {
|
||||||
text = _("%s declined the call").printf(who_terminated);
|
text = _("%s declined the call").printf(who_terminated);
|
||||||
} else {
|
} else {
|
||||||
text = "The call has been terminated: " + (reason_name ?? "") + " " + (reason_text ?? "");
|
if (reason_text == null) {
|
||||||
|
text = "The call has been terminated" + " " + (reason_name ?? "");
|
||||||
|
} else {
|
||||||
|
text = reason_text + " " + (reason_name ?? "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bottom_bar.show_counterpart_ended(text);
|
bottom_bar.show_counterpart_ended(text);
|
||||||
|
|
|
@ -134,16 +134,23 @@ public class Dino.Ui.CallWindowController : Object {
|
||||||
Jid peer_jid = peer_state.jid;
|
Jid peer_jid = peer_state.jid;
|
||||||
peer_states[peer_id] = peer_state;
|
peer_states[peer_id] = peer_state;
|
||||||
|
|
||||||
|
peer_state.stream_created.connect((media) => {
|
||||||
|
if (media == "audio") {
|
||||||
|
update_audio_device_choices();
|
||||||
|
} else if (media == "video") {
|
||||||
|
update_video_device_choices();
|
||||||
|
}
|
||||||
|
});
|
||||||
peer_state.connection_ready.connect(() => {
|
peer_state.connection_ready.connect(() => {
|
||||||
call_window.set_status(peer_state.internal_id, "");
|
call_window.set_status(peer_state.internal_id, "");
|
||||||
if (participant_widgets.size == 1) {
|
if (participant_widgets.size == 1) {
|
||||||
// This is the first peer.
|
// This is the first peer.
|
||||||
// If it can do MUJI, show invite button.
|
// If it can do MUJI, show invite button.
|
||||||
call_window.invite_button_revealer.visible = true;
|
|
||||||
// stream_interactor.get_module(EntityInfo.IDENTITY).has_feature.begin(call.account, peer_state.jid, Xep.Muji.NS_URI, (_, res) => {
|
call_state.can_convert_into_groupcall.begin((_, res) => {
|
||||||
// bool has_feature = stream_interactor.get_module(EntityInfo.IDENTITY).has_feature.end(res);
|
bool can_convert = call_state.can_convert_into_groupcall.end(res);
|
||||||
// call_window.invite_button_revealer.visible = has_feature;
|
call_window.invite_button_revealer.visible = can_convert;
|
||||||
// });
|
});
|
||||||
|
|
||||||
call_plugin.devices_changed.connect((media, incoming) => {
|
call_plugin.devices_changed.connect((media, incoming) => {
|
||||||
if (media == "audio") update_audio_device_choices();
|
if (media == "audio") update_audio_device_choices();
|
||||||
|
@ -165,7 +172,7 @@ public class Dino.Ui.CallWindowController : Object {
|
||||||
if (!(participant_videos[peer_id] is Widget)) return;
|
if (!(participant_videos[peer_id] is Widget)) return;
|
||||||
Widget widget = (Widget) participant_videos[peer_id];
|
Widget widget = (Widget) participant_videos[peer_id];
|
||||||
call_window.set_video(peer_id, widget);
|
call_window.set_video(peer_id, widget);
|
||||||
participant_videos[peer_id].display_stream(peer_state.get_video_stream(call), peer_jid);
|
participant_videos[peer_id].display_stream(peer_state.get_video_stream(), peer_jid);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
peer_state.info_received.connect((session_info) => {
|
peer_state.info_received.connect((session_info) => {
|
||||||
|
@ -264,7 +271,7 @@ public class Dino.Ui.CallWindowController : Object {
|
||||||
private void update_audio_device_choices() {
|
private void update_audio_device_choices() {
|
||||||
if (call_plugin.get_devices("audio", true).size == 0 || call_plugin.get_devices("audio", false).size == 0) {
|
if (call_plugin.get_devices("audio", true).size == 0 || call_plugin.get_devices("audio", false).size == 0) {
|
||||||
call_window.bottom_bar.show_audio_device_error();
|
call_window.bottom_bar.show_audio_device_error();
|
||||||
} /*else if (call_plugin.get_devices("audio", true).size == 1 && call_plugin.get_devices("audio", false).size == 1) {
|
} else if (call_plugin.get_devices("audio", true).size == 1 && call_plugin.get_devices("audio", false).size == 1) {
|
||||||
call_window.bottom_bar.show_audio_device_choices(false);
|
call_window.bottom_bar.show_audio_device_choices(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -273,34 +280,31 @@ public class Dino.Ui.CallWindowController : Object {
|
||||||
update_current_audio_device(audio_settings_popover);
|
update_current_audio_device(audio_settings_popover);
|
||||||
|
|
||||||
audio_settings_popover.microphone_selected.connect((device) => {
|
audio_settings_popover.microphone_selected.connect((device) => {
|
||||||
call_plugin.set_device(calls.get_audio_stream(call), device);
|
call_state.set_audio_device(device);
|
||||||
update_current_audio_device(audio_settings_popover);
|
update_current_audio_device(audio_settings_popover);
|
||||||
});
|
});
|
||||||
audio_settings_popover.speaker_selected.connect((device) => {
|
audio_settings_popover.speaker_selected.connect((device) => {
|
||||||
call_plugin.set_device(calls.get_audio_stream(call), device);
|
call_state.set_audio_device(device);
|
||||||
update_current_audio_device(audio_settings_popover);
|
update_current_audio_device(audio_settings_popover);
|
||||||
});
|
});
|
||||||
calls.stream_created.connect((call, media) => {
|
// calls.stream_created.connect((call, media) => {
|
||||||
if (media == "audio") {
|
// if (media == "audio") {
|
||||||
update_current_audio_device(audio_settings_popover);
|
// update_current_audio_device(audio_settings_popover);
|
||||||
}
|
// }
|
||||||
});*/
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
/*private void update_current_audio_device(AudioSettingsPopover audio_settings_popover) {
|
private void update_current_audio_device(AudioSettingsPopover audio_settings_popover) {
|
||||||
Xmpp.Xep.JingleRtp.Stream stream = calls.get_audio_stream(call);
|
audio_settings_popover.current_microphone_device = call_state.get_microphone_device();
|
||||||
if (stream != null) {
|
audio_settings_popover.current_speaker_device = call_state.get_speaker_device();
|
||||||
audio_settings_popover.current_microphone_device = call_plugin.get_device(stream, false);
|
|
||||||
audio_settings_popover.current_speaker_device = call_plugin.get_device(stream, true);
|
|
||||||
}
|
}
|
||||||
}*/
|
|
||||||
|
|
||||||
private void update_video_device_choices() {
|
private void update_video_device_choices() {
|
||||||
int device_count = call_plugin.get_devices("video", false).size;
|
int device_count = call_plugin.get_devices("video", false).size;
|
||||||
|
|
||||||
if (device_count == 0) {
|
if (device_count == 0) {
|
||||||
call_window.bottom_bar.show_video_device_error();
|
call_window.bottom_bar.show_video_device_error();
|
||||||
} /*else if (device_count == 1 || calls.get_video_stream(call) == null) {
|
} else if (device_count == 1 || call_state.get_video_device() == null) {
|
||||||
call_window.bottom_bar.show_video_device_choices(false);
|
call_window.bottom_bar.show_video_device_choices(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -309,23 +313,20 @@ public class Dino.Ui.CallWindowController : Object {
|
||||||
update_current_video_device(video_settings_popover);
|
update_current_video_device(video_settings_popover);
|
||||||
|
|
||||||
video_settings_popover.camera_selected.connect((device) => {
|
video_settings_popover.camera_selected.connect((device) => {
|
||||||
call_plugin.set_device(calls.get_video_stream(call), device);
|
call_state.set_video_device(device);
|
||||||
update_current_video_device(video_settings_popover);
|
update_current_video_device(video_settings_popover);
|
||||||
own_video.display_device(device);
|
own_video.display_device(device);
|
||||||
});
|
});
|
||||||
calls.stream_created.connect((call, media) => {
|
// call_state.stream_created.connect((call, media) => {
|
||||||
if (media == "video") {
|
// if (media == "video") {
|
||||||
update_current_video_device(video_settings_popover);
|
// update_current_video_device(video_settings_popover);
|
||||||
}
|
// }
|
||||||
});*/
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
/*private void update_current_video_device(VideoSettingsPopover video_settings_popover) {
|
private void update_current_video_device(VideoSettingsPopover video_settings_popover) {
|
||||||
Xmpp.Xep.JingleRtp.Stream stream = calls.get_video_stream(call);
|
video_settings_popover.current_device = call_state.get_video_device();
|
||||||
if (stream != null) {
|
|
||||||
video_settings_popover.current_device = call_plugin.get_device(stream, false);
|
|
||||||
}
|
}
|
||||||
}*/
|
|
||||||
|
|
||||||
private void update_own_video() {
|
private void update_own_video() {
|
||||||
if (this.call_window.bottom_bar.video_enabled) {
|
if (this.call_window.bottom_bar.video_enabled) {
|
||||||
|
|
|
@ -111,17 +111,30 @@ namespace Dino.Ui {
|
||||||
incoming_call_revealer.get_style_context().remove_class("incoming");
|
incoming_call_revealer.get_style_context().remove_class("incoming");
|
||||||
outer_additional_box.get_style_context().remove_class("incoming-call-box");
|
outer_additional_box.get_style_context().remove_class("incoming-call-box");
|
||||||
|
|
||||||
switch (call.state) {
|
// It doesn't make sense to display MUC calls as missed or declined by the whole MUC. Just display as ended.
|
||||||
|
// TODO: maybe not let them be missed/declined in first place.
|
||||||
|
Call.State relevant_state = call.state;
|
||||||
|
if (conversation.type_ == Conversation.Type.GROUPCHAT && call.direction == Call.DIRECTION_OUTGOING &&
|
||||||
|
(relevant_state == Call.State.MISSED || relevant_state == Call.State.DECLINED)) {
|
||||||
|
relevant_state = Call.State.ENDED;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (relevant_state) {
|
||||||
case Call.State.RINGING:
|
case Call.State.RINGING:
|
||||||
image.set_from_icon_name("dino-phone-ring-symbolic", IconSize.LARGE_TOOLBAR);
|
image.set_from_icon_name("dino-phone-ring-symbolic", IconSize.LARGE_TOOLBAR);
|
||||||
if (call.direction == Call.DIRECTION_INCOMING) {
|
if (call.direction == Call.DIRECTION_INCOMING) {
|
||||||
bool video = call_manager.should_we_send_video();
|
bool video = call_manager.should_we_send_video();
|
||||||
title_label.label = video ? _("Incoming video call") : _("Incoming call");
|
title_label.label = video ? _("Incoming video call") : _("Incoming call");
|
||||||
|
|
||||||
|
if (stream_interactor.get_module(Calls.IDENTITY).can_we_do_calls(call.account)) {
|
||||||
subtitle_label.label = "Ring ring…!";
|
subtitle_label.label = "Ring ring…!";
|
||||||
incoming_call_box.visible = true;
|
incoming_call_box.visible = true;
|
||||||
incoming_call_revealer.reveal_child = true;
|
incoming_call_revealer.reveal_child = true;
|
||||||
incoming_call_revealer.get_style_context().add_class("incoming");
|
incoming_call_revealer.get_style_context().add_class("incoming");
|
||||||
outer_additional_box.get_style_context().add_class("incoming-call-box");
|
outer_additional_box.get_style_context().add_class("incoming-call-box");
|
||||||
|
} else {
|
||||||
|
subtitle_label.label = "Dependencies for call support not met";
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
title_label.label = _("Calling…");
|
title_label.label = _("Calling…");
|
||||||
subtitle_label.label = "Ring ring…?";
|
subtitle_label.label = "Ring ring…?";
|
||||||
|
|
|
@ -115,17 +115,11 @@ namespace Dino.Ui {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conversation.type_ == Conversation.Type.CHAT) {
|
|
||||||
Conversation conv_bak = conversation;
|
Conversation conv_bak = conversation;
|
||||||
bool audio_works = yield stream_interactor.get_module(Calls.IDENTITY).can_do_audio_calls_async(conversation);
|
bool can_do_calls = yield stream_interactor.get_module(Calls.IDENTITY).can_conversation_do_calls(conversation);
|
||||||
bool video_works = yield stream_interactor.get_module(Calls.IDENTITY).can_do_video_calls_async(conversation);
|
|
||||||
if (conv_bak != conversation) return;
|
if (conv_bak != conversation) return;
|
||||||
|
|
||||||
visible = audio_works;
|
visible = video_button.visible = can_do_calls;
|
||||||
video_button.visible = video_works;
|
|
||||||
} else {
|
|
||||||
visible = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public new void unset_conversation() { }
|
public new void unset_conversation() { }
|
||||||
|
|
|
@ -101,11 +101,18 @@ namespace Dino.Plugins.Omemo.DtlsSrtpVerificationDraft {
|
||||||
if (fingerprint_node == null) continue;
|
if (fingerprint_node == null) continue;
|
||||||
string fingerprint = fingerprint_node.get_deep_string_content();
|
string fingerprint = fingerprint_node.get_deep_string_content();
|
||||||
|
|
||||||
|
StanzaNode? encrypted_node = null;
|
||||||
|
try {
|
||||||
Xep.Omemo.OmemoEncryptor encryptor = stream.get_module(Xep.Omemo.OmemoEncryptor.IDENTITY);
|
Xep.Omemo.OmemoEncryptor encryptor = stream.get_module(Xep.Omemo.OmemoEncryptor.IDENTITY);
|
||||||
Xep.Omemo.EncryptionData enc_data = encryptor.encrypt_plaintext(fingerprint);
|
Xep.Omemo.EncryptionData enc_data = encryptor.encrypt_plaintext(fingerprint);
|
||||||
encryptor.encrypt_key(enc_data, iq.to.bare_jid, device_id_by_jingle_sid[sid]);
|
encryptor.encrypt_key(enc_data, iq.to.bare_jid, device_id_by_jingle_sid[sid]);
|
||||||
|
encrypted_node = enc_data.get_encrypted_node();
|
||||||
|
} catch (Error e) {
|
||||||
|
warning("Error while OMEMO-encrypting call keys: %s", e.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
StanzaNode new_fingerprint_node = new StanzaNode.build("fingerprint", NS_URI).add_self_xmlns().put_node(enc_data.get_encrypted_node());
|
StanzaNode new_fingerprint_node = new StanzaNode.build("fingerprint", NS_URI).add_self_xmlns().put_node(encrypted_node);
|
||||||
string? hash_attr = fingerprint_node.get_attribute("hash", Xep.JingleIceUdp.DTLS_NS_URI);
|
string? hash_attr = fingerprint_node.get_attribute("hash", Xep.JingleIceUdp.DTLS_NS_URI);
|
||||||
string? setup_attr = fingerprint_node.get_attribute("setup", Xep.JingleIceUdp.DTLS_NS_URI);
|
string? setup_attr = fingerprint_node.get_attribute("setup", Xep.JingleIceUdp.DTLS_NS_URI);
|
||||||
if (hash_attr != null) new_fingerprint_node.put_attribute("hash", hash_attr);
|
if (hash_attr != null) new_fingerprint_node.put_attribute("hash", hash_attr);
|
||||||
|
|
|
@ -285,7 +285,7 @@ public class Dino.Plugins.Rtp.Plugin : RootInterface, VideoCallPlugin, Object {
|
||||||
Gst.deinit();
|
Gst.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool supports(string media) {
|
public bool supports(string? media) {
|
||||||
if (!codec_util.is_element_supported("rtpbin")) return false;
|
if (!codec_util.is_element_supported("rtpbin")) return false;
|
||||||
|
|
||||||
if (media == "audio") {
|
if (media == "audio") {
|
||||||
|
@ -310,6 +310,7 @@ public class Dino.Plugins.Rtp.Plugin : RootInterface, VideoCallPlugin, Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Gee.List<MediaDevice> get_devices(string media, bool incoming) {
|
public Gee.List<MediaDevice> get_devices(string media, bool incoming) {
|
||||||
|
|
||||||
if (media == "video" && !incoming) {
|
if (media == "video" && !incoming) {
|
||||||
return get_video_sources();
|
return get_video_sources();
|
||||||
}
|
}
|
||||||
|
|
|
@ -218,7 +218,10 @@ public class Module : XmppStreamModule {
|
||||||
public async void change_affiliation(XmppStream stream, Jid muc_jid, Jid? user_jid, string? nick, string new_affiliation) {
|
public async void change_affiliation(XmppStream stream, Jid muc_jid, Jid? user_jid, string? nick, string new_affiliation) {
|
||||||
StanzaNode item_node = new StanzaNode.build("item", NS_URI_ADMIN)
|
StanzaNode item_node = new StanzaNode.build("item", NS_URI_ADMIN)
|
||||||
.put_attribute("affiliation", new_affiliation, NS_URI_ADMIN);
|
.put_attribute("affiliation", new_affiliation, NS_URI_ADMIN);
|
||||||
if (user_jid != null) item_node.put_attribute("jid", user_jid.to_string(), NS_URI_ADMIN);
|
if (user_jid != null) {
|
||||||
|
// Some servers don't allow full JIDs and reply error:modify - jid-malformed - "Bare JID expected, got full JID". Make them bare JIDs.
|
||||||
|
item_node.put_attribute("jid", user_jid.bare_jid.to_string(), NS_URI_ADMIN);
|
||||||
|
}
|
||||||
if (nick != null) item_node.put_attribute("nick", nick, NS_URI_ADMIN);
|
if (nick != null) item_node.put_attribute("nick", nick, NS_URI_ADMIN);
|
||||||
|
|
||||||
StanzaNode query = new StanzaNode.build("query", NS_URI_ADMIN).add_self_xmlns().put_node(item_node);
|
StanzaNode query = new StanzaNode.build("query", NS_URI_ADMIN).add_self_xmlns().put_node(item_node);
|
||||||
|
|
|
@ -237,11 +237,14 @@ namespace Xmpp.Xep.Muji {
|
||||||
|
|
||||||
public override void attach(XmppStream stream) {
|
public override void attach(XmppStream stream) {
|
||||||
stream.add_flag(new Flag());
|
stream.add_flag(new Flag());
|
||||||
|
stream.get_module(ServiceDiscovery.Module.IDENTITY).add_feature(stream, NS_URI);
|
||||||
stream.get_module(Presence.Module.IDENTITY).received_available.connect(on_received_available);
|
stream.get_module(Presence.Module.IDENTITY).received_available.connect(on_received_available);
|
||||||
stream.get_module(Presence.Module.IDENTITY).received_unavailable.connect(on_received_unavailable);
|
stream.get_module(Presence.Module.IDENTITY).received_unavailable.connect(on_received_unavailable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void detach(XmppStream stream) { }
|
public override void detach(XmppStream stream) {
|
||||||
|
stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI);
|
||||||
|
}
|
||||||
|
|
||||||
public override string get_ns() {
|
public override string get_ns() {
|
||||||
return NS_URI;
|
return NS_URI;
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace Xmpp.Xep.MujiMeta {
|
||||||
public signal void call_accepted(Jid from, Jid muc_jid, string message_type);
|
public signal void call_accepted(Jid from, Jid muc_jid, string message_type);
|
||||||
public signal void call_rejected(Jid from, Jid to, Jid muc_jid, string message_type);
|
public signal void call_rejected(Jid from, Jid to, Jid muc_jid, string message_type);
|
||||||
|
|
||||||
public void send_invite(XmppStream stream, Jid invitee, Jid muc_jid, bool video, string? message_type = null) {
|
public void send_invite(XmppStream stream, Jid invitee, Jid muc_jid, bool video, string message_type) {
|
||||||
var invite_node = new StanzaNode.build("propose", NS_URI).put_attribute("muc", muc_jid.to_string());
|
var invite_node = new StanzaNode.build("propose", NS_URI).put_attribute("muc", muc_jid.to_string());
|
||||||
invite_node.put_node(new StanzaNode.build("description", Xep.JingleRtp.NS_URI).add_self_xmlns().put_attribute("media", "audio"));
|
invite_node.put_node(new StanzaNode.build("description", Xep.JingleRtp.NS_URI).add_self_xmlns().put_attribute("media", "audio"));
|
||||||
if (video) {
|
if (video) {
|
||||||
|
@ -23,27 +23,27 @@ namespace Xmpp.Xep.MujiMeta {
|
||||||
stream.get_module(MessageModule.IDENTITY).send_message.begin(stream, invite_message);
|
stream.get_module(MessageModule.IDENTITY).send_message.begin(stream, invite_message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void send_invite_retract_to_peer(XmppStream stream, Jid invitee, Jid muc_jid, string? message_type = null) {
|
public void send_invite_retract_to_peer(XmppStream stream, Jid invitee, Jid muc_jid, string message_type) {
|
||||||
send_jmi_message(stream, "retract", invitee, muc_jid, message_type);
|
send_jmi_message(stream, "retract", invitee, muc_jid, message_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void send_invite_accept_to_peer(XmppStream stream, Jid invitor, Jid muc_jid, string? message_type = null) {
|
public void send_invite_accept_to_peer(XmppStream stream, Jid invitor, Jid muc_jid, string message_type) {
|
||||||
send_jmi_message(stream, "accept", invitor, muc_jid, message_type);
|
send_jmi_message(stream, "accept", invitor, muc_jid, message_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void send_invite_accept_to_self(XmppStream stream, Jid muc_jid) {
|
public void send_invite_accept_to_self(XmppStream stream, Jid muc_jid) {
|
||||||
send_jmi_message(stream, "accept", Bind.Flag.get_my_jid(stream).bare_jid, muc_jid);
|
send_jmi_message(stream, "accept", Bind.Flag.get_my_jid(stream).bare_jid, muc_jid, MessageStanza.TYPE_CHAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void send_invite_reject_to_peer(XmppStream stream, Jid invitor, Jid muc_jid, string? message_type = null) {
|
public void send_invite_reject_to_peer(XmppStream stream, Jid invitor, Jid muc_jid, string message_type) {
|
||||||
send_jmi_message(stream, "reject", invitor, muc_jid, message_type);
|
send_jmi_message(stream, "reject", invitor, muc_jid, message_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void send_invite_reject_to_self(XmppStream stream, Jid muc_jid) {
|
public void send_invite_reject_to_self(XmppStream stream, Jid muc_jid) {
|
||||||
send_jmi_message(stream, "reject", Bind.Flag.get_my_jid(stream).bare_jid, muc_jid);
|
send_jmi_message(stream, "reject", Bind.Flag.get_my_jid(stream).bare_jid, muc_jid, MessageStanza.TYPE_CHAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void send_jmi_message(XmppStream stream, string name, Jid to, Jid muc, string? message_type = null) {
|
private void send_jmi_message(XmppStream stream, string name, Jid to, Jid muc, string message_type) {
|
||||||
var jmi_node = new StanzaNode.build(name, NS_URI).add_self_xmlns().put_attribute("muc", muc.to_string());
|
var jmi_node = new StanzaNode.build(name, NS_URI).add_self_xmlns().put_attribute("muc", muc.to_string());
|
||||||
var muji_node = new StanzaNode.build("muji", NS_URI).add_self_xmlns().put_node(jmi_node);
|
var muji_node = new StanzaNode.build("muji", NS_URI).add_self_xmlns().put_node(jmi_node);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue