diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,8 @@ pkg_check_modules(Flatpak IMPORTED_TARGET flatpak>=0.11.8) pkg_check_modules(Fwupd IMPORTED_TARGET fwupd>=1.0.6) pkg_check_modules(Markdown IMPORTED_TARGET libmarkdown) +find_package(KUserFeedback) + if(NOT CMAKE_VERSION VERSION_LESS "3.10.0") # CMake 3.9+ warns about automoc on files without Q_OBJECT, and doesn't know about other macros. diff --git a/discover/CMakeLists.txt b/discover/CMakeLists.txt --- a/discover/CMakeLists.txt +++ b/discover/CMakeLists.txt @@ -6,7 +6,7 @@ include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/..) ecm_qt_declare_logging_category(plasma_discover_SRCS HEADER discover_debug.h IDENTIFIER DISCOVER_LOG CATEGORY_NAME org.kde.plasma.discover) -kconfig_add_kcfg_files(plasma_discover_SRCS discoversettings.kcfgc GENERATE_MOC) +kconfig_add_kcfg_files(plasma_discover_SRCS discoversettings.kcfgc plasmauserfeedback.kcfgc GENERATE_MOC) add_executable(plasma-discover ${plasma_discover_SRCS} main.cpp @@ -34,6 +34,7 @@ KF5::QuickAddons Qt5::Quick Discover::Common + KUserFeedbackCore ) install(TARGETS plasma-discover ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/discover/DiscoverObject.cpp b/discover/DiscoverObject.cpp --- a/discover/DiscoverObject.cpp +++ b/discover/DiscoverObject.cpp @@ -72,6 +72,7 @@ #include #include +#include "plasmauserfeedback.h" #include "discoversettings.h" class CachedNetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory @@ -112,6 +113,7 @@ qmlRegisterType("org.kde.discover.app", 1, 0, "FeaturedModel"); qmlRegisterType("org.kde.discover.app", 1, 0, "QSortFilterProxyModel"); + qmlRegisterSingletonType("org.kde.discover.app", 1, 0, "UserFeedbackSettings", [](QQmlEngine*, QJSEngine*) -> QObject* { return new PlasmaUserFeedback(KSharedConfig::openConfig(QStringLiteral("PlasmaUserFeedback"), KConfig::NoGlobals)); }); qmlRegisterSingletonType("org.kde.discover.app", 1, 0, "DiscoverSettings", [](QQmlEngine*, QJSEngine*) -> QObject* { auto r = new DiscoverSettings; connect(r, &DiscoverSettings::installedPageSortingChanged, r, &DiscoverSettings::save); diff --git a/discover/discover.schema b/discover/discover.schema new file mode 100644 --- /dev/null +++ b/discover/discover.schema @@ -0,0 +1,329 @@ +{ + "aggregation": [ + { + "elements": [ + { + "schemaEntry": "", + "schemaEntryElement": "", + "type": "value" + } + ], + "name": "CPU Architecture Distribution", + "type": "category" + }, + { + "elements": [ + { + "schemaEntry": "", + "schemaEntryElement": "", + "type": "value" + } + ], + "name": "CPU Count Distribution", + "type": "numeric" + }, + { + "elements": [ + { + "schemaEntry": "", + "schemaEntryElement": "", + "type": "value" + } + ], + "name": "OS Distribution", + "type": "category" + }, + { + "elements": [ + { + "schemaEntry": "", + "schemaEntryElement": "", + "type": "value" + }, + { + "schemaEntry": "", + "schemaEntryElement": "", + "type": "value" + } + ], + "name": "Platform Details", + "type": "category" + }, + { + "elements": [ + { + "schemaEntry": "style", + "schemaEntryElement": "style", + "type": "value" + } + ], + "name": "Widget Style Distribution", + "type": "category" + }, + { + "elements": [ + { + "schemaEntry": "style", + "schemaEntryElement": "dark", + "type": "value" + } + ], + "name": "Palette Color Scheme", + "type": "category" + }, + { + "elements": [ + { + "schemaEntry": "usageTime", + "schemaEntryElement": "value", + "type": "value" + } + ], + "name": "Usage Time Distribution", + "type": "numeric" + }, + { + "elements": [ + { + "schemaEntry": "screens", + "type": "size" + } + ], + "name": "Amount of Screens", + "type": "category" + }, + { + "elements": [ + { + "schemaEntry": "screens", + "schemaEntryElement": "dpi", + "type": "value" + } + ], + "name": "DPI Distribution", + "type": "numeric" + }, + { + "elements": [ + { + "schemaEntry": "opengl", + "schemaEntryElement": "type", + "type": "value" + } + ], + "name": "OpenGL Stack Type", + "type": "category" + }, + { + "elements": [ + { + "schemaEntry": "opengl", + "schemaEntryElement": "vendor", + "type": "value" + } + ], + "name": "OpenGL Vendor Distribution", + "type": "category" + }, + { + "elements": [ + { + "schemaEntry": "opengl", + "schemaEntryElement": "renderer", + "type": "value" + } + ], + "name": "OpenGL Renderer Distribution", + "type": "category" + }, + { + "elements": [ + { + "schemaEntry": "opengl", + "schemaEntryElement": "type", + "type": "value" + }, + { + "schemaEntry": "opengl", + "schemaEntryElement": "version", + "type": "value" + } + ], + "name": "OpenGL Version Distribution", + "type": "category" + }, + { + "elements": [ + { + "schemaEntry": "opengl", + "schemaEntryElement": "type", + "type": "value" + }, + { + "schemaEntry": "opengl", + "schemaEntryElement": "glslVersion", + "type": "value" + } + ], + "name": "OpenGL GLSL Version Distribution", + "type": "category" + }, + { + "elements": [ + { + "schemaEntry": "opengl", + "schemaEntryElement": "profile", + "type": "value" + } + ], + "name": "OpenGL Profile Distribution", + "type": "category" + }, + { + "elements": [ + { + "schemaEntry": "locale", + "schemaEntryElement": "language", + "type": "value" + } + ], + "name": "Language Distribution", + "type": "category" + }, + { + "elements": [ + { + "schemaEntry": "locale", + "schemaEntryElement": "region", + "type": "value" + } + ], + "name": "Region Distribution", + "type": "category" + }, + { + "elements": [ + { + "schemaEntry": "qtVersion", + "schemaEntryElement": "value", + "type": "value" + } + ], + "name": "Qt Version Distribution", + "type": "category" + } + ], + "name": "org.kde.discover", + "schema": [ + { + "elements": [ + { + "name": "style", + "type": "string" + }, + { + "name": "dark", + "type": "bool" + } + ], + "name": "style", + "type": "scalar" + }, + { + "elements": [ + { + "name": "value", + "type": "int" + } + ], + "name": "usageTime", + "type": "scalar" + }, + { + "elements": [ + { + "name": "width", + "type": "int" + }, + { + "name": "height", + "type": "int" + }, + { + "name": "dpi", + "type": "int" + } + ], + "name": "screens", + "type": "list" + }, + { + "elements": [ + { + "name": "type", + "type": "string" + }, + { + "name": "vendor", + "type": "string" + }, + { + "name": "renderer", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "vendorVersion", + "type": "string" + }, + { + "name": "glslVersion", + "type": "string" + }, + { + "name": "profile", + "type": "string" + } + ], + "name": "opengl", + "type": "scalar" + }, + { + "elements": [ + { + "name": "language", + "type": "string" + }, + { + "name": "region", + "type": "string" + } + ], + "name": "locale", + "type": "scalar" + }, + { + "elements": [ + { + "name": "value", + "type": "string" + } + ], + "name": "qtVersion", + "type": "scalar" + }, + { + "elements": [ + { + "name": "value", + "type": "string" + } + ], + "name": "applicationSourceName", + "type": "scalar" + } + ] +} diff --git a/discover/plasmauserfeedback.kcfg b/discover/plasmauserfeedback.kcfg new file mode 100644 --- /dev/null +++ b/discover/plasmauserfeedback.kcfg @@ -0,0 +1,7 @@ + + + + + int(KUserFeedback::Provider::BasicUsageStatistics) + + diff --git a/discover/plasmauserfeedback.kcfgc b/discover/plasmauserfeedback.kcfgc new file mode 100644 --- /dev/null +++ b/discover/plasmauserfeedback.kcfgc @@ -0,0 +1,5 @@ +File=plasmauserfeedback.kcfg +ClassName=PlasmaUserFeedback +GenerateProperties=true +Mutators=true +IncludeFiles=KUserFeedback/Provider diff --git a/discover/qml/DiscoverWindow.qml b/discover/qml/DiscoverWindow.qml --- a/discover/qml/DiscoverWindow.qml +++ b/discover/qml/DiscoverWindow.qml @@ -3,7 +3,9 @@ import QtQuick.Controls 2.1 import org.kde.discover 2.0 import org.kde.discover.app 1.0 +import org.kde.kquickcontrolsaddons 2.0 import org.kde.kirigami 2.5 as Kirigami +import org.kde.userfeedback 1.0 as UserFeedback import "navigation.js" as Navigation Kirigami.ApplicationWindow @@ -46,6 +48,71 @@ showPassiveNotification(i18n("Running as root is discouraged and unnecessary.")); } + Kirigami.Action { + id: submitStatisticsAction + text: i18n("Submit usage information") + tooltip: i18n("Sends anonymized usage information to KDE so we can better understand our users. For more information see https://kde.org/privacypolicy-apps.php.") + onTriggered: { + provider.submit() + showPassiveNotification(i18n("Submitting usage information..."), "short", i18n("Configure"), provider.encouraged) + } + } + Kirigami.Action { + id: feedbackSettingsAction + text: i18n("Configure feedback...") + onTriggered: { + provider.encouraged() + } + } + + UserFeedback.Provider { + id: provider + + submissionInterval: 7 + surveyInterval: 30 + feedbackServer: "https://telemetry.kde.org/" + encouragementInterval: 30 + applicationStartsUntilEncouragement: 1 + applicationUsageTimeUntilEncouragement: 1 + telemetryMode: UserFeedbackSettings.feedbackLevel + + function encouraged() { + KCMShell.open("kcm_feedback"); + } + + property var lastSurvey: null + + function openSurvey() { + Qt.openUrlExternally(lastSurvey.url); + surveyCompleted(lastSurvey); + } + + onShowEncouragementMessage: { + showPassiveNotification(i18n("You can help us improving this application by sharing statistics and participate in surveys."), 5000, i18n("Contribute..."), encouraged) + } + + onSurveyAvailable: { + lastSurvey = survey + showPassiveNotification(i18n("We are looking for your feedback!"), 5000, i18n("Participate..."), openSurvey) + } + + UserFeedback.ApplicationVersionSource { mode: UserFeedback.Provider.BasicSystemInformation } + UserFeedback.PlatformInfoSource { mode: UserFeedback.Provider.BasicSystemInformation } + UserFeedback.QtVersionSource { mode: UserFeedback.Provider.BasicSystemInformation } + UserFeedback.StartCountSource { mode: UserFeedback.Provider.BasicUsageStatistics } + UserFeedback.UsageTimeSource { mode: UserFeedback.Provider.BasicUsageStatistics } + UserFeedback.LocaleInfoSource { mode: UserFeedback.Provider.DetailedSystemInformation } + UserFeedback.OpenGLInfoSource{ mode: UserFeedback.Provider.DetailedSystemInformation } + UserFeedback.ScreenInfoSource { mode: UserFeedback.Provider.DetailedSystemInformation } + UserFeedback.PropertySource { + id: dialRatioSource + mode: UserFeedback.Provider.DetailedUsageStatistics + sourceId: "applicationSourceName" + data: { "value": ResourcesModel.applicationSourceName } + description: "The source for applications" + } + } + TopLevelPageData { iconName: "tools-wizard" text: i18n("Discover") @@ -86,7 +153,7 @@ TopLevelPageData { id: sourcesAction iconName: "configure" - text: i18n("Sources") + text: i18n("Settings") component: topSourcesComp objectName: "sources" } diff --git a/discover/qml/SourcesPage.qml b/discover/qml/SourcesPage.qml --- a/discover/qml/SourcesPage.qml +++ b/discover/qml/SourcesPage.qml @@ -9,15 +9,20 @@ DiscoverPage { id: page clip: true - title: i18n("Sources") + title: i18n("Settings") property string search: "" background: Rectangle { color: Kirigami.Theme.backgroundColor Kirigami.Theme.colorSet: Kirigami.Theme.Window Kirigami.Theme.inherit: false } + contextualActions: [ + submitStatisticsAction, + feedbackSettingsAction + ] + mainItem: ListView { id: sourcesView model: QSortFilterProxyModel { diff --git a/libdiscover/resources/ResourcesModel.h b/libdiscover/resources/ResourcesModel.h --- a/libdiscover/resources/ResourcesModel.h +++ b/libdiscover/resources/ResourcesModel.h @@ -61,6 +61,7 @@ Q_PROPERTY(AbstractResourcesBackend* currentApplicationBackend READ currentApplicationBackend WRITE setCurrentApplicationBackend NOTIFY currentApplicationBackendChanged) Q_PROPERTY(QAction* updateAction READ updateAction CONSTANT) Q_PROPERTY(int fetchingUpdatesProgress READ fetchingUpdatesProgress NOTIFY fetchingUpdatesProgressChanged) + Q_PROPERTY(QString applicationSourceName READ applicationSourceName NOTIFY currentApplicationBackendChanged) public: /** This constructor should be only used by unit tests. * @p backendName defines what backend will be loaded when the backend is constructed. @@ -81,6 +82,8 @@ AggregatedResultsStream* search(const AbstractResourcesBackend::Filters &search); void checkForUpdates(); + QString applicationSourceName() const; + QVariantList applicationBackendsVariant() const; QVector applicationBackends() const; void setCurrentApplicationBackend(AbstractResourcesBackend* backend, bool writeConfig = true); diff --git a/libdiscover/resources/ResourcesModel.cpp b/libdiscover/resources/ResourcesModel.cpp --- a/libdiscover/resources/ResourcesModel.cpp +++ b/libdiscover/resources/ResourcesModel.cpp @@ -391,8 +391,7 @@ void ResourcesModel::initApplicationsBackend() { - KConfigGroup settings(KSharedConfig::openConfig(), "ResourcesModel"); - const QString name = settings.readEntry("currentApplicationBackend", QStringLiteral("packagekit-backend")); + const auto name = applicationSourceName(); const auto backends = applicationBackends(); auto idx = kIndexOf(backends, [name](AbstractResourcesBackend* b) { return b->name() == name; }); @@ -403,7 +402,6 @@ setCurrentApplicationBackend(backends.value(idx, nullptr), false); } - int ResourcesModel::fetchingUpdatesProgress() const { if (m_backends.isEmpty()) @@ -415,3 +413,9 @@ } return sum / m_backends.count(); } + +QString ResourcesModel::applicationSourceName() const +{ + KConfigGroup settings(KSharedConfig::openConfig(), "ResourcesModel"); + return settings.readEntry("currentApplicationBackend", QStringLiteral("packagekit-backend")); +}