blob: cf8dcfbf85cdc77341eaceecc88dbbc78c77b905 [file] [log] [blame]
Stepan Salenikovich69771842015-02-24 18:11:45 -05001/*
Guillaume Roguez77c579d2018-01-30 15:54:02 -05002 * Copyright (C) 2015-2018 Savoir-faire Linux Inc.
Stepan Salenikovich69771842015-02-24 18:11:45 -05003 * Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
Sébastien Blin55bff9d2017-10-03 15:15:23 -04004 * Author: Nicolas Jäger <nicolas.jager@savoirfairelinux.com>
5 * Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
Stepan Salenikovich69771842015-02-24 18:11:45 -05006 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Stepan Salenikovich69771842015-02-24 18:11:45 -050020 */
21
22#include "incomingcallview.h"
23
Sébastien Blin55bff9d2017-10-03 15:15:23 -040024// Gtk
Stepan Salenikovich69771842015-02-24 18:11:45 -050025#include <gtk/gtk.h>
Sébastien Blin55bff9d2017-10-03 15:15:23 -040026#include <QSize>
27
28// Lrc
29#include <api/newcallmodel.h>
30#include <api/contactmodel.h>
31#include <api/conversationmodel.h>
32#include <api/contact.h>
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -040033#include <globalinstances.h>
Sébastien Blin55bff9d2017-10-03 15:15:23 -040034
35// Client
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -040036#include "chatview.h"
Sébastien Blin55bff9d2017-10-03 15:15:23 -040037#include "native/pixbufmanipulator.h"
38#include "utils/drawing.h"
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -040039#include "utils/files.h"
Stepan Salenikovich69771842015-02-24 18:11:45 -050040
41struct _IncomingCallView
42{
43 GtkBox parent;
44};
45
46struct _IncomingCallViewClass
47{
48 GtkBoxClass parent_class;
49};
50
51typedef struct _IncomingCallViewPrivate IncomingCallViewPrivate;
52
53struct _IncomingCallViewPrivate
54{
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -040055 GtkWidget *paned_call;
Stepan Salenikovich69771842015-02-24 18:11:45 -050056 GtkWidget *image_incoming;
Stepan Salenikovich86e91682016-05-04 18:03:37 -040057 GtkWidget *label_name;
Nicolas Jager2e467c32017-01-18 08:52:23 -050058 GtkWidget *label_bestId;
Stepan Salenikovich9d51d532015-06-15 17:47:42 -040059 GtkWidget *spinner_status;
Stepan Salenikovich297b5d12015-02-26 17:51:13 -050060 GtkWidget *label_status;
61 GtkWidget *button_accept_incoming;
62 GtkWidget *button_reject_incoming;
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -040063 GtkWidget *frame_chat;
Stepan Salenikovich297b5d12015-02-26 17:51:13 -050064
Sébastien Blin55bff9d2017-10-03 15:15:23 -040065 // The webkit_chat_container is created once, then reused for all chat views
aviau039001d2016-09-29 16:39:05 -040066 GtkWidget *webkit_chat_container;
67
Sébastien Blin55bff9d2017-10-03 15:15:23 -040068 lrc::api::conversation::Info* conversation_;
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -040069 AccountInfoPointer const * accountInfo_;
Stepan Salenikovich09e0b782016-09-07 16:28:50 -040070
Stepan Salenikovich297b5d12015-02-26 17:51:13 -050071 QMetaObject::Connection state_change_connection;
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -040072
73 GSettings *settings;
Stepan Salenikovich69771842015-02-24 18:11:45 -050074};
75
76G_DEFINE_TYPE_WITH_PRIVATE(IncomingCallView, incoming_call_view, GTK_TYPE_BOX);
77
78#define INCOMING_CALL_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), INCOMING_CALL_VIEW_TYPE, IncomingCallViewPrivate))
79
80static void
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -040081incoming_call_view_dispose(GObject *object)
Stepan Salenikovich297b5d12015-02-26 17:51:13 -050082{
83 IncomingCallView *view;
84 IncomingCallViewPrivate *priv;
85
86 view = INCOMING_CALL_VIEW(object);
87 priv = INCOMING_CALL_VIEW_GET_PRIVATE(view);
88
89 QObject::disconnect(priv->state_change_connection);
90
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -040091 g_clear_object(&priv->settings);
92
Stepan Salenikovich297b5d12015-02-26 17:51:13 -050093 G_OBJECT_CLASS(incoming_call_view_parent_class)->dispose(object);
94}
95
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -040096static gboolean
97map_boolean_to_orientation(GValue *value, GVariant *variant, G_GNUC_UNUSED gpointer user_data)
98{
99 if (g_variant_is_of_type(variant, G_VARIANT_TYPE_BOOLEAN)) {
100 if (g_variant_get_boolean(variant)) {
101 // true, chat should be horizontal (to the right)
102 g_value_set_enum(value, GTK_ORIENTATION_HORIZONTAL);
103 } else {
104 // false, chat should be vertical (at the bottom)
105 g_value_set_enum(value, GTK_ORIENTATION_VERTICAL);
106 }
107 return TRUE;
108 }
109 return FALSE;
110}
111
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500112static void
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400113reject_incoming_call(G_GNUC_UNUSED GtkWidget *widget, ChatView *self)
114{
115 auto priv = INCOMING_CALL_VIEW_GET_PRIVATE(self);
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400116 (*priv->accountInfo_)->callModel->hangUp(priv->conversation_->callId);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400117}
118
119static void
120accept_incoming_call(G_GNUC_UNUSED GtkWidget *widget, ChatView *self)
121{
122 auto priv = INCOMING_CALL_VIEW_GET_PRIVATE(self);
123 auto contactUri = priv->conversation_->participants[0];
Guillaume Roguezc2095922017-12-14 14:07:10 -0500124 try {
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400125 auto contact = (*priv->accountInfo_)->contactModel->getContact(contactUri);
Guillaume Roguezc2095922017-12-14 14:07:10 -0500126 // If the contact is pending, we should accept its request
127 if (contact.profileInfo.type == lrc::api::profile::Type::PENDING)
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400128 (*priv->accountInfo_)->conversationModel->makePermanent(priv->conversation_->uid);
Guillaume Roguezc2095922017-12-14 14:07:10 -0500129 // Accept call
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400130 (*priv->accountInfo_)->callModel->accept(priv->conversation_->callId);
Guillaume Roguezc2095922017-12-14 14:07:10 -0500131 } catch (const std::out_of_range&) {
132 // ContactModel::getContact() exception
133 }
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400134}
135
136static void
Stepan Salenikovich69771842015-02-24 18:11:45 -0500137incoming_call_view_init(IncomingCallView *view)
138{
139 gtk_widget_init_template(GTK_WIDGET(view));
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -0400140
AmarOke7c02972017-07-17 15:21:20 -0400141 auto provider = gtk_css_provider_new();
142 gtk_css_provider_load_from_data(provider,
143 ".flat-button { border: 0; border-radius: 50%; transition: all 0.3s ease; } \
144 .red-button { background: #dc3a37; } \
145 .green-button { background: #27ae60; } \
146 .red-button:hover { background: #dc2719; } \
147 .green-button:hover { background: #219d55; }",
148 -1, nullptr
149 );
150 gtk_style_context_add_provider_for_screen(gdk_display_get_default_screen(gdk_display_get_default()),
151 GTK_STYLE_PROVIDER(provider),
152 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
153
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -0400154 auto priv = INCOMING_CALL_VIEW_GET_PRIVATE(view);
155
156 /* bind the chat orientation to the gsetting */
157 priv->settings = g_settings_new_full(get_ring_schema(), NULL, NULL);
158 g_settings_bind_with_mapping(priv->settings, "chat-pane-horizontal",
159 priv->paned_call, "orientation",
160 G_SETTINGS_BIND_GET,
161 map_boolean_to_orientation,
162 nullptr, nullptr, nullptr);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400163
164 g_signal_connect(priv->button_reject_incoming, "clicked", G_CALLBACK(reject_incoming_call), view);
165 g_signal_connect(priv->button_accept_incoming, "clicked", G_CALLBACK(accept_incoming_call), view);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500166}
167
168static void
169incoming_call_view_class_init(IncomingCallViewClass *klass)
170{
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -0400171 G_OBJECT_CLASS(klass)->dispose = incoming_call_view_dispose;
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500172
Stepan Salenikovich69771842015-02-24 18:11:45 -0500173 gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
174 "/cx/ring/RingGnome/incomingcallview.ui");
175
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -0400176 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, paned_call);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500177 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, image_incoming);
Stepan Salenikovich86e91682016-05-04 18:03:37 -0400178 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, label_name);
Nicolas Jager2e467c32017-01-18 08:52:23 -0500179 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, label_bestId);
Stepan Salenikovich9d51d532015-06-15 17:47:42 -0400180 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, spinner_status);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500181 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, label_status);
182 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, button_accept_incoming);
183 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, button_reject_incoming);
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -0400184 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, frame_chat);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500185}
186
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500187static void
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400188update_state(IncomingCallView *view)
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500189{
190 IncomingCallViewPrivate *priv = INCOMING_CALL_VIEW_GET_PRIVATE(view);
191
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400192 // change state label
193 auto callId = priv->conversation_->callId;
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400194 if (!(*priv->accountInfo_)->callModel->hasCall(callId)) return;
195 auto call = (*priv->accountInfo_)->callModel->getCall(callId);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500196
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400197 gchar *status = g_strdup_printf("%s", lrc::api::call::to_string(call.status).c_str());
Stepan Salenikovich7ec8fe82015-06-02 18:26:39 -0400198 gtk_label_set_text(GTK_LABEL(priv->label_status), status);
199 g_free(status);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500200
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400201 if (call.status == lrc::api::call::Status::INCOMING_RINGING)
202 gtk_widget_show(priv->button_accept_incoming);
203 else
204 gtk_widget_hide(priv->button_accept_incoming);
205 gtk_widget_show(priv->button_reject_incoming);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500206
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400207 gtk_widget_show(priv->spinner_status);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500208}
209
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400210static void
Stepan Salenikovichaa4ff432017-05-19 16:16:53 -0400211update_name_and_photo(IncomingCallView *view)
212{
213 auto priv = INCOMING_CALL_VIEW_GET_PRIVATE(view);
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400214
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400215 QVariant var_i = GlobalInstances::pixmapManipulator().conversationPhoto(
216 *priv->conversation_,
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400217 **(priv->accountInfo_),
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400218 QSize(110, 110),
219 false
220 );
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400221 std::shared_ptr<GdkPixbuf> image = var_i.value<std::shared_ptr<GdkPixbuf>>();
222 gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_incoming), image.get());
Stepan Salenikovich69771842015-02-24 18:11:45 -0500223
Guillaume Roguezc2095922017-12-14 14:07:10 -0500224 try {
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400225 auto contactInfo = (*priv->accountInfo_)->contactModel->getContact(priv->conversation_->participants.front());
Stepan Salenikovich86e91682016-05-04 18:03:37 -0400226
Guillaume Roguezc2095922017-12-14 14:07:10 -0500227 auto name = contactInfo.profileInfo.alias;
228 gtk_label_set_text(GTK_LABEL(priv->label_name), name.c_str());
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400229
Guillaume Roguezc2095922017-12-14 14:07:10 -0500230 auto bestId = contactInfo.registeredName;
231 if (name != bestId) {
232 gtk_label_set_text(GTK_LABEL(priv->label_bestId), bestId.c_str());
233 gtk_widget_show(priv->label_bestId);
234 }
235 } catch (const std::out_of_range&) {
236 // ContactModel::getContact() exception
Stepan Salenikovich86e91682016-05-04 18:03:37 -0400237 }
Stepan Salenikovichaa4ff432017-05-19 16:16:53 -0400238}
239
240static void
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400241set_call_info(IncomingCallView *view) {
Stepan Salenikovichaa4ff432017-05-19 16:16:53 -0400242 IncomingCallViewPrivate *priv = INCOMING_CALL_VIEW_GET_PRIVATE(view);
243
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400244 update_state(view);
245 update_name_and_photo(view);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500246
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400247 // Update view if call state changes
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500248 priv->state_change_connection = QObject::connect(
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400249 &*(*priv->accountInfo_)->callModel,
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400250 &lrc::api::NewCallModel::callStatusChanged,
251 [view, priv] (const std::string& callId) {
252 if (callId == priv->conversation_->callId) {
253 update_state(view);
254 update_name_and_photo(view);
255 }
256 });
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -0400257
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400258 auto chat_view = chat_view_new(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400259 *priv->accountInfo_,
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400260 priv->conversation_);
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -0400261 gtk_widget_show(chat_view);
262 chat_view_set_header_visible(CHAT_VIEW(chat_view), FALSE);
263 gtk_container_add(GTK_CONTAINER(priv->frame_chat), chat_view);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500264}
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400265
266GtkWidget *
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400267incoming_call_view_new(WebKitChatContainer* view,
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400268 AccountInfoPointer const & accountInfo,
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400269 lrc::api::conversation::Info* conversation)
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400270{
271 auto self = g_object_new(INCOMING_CALL_VIEW_TYPE, NULL);
aviau039001d2016-09-29 16:39:05 -0400272
273 IncomingCallViewPrivate *priv = INCOMING_CALL_VIEW_GET_PRIVATE(self);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400274 priv->webkit_chat_container = GTK_WIDGET(view);
275 priv->conversation_ = conversation;
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400276 priv->accountInfo_ = &accountInfo;
aviau039001d2016-09-29 16:39:05 -0400277
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400278 set_call_info(INCOMING_CALL_VIEW(self));
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400279
280 return GTK_WIDGET(self);
281}
282
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400283lrc::api::conversation::Info
284incoming_call_view_get_conversation(IncomingCallView *self)
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400285{
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400286 g_return_val_if_fail(IS_INCOMING_CALL_VIEW(self), lrc::api::conversation::Info());
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400287 auto priv = INCOMING_CALL_VIEW_GET_PRIVATE(self);
288
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400289 return *priv->conversation_;
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400290}