Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(Notify): Use notification categories on Linux #427

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/model/notificationdata.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ struct NotificationData
{
QString title;
QString message;
QString category;
QPixmap pixmap;
};
143 changes: 34 additions & 109 deletions src/model/notificationgenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,101 +10,12 @@
#include <QCollator>

namespace {
size_t getNumMessages(const QHash<const Friend*, size_t>& friendNotifications,
const QHash<const Conference*, size_t>& conferenceNotifications)
{
auto numMessages = std::accumulate(friendNotifications.begin(), friendNotifications.end(), 0);
numMessages =
std::accumulate(conferenceNotifications.begin(), conferenceNotifications.end(), numMessages);

return numMessages;
}

size_t getNumChats(const QHash<const Friend*, size_t>& friendNotifications,
const QHash<const Conference*, size_t>& conferenceNotifications)
{
return friendNotifications.size() + conferenceNotifications.size();
}

QString generateMultiChatTitle(size_t numChats, size_t numMessages)
{
//: e.g. 3 messages from 2 chats
return QObject::tr("%1 message(s) from %2 chats").arg(numMessages).arg(numChats);
}

template <typename T>
QString generateSingleChatTitle(const QHash<T, size_t> numNotifications, T contact)
{
if (numNotifications[contact] > 1) {
//: e.g. 2 messages from Bob
return QObject::tr("%1 message(s) from %2")
.arg(numNotifications[contact])
.arg(contact->getDisplayedName());
}
return contact->getDisplayedName();
}

QString generateTitle(const QHash<const Friend*, size_t>& friendNotifications,
const QHash<const Conference*, size_t>& conferenceNotifications, const Friend* f)
{
auto numChats = getNumChats(friendNotifications, conferenceNotifications);
if (numChats > 1) {
return generateMultiChatTitle(numChats,
getNumMessages(friendNotifications, conferenceNotifications));
}
return generateSingleChatTitle(friendNotifications, f);
}

QString generateTitle(const QHash<const Friend*, size_t>& friendNotifications,
const QHash<const Conference*, size_t>& conferenceNotifications,
const Conference* c)
{
auto numChats = getNumChats(friendNotifications, conferenceNotifications);
if (numChats > 1) {
return generateMultiChatTitle(numChats,
getNumMessages(friendNotifications, conferenceNotifications));
}
return generateSingleChatTitle(conferenceNotifications, c);
}

QString generateContent(const QHash<const Friend*, size_t>& friendNotifications,
const QHash<const Conference*, size_t>& conferenceNotifications,
QString generateContent(const QHash<const Conference*, size_t>& conferenceNotifications,
QString lastMessage, const ToxPk& sender)
{
assert(!friendNotifications.empty() || !conferenceNotifications.empty());

auto numChats = getNumChats(friendNotifications, conferenceNotifications);
if (numChats > 1) {
// Copy all names into a vector to simplify formatting logic between
// multiple lists
std::vector<QString> displayNames;
displayNames.reserve(numChats);

for (auto it = friendNotifications.begin(); it != friendNotifications.end(); ++it) {
displayNames.push_back(it.key()->getDisplayedName());
}

for (auto it = conferenceNotifications.begin(); it != conferenceNotifications.end(); ++it) {
displayNames.push_back(it.key()->getDisplayedName());
}
assert(!conferenceNotifications.empty());

assert(!displayNames.empty());

// Lexicographically sort all display names to ensure consistent formatting
QCollator collator;
std::sort(displayNames.begin(), displayNames.end(),
[&](const QString& a, const QString& b) { return collator.compare(a, b) < 1; });

auto it = displayNames.begin();

QString ret = *it;

while (++it != displayNames.end()) {
ret += ", " + *it;
}

return ret;
}
if (conferenceNotifications.size() == 1) {
auto it = conferenceNotifications.begin();
if (it == conferenceNotifications.end()) {
Expand Down Expand Up @@ -141,9 +52,29 @@ NotificationData NotificationGenerator::friendMessageNotification(const Friend*
return ret;
}

ret.title = generateTitle(friendNotifications, conferenceNotifications, f);
ret.message =
generateContent(friendNotifications, conferenceNotifications, message, f->getPublicKey());
ret.title = f->getDisplayedName();
ret.message = message;
ret.category = "im.received";
ret.pixmap = getSenderAvatar(profile, f->getPublicKey());

return ret;
}

NotificationData NotificationGenerator::incomingCallNotification(const Friend* f)
{
friendNotifications[f]++;

NotificationData ret;

if (notificationSettings.getNotifyHide()) {
ret.title = tr("Incoming call");
ret.category = "call.incoming";
return ret;
}

ret.title = f->getDisplayedName();
ret.message = tr("Incoming call");
ret.category = "call.incoming";
ret.pixmap = getSenderAvatar(profile, f->getPublicKey());

return ret;
Expand All @@ -162,8 +93,9 @@ NotificationData NotificationGenerator::conferenceMessageNotification(const Conf
return ret;
}

ret.title = generateTitle(friendNotifications, conferenceNotifications, c);
ret.message = generateContent(friendNotifications, conferenceNotifications, message, sender);
ret.title = c->getDisplayedName();
ret.message = generateContent(conferenceNotifications, message, sender);
ret.category = "im.received";
ret.pixmap = getSenderAvatar(profile, sender);

return ret;
Expand All @@ -182,19 +114,10 @@ NotificationData NotificationGenerator::fileTransferNotification(const Friend* f
return ret;
}

auto numChats = getNumChats(friendNotifications, conferenceNotifications);
auto numMessages = getNumMessages(friendNotifications, conferenceNotifications);

if (numChats > 1 || numMessages > 1) {
ret.title = generateTitle(friendNotifications, conferenceNotifications, f);
ret.message = generateContent(friendNotifications, conferenceNotifications,
tr("Incoming file transfer"), f->getPublicKey());
} else {
//: e.g. Bob - file transfer
ret.title = tr("%1 - file transfer").arg(f->getDisplayedName());
ret.message = filename + " (" + getHumanReadableSize(fileSize) + ")";
}

//: e.g. Bob - file transfer
ret.title = tr("%1 - file transfer").arg(f->getDisplayedName());
ret.message = filename + " (" + getHumanReadableSize(fileSize) + ")";
ret.category = "transfer";
ret.pixmap = getSenderAvatar(profile, f->getPublicKey());

return ret;
Expand All @@ -211,6 +134,7 @@ NotificationData NotificationGenerator::conferenceInvitationNotification(const F

ret.title = tr("%1 invites you to join a conference.").arg(from->getDisplayedName());
ret.message = "";
ret.category = "im";
ret.pixmap = getSenderAvatar(profile, from->getPublicKey());

return ret;
Expand All @@ -228,6 +152,7 @@ NotificationData NotificationGenerator::friendRequestNotification(const ToxPk& s

ret.title = tr("Friend request received from %1").arg(sender.toString());
ret.message = message;
ret.category = "im";

return ret;
}
Expand Down
1 change: 1 addition & 0 deletions src/model/notificationgenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class NotificationGenerator : public QObject
NotificationGenerator& operator=(NotificationGenerator&&) = delete;

NotificationData friendMessageNotification(const Friend* f, const QString& message);
NotificationData incomingCallNotification(const Friend* f);
NotificationData conferenceMessageNotification(const Conference* c, const ToxPk& sender,
const QString& message);
NotificationData fileTransferNotification(const Friend* f, const QString& filename,
Expand Down
2 changes: 1 addition & 1 deletion src/platform/desktop_notifications/desktopnotify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ void DesktopNotify::notifyMessage(const NotificationData& notificationData)
// Try system-backends first.
if (d->settings.getNotifySystemBackend()) {
if (d->dbus->showMessage(notificationData.title, notificationData.message,
notificationData.pixmap)) {
notificationData.category, notificationData.pixmap)) {
return;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/platform/desktop_notifications/desktopnotifybackend.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ class DesktopNotifyBackend : public QObject
public:
explicit DesktopNotifyBackend(QObject* parent);
~DesktopNotifyBackend() override;

bool showMessage(const QString& title, const QString& message, const QPixmap& pixmap);
bool showMessage(const QString& title, const QString& message, const QString& category,
Pigpog marked this conversation as resolved.
Show resolved Hide resolved
const QPixmap& pixmap);

signals:
void messageClicked();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,13 +324,13 @@ DesktopNotifyBackend::DesktopNotifyBackend(QObject* parent)
DesktopNotifyBackend::~DesktopNotifyBackend() = default;

bool DesktopNotifyBackend::showMessage(const QString& title, const QString& message,
const QPixmap& pixmap)
const QString& category, const QPixmap& pixmap)
{
// Try Notify first.
if (d->notifyInterface.isValid()) {
QVariantMap hints{
{QStringLiteral("action-icons"), true},
{QStringLiteral("category"), QStringLiteral("im.received")},
{QStringLiteral("category"), category},
{QStringLiteral("sender-pid"),
QVariant::fromValue<quint64>(QCoreApplication::applicationPid())},
};
Expand All @@ -342,7 +342,7 @@ bool DesktopNotifyBackend::showMessage(const QString& title, const QString& mess
// app_name
QApplication::applicationName(),
// replaces_id
d->id,
static_cast<uint32_t>(0),
// app_icon
QStringLiteral("dialog-password"),
// summary
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ DesktopNotifyBackend::DesktopNotifyBackend(QObject* parent)
DesktopNotifyBackend::~DesktopNotifyBackend() = default;

bool DesktopNotifyBackend::showMessage(const QString& title, const QString& message,
const QPixmap& pixmap)
const QString& category, const QPixmap& pixmap)
{
std::ignore = title;
std::ignore = message;
std::ignore = category;
std::ignore = pixmap;
// Always fail, fall back to QSystemTrayIcon.
return false;
Expand Down
26 changes: 17 additions & 9 deletions src/widget/widget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1048,7 +1048,11 @@ void Widget::setStatusMessage(const QString& statusMessage)
*/
void Widget::playNotificationSound(IAudioSink::Sound sound, bool loop)
{
if (!settings.getAudioOutDevEnabled()) {
bool isBusy = core->getStatus() == Status::Status::Busy;
bool busySound = settings.getBusySound();
bool notifySound = settings.getNotifySound();

if (!settings.getAudioOutDevEnabled() || !(notifySound && (!isBusy || busySound))) {
// don't try to play sounds if audio is disabled
return;
}
Expand Down Expand Up @@ -1566,10 +1570,17 @@ bool Widget::newFriendMessageAlert(const ToxPk& friendId, const QString& text, b
widget->updateStatusLight();
ui->friendList->trackWidget(settings, style, widget);
if (notifier != nullptr) {
auto notificationData =
filename.isEmpty()
? notificationGenerator->friendMessageNotification(f, text)
: notificationGenerator->fileTransferNotification(f, filename, filesize);
NotificationData notificationData;
if (filename.isEmpty()) {
if (text.isEmpty()) {
notificationData = notificationGenerator->incomingCallNotification(f);
} else {
notificationData = notificationGenerator->friendMessageNotification(f, text);
}
} else {
notificationData =
notificationGenerator->fileTransferNotification(f, filename, filesize);
}
notifier->notifyMessage(notificationData);
}

Expand Down Expand Up @@ -1668,11 +1679,8 @@ bool Widget::newMessageAlert(QWidget* currentWindow, bool isActive, bool sound,
}
eventFlag = true;
}
const bool isBusy = core->getStatus() == Status::Status::Busy;
const bool busySound = settings.getBusySound();
const bool notifySound = settings.getNotifySound();

if (notifySound && sound && (!isBusy || busySound)) {
if (sound) {
playNotificationSound(IAudioSink::Sound::NewMessage);
}
}
Expand Down
Loading