Commit d7d7f4cc authored by Amin Bandali's avatar Amin Bandali Committed by Sébastien Blin
Browse files

mainwindow: show notification for missed calls

Show a notification for each missed call, and do not show any
notification for incoming rendezvous calls or for accounts with
auto-answer.  Also don't show a missed call notification for declined
calls (using either the notification action or the red hang-up button
in the incoming call view).

GitLab: #1204
Depends-On: Ia20d2827f2b2e5400f233875f91a64f3587bca93
Change-Id: I62fd851908b1517571c32430c42d2dea1b7ab4b2
parent 8b816aaf
......@@ -43,6 +43,7 @@
// Client
#include "chatview.h"
#include "marshals.h"
#include "messagingwidget.h"
#include "native/pixbufmanipulator.h"
#include "utils/drawing.h"
......@@ -92,6 +93,14 @@ G_DEFINE_TYPE_WITH_PRIVATE(IncomingCallView, incoming_call_view, GTK_TYPE_BOX);
#define INCOMING_CALL_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), INCOMING_CALL_VIEW_TYPE, IncomingCallViewPrivate))
/* signals */
enum {
CALL_HUNGUP,
LAST_SIGNAL
};
static guint incoming_call_view_signals[LAST_SIGNAL] = { 0 };
static void
incoming_call_view_dispose(GObject *object)
{
......@@ -135,6 +144,9 @@ reject_incoming_call(IncomingCallView *self)
{
g_return_if_fail(IS_INCOMING_CALL_VIEW(self));
auto priv = INCOMING_CALL_VIEW_GET_PRIVATE(self);
g_signal_emit(G_OBJECT(self), incoming_call_view_signals[CALL_HUNGUP], 0,
(*priv->accountInfo_)->id.toStdString().c_str(),
priv->conversation_->callId.toStdString().c_str());
(*priv->accountInfo_)->callModel->hangUp(priv->conversation_->callId);
}
......@@ -217,6 +229,14 @@ incoming_call_view_class_init(IncomingCallViewClass *klass)
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, button_accept_incoming);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, frame_chat);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, box_messaging_widget);
/* add signals */
incoming_call_view_signals[CALL_HUNGUP] = g_signal_new("call-hungup",
G_TYPE_FROM_CLASS(klass),
(GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
0, nullptr, nullptr,
g_cclosure_user_marshal_VOID__STRING_STRING, G_TYPE_NONE,
2, G_TYPE_STRING, G_TYPE_STRING);
}
static void
......
......@@ -401,7 +401,7 @@ private:
void slotConversationCleared(const std::string& uid);
void slotModelSorted();
void slotNewIncomingCall(const std::string& accountId, lrc::api::conversation::Info origin);
void slotCallStatusChanged(const std::string& callId);
void slotCallStatusChanged(const std::string& accountId, const std::string& callId);
void slotCallStarted(const std::string& callId);
void slotCallEnded(const std::string& callId);
void slotFilterChanged();
......@@ -1089,15 +1089,36 @@ on_notification_decline_call(GtkWidget*, gchar *title, MainWindow* self)
auto secondMarker = titleStr.find(":", firstMarker + 1);
if (secondMarker == std::string::npos) return;
auto id = titleStr.substr(0, firstMarker);
auto accountId = titleStr.substr(0, firstMarker);
auto type = titleStr.substr(firstMarker + 1, secondMarker - firstMarker - 1);
auto information = titleStr.substr(secondMarker + 1);
auto callId = titleStr.substr(secondMarker + 1);
try {
auto& accountInfo = priv->cpp->lrc_->getAccountModel().getAccountInfo(id.c_str());
accountInfo.callModel->hangUp(information.c_str());
auto& accountInfo = priv->cpp->lrc_->getAccountModel().getAccountInfo(accountId.c_str());
accountInfo.callModel->hangUp(callId.c_str());
hide_notification(NOTIFIER(priv->notifier),
accountId + ":call:" + callId);
} catch (const std::out_of_range& e) {
g_warning("Can't get account %s: %s", id.c_str(), e.what());
g_warning("Can't get account %s: %s", accountId.c_str(), e.what());
}
}
static void
on_incoming_call_view_decline_call(GtkWidget*, gchar* accountId, gchar* callId,
MainWindow* self)
{
g_return_if_fail(IS_MAIN_WINDOW(self) && accountId && callId);
auto* priv = MAIN_WINDOW_GET_PRIVATE(MAIN_WINDOW(self));
if (!priv->cpp->accountInfo_) {
g_warning("Notification clicked but accountInfo_ currently empty");
return;
}
try {
std::string aId(accountId);
std::string cId(callId);
hide_notification(NOTIFIER(priv->notifier), aId + ":call:" + cId);
} catch (const std::out_of_range& e) {
g_warning("Can't get account %s: %s", accountId, e.what());
}
}
......@@ -1557,7 +1578,13 @@ GtkWidget*
CppImpl::displayIncomingView(lrc::api::conversation::Info conversation, bool redraw_webview)
{
chatViewConversation_.reset(new lrc::api::conversation::Info(conversation));
return incoming_call_view_new(webkitChatContainer(redraw_webview), lrc_->getAVModel(), accountInfo_, chatViewConversation_.get());
GtkWidget* incoming_call_view =
incoming_call_view_new(webkitChatContainer(redraw_webview),
lrc_->getAVModel(), accountInfo_,
chatViewConversation_.get());
g_signal_connect(incoming_call_view, "call-hungup",
G_CALLBACK(on_incoming_call_view_decline_call), self);
return incoming_call_view;
}
GtkWidget*
......@@ -2011,9 +2038,9 @@ CppImpl::updateLrc(const std::string& id, const std::string& accountIdToFlagFree
&lrc::api::ConversationModel::modelSorted,
[this] { slotModelSorted(); });
callChangedConnection_ = QObject::connect(&*accountInfo_->callModel,
&lrc::api::NewCallModel::callStatusChanged,
[this] (const QString& callId) { slotCallStatusChanged(callId.toStdString()); });
callChangedConnection_ = QObject::connect(&lrc_->getBehaviorController(),
&lrc::api::BehaviorController::callStatusChanged,
[this] (const QString& accountId, const QString& callId) { slotCallStatusChanged(accountId.toStdString(), callId.toStdString()); });
callStartedConnection_ = QObject::connect(&*accountInfo_->callModel,
&lrc::api::NewCallModel::callStarted,
......@@ -2240,26 +2267,46 @@ CppImpl::slotModelSorted()
}
void
CppImpl::slotCallStatusChanged(const std::string& callId)
CppImpl::slotCallStatusChanged(const std::string& accountId, const std::string& callId)
{
if (!accountInfo_) {
return;
}
try {
auto call = accountInfo_->callModel->getCall(callId.c_str());
auto& accountInfo = lrc_->getAccountModel().getAccountInfo(accountId.c_str());
auto call = accountInfo.callModel->getCall(callId.c_str());
auto peer = call.peerUri.remove("ring:");
QString avatar = "", name = "", uri = "";
std::string notifId = "";
std::string conversation = "";
try {
notifId = accountInfo_->id.toStdString() + ":call:" + callId;
auto contactInfo = accountInfo.contactModel->getContact(peer);
uri = contactInfo.profileInfo.uri;
avatar = contactInfo.profileInfo.avatar;
name = accountInfo.contactModel->bestNameForContact(peer);
if (name.isEmpty()) {
name = accountInfo.contactModel->bestIdForContact(peer);
}
notifId = accountId + ":call:" + callId;
} catch (...) {
g_warning("Can't get contact for account %s. Don't show notification", qUtf8Printable(accountInfo_->id));
g_warning("Can't get contact for account %s. Don't show notification", accountId.c_str());
return;
}
if (call.status == lrc::api::call::Status::IN_PROGRESS
|| call.status == lrc::api::call::Status::ENDED) {
// Call ended, close the notification
conversation = get_notification_conversation(NOTIFIER(widgets->notifier), notifId);
if (call.status == lrc::api::call::Status::IN_PROGRESS) {
// Call answered and in progress; close the notification
hide_notification(NOTIFIER(widgets->notifier), notifId);
} else if (call.status == lrc::api::call::Status::ENDED) {
// Call ended; close the notification
if (hide_notification(NOTIFIER(widgets->notifier), notifId)
&& call.startTime.time_since_epoch().count() == 0) {
// This was a missed call; show a missed call notification
name.remove('\r');
auto body = _("Missed call from ") + name.toStdString();
show_notification(NOTIFIER(widgets->notifier),
avatar.toStdString(), uri.toStdString(), name.toStdString(),
accountId + ":interaction:" + conversation + ":0",
_("Missed call"), body, NotificationType::CHAT);
}
}
} catch (const std::exception& e) {
g_warning("Can't get call %s for this account.", callId.c_str());
......@@ -2326,12 +2373,16 @@ CppImpl::slotNewIncomingCall(const std::string& accountId, lrc::api::conversatio
return;
}
if (g_settings_get_boolean(widgets->window_settings, "enable-call-notifications")) {
if (g_settings_get_boolean(widgets->window_settings, "enable-call-notifications")
&& !accountInfo.confProperties.isRendezVous
&& !accountInfo.confProperties.autoAnswer
&& !has_notification(NOTIFIER(widgets->notifier), notifId)) {
name.remove('\r');
auto body = name.toStdString() + _(" is calling you!");
show_notification(NOTIFIER(widgets->notifier),
avatar.toStdString(), uri.toStdString(), name.toStdString(),
notifId, _("Incoming call"), body, NotificationType::CALL);
notifId, _("Incoming call"), body, NotificationType::CALL,
origin.uid.toStdString());
}
}
} catch (const std::exception& e) {
......
......@@ -66,6 +66,12 @@ G_DEFINE_TYPE_WITH_PRIVATE(Notifier, notifier, GTK_TYPE_BOX);
#define NOTIFIER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), NOTIFIER_TYPE, NotifierPrivate))
struct Notification
{
std::shared_ptr<NotifyNotification> nn;
std::string conversation;
};
/* signals */
enum {
SHOW_CHAT,
......@@ -99,7 +105,7 @@ public:
gboolean actions;
#if USE_LIBNOTIFY
std::map<std::string, std::shared_ptr<NotifyNotification>> notifications_;
std::map<std::string, Notification> notifications_;
#endif
private:
CppImpl() = delete;
......@@ -283,7 +289,8 @@ gboolean
show_notification(Notifier* view, const std::string& icon,
const std::string& uri, const std::string& name,
const std::string& id, const std::string& title,
const std::string& body, NotificationType type)
const std::string& body, NotificationType type,
const std::string& conversation)
{
g_return_val_if_fail(IS_NOTIFIER(view), false);
gboolean success = FALSE;
......@@ -292,7 +299,11 @@ show_notification(Notifier* view, const std::string& icon,
#if USE_LIBNOTIFY
std::shared_ptr<NotifyNotification> notification(
notify_notification_new(title.c_str(), body.c_str(), nullptr), g_object_unref);
priv->cpp->notifications_.emplace(id, notification);
struct Notification n = {
notification,
conversation
};
priv->cpp->notifications_.emplace(id, n);
// Draw icon
auto firstLetter = (name == uri || name.empty()) ?
......@@ -397,7 +408,7 @@ hide_notification(Notifier* view, const std::string& id)
// Close
GError *error = nullptr;
if (!notify_notification_close(notification->second.get(), &error)) {
if (!notify_notification_close(notification->second.nn.get(), &error)) {
g_warning("could not close notification: %s", error->message);
g_clear_error(&error);
return FALSE;
......@@ -409,3 +420,32 @@ hide_notification(Notifier* view, const std::string& id)
return TRUE;
}
gboolean
has_notification(Notifier* view, const std::string& id)
{
g_return_val_if_fail(IS_NOTIFIER(view), false);
NotifierPrivate *priv = NOTIFIER_GET_PRIVATE(view);
#if USE_LIBNOTIFY
return priv->cpp->notifications_.find(id) != priv->cpp->notifications_.end();
#endif
return FALSE;
}
std::string
get_notification_conversation(Notifier* view, const std::string& id)
{
g_return_val_if_fail(IS_NOTIFIER(view), "");
NotifierPrivate *priv = NOTIFIER_GET_PRIVATE(view);
#if USE_LIBNOTIFY
auto n = priv->cpp->notifications_.find(id);
if (n != priv->cpp->notifications_.end()) {
return n->second.conversation;
}
#endif
return {};
}
......@@ -51,8 +51,11 @@ gboolean show_notification(Notifier* view,
const std::string& id,
const std::string& title,
const std::string& body,
NotificationType type);
NotificationType type,
const std::string& conversation = "");
gboolean hide_notification(Notifier* view, const std::string& id);
gboolean has_notification(Notifier* view, const std::string& id);
std::string get_notification_conversation(Notifier* view, const std::string& id);
G_END_DECLS
......
Supports Markdown
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