diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,8 @@ find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS KIO I18n + Config + DBusAddons Notifications Runner WindowSystem @@ -36,6 +38,7 @@ add_subdirectory(host) add_subdirectory(tabsrunner) +add_subdirectory(reminder) if(NOT DEFINED CHROME_EXTENSION_ID) # The extension ID is based on the key used to sign the extension diff --git a/advert/CMakeLists.txt b/advert/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/advert/CMakeLists.txt @@ -0,0 +1,11 @@ +add_definitions(-DQT_NO_URL_CAST_FROM_STRING) + +set(kded_browserlaunchwatcher browserlaunchwatcher.cpp ) + +add_library(browserlaunchwatcher MODULE ${kded_browserlaunchwatcher}) +kcoreaddons_desktop_to_json(browserlaunchwatcher browserlaunchwatcher.desktop) + + +target_link_libraries(browserlaunchwatcher Qt5::DBus KF5::CoreAddons KF5::DBusAddons KF5::I18n Qt5::Widgets KF5::Notifications KF5::ConfigCore) + +install(TARGETS browserlaunchwatcher DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf5/kded) diff --git a/advert/browserlaunchwatcher.h b/advert/browserlaunchwatcher.h new file mode 100644 --- /dev/null +++ b/advert/browserlaunchwatcher.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright 2009 by Marco Martin * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +class KStatusNotifierItem; +class QDBusServiceWatcher; + +/** + * This plugin's purpose is to provide the user a link to the PBI extension. + * It should run if the user has installed the host, but doesn't have the browser side. + * + * It creates an SNI for the first N times the user opens a browser with this setup. + * Then we give up + */ + +class BrowserLaunchWatcher : public KDEDModule +{ + Q_OBJECT + +public: + BrowserLaunchWatcher(QObject *parent, const QList&); + ~BrowserLaunchWatcher() override; + +private Q_SLOTS: + void onResourceScoresChanged(const QString &activity, + const QString &client, + const QString &resource, + double score, + unsigned int lastUpdate, + unsigned int firstUpdate); + + void onBrowserStarted(const QString &browserName); + + void unload(); + void disableAutoload(); + +private: + struct BrowserInfo + { + QString icon; + QUrl url; + }; + QHash m_browsers; + QPointer m_sni; + bool m_debug; + int m_shownCount; +}; diff --git a/advert/browserlaunchwatcher.cpp b/advert/browserlaunchwatcher.cpp new file mode 100644 --- /dev/null +++ b/advert/browserlaunchwatcher.cpp @@ -0,0 +1,169 @@ +/*************************************************************************** + * Copyright 2018 by David Edmundson * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#include "browserlaunchwatcher.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "browserlaunchwatcher.h" + +K_PLUGIN_FACTORY_WITH_JSON(BrowserLaunchWatcherFactory, + "browserlaunchwatcher.json", + registerPlugin();) + +#define MAX_SHOW_COUNT 3 + +BrowserLaunchWatcher::BrowserLaunchWatcher(QObject *parent, const QList&) + : KDEDModule(parent) +{ + m_debug = qEnvironmentVariableIsSet("PLASMA_BROWSE_REMIND_FORCE"); + auto config = KSharedConfig::openConfig()->group("PlasmaBrowserIntegration"); + m_shownCount = config.readEntry("shownCount", 0); + + if (m_shownCount > MAX_SHOW_COUNT && !m_debug) { + disableAutoload(); //safer than it looks it won't be processed till we hit the event loop + return; + } + + m_browsers["firefox.desktop"] = {.icon="firefox", .url=QUrl("https://addons.mozilla.org/en-US/firefox/addon/plasma-integration/")}; + BrowserInfo chrome = {.icon="google-chrome", .url=QUrl("https://chrome.google.com/webstore/detail/plasma-integration/cimiefiiaegbelhefglklhhakcgmhkai")}; + m_browsers["google-chrome.desktop"] = chrome; + m_browsers["google-chrome-unstable.desktop"] = chrome; + m_browsers["chromium-browser.desktop"] = {.icon="chromium-browser", .url=QUrl("https://chrome.google.com/webstore/detail/plasma-integration/cimiefiiaegbelhefglklhhakcgmhkai")}; + + setModuleName(QStringLiteral("BrowserLaunchWatcher")); + QDBusConnection dbus = QDBusConnection::sessionBus(); + dbus.connect(QStringLiteral("org.kde.ActivityManager"), + QStringLiteral("/ActivityManager/Resources/Scoring"), + QStringLiteral("org.kde.ActivityManager.ResourcesScoring"), + QStringLiteral("ResourceScoreUpdated"), + this, + SLOT(onResourceScoresChanged(QString,QString,QString,double,unsigned int,unsigned int))); +} + +BrowserLaunchWatcher::~BrowserLaunchWatcher() +{ +} + +void BrowserLaunchWatcher::onResourceScoresChanged(const QString &activity, const QString &client, const QString &resource, double score, unsigned int lastUpdate, unsigned int firstUpdate) +{ + Q_UNUSED(activity); + Q_UNUSED(score); + Q_UNUSED(lastUpdate); + Q_UNUSED(firstUpdate); + Q_UNUSED(client) + + if (!resource.startsWith("applications:")) { + return; + } + const QString desktopFile = resource.mid(strlen("applications:")); + if (!m_browsers.contains(desktopFile)) { + return; + } + //wait a few seconds and then query if the extension is active + QTimer::singleShot(10 * 1000, this, [this, desktopFile]() { + onBrowserStarted(desktopFile); + }); +} + +void BrowserLaunchWatcher::onBrowserStarted(const QString &browser) +{ + QDBusConnection bus = QDBusConnection::sessionBus(); + if (m_sni) { + return; + } + + if (bus.interface()->isServiceRegistered(QStringLiteral("org.kde.plasma.browser_integration")) && !m_debug) { + //the user has the extension installed, we don't need to keep checking + //env var exists for easier testing + disableAutoload(); + return; + } + + auto info = m_browsers[browser]; + + m_sni = new KStatusNotifierItem(this); + m_shownCount++; + auto config = KSharedConfig::openConfig()->group("PlasmaBrowserIntegration"); + config.writeEntry("shownCount", m_shownCount); + + m_sni->setTitle(i18n("Get Plasma Browser Integration")); + m_sni->setIconByName(info.icon); + m_sni->setStandardActionsEnabled(false); + m_sni->setStatus(KStatusNotifierItem::Active); + + connect(m_sni, &KStatusNotifierItem::activateRequested, this, [this, info]() { + QDesktopServices::openUrl(info.url); + //remove for this session. + //If the user installed it successfully we won't show anything next session + //If they didn't they'll get the link next login + unload(); + }); + + QMenu *menu = new QMenu; + auto action = new QAction(i18n("Do not show again")); + menu->addAction(action); + connect(action, &QAction::triggered, this, [this]() { + auto config = KSharedConfig::openConfig()->group("PlasmaBrowserIntegration"); + config.writeEntry("shownCount", 100); + disableAutoload(); + }); + + m_sni->setContextMenu(menu); +} + +void BrowserLaunchWatcher::unload() +{ + QDBusConnection dbus = QDBusConnection::sessionBus(); + QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kded5"), + QStringLiteral("/kded"), + QStringLiteral("org.kde.kded5"), + QStringLiteral("unloadModule")); + msg.setArguments({QVariant(QStringLiteral("browserlaunchwatcher"))}); + dbus.call(msg, QDBus::NoBlock); +} + +void BrowserLaunchWatcher::disableAutoload() +{ + QDBusConnection dbus = QDBusConnection::sessionBus(); + QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kded5"), + QStringLiteral("/kded"), + QStringLiteral("org.kde.kded5"), + QStringLiteral("setModuleAutoloading")); + msg.setArguments({QVariant(QStringLiteral("browserlaunchwatcher")), QVariant(false)}); + dbus.call(msg, QDBus::NoBlock); + unload(); +} + +#include "browserlaunchwatcher.moc" diff --git a/advert/browserlaunchwatcher.desktop b/advert/browserlaunchwatcher.desktop new file mode 100644 --- /dev/null +++ b/advert/browserlaunchwatcher.desktop @@ -0,0 +1,12 @@ +[Desktop Entry] +Type=Service +Name=Plasma Browser Integration Installation Reminder +Comment=Provides a link to the browser extension if the Plasma Browser Integration host is installed + +X-KDE-ServiceTypes=KDEDModule +X-KDE-Library=browserlaunchwatcher +X-KDE-DBus-ModuleName=browserlaunchwatcher +X-KDE-Kded-autoload=true +X-KDE-Kded-load-on-demand=false +X-KDE-Kded-phase=2 + diff --git a/reminder/CMakeLists.txt b/reminder/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/reminder/CMakeLists.txt @@ -0,0 +1,11 @@ +add_definitions(-DQT_NO_URL_CAST_FROM_STRING) + +set(kded_browserintegrationreminder browserintegrationreminder.cpp ) + +add_library(browserintegrationreminder MODULE ${kded_browserintegrationreminder}) +kcoreaddons_desktop_to_json(browserintegrationreminder browserintegrationreminder.desktop) + + +target_link_libraries(browserintegrationreminder Qt5::DBus KF5::CoreAddons KF5::DBusAddons KF5::I18n Qt5::Widgets KF5::Notifications KF5::ConfigCore) + +install(TARGETS browserintegrationreminder DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf5/kded) diff --git a/reminder/browserintegrationreminder.h b/reminder/browserintegrationreminder.h new file mode 100644 --- /dev/null +++ b/reminder/browserintegrationreminder.h @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright 2009 by Marco Martin * + * Copyright 2018 by David Edmundson * + * Copyright 2018 by Eike Hein * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +class KStatusNotifierItem; +class QDBusServiceWatcher; + +/** + * This plugin's purpose is to provide the user a link to the PBI extension. + * It should run if the user has installed the host, but doesn't have the browser side. + * + * It creates an SNI for the first N times the user opens a browser with this setup. + * Then we give up + */ + +class BrowserIntegrationReminder : public KDEDModule +{ + Q_OBJECT + +public: + BrowserIntegrationReminder(QObject *parent, const QList&); + ~BrowserIntegrationReminder() override; + +private Q_SLOTS: + void onResourceScoresChanged(const QString &activity, + const QString &client, + const QString &resource, + double score, + unsigned int lastUpdate, + unsigned int firstUpdate); + + void onBrowserStarted(const QString &browserName); + + void unload(); + void disableAutoload(); + +private: + QHash m_browsers; + QPointer m_sni; + bool m_debug; + int m_shownCount; +}; diff --git a/reminder/browserintegrationreminder.cpp b/reminder/browserintegrationreminder.cpp new file mode 100644 --- /dev/null +++ b/reminder/browserintegrationreminder.cpp @@ -0,0 +1,168 @@ +/*************************************************************************** + * Copyright 2018 by David Edmundson * + * Copyright 2018 by Eike Hein * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#include "browserintegrationreminder.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "browserintegrationreminder.h" + +K_PLUGIN_FACTORY_WITH_JSON(BrowserIntegrationReminderFactory, + "browserintegrationreminder.json", + registerPlugin();) + +#define MAX_SHOW_COUNT 3 + +BrowserIntegrationReminder::BrowserIntegrationReminder(QObject *parent, const QList&) + : KDEDModule(parent) +{ + m_debug = qEnvironmentVariableIsSet("PLASMA_BROWSE_REMIND_FORCE"); + auto config = KSharedConfig::openConfig()->group("PlasmaBrowserIntegration"); + m_shownCount = config.readEntry("shownCount", 0); + + if (m_shownCount > MAX_SHOW_COUNT && !m_debug) { + disableAutoload(); //safer than it looks it won't be processed till we hit the event loop + return; + } + + m_browsers["firefox.desktop"] = QUrl("https://addons.mozilla.org/en-US/firefox/addon/plasma-integration/"); + QUrl chrome("https://chrome.google.com/webstore/detail/plasma-integration/cimiefiiaegbelhefglklhhakcgmhkai"); + m_browsers["google-chrome.desktop"] = chrome; + m_browsers["google-chrome-unstable.desktop"] = chrome; + m_browsers["chromium-browser.desktop"] = chrome; + + setModuleName(QStringLiteral("BrowserIntegrationReminder")); + QDBusConnection dbus = QDBusConnection::sessionBus(); + dbus.connect(QStringLiteral("org.kde.ActivityManager"), + QStringLiteral("/ActivityManager/Resources/Scoring"), + QStringLiteral("org.kde.ActivityManager.ResourcesScoring"), + QStringLiteral("ResourceScoreUpdated"), + this, + SLOT(onResourceScoresChanged(QString,QString,QString,double,unsigned int,unsigned int))); +} + +BrowserIntegrationReminder::~BrowserIntegrationReminder() +{ +} + +void BrowserIntegrationReminder::onResourceScoresChanged(const QString &activity, const QString &client, const QString &resource, double score, unsigned int lastUpdate, unsigned int firstUpdate) +{ + Q_UNUSED(activity); + Q_UNUSED(score); + Q_UNUSED(lastUpdate); + Q_UNUSED(firstUpdate); + Q_UNUSED(client) + + if (!resource.startsWith("applications:")) { + return; + } + const QString desktopFile = resource.mid(strlen("applications:")); + if (!m_browsers.contains(desktopFile)) { + return; + } + //wait a few seconds and then query if the extension is active + QTimer::singleShot(10 * 1000, this, [this, desktopFile]() { + onBrowserStarted(desktopFile); + }); +} + +void BrowserIntegrationReminder::onBrowserStarted(const QString &browser) +{ + QDBusConnection bus = QDBusConnection::sessionBus(); + if (m_sni) { + return; + } + + if (bus.interface()->isServiceRegistered(QStringLiteral("org.kde.plasma.browser_integration")) && !m_debug) { + //the user has the extension installed, we don't need to keep checking + //env var exists for easier testing + disableAutoload(); + return; + } + + m_sni = new KStatusNotifierItem(this); + m_shownCount++; + auto config = KSharedConfig::openConfig()->group("PlasmaBrowserIntegration"); + config.writeEntry("shownCount", m_shownCount); + + m_sni->setTitle(i18n("Get Plasma Browser Integration")); + m_sni->setIconByName("internet-web-browser"); + m_sni->setStandardActionsEnabled(false); + m_sni->setStatus(KStatusNotifierItem::Active); + + connect(m_sni, &KStatusNotifierItem::activateRequested, this, [this, browser]() { + QDesktopServices::openUrl(m_browsers[browser]); + //remove for this session. + //If the user installed it successfully we won't show anything next session + //If they didn't they'll get the link next login + unload(); + }); + + QMenu *menu = new QMenu; + auto action = new QAction(i18n("Do not show again")); + menu->addAction(action); + connect(action, &QAction::triggered, this, [this]() { + auto config = KSharedConfig::openConfig()->group("PlasmaBrowserIntegration"); + config.writeEntry("shownCount", 100); + disableAutoload(); + }); + + m_sni->setContextMenu(menu); +} + +void BrowserIntegrationReminder::unload() +{ + QDBusConnection dbus = QDBusConnection::sessionBus(); + QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kded5"), + QStringLiteral("/kded"), + QStringLiteral("org.kde.kded5"), + QStringLiteral("unloadModule")); + msg.setArguments({QVariant(QStringLiteral("browserintegrationreminder"))}); + dbus.call(msg, QDBus::NoBlock); +} + +void BrowserIntegrationReminder::disableAutoload() +{ + QDBusConnection dbus = QDBusConnection::sessionBus(); + QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kded5"), + QStringLiteral("/kded"), + QStringLiteral("org.kde.kded5"), + QStringLiteral("setModuleAutoloading")); + msg.setArguments({QVariant(QStringLiteral("browserintegrationreminder")), QVariant(false)}); + dbus.call(msg, QDBus::NoBlock); + unload(); +} + +#include "browserintegrationreminder.moc" diff --git a/reminder/browserintegrationreminder.desktop b/reminder/browserintegrationreminder.desktop new file mode 100644 --- /dev/null +++ b/reminder/browserintegrationreminder.desktop @@ -0,0 +1,12 @@ +[Desktop Entry] +Type=Service +Name=Plasma Browser Integration Installation Reminder +Comment=Provides a link to the browser extension if the Plasma Browser Integration host is installed + +X-KDE-ServiceTypes=KDEDModule +X-KDE-Library=browserintegrationreminder +X-KDE-DBus-ModuleName=browserintegrationreminder +X-KDE-Kded-autoload=true +X-KDE-Kded-load-on-demand=false +X-KDE-Kded-phase=2 +