mirror of
https://gitlab.linphone.org/BC/public/linphone-desktop.git
synced 2026-04-17 20:08:28 +00:00
Merge branch 'feature/test_native_notif' into 'master'
linux system notification + icons on Windows notification buttons See merge request BC/public/linphone-desktop!1794
This commit is contained in:
commit
ba0994dcc3
18 changed files with 474 additions and 155 deletions
|
|
@ -122,9 +122,9 @@
|
|||
#include "core/event-count-notifier/EventCountNotifierSystemTrayIcon.hpp"
|
||||
#endif // if defined(Q_OS_MACOS)
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
#ifdef Q_OS_WIN
|
||||
#include "core/notifier/WindowsNotificationBackend.hpp"
|
||||
#else
|
||||
#elif defined(Q_OS_LINUX)
|
||||
#include "core/notifier/SysTrayNotificationBackend.hpp"
|
||||
#endif
|
||||
|
||||
|
|
@ -569,7 +569,7 @@ void App::setSelf(QSharedPointer<App>(me)) {
|
|||
});
|
||||
mCoreModelConnection->makeConnectToCore(&App::lForceOidcTimeout, [this] {
|
||||
qDebug() << "App: force oidc timeout";
|
||||
mCoreModelConnection->invokeToModel([this] { emit CoreModel::getInstance() -> forceOidcTimeout(); });
|
||||
mCoreModelConnection->invokeToModel([this] { emit CoreModel::getInstance()->forceOidcTimeout(); });
|
||||
});
|
||||
mCoreModelConnection->makeConnectToModel(&CoreModel::timeoutTimerStarted, [this]() {
|
||||
qDebug() << "App: oidc timer started";
|
||||
|
|
@ -620,9 +620,11 @@ int App::getEventCount() const {
|
|||
return mEventCountNotifier ? mEventCountNotifier->getEventCount() : 0;
|
||||
}
|
||||
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_LINUX)
|
||||
NotificationBackend *App::getNotificationBackend() const {
|
||||
return mNotificationBackend;
|
||||
}
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// Initializations
|
||||
|
|
@ -754,7 +756,9 @@ void App::initCore() {
|
|||
mEngine->setObjectOwnership(settings.get(), QQmlEngine::CppOwnership);
|
||||
mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
||||
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_LINUX)
|
||||
mNotificationBackend = new NotificationBackend(this);
|
||||
#endif
|
||||
|
||||
auto initLists = [this] {
|
||||
if (mCoreStarted) {
|
||||
|
|
|
|||
|
|
@ -43,8 +43,9 @@ class Notifier;
|
|||
class QQuickWindow;
|
||||
class QSystemTrayIcon;
|
||||
class DefaultTranslatorCore;
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_LINUX)
|
||||
class NotificationBackend;
|
||||
|
||||
#endif
|
||||
class App : public SingleApplication, public AbstractObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool coreStarted READ getCoreStarted WRITE setCoreStarted NOTIFY coreStartedChanged)
|
||||
|
|
@ -197,8 +198,9 @@ public:
|
|||
QString getGitBranchName();
|
||||
QString getSdkVersion();
|
||||
QString getQtVersion() const;
|
||||
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_LINUX)
|
||||
NotificationBackend *getNotificationBackend() const;
|
||||
#endif
|
||||
|
||||
Q_INVOKABLE void checkForUpdate(bool requestedByUser = false);
|
||||
|
||||
|
|
@ -256,7 +258,9 @@ private:
|
|||
Thread *mLinphoneThread = nullptr;
|
||||
Notifier *mNotifier = nullptr;
|
||||
EventCountNotifier *mEventCountNotifier = nullptr;
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_LINUX)
|
||||
NotificationBackend *mNotificationBackend = nullptr;
|
||||
#endif
|
||||
QSystemTrayIcon *mSystemTrayIcon = nullptr;
|
||||
QQuickWindow *mMainWindow = nullptr;
|
||||
QQuickWindow *mCallsWindow = nullptr;
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ if(WIN32)
|
|||
core/notifier/NotificationActivator.cpp
|
||||
core/notifier/DesktopNotificationManagerCompat.cpp
|
||||
core/notifier/WindowsNotificationBackend.cpp)
|
||||
else()
|
||||
elseif (UNIX AND NOT APPLE)
|
||||
list(APPEND _LINPHONEAPP_SOURCES
|
||||
core/notifier/SysTrayNotificationBackend.cpp)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -4,14 +4,16 @@
|
|||
#include "tool/AbstractObject.hpp"
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QSize>
|
||||
#include <QString>
|
||||
|
||||
struct ToastButton {
|
||||
QString label;
|
||||
QString argument;
|
||||
ToastButton(QString title, QString arg) {
|
||||
label = title;
|
||||
argument = arg;
|
||||
QString icon;
|
||||
ToastButton(QString label, QString arg, QString icon = QString()) {
|
||||
this->label = label;
|
||||
this->argument = arg;
|
||||
this->icon = icon;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ using namespace Microsoft::WRL::Wrappers;
|
|||
|
||||
namespace DesktopNotificationManagerCompat {
|
||||
HRESULT RegisterComServer(GUID clsid, const wchar_t exePath[]);
|
||||
HRESULT RegisterAumidInRegistry(const wchar_t *aumid);
|
||||
HRESULT RegisterAumidInRegistry(const wchar_t *aumid, const wchar_t *iconPath = nullptr);
|
||||
HRESULT EnsureRegistered();
|
||||
bool IsRunningAsUwp();
|
||||
|
||||
|
|
@ -100,7 +100,7 @@ HRESULT CreateStartMenuShortcut(const wchar_t *aumid, GUID clsid) {
|
|||
return persistFile->Save(shortcutPath, TRUE);
|
||||
}
|
||||
|
||||
HRESULT RegisterAumidInRegistry(const wchar_t *aumid) {
|
||||
HRESULT RegisterAumidInRegistry(const wchar_t *aumid, const wchar_t *iconPath) {
|
||||
std::wstring keyPath = std::wstring(L"Software\\Classes\\AppUserModelId\\") + aumid;
|
||||
|
||||
HKEY key;
|
||||
|
|
@ -114,11 +114,16 @@ HRESULT RegisterAumidInRegistry(const wchar_t *aumid) {
|
|||
res = ::RegSetValueExW(key, L"DisplayName", 0, REG_SZ, reinterpret_cast<const BYTE *>(displayName),
|
||||
static_cast<DWORD>((wcslen(displayName) + 1) * sizeof(wchar_t)));
|
||||
|
||||
if (iconPath != nullptr) {
|
||||
res = ::RegSetValueExW(key, L"IconUri", 0, REG_SZ, reinterpret_cast<const BYTE *>(iconPath),
|
||||
static_cast<DWORD>((wcslen(iconPath) + 1) * sizeof(wchar_t)));
|
||||
}
|
||||
|
||||
::RegCloseKey(key);
|
||||
return HRESULT_FROM_WIN32(res);
|
||||
}
|
||||
|
||||
HRESULT RegisterAumidAndComServer(const wchar_t *aumid, GUID clsid) {
|
||||
HRESULT RegisterAumidAndComServer(const wchar_t *aumid, GUID clsid, const wchar_t *iconPath) {
|
||||
// If running as Desktop Bridge
|
||||
qDebug() << QString("CLSID : {%1-%2-%3-%4%5-%6%7%8%9%10%11}")
|
||||
.arg(clsid.Data1, 8, 16, QChar('0'))
|
||||
|
|
@ -168,7 +173,7 @@ HRESULT RegisterAumidAndComServer(const wchar_t *aumid, GUID clsid) {
|
|||
RETURN_IF_FAILED(RegisterComServer(clsid, exePath));
|
||||
|
||||
qInfo() << "Register aumid in registry";
|
||||
RETURN_IF_FAILED(RegisterAumidInRegistry(aumid));
|
||||
RETURN_IF_FAILED(RegisterAumidInRegistry(aumid, iconPath));
|
||||
|
||||
s_registeredAumidAndComServer = true;
|
||||
return S_OK;
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ HRESULT CreateStartMenuShortcut(const wchar_t *aumid, GUID clsid);
|
|||
/// </summary>
|
||||
/// <param name="aumid">An AUMID that uniquely identifies your application.</param>
|
||||
/// <param name="clsid">The CLSID of your NotificationActivator class.</param>
|
||||
HRESULT RegisterAumidAndComServer(const wchar_t *aumid, GUID clsid);
|
||||
HRESULT RegisterAumidAndComServer(const wchar_t *aumid, GUID clsid, const wchar_t *iconPath = nullptr);
|
||||
|
||||
/// <summary>
|
||||
/// Registers your module to handle COM activations. Call this upon application startup.
|
||||
|
|
|
|||
|
|
@ -30,7 +30,11 @@
|
|||
#include <QTimer>
|
||||
|
||||
#include "Notifier.hpp"
|
||||
#ifdef Q_OS_WIN
|
||||
#include "WindowsNotificationBackend.hpp"
|
||||
#elif defined(Q_OS_LINUX)
|
||||
#include "SysTrayNotificationBackend.hpp"
|
||||
#endif
|
||||
|
||||
#include "core/App.hpp"
|
||||
#include "core/call/CallGui.hpp"
|
||||
|
|
@ -129,8 +133,10 @@ Notifier::~Notifier() {
|
|||
|
||||
bool Notifier::createNotification(AbstractNotificationBackend::NotificationType type, QVariantMap data) {
|
||||
mMutex->lock();
|
||||
// Q_ASSERT(mInstancesNumber <= MaxNotificationsNumber);
|
||||
#ifdef Q_OS_WIN
|
||||
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
||||
|
||||
// Q_ASSERT(mInstancesNumber <= MaxNotificationsNumber);
|
||||
#if defined(Q_OS_WIN)
|
||||
|
||||
auto notifBackend = App::getInstance()->getNotificationBackend();
|
||||
if (!notifBackend) {
|
||||
|
|
@ -143,98 +149,100 @@ bool Notifier::createNotification(AbstractNotificationBackend::NotificationType
|
|||
|
||||
if (mInstancesNumber >= MaxNotificationsNumber) { // Check existing instances.
|
||||
qWarning() << QStringLiteral("Unable to create another notification.");
|
||||
QList<QScreen *> allScreens = QGuiApplication::screens();
|
||||
if (allScreens.size() > 0) { // Ensure to have a screen to avoid errors
|
||||
QQuickItem *previousWrapper = nullptr;
|
||||
bool showAsTool = false;
|
||||
mMutex->unlock();
|
||||
return false;
|
||||
}
|
||||
QList<QScreen *> allScreens = QGuiApplication::screens();
|
||||
if (allScreens.size() > 0) { // Ensure to have a screen to avoid errors
|
||||
QQuickItem *previousWrapper = nullptr;
|
||||
bool showAsTool = false;
|
||||
#ifdef Q_OS_MACOS
|
||||
for (auto w : QGuiApplication::topLevelWindows()) {
|
||||
if (w->visibility() == QWindow::FullScreen) {
|
||||
showAsTool = true;
|
||||
w->raise(); // Used to get focus on Mac (On Mac, A Tool is hidden if the app has not focus and the
|
||||
// only way to rid it is to use Widget Attributes(Qt::WA_MacAlwaysShowToolWindow) that is not
|
||||
// available)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (int i = 0; i < allScreens.size(); ++i) {
|
||||
|
||||
++mInstancesNumber;
|
||||
// Use QQuickView to create a visual root object that is
|
||||
// independant from current application Window
|
||||
QScreen *screen = allScreens[i];
|
||||
auto engine = App::getInstance()->mEngine;
|
||||
const QUrl url(QString(NotificationsPath) + Notifier::Notifications[type].filename);
|
||||
QObject::connect(
|
||||
engine, &QQmlApplicationEngine::objectCreated, this,
|
||||
[this, url, screen, engine, type, data, showAsTool](QObject *obj, const QUrl &objUrl) {
|
||||
if (!obj && url == objUrl) {
|
||||
lCritical() << "[App] Notifier.qml couldn't be load.";
|
||||
engine->deleteLater();
|
||||
exit(-1);
|
||||
} else {
|
||||
auto window = qobject_cast<QQuickWindow *>(obj);
|
||||
if (window) {
|
||||
window->setParent(nullptr);
|
||||
window->setProperty(NotificationPropertyData, data);
|
||||
window->setScreen(screen);
|
||||
// Don't use Popup for flags : it could lead to error in geometry. On Mac, Using Tool
|
||||
// ensure to have the Window on Top and fullscreen independant
|
||||
window->setFlags((showAsTool ? Qt::Tool : Qt::WindowStaysOnTopHint) |
|
||||
Qt::FramelessWindowHint);
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_WIN)
|
||||
window->setFlag(Qt::WindowDoesNotAcceptFocus);
|
||||
#endif
|
||||
// for (auto it = data.begin(); it != data.end(); ++it)
|
||||
// window->setProperty(it.key().toLatin1(), it.value());
|
||||
const int timeout = Notifications[type].getTimeout() * 1000;
|
||||
auto updateNotificationCoordinates = [this, window, screen](int width, int height) {
|
||||
auto screenHeightOffset =
|
||||
screen ? mScreenHeightOffset.value(screen->name()) : 0; // Access optimization
|
||||
QRect availableGeometry = screen->availableGeometry();
|
||||
|
||||
window->setX(availableGeometry.x() +
|
||||
(availableGeometry.width() -
|
||||
width)); //*screen->devicePixelRatio()); when using manual scaler
|
||||
window->setY(availableGeometry.y() + availableGeometry.height() -
|
||||
screenHeightOffset - height);
|
||||
};
|
||||
updateNotificationCoordinates(window->width(), window->height());
|
||||
auto screenHeightOffset =
|
||||
screen ? mScreenHeightOffset.take(screen->name()) : 0; // Access optimization
|
||||
mScreenHeightOffset.insert(screen->name(), screenHeightOffset + window->height());
|
||||
QObject::connect(window, &QQuickWindow::closing, window, [this, window] {
|
||||
qDebug() << "closing notification";
|
||||
deleteNotification(QVariant::fromValue(window));
|
||||
});
|
||||
QObject::connect(window, &QQuickWindow::visibleChanged, window, [this](bool visible) {
|
||||
lInfo() << log().arg("Notification visible changed") << visible;
|
||||
});
|
||||
QObject::connect(window, &QQuickWindow::activeChanged, window, [this, window] {
|
||||
lInfo() << log().arg("Notification active changed") << window->isActive();
|
||||
});
|
||||
QObject::connect(window, &QQuickWindow::visibilityChanged, window,
|
||||
[this](QWindow::Visibility visibility) {
|
||||
lInfo()
|
||||
<< log().arg("Notification visibility changed") << visibility;
|
||||
});
|
||||
QObject::connect(window, &QQuickWindow::widthChanged, window, [this](int width) {
|
||||
lInfo() << log().arg("Notification width changed") << width;
|
||||
});
|
||||
QObject::connect(window, &QQuickWindow::heightChanged, window, [this](int height) {
|
||||
lInfo() << log().arg("Notification height changed") << height;
|
||||
});
|
||||
lInfo() << QStringLiteral("Create notification:") << window;
|
||||
showNotification(window, timeout);
|
||||
}
|
||||
}
|
||||
},
|
||||
static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection));
|
||||
lDebug() << log().arg("Engine loading notification");
|
||||
engine->load(url);
|
||||
for (auto w : QGuiApplication::topLevelWindows()) {
|
||||
if (w->visibility() == QWindow::FullScreen) {
|
||||
showAsTool = true;
|
||||
w->raise(); // Used to get focus on Mac (On Mac, A Tool is hidden if the app has not focus and the
|
||||
// only way to rid it is to use Widget Attributes(Qt::WA_MacAlwaysShowToolWindow) that is not
|
||||
// available)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (int i = 0; i < allScreens.size(); ++i) {
|
||||
|
||||
++mInstancesNumber;
|
||||
// Use QQuickView to create a visual root object that is
|
||||
// independant from current application Window
|
||||
QScreen *screen = allScreens[i];
|
||||
auto engine = App::getInstance()->mEngine;
|
||||
const QUrl url(QString(NotificationsPath) + Notifier::Notifications[type].filename);
|
||||
QObject::connect(
|
||||
engine, &QQmlApplicationEngine::objectCreated, this,
|
||||
[this, url, screen, engine, type, data, showAsTool](QObject *obj, const QUrl &objUrl) {
|
||||
if (!obj && url == objUrl) {
|
||||
lCritical() << "[App] Notifier.qml couldn't be load.";
|
||||
engine->deleteLater();
|
||||
exit(-1);
|
||||
} else {
|
||||
auto window = qobject_cast<QQuickWindow *>(obj);
|
||||
if (window) {
|
||||
window->setParent(nullptr);
|
||||
window->setProperty(NotificationPropertyData, data);
|
||||
window->setScreen(screen);
|
||||
// Don't use Popup for flags : it could lead to error in geometry. On Mac, Using Tool
|
||||
// ensure to have the Window on Top and fullscreen independant
|
||||
window->setFlags((showAsTool ? Qt::Tool : Qt::WindowStaysOnTopHint) |
|
||||
Qt::FramelessWindowHint);
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_WIN)
|
||||
window->setFlag(Qt::WindowDoesNotAcceptFocus);
|
||||
#endif
|
||||
// for (auto it = data.begin(); it != data.end(); ++it)
|
||||
// window->setProperty(it.key().toLatin1(), it.value());
|
||||
const int timeout = Notifications[type].getTimeout() * 1000;
|
||||
auto updateNotificationCoordinates = [this, window, screen](int width, int height) {
|
||||
auto screenHeightOffset =
|
||||
screen ? mScreenHeightOffset.value(screen->name()) : 0; // Access optimization
|
||||
QRect availableGeometry = screen->availableGeometry();
|
||||
|
||||
window->setX(availableGeometry.x() +
|
||||
(availableGeometry.width() -
|
||||
width)); //*screen->devicePixelRatio()); when using manual scaler
|
||||
window->setY(availableGeometry.y() + availableGeometry.height() - screenHeightOffset -
|
||||
height);
|
||||
};
|
||||
updateNotificationCoordinates(window->width(), window->height());
|
||||
auto screenHeightOffset =
|
||||
screen ? mScreenHeightOffset.take(screen->name()) : 0; // Access optimization
|
||||
mScreenHeightOffset.insert(screen->name(), screenHeightOffset + window->height());
|
||||
QObject::connect(window, &QQuickWindow::closing, window, [this, window] {
|
||||
qDebug() << "closing notification";
|
||||
deleteNotification(QVariant::fromValue(window));
|
||||
});
|
||||
QObject::connect(window, &QQuickWindow::visibleChanged, window, [this](bool visible) {
|
||||
lInfo() << log().arg("Notification visible changed") << visible;
|
||||
});
|
||||
QObject::connect(window, &QQuickWindow::activeChanged, window, [this, window] {
|
||||
lInfo() << log().arg("Notification active changed") << window->isActive();
|
||||
});
|
||||
QObject::connect(window, &QQuickWindow::visibilityChanged, window,
|
||||
[this](QWindow::Visibility visibility) {
|
||||
lInfo() << log().arg("Notification visibility changed") << visibility;
|
||||
});
|
||||
QObject::connect(window, &QQuickWindow::widthChanged, window, [this](int width) {
|
||||
lInfo() << log().arg("Notification width changed") << width;
|
||||
});
|
||||
QObject::connect(window, &QQuickWindow::heightChanged, window, [this](int height) {
|
||||
lInfo() << log().arg("Notification height changed") << height;
|
||||
});
|
||||
lInfo() << QStringLiteral("Create notification:") << window;
|
||||
showNotification(window, timeout);
|
||||
}
|
||||
}
|
||||
},
|
||||
static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection));
|
||||
lDebug() << log().arg("Engine loading notification");
|
||||
engine->load(url);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
mMutex->unlock();
|
||||
return true;
|
||||
|
|
@ -341,17 +349,19 @@ void Notifier::notifyReceivedCall(const shared_ptr<linphone::Call> &call) {
|
|||
auto callLog = call->getCallLog();
|
||||
auto displayName = callLog && callLog->getConferenceInfo()
|
||||
? Utils::coreStringToAppString(callLog->getConferenceInfo()->getSubject())
|
||||
: ToolModel::getDisplayName(call->getRemoteAddress());
|
||||
: ToolModel::getDisplayName(remoteAddress);
|
||||
auto remoteAddrString = Utils::coreStringToAppString(remoteAddress->asStringUriOnly());
|
||||
|
||||
// Accessibility alert
|
||||
//: New call from %1
|
||||
AccessibilityHelper::announceMessage(tr("new_call_alert_accessible_name").arg(displayName));
|
||||
|
||||
App::postCoreAsync([this, gui, displayName]() {
|
||||
App::postCoreAsync([this, gui, displayName, remoteAddrString]() {
|
||||
mustBeInMainThread(getClassName());
|
||||
QVariantMap map;
|
||||
|
||||
map["displayName"].setValue(displayName);
|
||||
map["remoteAddress"].setValue(remoteAddrString);
|
||||
map["call"].setValue(gui);
|
||||
|
||||
CREATE_NOTIFICATION(AbstractNotificationBackend::ReceivedCall, map)
|
||||
|
|
|
|||
|
|
@ -1,41 +1,183 @@
|
|||
#include "tool/Utils.hpp"
|
||||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SysTrayNotificationBackend.hpp"
|
||||
|
||||
// #include "NotificationActivator.hpp"
|
||||
#include "WindowsNotificationBackend.hpp"
|
||||
#include "core/App.hpp"
|
||||
#include "core/call/CallGui.hpp"
|
||||
#include "core/chat/ChatGui.hpp"
|
||||
#include "core/event-filter/LockEventFilter.hpp"
|
||||
#include "tool/Utils.hpp"
|
||||
#include "tool/providers/ImageProvider.hpp"
|
||||
|
||||
#include <QDBusReply>
|
||||
#include <QDebug>
|
||||
#include <QQuickItem>
|
||||
#include <QQuickWindow>
|
||||
|
||||
static constexpr const char *SERVICE = "org.freedesktop.Notifications";
|
||||
static constexpr const char *PATH = "/org/freedesktop/Notifications";
|
||||
static constexpr const char *INTERFACE = "org.freedesktop.Notifications";
|
||||
|
||||
NotificationBackend::NotificationBackend(QObject *parent) : AbstractNotificationBackend(parent) {
|
||||
// connect(App::getInstance(), &App::sessionLockedChanged, this, [this] {
|
||||
// if (!App::getInstance()->getSessionLocked()) {
|
||||
// qDebug() << "Session unlocked, flush pending notifications";
|
||||
// flushPendingNotifications();
|
||||
// }
|
||||
// });
|
||||
}
|
||||
mInterface = new QDBusInterface(SERVICE, PATH, INTERFACE, QDBusConnection::sessionBus(), this);
|
||||
|
||||
void NotificationBackend::flushPendingNotifications() {
|
||||
for (const auto ¬if : mPendingNotifications) {
|
||||
sendNotification(notif.type, notif.data);
|
||||
if (!mInterface->isValid()) {
|
||||
qWarning() << "Notification service unavailable:" << mInterface->lastError().message();
|
||||
return;
|
||||
}
|
||||
mPendingNotifications.clear();
|
||||
|
||||
// Connecte les signaux D-Bus entrants
|
||||
QDBusConnection::sessionBus().connect(SERVICE, PATH, INTERFACE, "ActionInvoked", this,
|
||||
SLOT(onActionInvoked(uint, QString)));
|
||||
|
||||
QDBusConnection::sessionBus().connect(SERVICE, PATH, INTERFACE, "NotificationClosed", this,
|
||||
SLOT(onNotificationClosed(uint, uint)));
|
||||
}
|
||||
|
||||
void NotificationBackend::sendMessageNotification(QVariantMap data) {
|
||||
}
|
||||
|
||||
void NotificationBackend::sendCallNotification(QVariantMap data) {
|
||||
uint NotificationBackend::sendCallNotification(QVariantMap data) {
|
||||
if (!mInterface->isValid()) return 0;
|
||||
|
||||
auto displayName = data["displayName"].toString();
|
||||
CallGui *call = data["call"].value<CallGui *>();
|
||||
|
||||
// Corps de la notification
|
||||
QString title = tr("incoming_call");
|
||||
|
||||
qDebug() << "isValid:" << mInterface->isValid();
|
||||
qDebug() << "lastError:" << mInterface->lastError().message();
|
||||
|
||||
// Hints
|
||||
QVariantMap hints;
|
||||
hints["urgency"] = QVariant::fromValue(uchar(2)); // critical
|
||||
hints["resident"] = true; // reste visible jusqu'à action
|
||||
hints["x-gnome-priority"] = int(2); // force banner display
|
||||
hints["x-gnome-stack"] = QString("persistent");
|
||||
hints["transient"] = false;
|
||||
hints["category"] = QString("call.incoming");
|
||||
|
||||
// Actions : paires (clé, label)
|
||||
QStringList actions = {"accept", tr("accept_button"), "decline", tr("decline_button")};
|
||||
|
||||
QString appIcon = Utils::getIconAsPng(Utils::getAppIcon("logo").toString());
|
||||
// QString appIcon = QString("call-start"); // icône freedesktop standard
|
||||
|
||||
QDBusReply<uint> reply = mInterface->call(QString("Notify"),
|
||||
APPLICATION_NAME, // app_name
|
||||
uint(mActiveCallNotifId), // replaces_id (0 = nouvelle notif)
|
||||
appIcon, // app_icon (nom d'icône freedesktop)
|
||||
title, displayName, actions, hints,
|
||||
int(-1) // expire_timeout (-1 = jamais)
|
||||
);
|
||||
|
||||
if (!reply.isValid()) {
|
||||
qWarning() << "Notify() failed:" << reply.error().message();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint id = reply.value();
|
||||
mActiveCallNotifId = id;
|
||||
|
||||
mCurrentNotifications.insert({id}, {AbstractNotificationBackend::NotificationType::ReceivedCall, data});
|
||||
connect(call->mCore.get(), &CallCore::stateChanged, this, [this, call, id] {
|
||||
if (call->mCore->getState() == LinphoneEnums::CallState::End ||
|
||||
call->mCore->getState() == LinphoneEnums::CallState::Error) {
|
||||
qDebug() << "Call ended or error, remove toast";
|
||||
auto callId = call->mCore->getCallId();
|
||||
call->deleteLater();
|
||||
closeNotification(id);
|
||||
}
|
||||
});
|
||||
qDebug() << "Notification d'appel envoyée, id =" << id;
|
||||
|
||||
// qDebug() << "Reply valid:" << reply.isValid();
|
||||
// qDebug() << "Reply value:" << reply.value();
|
||||
// qDebug() << "Reply error:" << reply.error().name() << reply.error().message();
|
||||
// qDebug() << "Interface valid:" << mInterface->isValid();
|
||||
// qDebug() << "Interface service:" << mInterface->service();
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void NotificationBackend::closeNotification(uint id) {
|
||||
if (!mInterface->isValid()) {
|
||||
qWarning() << "invalid interface, return";
|
||||
return;
|
||||
}
|
||||
mInterface->call("CloseNotification", id);
|
||||
mCurrentNotifications.remove(id);
|
||||
|
||||
if (mActiveCallNotifId == id) mActiveCallNotifId = 0;
|
||||
}
|
||||
|
||||
void NotificationBackend::onActionInvoked(uint id, const QString &actionKey) {
|
||||
if (!mCurrentNotifications.contains(id)) return; // pas notre notification
|
||||
|
||||
qDebug() << "Action invoquée — id:" << id << "key:" << actionKey;
|
||||
auto notif = mCurrentNotifications.value(id);
|
||||
if (notif.type == AbstractNotificationBackend::NotificationType::ReceivedCall) {
|
||||
auto callGui = notif.data["call"].value<CallGui *>();
|
||||
if (!callGui) {
|
||||
qWarning() << "Could not retrieve call associated to notification, return";
|
||||
return;
|
||||
}
|
||||
if (actionKey == "accept") {
|
||||
qDebug() << "Accept call";
|
||||
Utils::openCallsWindow(callGui);
|
||||
callGui->mCore->lAccept(false);
|
||||
} else if (actionKey == "decline") {
|
||||
qDebug() << "Decline call";
|
||||
callGui->mCore->lDecline();
|
||||
}
|
||||
} else if (notif.type == AbstractNotificationBackend::NotificationType::ReceivedMessage) {
|
||||
}
|
||||
|
||||
qDebug() << "Close notification";
|
||||
mCurrentNotifications.remove(id);
|
||||
closeNotification(id);
|
||||
}
|
||||
|
||||
void NotificationBackend::onNotificationClosed(uint id, uint reason) {
|
||||
// Raisons : 1=expired, 2=dismissed, 3=CloseNotification(), 4=undefined
|
||||
if (mCurrentNotifications.contains(id)) {
|
||||
qDebug() << "Notification fermée — id:" << id << "raison:" << reason;
|
||||
mCurrentNotifications.remove(id);
|
||||
if (mActiveCallNotifId == id) mActiveCallNotifId = 0;
|
||||
emit notificationClosed(id, reason);
|
||||
|
||||
auto notif = mCurrentNotifications.value(id);
|
||||
if (notif.type == AbstractNotificationBackend::NotificationType::ReceivedCall) {
|
||||
auto callGui = notif.data["call"].value<CallGui *>();
|
||||
if (!callGui) {
|
||||
qWarning() << "Could not retrieve call associated to notification, return";
|
||||
return;
|
||||
}
|
||||
callGui->mCore->lDecline();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationBackend::sendNotification(NotificationType type, QVariantMap data) {
|
||||
// if (App::getInstance()->getSessionLocked()) {
|
||||
// mPendingNotifications.append({type, data});
|
||||
// return;
|
||||
// }
|
||||
switch (type) {
|
||||
case NotificationType::ReceivedCall:
|
||||
sendCallNotification(data);
|
||||
|
|
@ -45,3 +187,98 @@ void NotificationBackend::sendNotification(NotificationType type, QVariantMap da
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
namespace {
|
||||
constexpr char ServiceName[] = "org.freedesktop.Notifications";
|
||||
constexpr char ServicePath[] = "/org/freedesktop/Notifications";
|
||||
} // namespace
|
||||
|
||||
QDBusArgument &operator<<(QDBusArgument &arg, const QImage &image) {
|
||||
QImage scaledImage;
|
||||
if (!image.isNull()) {
|
||||
scaledImage = image.scaled(200, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
if (scaledImage.format() != QImage::Format_ARGB32)
|
||||
scaledImage = scaledImage.convertToFormat(QImage::Format_ARGB32);
|
||||
scaledImage = scaledImage.rgbSwapped();
|
||||
}
|
||||
|
||||
const int channels = 4; // ARGB32 has 4 channels
|
||||
|
||||
arg.beginStructure();
|
||||
arg << scaledImage.width();
|
||||
arg << scaledImage.height();
|
||||
arg << scaledImage.bytesPerLine();
|
||||
arg << true; // ARGB32 has alpha
|
||||
arg << scaledImage.depth() / channels;
|
||||
arg << channels;
|
||||
arg << QByteArray::fromRawData((const char *)scaledImage.constBits(),
|
||||
scaledImage.height() * scaledImage.bytesPerLine());
|
||||
arg.endStructure();
|
||||
|
||||
return arg;
|
||||
}
|
||||
const QDBusArgument &operator>>(const QDBusArgument &arg, QImage &image) {
|
||||
Q_UNUSED(image)
|
||||
return arg;
|
||||
}
|
||||
|
||||
// static void openUrl(QFileInfo info) {
|
||||
// bool showDirectory = showDirectory || !info.exists();
|
||||
// if (!QDesktopServices::openUrl(
|
||||
// QUrl(QStringLiteral("file:///%1").arg(showDirectory ? info.absolutePath() : info.absoluteFilePath()))) &&
|
||||
// !showDirectory) {
|
||||
// QDesktopServices::openUrl(QUrl(QStringLiteral("file:///%1").arg(info.absolutePath())));
|
||||
// }
|
||||
// }
|
||||
|
||||
// void NotificationsDBus::onNotificationClosed(quint32 id, quint32 reason) {
|
||||
// if (!mProcessed) { // Is was closed from system.
|
||||
// if (reason != 2)
|
||||
// qWarning() << "Notification has been closed by system. If this is an issue, please deactivate native "
|
||||
// "notifications ["
|
||||
// << id << reason << "]";
|
||||
// // open();// Not a workaround because of infinite openning loop.
|
||||
// }
|
||||
// }
|
||||
|
||||
// // QDBusMessage
|
||||
// NotificationsDBus::createMessage(const QString &title, const QString &message, QVariantMap hints, QStringList
|
||||
// actions) {
|
||||
|
||||
// QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.Notifications", "/org/freedesktop/Notifications",
|
||||
// "org.freedesktop.Notifications", "Notify");
|
||||
// hints["urgency"] = 2; // if not 2, it can be timeout without taking account of custom timeout
|
||||
// hints["category"] = "im";
|
||||
// // hints["resident"] = true;
|
||||
// hints["transient"] = true;
|
||||
// // hints["desktop-entry"] = "com.belledonnecommunications.linphone";
|
||||
// hints["suppress-sound"] = true;
|
||||
|
||||
// msg << APPLICATION_NAME; // Application name
|
||||
// msg << quint32(0); // ID
|
||||
// msg << ""; // Icon to display
|
||||
// msg << APPLICATION_NAME + QString(": ") + title; // Summary / Header of the message to display
|
||||
// msg << message; // Body of the message to display
|
||||
// msg << actions; // Actions from which the user may choose
|
||||
// msg << hints; // Hints to the server displaying the message
|
||||
// msg << qint32(0); // Timeout in milliseconds
|
||||
|
||||
// return msg;
|
||||
// }
|
||||
|
||||
// void NotificationsDBus::open() {
|
||||
// QDBusPendingReply<quint32> asyncReply(QDBusConnection::sessionBus().asyncCall(
|
||||
// mMessage)); // Would return a message containing the id of this notification
|
||||
// asyncReply.waitForFinished();
|
||||
// if (asyncReply.isValid()) mId = asyncReply.argumentAt(0).toInt();
|
||||
// else qWarning() << asyncReply.error();
|
||||
// }
|
||||
|
||||
// void NotificationsDBus::closeNotification() {
|
||||
// QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.Notifications", "/org/freedesktop/Notifications",
|
||||
// "org.freedesktop.Notifications", "CloseNotification");
|
||||
// msg << quint32(mId);
|
||||
// QDBusConnection::sessionBus().call(msg);
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -2,16 +2,21 @@
|
|||
#define SYSTRAYNOTIFICATIONBACKEND_HPP
|
||||
|
||||
#include "AbstractNotificationBackend.hpp"
|
||||
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusInterface>
|
||||
#include <QDebug>
|
||||
#include <QLocalServer>
|
||||
#include <QLocalSocket>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QVariantMap>
|
||||
|
||||
class NotificationBackend : public AbstractNotificationBackend {
|
||||
|
||||
Q_OBJECT
|
||||
public:
|
||||
struct PendingNotification {
|
||||
struct CurrentNotification {
|
||||
NotificationType type;
|
||||
QVariantMap data;
|
||||
};
|
||||
|
|
@ -19,19 +24,25 @@ public:
|
|||
NotificationBackend(QObject *parent = nullptr);
|
||||
~NotificationBackend() = default;
|
||||
|
||||
void sendCallNotification(QVariantMap data);
|
||||
uint sendCallNotification(QVariantMap data);
|
||||
void closeNotification(uint id);
|
||||
void sendMessageNotification(QVariantMap data);
|
||||
|
||||
void sendNotification(NotificationType type, QVariantMap data) override;
|
||||
|
||||
void flushPendingNotifications();
|
||||
|
||||
signals:
|
||||
void toastButtonTriggered(const QString &arg);
|
||||
void sessionLockedChanged(bool locked);
|
||||
void notificationClosed(uint id, uint reason);
|
||||
|
||||
private slots:
|
||||
void onActionInvoked(uint id, const QString &actionKey);
|
||||
void onNotificationClosed(uint id, uint reason);
|
||||
|
||||
private:
|
||||
QList<PendingNotification> mPendingNotifications;
|
||||
QDBusInterface *mInterface = nullptr;
|
||||
uint mActiveCallNotifId = 0;
|
||||
QMap<uint, CurrentNotification> mCurrentNotifications; // IDs des notifs d'appel actives
|
||||
};
|
||||
|
||||
#endif // SYSTRAYNOTIFICATIONBACKEND_HPP
|
||||
|
|
|
|||
|
|
@ -1,18 +1,14 @@
|
|||
#include "tool/Utils.hpp"
|
||||
|
||||
// #include "NotificationActivator.hpp"
|
||||
#include "WindowsNotificationBackend.hpp"
|
||||
#include "core/App.hpp"
|
||||
#include "core/call/CallGui.hpp"
|
||||
#include "core/chat/ChatGui.hpp"
|
||||
#include "core/event-filter/LockEventFilter.hpp"
|
||||
#include "tool/Constants.hpp"
|
||||
#include "tool/Utils.hpp"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include "DesktopNotificationManagerCompat.hpp"
|
||||
#include <windows.foundation.h>
|
||||
#include <windows.ui.notifications.h>
|
||||
#endif
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
|
@ -50,6 +46,7 @@ void NotificationBackend::sendMessageNotification(QVariantMap data) {
|
|||
auto remoteAddress = data["remoteAddress"].toString().toStdWString();
|
||||
auto chatRoomName = data["chatRoomName"].toString().toStdWString();
|
||||
auto chatRoomAddress = data["chatRoomAddress"].toString().toStdWString();
|
||||
auto appIcon = Utils::getIconAsPng(Utils::getAppIcon("logo").toString()).toStdWString();
|
||||
auto avatarUri = data["avatarUri"].toString().toStdWString();
|
||||
bool isGroup = data["isGroupChat"].toBool();
|
||||
ChatGui *chat = data["chat"].value<ChatGui *>();
|
||||
|
|
@ -57,6 +54,9 @@ void NotificationBackend::sendMessageNotification(QVariantMap data) {
|
|||
std::wstring xml = L"<toast>"
|
||||
L" <visual>"
|
||||
L" <binding template=\"ToastGeneric\">"
|
||||
L" <image src=\"file:///" +
|
||||
appIcon +
|
||||
L"\" placement=\"appLogoOverride\"/>"
|
||||
L" <text><![CDATA[" +
|
||||
chatRoomName +
|
||||
L"]]></text>"
|
||||
|
|
@ -86,7 +86,7 @@ void NotificationBackend::sendMessageNotification(QVariantMap data) {
|
|||
IToastNotification *toast = nullptr;
|
||||
hr = DesktopNotificationManagerCompat::CreateToastNotification(doc, &toast);
|
||||
if (FAILED(hr) || !toast) {
|
||||
qWarning() << "CreateToastNotification failed:" << Qt::hex << hr;
|
||||
lWarning() << "CreateToastNotification failed:" << Qt::hex << hr;
|
||||
doc->Release();
|
||||
notifier->Release();
|
||||
Utils::showInformationPopup(tr("info_popup_error_title"), tr("info_popup_error_creating_notification"), false);
|
||||
|
|
@ -107,7 +107,7 @@ void NotificationBackend::sendMessageNotification(QVariantMap data) {
|
|||
|
||||
hr = notifier->Show(toast);
|
||||
if (FAILED(hr)) {
|
||||
qWarning() << "Toast Show failed:" << Qt::hex << hr;
|
||||
lWarning() << "Toast Show failed:" << Qt::hex << hr;
|
||||
}
|
||||
|
||||
toast->Release();
|
||||
|
|
@ -125,22 +125,30 @@ void NotificationBackend::sendCallNotification(QVariantMap data) {
|
|||
}
|
||||
|
||||
auto displayName = data["displayName"].toString().toStdWString();
|
||||
auto remoteAddress = data["remoteAddress"].toString().toStdWString();
|
||||
CallGui *call = data["call"].value<CallGui *>();
|
||||
int timeout = 2;
|
||||
// AbstractNotificationBackend::Notifications[(int)NotificationType::ReceivedCall].getTimeout();
|
||||
|
||||
// Incoming call
|
||||
auto callDescription = tr("incoming_call").toStdWString();
|
||||
|
||||
QList<ToastButton> actions;
|
||||
actions.append(ToastButton(tr("accept_button"), "accept"));
|
||||
actions.append(ToastButton(tr("decline_button"), "decline"));
|
||||
QString declineIcon = Utils::getIconAsPng(Utils::getAppIcon("endCall").toString());
|
||||
QString acceptIcon = Utils::getIconAsPng(Utils::getAppIcon("phone").toString());
|
||||
auto appIcon = Utils::getIconAsPng(Utils::getAppIcon("logo").toString()).toStdWString();
|
||||
actions.append(ToastButton(tr("accept_button"), "accept", acceptIcon));
|
||||
actions.append(ToastButton(tr("decline_button"), "decline", declineIcon));
|
||||
std::wstring wActions;
|
||||
if (!actions.isEmpty()) {
|
||||
wActions += L"<actions>";
|
||||
for (const auto &action : actions) {
|
||||
std::wstring wLabel = action.label.toStdWString();
|
||||
std::wstring wArg = action.argument.toStdWString();
|
||||
wActions += L"<action content=\"" + wLabel + L"\" arguments=\"" + wArg + L"\"/>";
|
||||
std::wstring wIcon = action.icon.toStdWString();
|
||||
qDebug() << "toast icon action" << wIcon;
|
||||
wActions +=
|
||||
L"<action content=\"" + wLabel + L"\" arguments=\"" + wArg + L"\" imageUri=\"" + wIcon + L"\"/>";
|
||||
}
|
||||
wActions += L"</actions>";
|
||||
}
|
||||
|
|
@ -148,9 +156,15 @@ void NotificationBackend::sendCallNotification(QVariantMap data) {
|
|||
std::wstring xml = L"<toast scenario=\"reminder\">"
|
||||
L" <visual>"
|
||||
L" <binding template=\"ToastGeneric\">"
|
||||
L" <image src=\"file:///" +
|
||||
appIcon +
|
||||
L"\" placement=\"appLogoOverride\"/>"
|
||||
L" <text hint-style=\"header\">" +
|
||||
displayName +
|
||||
L"</text>"
|
||||
L" <text hint-style=\"base\">" +
|
||||
remoteAddress +
|
||||
L"</text>"
|
||||
L" <text hint-style=\"body\">" +
|
||||
callDescription +
|
||||
L"</text>"
|
||||
|
|
@ -171,7 +185,7 @@ void NotificationBackend::sendCallNotification(QVariantMap data) {
|
|||
IToastNotification *toast = nullptr;
|
||||
hr = DesktopNotificationManagerCompat::CreateToastNotification(doc, &toast);
|
||||
if (FAILED(hr) || !toast) {
|
||||
qWarning() << "CreateToastNotification failed:" << Qt::hex << hr;
|
||||
lWarning() << "CreateToastNotification failed:" << Qt::hex << hr;
|
||||
doc->Release();
|
||||
notifier->Release();
|
||||
Utils::showInformationPopup(tr("info_popup_error_title"), tr("info_popup_error_creating_notification"), false);
|
||||
|
|
@ -180,16 +194,17 @@ void NotificationBackend::sendCallNotification(QVariantMap data) {
|
|||
|
||||
ComPtr<IToastNotification2> toast2;
|
||||
hr = toast->QueryInterface(IID_PPV_ARGS(&toast2));
|
||||
if (FAILED(hr)) qWarning() << "QueryInterface failed";
|
||||
if (FAILED(hr)) lWarning() << "QueryInterface failed";
|
||||
auto callId = call->mCore->getCallId();
|
||||
qDebug() << "put tag to toast" << callId;
|
||||
hr = toast2->put_Tag(HStringReference(reinterpret_cast<const wchar_t *>(callId.utf16())).Get());
|
||||
toast2->put_Group(HStringReference(L"linphone").Get());
|
||||
if (FAILED(hr)) qWarning() << "puting tag on toast failed";
|
||||
if (FAILED(hr)) lWarning() << "puting tag on toast failed";
|
||||
|
||||
connect(call->mCore.get(), &CallCore::stateChanged, this, [this, call, notifier, toast] {
|
||||
if (call->mCore->getState() == LinphoneEnums::CallState::End) {
|
||||
qDebug() << "Call ended, remove toast";
|
||||
if (call->mCore->getState() == LinphoneEnums::CallState::End ||
|
||||
call->mCore->getState() == LinphoneEnums::CallState::Error) {
|
||||
qDebug() << "Call ended or error, remove toast";
|
||||
auto callId = call->mCore->getCallId();
|
||||
call->deleteLater();
|
||||
|
||||
|
|
@ -198,7 +213,7 @@ void NotificationBackend::sendCallNotification(QVariantMap data) {
|
|||
|
||||
auto hr = history->RemoveGroupedTag(reinterpret_cast<const wchar_t *>(callId.utf16()), L"linphone");
|
||||
if (FAILED(hr)) {
|
||||
qWarning() << "removing toast failed";
|
||||
lWarning() << "removing toast failed";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -245,7 +260,7 @@ void NotificationBackend::sendCallNotification(QVariantMap data) {
|
|||
|
||||
hr = notifier->Show(toast);
|
||||
if (FAILED(hr)) {
|
||||
qWarning() << "Toast Show failed:" << Qt::hex << hr;
|
||||
lWarning() << "Toast Show failed:" << Qt::hex << hr;
|
||||
}
|
||||
|
||||
toast->Release();
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include <QDebug>
|
||||
#include <QLocalServer>
|
||||
#include <QLocalSocket>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
class NotificationBackend : public AbstractNotificationBackend {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ FILE *gStream = NULL;
|
|||
#define WIDEN2(x) L##x
|
||||
#define WIDEN(x) WIDEN2(x)
|
||||
|
||||
static const wchar_t *mAumid = WIDEN(APPLICATION_ID);
|
||||
static const wchar_t *mAumid = WIDEN(APPLICATION_NAME);
|
||||
|
||||
void cleanStream() {
|
||||
#ifdef _WIN32
|
||||
|
|
@ -69,13 +69,8 @@ int main(int argc, char *argv[]) {
|
|||
HRESULT hrCom = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
|
||||
qInfo() << "CoInitializeEx STA result:" << Qt::hex << hrCom;
|
||||
|
||||
auto hr = DesktopNotificationManagerCompat::CreateStartMenuShortcut(mAumid, __uuidof(NotificationActivator));
|
||||
if (FAILED(hr)) {
|
||||
qWarning() << "CreateStartMenuShortcut failed:" << Qt::hex << hr;
|
||||
}
|
||||
|
||||
// Register AUMID and COM server (for a packaged app, this is a no-operation)
|
||||
hr = DesktopNotificationManagerCompat::RegisterAumidAndComServer(L"Linphone", __uuidof(NotificationActivator));
|
||||
auto hr = DesktopNotificationManagerCompat::RegisterAumidAndComServer(mAumid, __uuidof(NotificationActivator));
|
||||
if (FAILED(hr)) {
|
||||
qWarning() << "RegisterAumidAndComServer failed:" << Qt::hex << hr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,6 +92,8 @@ bool ChatMessageContentModel::downloadFile(const QString &name, QString *error)
|
|||
case linphone::ChatMessage::State::DeliveredToUser:
|
||||
case linphone::ChatMessage::State::Displayed:
|
||||
case linphone::ChatMessage::State::FileTransferDone:
|
||||
case linphone::ChatMessage::State::FileTransferError:
|
||||
case linphone::ChatMessage::State::FileTransferCancelling:
|
||||
break;
|
||||
case linphone::ChatMessage::State::FileTransferInProgress:
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@ public:
|
|||
|
||||
static constexpr char LinphoneDomain[] = "sip.linphone.org"; // Use for checking if config are a Linphone
|
||||
static constexpr char WindowIconPath[] = ":/data/image/logo.svg";
|
||||
static constexpr char AppIconsPath[] = ":/data/image/";
|
||||
static constexpr char ApplicationMinimalQtVersion[] = "6.10.0";
|
||||
static constexpr char DefaultConferenceURI[] =
|
||||
"sip:conference-factory@sip.linphone.org"; // Default for a Linphone account
|
||||
|
|
|
|||
|
|
@ -46,15 +46,19 @@
|
|||
#include <QClipboard>
|
||||
#include <QCryptographicHash>
|
||||
#include <QDesktopServices>
|
||||
#include <QFileInfo>
|
||||
#include <QHostAddress>
|
||||
#include <QImageReader>
|
||||
#include <QMimeDatabase>
|
||||
#include <QPainter>
|
||||
#include <QProcess>
|
||||
#include <QQmlComponent>
|
||||
#include <QQmlProperty>
|
||||
#include <QQuickWindow>
|
||||
#include <QRandomGenerator>
|
||||
#include <QRegularExpression>
|
||||
#include <QStandardPaths>
|
||||
#include <QSvgRenderer>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#ifndef NOMINMAX
|
||||
|
|
@ -1811,9 +1815,33 @@ QUrl Utils::getAppIcon(const QString &iconName) {
|
|||
QQmlComponent component(App::getInstance()->mEngine, QUrl("qrc:/qt/qml/Linphone/view/Style/AppIcons.qml"));
|
||||
appIconsSingleton = component.create();
|
||||
}
|
||||
|
||||
return QQmlProperty::read(appIconsSingleton, iconName).value<QUrl>();
|
||||
}
|
||||
|
||||
QString Utils::getIconAsPng(const QString &imagePath, const QSize &size) {
|
||||
// Convertit "image://internal/phone-disconnect.svg" en ":/data/image/phone-disconnect.svg"
|
||||
QString resourcePath = imagePath;
|
||||
if (imagePath.startsWith("image://internal/"))
|
||||
resourcePath = ":/data/image/" + imagePath.mid(QString("image://internal/").length());
|
||||
|
||||
QSvgRenderer renderer(resourcePath);
|
||||
if (!renderer.isValid()) return QString();
|
||||
|
||||
QImage image(size, QImage::Format_ARGB32_Premultiplied);
|
||||
image.fill(Qt::transparent);
|
||||
QPainter painter(&image);
|
||||
renderer.render(&painter);
|
||||
painter.end();
|
||||
|
||||
QString outPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/linphone_" +
|
||||
QFileInfo(resourcePath).baseName() + ".png";
|
||||
|
||||
if (!QFile::exists(outPath)) image.save(outPath, "PNG");
|
||||
|
||||
return outPath;
|
||||
}
|
||||
|
||||
QColor Utils::getPresenceColor(LinphoneEnums::Presence presence) {
|
||||
mustBeInMainThread(sLog().arg(Q_FUNC_INFO));
|
||||
QColor presenceColor = QColorConstants::Transparent;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include <QDebug>
|
||||
#include <QObject>
|
||||
#include <QSize>
|
||||
#include <QString>
|
||||
|
||||
#include "Constants.hpp"
|
||||
|
|
@ -253,6 +254,7 @@ public:
|
|||
|
||||
// Presence
|
||||
|
||||
static QString getIconAsPng(const QString &imagePath, const QSize &size = QSize(64, 64));
|
||||
static QColor getDefaultStyleColor(const QString &colorName);
|
||||
static QUrl getAppIcon(const QString &iconName);
|
||||
static QUrl getRegistrationStateIcon(LinphoneEnums::RegistrationState state);
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@ public:
|
|||
}
|
||||
|
||||
bool tryLock() {
|
||||
if (!this) return false;
|
||||
mLocker.lock();
|
||||
auto coreLocked = mCore.lock();
|
||||
auto modelLocked = mModel.lock();
|
||||
|
|
|
|||
|
|
@ -529,6 +529,7 @@ Item {
|
|||
text: qsTr("recordings_title")
|
||||
icon.source: AppIcons.recordFill
|
||||
onClicked: {
|
||||
settingsMenuButton.popup.close()
|
||||
UtilsCpp.openNativeDialog(UtilsCpp.getCaptureDirpaths())
|
||||
// fileDialog.folder = UtilsCpp.getCaptureDirpaths()
|
||||
// fileDialog.open()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue