Workaround snap's activation restriction

This commit is contained in:
Ilya Fedin 2021-02-23 06:34:01 +04:00 committed by John Preston
parent 073b5b106c
commit 6463890b11
2 changed files with 104 additions and 68 deletions

View File

@ -20,21 +20,21 @@ namespace Platform {
namespace internal { namespace internal {
namespace { namespace {
constexpr auto kNotificationService = "org.freedesktop.Notifications"_cs; constexpr auto kService = "org.freedesktop.Notifications"_cs;
bool IsNotificationServiceActivatable() { auto Activatable() {
static const auto Result = [] { static const auto Result = []() -> std::optional<bool> {
try { try {
const auto connection = Gio::DBus::Connection::get_sync( const auto connection = Gio::DBus::Connection::get_sync(
Gio::DBus::BusType::BUS_TYPE_SESSION); Gio::DBus::BusType::BUS_TYPE_SESSION);
return ranges::contains( return ranges::contains(
base::Platform::DBus::ListActivatableNames(connection), base::Platform::DBus::ListActivatableNames(connection),
Glib::ustring(std::string(kNotificationService))); Glib::ustring(std::string(kService)));
} catch (...) { } catch (...) {
} }
return false; return std::nullopt;
}(); }();
return Result; return Result;
@ -56,14 +56,13 @@ NotificationServiceWatcher::NotificationServiceWatcher()
_private->signalId = base::Platform::DBus::RegisterServiceWatcher( _private->signalId = base::Platform::DBus::RegisterServiceWatcher(
_private->dbusConnection, _private->dbusConnection,
std::string(kNotificationService), std::string(kService),
[]( [](
const Glib::ustring &service, const Glib::ustring &service,
const Glib::ustring &oldOwner, const Glib::ustring &oldOwner,
const Glib::ustring &newOwner) { const Glib::ustring &newOwner) {
if (!Core::App().domain().started() if (!Core::App().domain().started()
|| (IsNotificationServiceActivatable() || (Activatable().value_or(true) && newOwner.empty())) {
&& newOwner.empty())) {
return; return;
} }

View File

@ -46,6 +46,40 @@ bool InhibitionSupported = false;
std::optional<ServerInformation> CurrentServerInformation; std::optional<ServerInformation> CurrentServerInformation;
QStringList CurrentCapabilities; QStringList CurrentCapabilities;
void StartServiceAsync(
Fn<void()> callback,
const Glib::RefPtr<Gio::Cancellable> &cancellable = Glib::RefPtr<Gio::Cancellable>()) {
try {
const auto connection = Gio::DBus::Connection::get_sync(
Gio::DBus::BusType::BUS_TYPE_SESSION);
base::Platform::DBus::StartServiceByNameAsync(
connection,
std::string(kService),
[=](Fn<base::Platform::DBus::StartReply()> result) {
try {
result(); // get the error if any
} catch (const Glib::Error &e) {
LOG(("Native Notification Error: %1").arg(
QString::fromStdString(e.what())));
} catch (const std::exception &e) {
LOG(("Native Notification Error: %1").arg(
QString::fromStdString(e.what())));
}
crl::on_main([=] { callback(); });
},
cancellable);
return;
} catch (const Glib::Error &e) {
LOG(("Native Notification Error: %1").arg(
QString::fromStdString(e.what())));
}
crl::on_main([=] { callback(); });
}
bool GetServiceRegistered() { bool GetServiceRegistered() {
try { try {
const auto connection = Gio::DBus::Connection::get_sync( const auto connection = Gio::DBus::Connection::get_sync(
@ -125,7 +159,7 @@ void GetServerInformation(
QString::fromStdString(e.what()))); QString::fromStdString(e.what())));
} }
crl::on_main([=] { callback({}); }); crl::on_main([=] { callback(std::nullopt); });
}, },
std::string(kService)); std::string(kService));
@ -244,6 +278,11 @@ bool Inhibited() {
const auto connection = Gio::DBus::Connection::get_sync( const auto connection = Gio::DBus::Connection::get_sync(
Gio::DBus::BusType::BUS_TYPE_SESSION); Gio::DBus::BusType::BUS_TYPE_SESSION);
// a hack for snap's activation restriction
base::Platform::DBus::StartServiceByName(
connection,
std::string(kService));
auto reply = connection->call_sync( auto reply = connection->call_sync(
std::string(kObjectPath), std::string(kObjectPath),
std::string(kPropertiesInterface), std::string(kPropertiesInterface),
@ -336,6 +375,7 @@ public:
private: private:
Glib::RefPtr<Gio::DBus::Connection> _dbusConnection; Glib::RefPtr<Gio::DBus::Connection> _dbusConnection;
Glib::RefPtr<Gio::Cancellable> _cancellable;
base::weak_ptr<Manager> _manager; base::weak_ptr<Manager> _manager;
Glib::ustring _title; Glib::ustring _title;
@ -350,13 +390,13 @@ private:
uint _notificationClosedSignalId = 0; uint _notificationClosedSignalId = 0;
NotificationId _id; NotificationId _id;
void notificationShown(
const Glib::RefPtr<Gio::AsyncResult> &result);
void notificationClosed(uint id, uint reason); void notificationClosed(uint id, uint reason);
void actionInvoked(uint id, const Glib::ustring &actionName); void actionInvoked(uint id, const Glib::ustring &actionName);
void notificationReplied(uint id, const Glib::ustring &text); void notificationReplied(uint id, const Glib::ustring &text);
void notificationShown(
const Glib::RefPtr<Gio::AsyncResult> &result);
void signalEmitted( void signalEmitted(
const Glib::RefPtr<Gio::DBus::Connection> &connection, const Glib::RefPtr<Gio::DBus::Connection> &connection,
const Glib::ustring &sender_name, const Glib::ustring &sender_name,
@ -376,7 +416,8 @@ NotificationData::NotificationData(
const QString &msg, const QString &msg,
NotificationId id, NotificationId id,
bool hideReplyButton) bool hideReplyButton)
: _manager(manager) : _cancellable(Gio::Cancellable::create())
, _manager(manager)
, _title(title.toStdString()) , _title(title.toStdString())
, _imageKey(GetImageKey(CurrentServerInformationValue().specVersion)) , _imageKey(GetImageKey(CurrentServerInformationValue().specVersion))
, _id(id) { , _id(id) {
@ -475,6 +516,10 @@ NotificationData::NotificationData(
} }
NotificationData::~NotificationData() { NotificationData::~NotificationData() {
if (_cancellable) {
_cancellable->cancel();
}
if (_dbusConnection) { if (_dbusConnection) {
if (_actionInvokedSignalId != 0) { if (_actionInvokedSignalId != 0) {
_dbusConnection->signal_unsubscribe(_actionInvokedSignalId); _dbusConnection->signal_unsubscribe(_actionInvokedSignalId);
@ -491,7 +536,8 @@ NotificationData::~NotificationData() {
} }
void NotificationData::show() { void NotificationData::show() {
try { // a hack for snap's activation restriction
StartServiceAsync([=] {
const auto iconName = _imageKey.empty() const auto iconName = _imageKey.empty()
|| _hints.find(_imageKey) == end(_hints) || _hints.find(_imageKey) == end(_hints)
? Glib::ustring(GetIconName().toStdString()) ? Glib::ustring(GetIconName().toStdString())
@ -513,16 +559,7 @@ void NotificationData::show() {
}), }),
sigc::mem_fun(this, &NotificationData::notificationShown), sigc::mem_fun(this, &NotificationData::notificationShown),
std::string(kService)); std::string(kService));
} catch (const Glib::Error &e) { }, _cancellable);
LOG(("Native Notification Error: %1").arg(
QString::fromStdString(e.what())));
const auto manager = _manager;
const auto my = _id;
crl::on_main(manager, [=] {
manager->clearNotification(my);
});
}
} }
void NotificationData::notificationShown( void NotificationData::notificationShown(
@ -549,20 +586,15 @@ void NotificationData::notificationShown(
} }
void NotificationData::close() { void NotificationData::close() {
try { _dbusConnection->call(
_dbusConnection->call( std::string(kObjectPath),
std::string(kObjectPath), std::string(kInterface),
std::string(kInterface), "CloseNotification",
"CloseNotification", base::Platform::MakeGlibVariant(std::tuple{
base::Platform::MakeGlibVariant(std::tuple{ _notificationId,
_notificationId, }),
}), {},
{}, std::string(kService));
std::string(kService));
} catch (const Glib::Error &e) {
LOG(("Native Notification Error: %1").arg(
QString::fromStdString(e.what())));
}
} }
void NotificationData::setImage(const QString &imagePath) { void NotificationData::setImage(const QString &imagePath) {
@ -710,8 +742,6 @@ bool ByDefault() {
} }
void Create(Window::Notifications::System *system) { void Create(Window::Notifications::System *system) {
ServiceRegistered = GetServiceRegistered();
const auto managerSetter = [=] { const auto managerSetter = [=] {
using ManagerType = Window::Notifications::ManagerType; using ManagerType = Window::Notifications::ManagerType;
if ((Core::App().settings().nativeNotifications() && Supported()) if ((Core::App().settings().nativeNotifications() && Supported())
@ -726,13 +756,39 @@ void Create(Window::Notifications::System *system) {
} }
}; };
if (!ServiceRegistered) { const auto counter = std::make_shared<int>(3);
CurrentServerInformation = std::nullopt; const auto oneReady = [=] {
CurrentCapabilities = QStringList{}; if (!--*counter) {
InhibitionSupported = false; managerSetter();
managerSetter(); }
return; };
}
const auto serviceActivated = [=] {
ServiceRegistered = GetServiceRegistered();
if (!ServiceRegistered) {
CurrentServerInformation = std::nullopt;
CurrentCapabilities = QStringList{};
InhibitionSupported = false;
managerSetter();
return;
}
GetServerInformation([=](const std::optional<ServerInformation> &result) {
CurrentServerInformation = result;
oneReady();
});
GetCapabilities([=](const QStringList &result) {
CurrentCapabilities = result;
oneReady();
});
GetInhibitionSupported([=](bool result) {
InhibitionSupported = result;
oneReady();
});
};
// There are some asserts that manager is not nullptr, // There are some asserts that manager is not nullptr,
// avoid crashes until some real manager is created // avoid crashes until some real manager is created
@ -741,27 +797,8 @@ void Create(Window::Notifications::System *system) {
system->setManager(std::make_unique<DummyManager>(system)); system->setManager(std::make_unique<DummyManager>(system));
} }
const auto counter = std::make_shared<int>(3); // snap doesn't allow access when the daemon is not running :(
const auto oneReady = [=] { StartServiceAsync(serviceActivated);
if (!--*counter) {
managerSetter();
}
};
GetServerInformation([=](const std::optional<ServerInformation> &result) {
CurrentServerInformation = result;
oneReady();
});
GetCapabilities([=](const QStringList &result) {
CurrentCapabilities = result;
oneReady();
});
GetInhibitionSupported([=](bool result) {
InhibitionSupported = result;
oneReady();
});
} }
class Manager::Private { class Manager::Private {