Commit 784f2a39 authored by Sébastien Blin's avatar Sébastien Blin Committed by Philippe Gorley

sip: fix the transfer feature

Readd the ability to transfer a SIP call (blind or attended) from
the current call view.

Change-Id: I46f5ade5ef71e5479bc3410f9b1f4c200825132c
Gitlab: #803Reviewed-by: Philippe Gorley's avatarPhilippe Gorley <philippe.gorley@savoirfairelinux.com>
parent 3587e495
......@@ -33,5 +33,6 @@
<file alias="temporary-item">ic_search_black_48px.svg</file>
<file alias="audio_only_call_start">ic_call_black_24px.svg</file>
<file alias="fallbackavatar">fallbackavatar.svg</file>
<file alias="transfer">transfer.svg</file>
</gresource>
</gresources>
<svg fill="#ffffff" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M18 11l5-5-5-5v3h-4v4h4v3zm2 4.5c-1.25 0-2.45-.2-3.57-.57-.35-.11-.74-.03-1.02.24l-2.2 2.2c-2.83-1.44-5.15-3.75-6.59-6.59l2.2-2.21c.28-.26.36-.65.25-1C8.7 6.45 8.5 5.25 8.5 4c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.5c0-.55-.45-1-1-1z"/>
</svg>
......@@ -44,6 +44,8 @@
#include "utils/files.h"
#include "video/video_widget.h"
#include <iostream>
namespace { namespace details
{
class CppImpl;
......@@ -79,6 +81,10 @@ struct CurrentCallViewPrivate
GtkWidget *togglebutton_chat;
GtkWidget *togglebutton_muteaudio;
GtkWidget *togglebutton_mutevideo;
GtkWidget *togglebutton_transfer;
GtkWidget* siptransfer_popover;
GtkWidget* siptransfer_filter_entry;
GtkWidget* list_conversations;
GtkWidget *togglebutton_hold;
GtkWidget *togglebutton_record;
GtkWidget *button_hangup;
......@@ -234,6 +240,7 @@ public:
void setup(WebKitChatContainer* chat_widget,
AccountInfoPointer const & account_info,
lrc::api::conversation::Info* conversation);
void add_transfer_contact(const std::string& uri);
void insertControls();
void checkControlsFading();
......@@ -542,6 +549,127 @@ on_toggle_smartinfo(GSimpleAction* action, G_GNUC_UNUSED GVariant* state, GtkWid
}
}
static void
transfer_to_peer(CurrentCallViewPrivate* priv, const std::string& peerUri)
{
if (peerUri == priv->cpp->conversation->participants.front()) {
g_warning("avoid to transfer to the same call, abort.");
#if GTK_CHECK_VERSION(3,22,0)
gtk_popover_popdown(GTK_POPOVER(priv->siptransfer_popover));
#else
gtk_widget_hide(GTK_WIDGET(priv->siptransfer_popover));
#endif
return;
}
try {
// If a call is already present with a peer, try an attended transfer.
auto callInfo = (*priv->cpp->accountInfo)->callModel->getCallFromURI(peerUri, true);
(*priv->cpp->accountInfo)->callModel->transferToCall(
priv->cpp->conversation->callId, callInfo.id);
} catch (std::out_of_range&) {
// No current call found with this URI, perform a blind transfer
(*priv->cpp->accountInfo)->callModel->transfer(
priv->cpp->conversation->callId, peerUri);
}
#if GTK_CHECK_VERSION(3,22,0)
gtk_popover_popdown(GTK_POPOVER(priv->siptransfer_popover));
#else
gtk_widget_hide(GTK_WIDGET(priv->siptransfer_popover));
#endif
}
static void
on_siptransfer_filter_activated(CurrentCallView* self)
{
g_return_if_fail(IS_CURRENT_CALL_VIEW(self));
auto* priv = CURRENT_CALL_VIEW_GET_PRIVATE(self);
transfer_to_peer(priv, gtk_entry_get_text(GTK_ENTRY(priv->siptransfer_filter_entry)));
}
static GtkLabel*
get_sip_address_label(GtkListBoxRow* row)
{
auto* row_children = gtk_container_get_children(GTK_CONTAINER(row));
auto* box_infos = g_list_first(row_children)->data;
auto* children = gtk_container_get_children(GTK_CONTAINER(box_infos));
return GTK_LABEL(g_list_last(children)->data);
}
static void
transfer_to_conversation(GtkListBox*, GtkListBoxRow* row, CurrentCallView* self)
{
g_return_if_fail(IS_CURRENT_CALL_VIEW(self));
auto* priv = CURRENT_CALL_VIEW_GET_PRIVATE(self);
auto* sip_address = get_sip_address_label(row);
transfer_to_peer(priv, gtk_label_get_text(GTK_LABEL(sip_address)));
}
static void
filter_transfer_list(CurrentCallView *self)
{
g_return_if_fail(IS_CURRENT_CALL_VIEW(self));
auto* priv = CURRENT_CALL_VIEW_GET_PRIVATE(self);
std::string currentFilter = gtk_entry_get_text(GTK_ENTRY(priv->siptransfer_filter_entry));
auto row = 0;
while (GtkWidget* children = GTK_WIDGET(gtk_list_box_get_row_at_index(GTK_LIST_BOX(priv->list_conversations), row))) {
auto* sip_address = get_sip_address_label(GTK_LIST_BOX_ROW(children));;
if (row == 0) {
// Update searching item
if (currentFilter.empty() || currentFilter == priv->cpp->conversation->participants.front()) {
// Hide temporary item if filter is empty or same number
gtk_widget_hide(children);
} else {
// Else, show the temporary item (and select it)
gtk_label_set_text(GTK_LABEL(sip_address), currentFilter.c_str());
gtk_widget_show_all(children);
gtk_list_box_select_row(GTK_LIST_BOX(priv->list_conversations), GTK_LIST_BOX_ROW(children));
}
} else {
// It's a contact
std::string item_address = gtk_label_get_text(GTK_LABEL(sip_address));
if (item_address == priv->cpp->conversation->participants.front())
// if item is the current conversation, hide it
gtk_widget_hide(children);
else if (currentFilter.empty())
// filter is empty, show all items
gtk_widget_show_all(children);
else if (item_address.find(currentFilter) == std::string::npos || item_address == currentFilter)
// avoid duplicates and unwanted numbers
gtk_widget_hide(children);
else
// Item is filtered
gtk_widget_show_all(children);
}
++row;
}
}
static void
on_button_transfer_clicked(CurrentCallView *self)
{
// Show and init list
g_return_if_fail(IS_CURRENT_CALL_VIEW(self));
auto* priv = CURRENT_CALL_VIEW_GET_PRIVATE(self);
gtk_popover_set_relative_to(GTK_POPOVER(priv->siptransfer_popover), GTK_WIDGET(priv->togglebutton_transfer));
#if GTK_CHECK_VERSION(3,22,0)
gtk_popover_popdown(GTK_POPOVER(priv->siptransfer_popover));
#else
gtk_widget_show_all(GTK_WIDGET(priv->siptransfer_popover));
#endif
gtk_widget_show_all(priv->siptransfer_popover);
filter_transfer_list(self);
}
static void
on_siptransfer_text_changed(GtkSearchEntry*, CurrentCallView* self)
{
filter_transfer_list(self);
}
} // namespace gtk_callbacks
CppImpl::CppImpl(CurrentCallView& widget)
......@@ -571,7 +699,8 @@ CppImpl::init()
// CSS styles
auto provider = gtk_css_provider_new();
gtk_css_provider_load_from_data(provider,
".smartinfo-block-style { color: #8ae234; background-color: rgba(1, 1, 1, 0.33); } \
".search-entry-style { border: 0; border-radius: 0; } \
.smartinfo-block-style { color: #8ae234; background-color: rgba(1, 1, 1, 0.33); } \
@keyframes blink { 0% {opacity: 1;} 49% {opacity: 1;} 50% {opacity: 0;} 100% {opacity: 0;} } \
.record-button { background: rgba(0, 0, 0, 1); border-radius: 50%; border: 0; transition: all 0.3s ease; } \
.record-button:checked { animation: blink 1s; animation-iteration-count: infinite; } \
......@@ -605,6 +734,34 @@ CppImpl::setup(WebKitChatContainer* chat_widget,
conversation = conv_info;
accountInfo = &account_info;
setCallInfo();
if ((*accountInfo)->profileInfo.type == lrc::api::profile::Type::RING)
gtk_widget_hide(widgets->togglebutton_transfer);
else {
// Remove previous list
while (GtkWidget* children = GTK_WIDGET(gtk_list_box_get_row_at_index(GTK_LIST_BOX(widgets->list_conversations), 10)))
gtk_container_remove(GTK_CONTAINER(widgets->list_conversations), children);
// Fill with SIP contacts
add_transfer_contact(""); // Temporary item
for (const auto& c : (*accountInfo)->conversationModel->getFilteredConversations(lrc::api::profile::Type::SIP))
add_transfer_contact(c.participants.front());
gtk_widget_show_all(widgets->list_conversations);
gtk_widget_show(widgets->togglebutton_transfer);
}
}
void
CppImpl::add_transfer_contact(const std::string& uri)
{
auto* box_item = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
auto pixbufmanipulator = Interfaces::PixbufManipulator();
auto image_buf = pixbufmanipulator.generateAvatar("", uri.empty() ? uri : "sip" + uri);
auto scaled = pixbufmanipulator.scaleAndFrame(image_buf.get(), QSize(48, 48));
auto* avatar = gtk_image_new_from_pixbuf(scaled.get());
auto* address = gtk_label_new(uri.c_str());
gtk_container_add(GTK_CONTAINER(box_item), GTK_WIDGET(avatar));
gtk_container_add(GTK_CONTAINER(box_item), GTK_WIDGET(address));
gtk_list_box_insert(GTK_LIST_BOX(widgets->list_conversations), GTK_WIDGET(box_item), -1);
}
void
......@@ -748,6 +905,10 @@ CppImpl::insertControls()
/* connect the controllers (new model) */
g_signal_connect_swapped(widgets->button_hangup, "clicked", G_CALLBACK(on_button_hangup_clicked), self);
g_signal_connect_swapped(widgets->togglebutton_transfer, "clicked", G_CALLBACK(on_button_transfer_clicked), self);
g_signal_connect_swapped(widgets->siptransfer_filter_entry, "activate", G_CALLBACK(on_siptransfer_filter_activated), self);
g_signal_connect(widgets->siptransfer_filter_entry, "search-changed", G_CALLBACK(on_siptransfer_text_changed), self);
g_signal_connect(widgets->list_conversations, "row-activated", G_CALLBACK(transfer_to_conversation), self);
g_signal_connect_swapped(widgets->togglebutton_hold, "clicked", G_CALLBACK(on_togglebutton_hold_clicked), self);
g_signal_connect_swapped(widgets->togglebutton_muteaudio, "clicked", G_CALLBACK(on_togglebutton_muteaudio_clicked), self);
g_signal_connect_swapped(widgets->togglebutton_record, "clicked", G_CALLBACK(on_togglebutton_record_clicked), self);
......@@ -1065,12 +1226,16 @@ current_call_view_class_init(CurrentCallViewClass *klass)
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, frame_video);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, frame_chat);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, togglebutton_chat);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, togglebutton_transfer);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, togglebutton_hold);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, togglebutton_muteaudio);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, togglebutton_record);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, togglebutton_mutevideo);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, button_hangup);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, scalebutton_quality);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, siptransfer_popover);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, siptransfer_filter_entry);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, list_conversations);
details::current_call_view_signals[VIDEO_DOUBLE_CLICKED] = g_signal_new (
"video-double-clicked",
......@@ -1092,6 +1257,5 @@ current_call_view_new(WebKitChatContainer* chat_widget,
auto* priv = CURRENT_CALL_VIEW_GET_PRIVATE(self);
priv->cpp->setup(chat_widget, accountInfo, conversation);
return GTK_WIDGET(self);
}
......@@ -261,6 +261,25 @@
<property name="fill">True</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="togglebutton_transfer">
<style>
<class name="call-button"/>
</style>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="width-request">48</property>
<property name="height-request">48</property>
<property name="has_tooltip">True</property>
<property name="tooltip-text" translatable="yes">Toggle transfer</property>
<property name="image">image_transfer</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="togglebutton_hold">
<style>
......@@ -449,6 +468,15 @@
</object>
</child>
</object>
<object class="GtkImage" id="image_transfer">
<property name="visible">True</property>
<property name="resource">/cx/ring/RingGnome/transfer</property>
<child internal-child="accessible">
<object class="AtkObject" id="image_transfer-atkobject">
<property name="AtkObject::accessible-description" translatable="yes">Transfer</property>
</object>
</child>
</object>
<object class="GtkImage" id="image_end">
<property name="visible">True</property>
<property name="resource">/cx/ring/RingGnome/call_end</property>
......@@ -483,4 +511,67 @@
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkPopover" id="siptransfer_popover">
<property name="can_focus">False</property>
<property name="height_request">300</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkLabel">
<property name="visible">False</property>
<property name="halign">center</property>
<property name="label" translatable="yes">Transfer to</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkSearchEntry" id="siptransfer_filter_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="primary_icon_name">edit-find-symbolic</property>
<property name="primary_icon_activatable">False</property>
<property name="primary_icon_sensitive">False</property>
<style>
<class name="search-entry-style"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkListBox" id="list_conversations">
<property name="visible">True</property>
<property name="can_focus">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment