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 "main/main_domain.h"
#include "window/notifications_manager.h"
#include "platform/linux/specific_linux.h"
#include <QtDBus/QDBusConnection>
@ -22,10 +23,16 @@ NotificationServiceWatcher::NotificationServiceWatcher()
QDBusConnection::sessionBus(),
QDBusServiceWatcher::WatchForOwnerChange) {
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([=] {
if (!Core::App().domain().started()) {
return;
} else if (IsNotificationServiceActivatable()
&& newOwner.isEmpty()) {
return;
}
Core::App().notifications().createManager();

View file

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

View file

@ -33,13 +33,13 @@ bool Enforced() {
return IsWayland();
}
std::unique_ptr<Window::Notifications::Manager> Create(
Window::Notifications::System *system) {
void Create(Window::Notifications::System *system) {
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

View file

@ -1247,18 +1247,13 @@ void start() {
}
#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
}
void finish() {
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
if (NSWInstance) {
NSWInstance = nullptr;
}
NSWInstance = nullptr;
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
}

View file

@ -166,11 +166,12 @@ bool Enforced() {
return Supported();
}
std::unique_ptr<Window::Notifications::Manager> Create(Window::Notifications::System *system) {
void Create(Window::Notifications::System *system) {
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 {

View file

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

View file

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

View file

@ -66,12 +66,23 @@ System::System()
}
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) {
_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 {
for (const auto &[index, account] : Core::App().domain().accounts()) {
if (const auto session = account->maybeSession()) {

View file

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

View file

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