Make notification manager creation async

This commit is contained in:
Ilya Fedin 2021-01-21 14:18:40 +04:00 committed by John Preston
parent a0a71687e7
commit e8edbb16ae
10 changed files with 152 additions and 82 deletions

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h" #include "core/application.h"
#include "main/main_domain.h" #include "main/main_domain.h"
#include "window/notifications_manager.h" #include "window/notifications_manager.h"
#include "platform/linux/specific_linux.h"
#include <QtDBus/QDBusConnection> #include <QtDBus/QDBusConnection>
@ -22,10 +23,16 @@ NotificationServiceWatcher::NotificationServiceWatcher()
QDBusConnection::sessionBus(), QDBusConnection::sessionBus(),
QDBusServiceWatcher::WatchForOwnerChange) { QDBusServiceWatcher::WatchForOwnerChange) {
const auto signal = &QDBusServiceWatcher::serviceOwnerChanged; const auto signal = &QDBusServiceWatcher::serviceOwnerChanged;
QObject::connect(&_dbusWatcher, signal, [=] { QObject::connect(&_dbusWatcher, signal, [=](
const QString &service,
const QString &oldOwner,
const QString &newOwner) {
crl::on_main([=] { crl::on_main([=] {
if (!Core::App().domain().started()) { if (!Core::App().domain().started()) {
return; return;
} else if (IsNotificationServiceActivatable()
&& newOwner.isEmpty()) {
return;
} }
Core::App().notifications().createManager(); Core::App().notifications().createManager();

View file

@ -21,6 +21,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtDBus/QDBusConnection> #include <QtDBus/QDBusConnection>
#include <QtDBus/QDBusConnectionInterface> #include <QtDBus/QDBusConnectionInterface>
#include <QtDBus/QDBusMessage> #include <QtDBus/QDBusMessage>
#include <QtDBus/QDBusPendingCall>
#include <QtDBus/QDBusPendingCallWatcher>
#include <QtDBus/QDBusPendingReply> #include <QtDBus/QDBusPendingReply>
#include <QtDBus/QDBusReply> #include <QtDBus/QDBusReply>
#include <QtDBus/QDBusError> #include <QtDBus/QDBusError>
@ -64,7 +66,7 @@ bool GetServiceRegistered() {
: activatable; : activatable;
} }
std::optional<ServerInformation> GetServerInformation() { void GetServerInformation(Fn<void(std::optional<ServerInformation>)> callback) {
using ServerInformationReply = QDBusPendingReply< using ServerInformationReply = QDBusPendingReply<
QString, QString,
QString, QString,
@ -77,59 +79,63 @@ std::optional<ServerInformation> GetServerInformation() {
kInterface.utf16(), kInterface.utf16(),
qsl("GetServerInformation")); qsl("GetServerInformation"));
// We may be launched earlier than notification daemon const auto async = QDBusConnection::sessionBus().asyncCall(message);
while (true) { auto watcher = new QDBusPendingCallWatcher(async);
ServerInformationReply reply = QDBusConnection::sessionBus()
.asyncCall(message);
reply.waitForFinished(); const auto finished = [=](QDBusPendingCallWatcher *call) {
const ServerInformationReply reply = *call;
if (reply.isValid()) { if (reply.isValid()) {
return { crl::on_main([=] {
reply.argumentAt<0>(), callback(ServerInformation{
reply.argumentAt<1>(), reply.argumentAt<0>(),
QVersionNumber::fromString(reply.argumentAt<2>()), reply.argumentAt<1>(),
QVersionNumber::fromString(reply.argumentAt<3>()), QVersionNumber::fromString(reply.argumentAt<2>()),
}; QVersionNumber::fromString(reply.argumentAt<3>()),
});
});
} else {
LOG(("Native notification error: %1").arg(
reply.error().message()));
crl::on_main([=] { callback(std::nullopt); });
} }
LOG(("Native notification error: %1").arg(reply.error().message())); call->deleteLater();
};
if (reply.error().type() != QDBusError::NoReply) { QObject::connect(watcher, &QDBusPendingCallWatcher::finished, finished);
break;
}
}
return std::nullopt;
} }
QStringList GetCapabilities() { void GetCapabilities(Fn<void(QStringList)> callback) {
const auto message = QDBusMessage::createMethodCall( const auto message = QDBusMessage::createMethodCall(
kService.utf16(), kService.utf16(),
kObjectPath.utf16(), kObjectPath.utf16(),
kInterface.utf16(), kInterface.utf16(),
qsl("GetCapabilities")); qsl("GetCapabilities"));
// We may be launched earlier than notification daemon const auto async = QDBusConnection::sessionBus().asyncCall(message);
while (true) { auto watcher = new QDBusPendingCallWatcher(async);
const QDBusReply<QStringList> reply = QDBusConnection::sessionBus()
.call(message); const auto finished = [=](QDBusPendingCallWatcher *call) {
const QDBusPendingReply<QStringList> reply = *call;
if (reply.isValid()) { if (reply.isValid()) {
return reply.value(); crl::on_main([=] { callback(reply.value()); });
} else {
LOG(("Native notification error: %1").arg(
reply.error().message()));
crl::on_main([=] { callback({}); });
} }
LOG(("Native notification error: %1").arg(reply.error().message())); call->deleteLater();
};
if (reply.error().type() != QDBusError::NoReply) { QObject::connect(watcher, &QDBusPendingCallWatcher::finished, finished);
break;
}
}
return {};
} }
bool GetInhibitionSupported() { void GetInhibitionSupported(Fn<void(bool)> callback) {
auto message = QDBusMessage::createMethodCall( auto message = QDBusMessage::createMethodCall(
kService.utf16(), kService.utf16(),
kObjectPath.utf16(), kObjectPath.utf16(),
@ -141,24 +147,21 @@ bool GetInhibitionSupported() {
qsl("Inhibited") qsl("Inhibited")
}); });
// We may be launched earlier than notification daemon const auto async = QDBusConnection::sessionBus().asyncCall(message);
while (true) { auto watcher = new QDBusPendingCallWatcher(async);
const QDBusError error = QDBusConnection::sessionBus().call(message);
if (!error.isValid()) { const auto finished = [=](QDBusPendingCallWatcher *call) {
return true; const auto error = QDBusPendingReply<QVariant>(*call).error();
} else if (error.type() == QDBusError::InvalidArgs) {
break; if (error.isValid() && error.type() != QDBusError::InvalidArgs) {
LOG(("Native notification error: %1").arg(error.message()));
} }
LOG(("Native notification error: %1").arg(error.message())); crl::on_main([=] { callback(!error.isValid()); });
call->deleteLater();
};
if (error.type() != QDBusError::NoReply) { QObject::connect(watcher, &QDBusPendingCallWatcher::finished, finished);
break;
}
}
return false;
} }
bool Inhibited() { bool Inhibited() {
@ -681,26 +684,56 @@ bool Enforced() {
return IsQualifiedDaemon() || IsWayland(); return IsQualifiedDaemon() || IsWayland();
} }
std::unique_ptr<Window::Notifications::Manager> Create( void Create(Window::Notifications::System *system) {
Window::Notifications::System *system) {
ServiceRegistered = GetServiceRegistered(); ServiceRegistered = GetServiceRegistered();
if (Supported()) { const auto managerSetter = [=] {
CurrentServerInformation = GetServerInformation(); using ManagerType = Window::Notifications::ManagerType;
CurrentCapabilities = GetCapabilities(); if ((Core::App().settings().nativeNotifications() && Supported())
InhibitionSupported = GetInhibitionSupported(); || Enforced()) {
if (*system->managerType() != ManagerType::Native) {
system->setManager(std::make_unique<Manager>(system));
}
} else {
if (*system->managerType() != ManagerType::Default) {
system->setManager(nullptr);
}
}
};
if (!system->managerType().has_value()) {
using DummyManager = Window::Notifications::DummyManager;
system->setManager(std::make_unique<DummyManager>(system));
}
if (ServiceRegistered) {
const auto counter = std::make_shared<int>(3);
const auto oneReady = [=] {
if (!--*counter) {
managerSetter();
}
};
GetServerInformation([=](std::optional<ServerInformation> result) {
CurrentServerInformation = result;
oneReady();
});
GetCapabilities([=](QStringList result) {
CurrentCapabilities = result;
oneReady();
});
GetInhibitionSupported([=](bool result) {
InhibitionSupported = result;
oneReady();
});
} else { } else {
CurrentServerInformation = std::nullopt; CurrentServerInformation = std::nullopt;
CurrentCapabilities = QStringList{}; CurrentCapabilities = QStringList{};
InhibitionSupported = false; InhibitionSupported = false;
managerSetter();
} }
if ((Core::App().settings().nativeNotifications() && Supported())
|| Enforced()) {
return std::make_unique<Manager>(system);
}
return nullptr;
} }
class Manager::Private { class Manager::Private {

View file

@ -33,13 +33,13 @@ bool Enforced() {
return IsWayland(); return IsWayland();
} }
std::unique_ptr<Window::Notifications::Manager> Create( void Create(Window::Notifications::System *system) {
Window::Notifications::System *system) {
if (Enforced()) { if (Enforced()) {
return std::make_unique<Window::Notifications::DummyManager>(system); using DummyManager = Window::Notifications::DummyManager;
system->setManager(std::make_unique<DummyManager>(system));
} else {
system->setManager(nullptr);
} }
return nullptr;
} }
} // namespace Notifications } // namespace Notifications

View file

@ -1247,18 +1247,13 @@ void start() {
} }
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
if (!IsNotificationServiceActivatable()) { NSWInstance = std::make_unique<internal::NotificationServiceWatcher>();
NSWInstance = std::make_unique<
internal::NotificationServiceWatcher>();
}
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION #endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
} }
void finish() { void finish() {
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
if (NSWInstance) { NSWInstance = nullptr;
NSWInstance = nullptr;
}
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION #endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
} }

View file

@ -166,11 +166,12 @@ bool Enforced() {
return Supported(); return Supported();
} }
std::unique_ptr<Window::Notifications::Manager> Create(Window::Notifications::System *system) { void Create(Window::Notifications::System *system) {
if (Supported()) { if (Supported()) {
return std::make_unique<Manager>(system); system->setManager(std::make_unique<Manager>(system));
} else {
system->setManager(nullptr);
} }
return nullptr;
} }
class Manager::Private : public QObject, private base::Subscriber { class Manager::Private : public QObject, private base::Subscriber {

View file

@ -18,8 +18,7 @@ namespace Notifications {
[[nodiscard]] bool Supported(); [[nodiscard]] bool Supported();
[[nodiscard]] bool Enforced(); [[nodiscard]] bool Enforced();
[[nodiscard]] std::unique_ptr<Window::Notifications::Manager> Create( void Create(Window::Notifications::System *system);
Window::Notifications::System *system);
} // namespace Notifications } // namespace Notifications
} // namespace Platform } // namespace Platform

View file

@ -329,16 +329,17 @@ bool Enforced() {
return false; return false;
} }
std::unique_ptr<Window::Notifications::Manager> Create(Window::Notifications::System *system) { void Create(Window::Notifications::System *system) {
#ifndef __MINGW32__ #ifndef __MINGW32__
if (Core::App().settings().nativeNotifications() && Supported()) { if (Core::App().settings().nativeNotifications() && Supported()) {
auto result = std::make_unique<Manager>(system); auto result = std::make_unique<Manager>(system);
if (result->init()) { if (result->init()) {
return std::move(result); system->setManager(std::move(result));
return;
} }
} }
#endif // !__MINGW32__ #endif // !__MINGW32__
return nullptr; system->setManager(nullptr);
} }
#ifndef __MINGW32__ #ifndef __MINGW32__

View file

@ -66,12 +66,23 @@ System::System()
} }
void System::createManager() { void System::createManager() {
_manager = Platform::Notifications::Create(this); Platform::Notifications::Create(this);
}
void System::setManager(std::unique_ptr<Manager> manager) {
_manager = std::move(manager);
if (!_manager) { if (!_manager) {
_manager = std::make_unique<Default::Manager>(this); _manager = std::make_unique<Default::Manager>(this);
} }
} }
std::optional<ManagerType> System::managerType() const {
if (_manager) {
return _manager->type();
}
return std::nullopt;
}
Main::Session *System::findSession(uint64 sessionId) const { Main::Session *System::findSession(uint64 sessionId) const {
for (const auto &[index, account] : Core::App().domain().accounts()) { for (const auto &[index, account] : Core::App().domain().accounts()) {
if (const auto session = account->maybeSession()) { if (const auto session = account->maybeSession()) {

View file

@ -34,6 +34,12 @@ class Track;
namespace Window { namespace Window {
namespace Notifications { namespace Notifications {
enum class ManagerType {
Dummy,
Default,
Native,
};
enum class ChangeType { enum class ChangeType {
SoundEnabled, SoundEnabled,
FlashBounceEnabled, FlashBounceEnabled,
@ -70,6 +76,8 @@ public:
[[nodiscard]] Main::Session *findSession(uint64 sessionId) const; [[nodiscard]] Main::Session *findSession(uint64 sessionId) const;
void createManager(); void createManager();
void setManager(std::unique_ptr<Manager> manager);
[[nodiscard]] std::optional<ManagerType> managerType() const;
void checkDelayed(); void checkDelayed();
void schedule(not_null<HistoryItem*> item); void schedule(not_null<HistoryItem*> item);
@ -189,6 +197,8 @@ public:
const QString &title, const QString &title,
not_null<Main::Session*> session); not_null<Main::Session*> session);
[[nodiscard]] virtual ManagerType type() const = 0;
virtual ~Manager() = default; virtual ~Manager() = default;
protected: protected:
@ -221,6 +231,11 @@ private:
}; };
class NativeManager : public Manager { class NativeManager : public Manager {
public:
[[nodiscard]] ManagerType type() const override {
return ManagerType::Native;
}
protected: protected:
using Manager::Manager; using Manager::Manager;
@ -252,6 +267,10 @@ class DummyManager : public NativeManager {
public: public:
using NativeManager::NativeManager; using NativeManager::NativeManager;
[[nodiscard]] ManagerType type() const override {
return ManagerType::Dummy;
}
protected: protected:
void doShowNativeNotification( void doShowNativeNotification(
not_null<PeerData*> peer, not_null<PeerData*> peer,

View file

@ -43,6 +43,10 @@ public:
Manager(System *system); Manager(System *system);
~Manager(); ~Manager();
[[nodiscard]] ManagerType type() const override {
return ManagerType::Default;
}
template <typename Method> template <typename Method>
void enumerateNotifications(Method method) { void enumerateNotifications(Method method) {
for (const auto &notification : _notifications) { for (const auto &notification : _notifications) {