From d2b5416513992ca63865d75a9217e21ca2ce3599 Mon Sep 17 00:00:00 2001 From: Devin Lin Date: Thu, 23 Feb 2023 16:43:38 +0000 Subject: [PATCH] kded: Add startup settings manager This adds a new component that manages Plasma Mobile specific configuration on every session startup. It also restores configuration when logged into a desktop session. This allows us to remove https://invent.kde.org/plasma-mobile/plasma-phone-settings, as well as configuration that was set in the look and feel. This also gives us an easy way to control configuration upgrade paths, and in the future, add ways for the configuration to easily be reset for debugging purposes. --- CMakeLists.txt | 7 + components/mmplugin/CMakeLists.txt | 2 + components/mmplugin/signalindicator.cpp | 6 +- components/mmplugin/signalindicator.h | 2 + kded/CMakeLists.txt | 21 +++ kded/config.h | 34 ++++ kded/kded_plasma-mobile-start.json | 9 ++ kded/settings.cpp | 198 ++++++++++++++++++++++++ kded/settings.h | 49 ++++++ kded/startdaemon.cpp | 23 +++ kded/startdaemon.h | 14 ++ kded/utils.h | 12 ++ look-and-feel/contents/defaults | 8 - 13 files changed, 375 insertions(+), 10 deletions(-) create mode 100644 kded/CMakeLists.txt create mode 100644 kded/config.h create mode 100644 kded/kded_plasma-mobile-start.json create mode 100644 kded/settings.cpp create mode 100644 kded/settings.h create mode 100644 kded/startdaemon.cpp create mode 100644 kded/startdaemon.h create mode 100644 kded/utils.h diff --git a/CMakeLists.txt b/CMakeLists.txt index faa27d3a..b97a9535 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,11 @@ include(FeatureSummary) add_definitions(-DQT_NO_URL_CAST_FROM_STRING) +ecm_setup_version(${PROJECT_VERSION} + VARIABLE_PREFIX PLASMA_MOBILE + VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/version.h +) + find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} CONFIG REQUIRED Core Qml @@ -56,6 +61,7 @@ endif() find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS I18n KIO + DBusAddons Plasma PlasmaQuick Service @@ -90,6 +96,7 @@ add_subdirectory(containments) add_subdirectory(components) add_subdirectory(quicksettings) add_subdirectory(kcms) +add_subdirectory(kded) find_program(PlasmaOpenSettings plasma-open-settings) set_package_properties(PlasmaOpenSettings PROPERTIES diff --git a/components/mmplugin/CMakeLists.txt b/components/mmplugin/CMakeLists.txt index e8519c2d..07128ef4 100644 --- a/components/mmplugin/CMakeLists.txt +++ b/components/mmplugin/CMakeLists.txt @@ -11,6 +11,8 @@ target_link_libraries(ppc-mmqmlplugin Qt::Qml KF5::ModemManagerQt KF5::NetworkManagerQt + KF5::CoreAddons + KF5::I18n ) set_property(TARGET ppc-mmqmlplugin PROPERTY LIBRARY_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/org/kde/plasma/mm) diff --git a/components/mmplugin/signalindicator.cpp b/components/mmplugin/signalindicator.cpp index cf142880..3ebfd5b6 100644 --- a/components/mmplugin/signalindicator.cpp +++ b/components/mmplugin/signalindicator.cpp @@ -1,13 +1,15 @@ // SPDX-FileCopyrightText: 2021 Tobias Fella -// SPDX-FileCopyrightText: 2022 Devin Lin +// SPDX-FileCopyrightText: 2022-2023 Devin Lin // SPDX-License-Identifier: GPL-2.0-or-later +#include "signalindicator.h" + #include #include #include #include -#include "signalindicator.h" +#include SignalIndicator::SignalIndicator(QObject *parent) : QObject{parent} diff --git a/components/mmplugin/signalindicator.h b/components/mmplugin/signalindicator.h index e78a009f..e0f40005 100644 --- a/components/mmplugin/signalindicator.h +++ b/components/mmplugin/signalindicator.h @@ -22,8 +22,10 @@ class SignalIndicator : public QObject Q_PROPERTY(bool modemAvailable READ modemAvailable NOTIFY modemAvailableChanged) Q_PROPERTY(bool simLocked READ simLocked NOTIFY simLockedChanged) Q_PROPERTY(bool simEmpty READ simEmpty NOTIFY simEmptyChanged) + Q_PROPERTY(bool mobileDataSupported READ mobileDataSupported NOTIFY mobileDataSupportedChanged) Q_PROPERTY(bool mobileDataEnabled READ mobileDataEnabled WRITE setMobileDataEnabled NOTIFY mobileDataEnabledChanged) + Q_PROPERTY(bool needsAPNAdded READ needsAPNAdded NOTIFY mobileDataEnabledChanged) public: diff --git a/kded/CMakeLists.txt b/kded/CMakeLists.txt new file mode 100644 index 00000000..c144b57c --- /dev/null +++ b/kded/CMakeLists.txt @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2023 Devin Lin +# SPDX-License-Identifier: GPL-2.0-or-later + +kcoreaddons_add_plugin(kded_plasma-mobile-start INSTALL_NAMESPACE "kf${QT_MAJOR_VERSION}/kded") + +target_sources(kded_plasma-mobile-start PRIVATE + startdaemon.cpp + settings.cpp + config.h + utils.h +) + +target_link_libraries(kded_plasma-mobile-start PRIVATE + Qt::Core + KF5::DBusAddons + KF5::ConfigWidgets + KF5::KIOGui + KF5::Notifications + KF5::Package +) + diff --git a/kded/config.h b/kded/config.h new file mode 100644 index 00000000..3443eca1 --- /dev/null +++ b/kded/config.h @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2023 Devin Lin +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include +#include +#include + +// kwinrc +const QMap> KWINRC_SETTINGS = { + {"Plugins", {{"blurEnabled", false}}}, + {"Wayland", {{"InputMethod", "/usr/share/applications/com.github.maliit.keyboard.desktop"}, {"VirtualKeyboardEnabled", true}}}, + {"Windows", + { + {"Placement", "Maximizing"}, + }}, + {"org.kde.kdecoration2", {{"NoPlugin", true}}}, +}; + +// applications-blacklistrc +// NOTE: we only write these entries if they are not already defined in the config +const QMap> APPLICATIONS_BLACKLIST_SETTINGS = { + {"Applications", + {{"blacklist", + "cuttlefish,org.kde.plasma.themeexplorer,org.kde.klipper,ciborium,syncmonitorhelper,org.kde.okular,wordview,assistant,assistant-qt5,designer,designer-" + "qt5,linguist,linguist-qt5,org.kde.perusecreator,UserFeedbackConsole,org.kde.kuserfeedback-console,avahi-discover,bssh,bvnc,ktelnetservice5,qv4l2," + "qvidcap"}}}}; + +// kdeglobals +// NOTE: we only write these entries if they are not already defined in the config +const QMap> KDEGLOBALS_SETTINGS = {{"General", {{"BrowserApplication", "angelfish"}}}}; diff --git a/kded/kded_plasma-mobile-start.json b/kded/kded_plasma-mobile-start.json new file mode 100644 index 00000000..03febd4d --- /dev/null +++ b/kded/kded_plasma-mobile-start.json @@ -0,0 +1,9 @@ +{ + "KPlugin": { + "Description": "Run initial tasks for Plasma Mobile during session startup", + "Name": "Plasma Mobile Start" + }, + "X-KDE-Kded-autoload": true, + "X-KDE-Kded-phase": 1, + "X-KDE-Library": "plasma-mobile" +} diff --git a/kded/settings.cpp b/kded/settings.cpp new file mode 100644 index 00000000..cb6f1e34 --- /dev/null +++ b/kded/settings.cpp @@ -0,0 +1,198 @@ +// SPDX-FileCopyrightText: 2023 Devin Lin +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "settings.h" +#include "config.h" +#include "utils.h" + +#include +#include + +#include +#include +#include +#include + +const QString CONFIG_FILE = QStringLiteral("plasmamobilerc"); +const QString INITIAL_START_CONFIG_GROUP = QStringLiteral("InitialStart"); +const QString SAVED_CONFIG_GROUP = QStringLiteral("SavedConfig"); + +const QString MOBILE_LOOK_AND_FEEL = QStringLiteral("org.kde.plasma.phone"); +const QString LOOK_AND_FEEL_KEY = QStringLiteral("LookAndFeelPackage"); + +Settings::Settings(QObject *parent) + : QObject{parent} + , m_isMobilePlatform{KRuntimePlatform::runtimePlatform().contains(QStringLiteral("phone"))} + , m_initialStartConfig{KSharedConfig::openConfig(CONFIG_FILE, KConfig::SimpleConfig)} + , m_kwinrcConfig{KSharedConfig::openConfig(QStringLiteral("kwinrc"), KConfig::SimpleConfig)} + , m_appBlacklistConfig{KSharedConfig::openConfig(QStringLiteral("applications-blacklistrc"), KConfig::SimpleConfig)} + , m_kdeglobalsConfig{KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::SimpleConfig)} +{ +} + +Settings *Settings::self() +{ + static Settings *settings = new Settings; + return settings; +} + +bool Settings::shouldStartWizard() +{ + if (!m_isMobilePlatform) { + return false; + } + + const auto group = KConfigGroup{m_initialStartConfig, INITIAL_START_CONFIG_GROUP}; + return !group.readEntry("wizardRun", false); +} + +void Settings::setWizardFinished() +{ + auto group = KConfigGroup{m_initialStartConfig, INITIAL_START_CONFIG_GROUP}; + group.writeEntry("wizardRun", true, KConfigGroup::Notify); + m_initialStartConfig->sync(); +} + +void Settings::applyConfiguration() +{ + if (!m_isMobilePlatform) { + qCDebug(LOGGING_CATEGORY) << "Configuration will not be applied, as the session is not Plasma Mobile."; + qCDebug(LOGGING_CATEGORY) << "Restoring any previously saved configuration..."; + loadSavedConfiguration(); + return; + } + + qCDebug(LOGGING_CATEGORY) << "Checking and applying mobile configuration..."; + applyMobileConfiguration(); +} + +void Settings::loadSavedConfiguration() +{ + // check look and feel + loadSavedConfigSetting(m_kdeglobalsConfig, QStringLiteral("kdeglobals"), QStringLiteral("KDE"), LOOK_AND_FEEL_KEY); + + // kwinrc + loadKeys(QStringLiteral("kwinrc"), m_kwinrcConfig, KWINRC_SETTINGS); + m_kwinrcConfig->sync(); + reloadKWinConfig(); + + // applications-blacklistrc + loadKeys(QStringLiteral("applications-blacklistrc"), m_appBlacklistConfig, APPLICATIONS_BLACKLIST_SETTINGS); + m_appBlacklistConfig->sync(); + + // kdeglobals + loadKeys(QStringLiteral("kdeglobals"), m_kdeglobalsConfig, KDEGLOBALS_SETTINGS); + m_kdeglobalsConfig->sync(); + + // save our changes + m_initialStartConfig->sync(); +} + +void Settings::applyMobileConfiguration() +{ + // check look and feel + KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel")); + + if (package.path() != MOBILE_LOOK_AND_FEEL) { + // save it to be loaded when back on desktop + saveConfigSetting(QStringLiteral("kdeglobals"), QStringLiteral("KDE"), LOOK_AND_FEEL_KEY, package.path()); + + // ensure correct look and feel is applied + QProcess::execute("plasma-apply-lookandfeel", {"-a", MOBILE_LOOK_AND_FEEL}); + } + + // kwinrc + writeKeys(QStringLiteral("kwinrc"), m_kwinrcConfig, KWINRC_SETTINGS, false); + m_kwinrcConfig->sync(); + reloadKWinConfig(); + + // applications-blacklistrc + // NOTE: we only write entries if they are not already defined in the config + writeKeys(QStringLiteral("applications-blacklistrc"), m_appBlacklistConfig, APPLICATIONS_BLACKLIST_SETTINGS, true); + m_appBlacklistConfig->sync(); + + // kdeglobals + // NOTE: we only write entries if they are not already defined in the config + writeKeys(QStringLiteral("kdeglobals"), m_kdeglobalsConfig, KDEGLOBALS_SETTINGS, true); + m_kdeglobalsConfig->sync(); + + // save our changes + m_initialStartConfig->sync(); +} + +void Settings::writeKeys(const QString &fileName, KSharedConfig::Ptr &config, const QMap> &settings, bool overwriteOnlyIfEmpty) +{ + for (auto groupName : settings.keys()) { + auto group = KConfigGroup{config, groupName}; + + for (auto key : settings[groupName].keys()) { + if (!group.hasKey(key) || !overwriteOnlyIfEmpty) { + // save key + saveConfigSetting(fileName, groupName, key, group.readEntry(key)); + + // overwrite with mobile setting + group.writeEntry(key, settings[groupName][key], KConfigGroup::Notify); + } + } + } +} + +void Settings::loadKeys(const QString &fileName, KSharedConfig::Ptr &config, const QMap> &settings) +{ + for (auto groupName : settings.keys()) { + auto group = KConfigGroup{config, groupName}; + + for (auto key : settings[groupName].keys()) { + loadSavedConfigSetting(config, fileName, groupName, key); + } + } +} + +// NOTE: this only saves a value if it hasn't already been saved +void Settings::saveConfigSetting(const QString &fileName, const QString &group, const QString &key, const QVariant value) +{ + auto savedGroup = KConfigGroup{m_initialStartConfig, SAVED_CONFIG_GROUP}; + auto fileGroup = KConfigGroup{&savedGroup, fileName}; + auto keyGroup = KConfigGroup{&fileGroup, group}; + + if (!keyGroup.hasKey(key)) { + qCDebug(LOGGING_CATEGORY) << "In" << fileName << "set" << key << "to" << value; + keyGroup.writeEntry(key, value); + } +} + +// NOTE: this deletes the stored value from the config after loading +void Settings::loadSavedConfigSetting(KSharedConfig::Ptr &config, const QString &fileName, const QString &group, const QString &key) +{ + const auto savedGroup = KConfigGroup{m_initialStartConfig, SAVED_CONFIG_GROUP}; + const auto fileGroup = KConfigGroup{&savedGroup, fileName}; + auto keyGroup = KConfigGroup{&fileGroup, group}; + + if (!keyGroup.hasKey(key)) { + return; + } + + const auto value = keyGroup.readEntry(key); + + // write to real config + auto configGroup = KConfigGroup{config, group}; + + if (!configGroup.hasKey(key) || configGroup.readEntry(key) != value) { + qCDebug(LOGGING_CATEGORY) << "In" << fileName << "loading saved value of" << key << "which is" << value; + + if (value.isEmpty()) { // delete blank entries! + configGroup.deleteEntry(key); + } else { + configGroup.writeEntry(key, value, KConfigGroup::Notify); + } + } + + // remove saved config option + keyGroup.deleteEntry(key); +} + +void Settings::reloadKWinConfig() +{ + QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KWin"), QStringLiteral("org.kde.KWin"), QStringLiteral("reloadConfig")); + QDBusConnection::sessionBus().send(message); +} diff --git a/kded/settings.h b/kded/settings.h new file mode 100644 index 00000000..28d3b179 --- /dev/null +++ b/kded/settings.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2023 Devin Lin +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include +#include + +class Settings : public QObject +{ + Q_OBJECT + +public: + Settings(QObject *parent = nullptr); + static Settings *self(); + + // whether the initial start wizard should be started + bool shouldStartWizard(); + + // set that the wizard has finished + void setWizardFinished(); + + // apply the configuration + void applyConfiguration(); + +private: + // loads the saved configuration, so it can be restored on desktop + void loadSavedConfiguration(); + + // applies our mobile configuration + void applyMobileConfiguration(); + + void writeKeys(const QString &fileName, KSharedConfig::Ptr &config, const QMap> &settings, bool overwriteOnlyIfEmpty); + void loadKeys(const QString &fileName, KSharedConfig::Ptr &config, const QMap> &settings); + void saveConfigSetting(const QString &fileName, const QString &group, const QString &key, const QVariant value); + void loadSavedConfigSetting(KSharedConfig::Ptr &config, const QString &fileName, const QString &group, const QString &key); + + void reloadKWinConfig(); + + // whether this is Plasma Mobile + bool m_isMobilePlatform; + + KSharedConfig::Ptr m_initialStartConfig; + KSharedConfig::Ptr m_kwinrcConfig; + KSharedConfig::Ptr m_appBlacklistConfig; + KSharedConfig::Ptr m_kdeglobalsConfig; +}; diff --git a/kded/startdaemon.cpp b/kded/startdaemon.cpp new file mode 100644 index 00000000..acfcf6d5 --- /dev/null +++ b/kded/startdaemon.cpp @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 Devin Lin +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include + +#include + +#include "settings.h" +#include "startdaemon.h" + +K_PLUGIN_CLASS_WITH_JSON(PlasmaMobileStartDaemon, "kded_plasma-mobile-start.json") + +PlasmaMobileStartDaemon::PlasmaMobileStartDaemon(QObject *parent, const QList &) + : KDEDModule{parent} +{ + // apply configuration + Settings::self()->applyConfiguration(); +} + +#include "startdaemon.moc" diff --git a/kded/startdaemon.h b/kded/startdaemon.h new file mode 100644 index 00000000..ce7fd2e4 --- /dev/null +++ b/kded/startdaemon.h @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2023 Devin Lin +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +class PlasmaMobileStartDaemon : public KDEDModule +{ + Q_OBJECT + +public: + PlasmaMobileStartDaemon(QObject *parent, const QList &); +}; diff --git a/kded/utils.h b/kded/utils.h new file mode 100644 index 00000000..78050620 --- /dev/null +++ b/kded/utils.h @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2023 Devin Lin +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +static const QLoggingCategory &LOGGING_CATEGORY() +{ + static const QLoggingCategory category("plasma-mobile-initial-start"); + return category; +} diff --git a/look-and-feel/contents/defaults b/look-and-feel/contents/defaults index 92c73541..22ed036d 100644 --- a/look-and-feel/contents/defaults +++ b/look-and-feel/contents/defaults @@ -15,11 +15,3 @@ Theme=breeze [kdeglobals][General] ColorScheme=BreezeLight Name=Breeze - -## kwinrc - -[kwinrc][Windows] -Placement=Maximizing - -[kwinrc][org.kde.kdecoration2] -NoPlugin=true