System to collect usage statistic.

This commit is contained in:
Roman Telezhynskyi 2023-06-27 14:15:21 +03:00
parent 53dc4135e3
commit eaf9d65339
43 changed files with 1871 additions and 214 deletions

View File

@ -22,7 +22,8 @@ DEPENDPATH += \
../../src/libs/vtools \
../../src/libs/vformat \
../../src/libs/fervor \
../../src/libs/vwidgets
../../src/libs/vwidgets \
../../src/libs/vganalytics
include(../../src/app/valentina/valentina.pri)
include(../../src/app/tape/tape.pri)
@ -39,6 +40,7 @@ include(../../src/libs/vtools/vtools.pri)
include(../../src/libs/vformat/vformat.pri)
include(../../src/libs/fervor/fervor.pri)
include(../../src/libs/vwidgets/vwidgets.pri)
include(../../src/libs/vganalytics/vganalytics.pri)
# Add here path to new translation file with name "valentina_*_*.ts" if you want to add new language.
# Same paths in variable INSTALL_TRANSLATIONS (translations.pri).

View File

@ -33,6 +33,8 @@
#include "../vmisc/backport/qoverload.h"
#endif // QT_VERSION < QT_VERSION_CHECK(5, 7, 0)
#include "../vganalytics/vganalytics.h"
//---------------------------------------------------------------------------------------------------------------------
PuzzlePreferencesConfigurationPage::PuzzlePreferencesConfigurationPage(QWidget *parent)
: QWidget(parent),
@ -89,6 +91,9 @@ PuzzlePreferencesConfigurationPage::PuzzlePreferencesConfigurationPage(QWidget *
ui->doubleSpinBoxAcceleration->setMinimum(VCommonSettings::scrollingAccelerationMin);
ui->doubleSpinBoxAcceleration->setMaximum(VCommonSettings::scrollingAccelerationMax);
ui->doubleSpinBoxAcceleration->setValue(settings->GetScrollingAcceleration());
// Tab Privacy
ui->checkBoxSendUsageStatistics->setChecked(settings->IsCollectStatistic());
}
//---------------------------------------------------------------------------------------------------------------------
@ -166,6 +171,10 @@ auto PuzzlePreferencesConfigurationPage::Apply() -> QStringList
settings->SetWheelMouseScale(ui->doubleSpinBoxWheel->value());
settings->SetScrollingAcceleration(ui->doubleSpinBoxAcceleration->value());
// Tab Privacy
settings->SetCollectStatistic(ui->checkBoxSendUsageStatistics->isChecked());
VGAnalytics::Instance()->Enable(ui->checkBoxSendUsageStatistics->isChecked());
return preferences;
}

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>545</width>
<height>696</height>
<width>539</width>
<height>702</height>
</rect>
</property>
<property name="windowTitle">
@ -34,8 +34,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>503</width>
<height>627</height>
<width>497</width>
<height>633</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_10">
@ -424,6 +424,43 @@ This option will take an affect after restart.</string>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabPrivacy">
<attribute name="title">
<string>Privacy</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_11">
<item>
<widget class="QCheckBox" name="checkBoxSendUsageStatistics">
<property name="text">
<string>Send usage statistics</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Please help to improve Valentina's quality by automatically sending usage statistics. Sent data contains &lt;span style=&quot; font-weight:700;&quot;&gt;no potentially sensitive information&lt;/span&gt; like user names, email addresses, file contents or file paths.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>540</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>

View File

@ -282,6 +282,15 @@ DEPENDPATH += $$PWD/../../libs/vdxf
win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vdxf/$${DESTDIR}/vdxf.lib
else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vdxf/$${DESTDIR}/libvdxf.a
# VGAnalytics static library
unix|win32: LIBS += -L$$OUT_PWD/../../libs/vganalytics/$${DESTDIR}/ -lvganalytics
INCLUDEPATH += $$PWD/../../libs/vganalytics
DEPENDPATH += $$PWD/../../libs/vganalytics
win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vganalytics/$${DESTDIR}/vganalytics.lib
else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vganalytics/$${DESTDIR}/libvganalytics.a
# QMuParser library
win32:CONFIG(release, debug|release): LIBS += -L$${OUT_PWD}/../../libs/qmuparser/$${DESTDIR} -lqmuparser2
else:win32:CONFIG(debug, debug|release): LIBS += -L$${OUT_PWD}/../../libs/qmuparser/$${DESTDIR} -lqmuparser2

View File

@ -12,6 +12,7 @@ VToolApp {
Depends { name: "VWidgetsLib" }
Depends { name: "FervorLib" }
Depends { name: "multibundle"; }
Depends { name: "VGAnalyticsLib" }
name: "Puzzle"
buildconfig.appTarget: qbs.targetOS.contains("macos") ? "Puzzle" : "puzzle"

View File

@ -32,6 +32,8 @@
#include "../ifc/exception/vexceptionemptyparameter.h"
#include "../ifc/exception/vexceptionobjecterror.h"
#include "../ifc/exception/vexceptionwrongid.h"
#include "../vganalytics/def.h"
#include "../vganalytics/vganalytics.h"
#include "../vmisc/vsysexits.h"
#include "version.h"
#include "vpmainwindow.h"
@ -277,6 +279,17 @@ VPApplication::VPApplication(int &argc, char **argv)
//---------------------------------------------------------------------------------------------------------------------
VPApplication::~VPApplication()
{
if (settings->IsCollectStatistic())
{
auto *statistic = VGAnalytics::Instance();
QString clientID = settings->GetClientID();
if (!clientID.isEmpty())
{
statistic->SendAppCloseEvent(m_uptimeTimer.elapsed());
}
}
qDeleteAll(m_mainWindows);
}
@ -430,6 +443,19 @@ void VPApplication::InitOptions()
QIcon::setThemeName(QStringLiteral("win.icon.theme"));
}
ActivateDarkMode();
auto *statistic = VGAnalytics::Instance();
QString clientID = settings->GetClientID();
if (clientID.isEmpty())
{
clientID = QUuid::createUuid().toString();
settings->SetClientID(clientID);
}
statistic->SetClientID(clientID);
statistic->SetGUILanguage(settings->GetLocale());
statistic->SetMeasurementId(GA_MEASUREMENT_ID);
statistic->SetApiSecret(GA_API_SECRET);
statistic->Enable(settings->IsCollectStatistic());
}
//---------------------------------------------------------------------------------------------------------------------

View File

@ -47,6 +47,7 @@
#include "../vlayout/vlayoutexporter.h"
#include "../vlayout/vprintlayout.h"
#include "../vlayout/vrawlayout.h"
#include "../vmisc/dialogs/dialogaskcollectstatistic.h"
#include "../vmisc/dialogs/dialogselectlanguage.h"
#include "../vmisc/lambdaconstants.h"
#include "../vmisc/projectversion.h"
@ -67,6 +68,7 @@
#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0)
#include "../vmisc/backport/qoverload.h"
#endif // QT_VERSION < QT_VERSION_CHECK(5, 7, 0)
#include "../vganalytics/vganalytics.h"
#include "layout/vppiece.h"
#include "vptilefactory.h"
@ -389,7 +391,7 @@ VPMainWindow::VPMainWindow(const VPCommandLinePtr &cmd, QWidget *parent)
if (m_cmd->IsGuiEnabled())
{
QTimer::singleShot(V_SECONDS(1), this, &VPMainWindow::SetDefaultGUILanguage);
QTimer::singleShot(V_SECONDS(1), this, &VPMainWindow::AskDefaultSettings);
}
}
@ -4536,7 +4538,7 @@ void VPMainWindow::RemoveWatermark()
}
//---------------------------------------------------------------------------------------------------------------------
void VPMainWindow::SetDefaultGUILanguage()
void VPMainWindow::AskDefaultSettings()
{
if (m_cmd->IsGuiEnabled())
{
@ -4554,6 +4556,38 @@ void VPMainWindow::SetDefaultGUILanguage()
VAbstractApplication::VApp()->LoadTranslation(locale);
}
}
if (settings->IsAskCollectStatistic())
{
DialogAskCollectStatistic dialog(this);
if (dialog.exec() == QDialog::Accepted)
{
settings->SetCollectStatistic(dialog.CollectStatistic());
}
settings->SetAskCollectStatistic(false);
}
if (settings->IsCollectStatistic())
{
auto *statistic = VGAnalytics::Instance();
statistic->SetGUILanguage(settings->GetLocale());
QString clientID = settings->GetClientID();
bool freshID = false;
if (clientID.isEmpty())
{
clientID = QUuid::createUuid().toString();
settings->SetClientID(clientID);
statistic->SetClientID(clientID);
freshID = true;
}
statistic->Enable(true);
const qint64 uptime = VPApplication::VApp()->AppUptime();
freshID ? statistic->SendAppFreshInstallEvent(uptime) : statistic->SendAppStartEvent(uptime);
}
}
}

View File

@ -286,7 +286,7 @@ private slots:
void AboutToShowDockMenu();
#endif //defined(Q_OS_MAC)
void SetDefaultGUILanguage();
void AskDefaultSettings();
void HorizontalScaleChanged(double value);
void VerticalScaleChanged(double value);

View File

@ -35,6 +35,7 @@
#include "../vmisc/backport/qoverload.h"
#endif // QT_VERSION < QT_VERSION_CHECK(5, 7, 0)
#include "../qmuparser/qmudef.h"
#include "../vganalytics/vganalytics.h"
//---------------------------------------------------------------------------------------------------------------------
TapePreferencesConfigurationPage::TapePreferencesConfigurationPage(QWidget *parent)
@ -53,13 +54,13 @@ TapePreferencesConfigurationPage::TapePreferencesConfigurationPage(QWidget *pare
VTapeSettings *settings = MApplication::VApp()->TapeSettings();
//-------------------- Decimal separator setup
ui->osOptionCheck->setChecked(MApplication::VApp()->TapeSettings()->GetOsSeparator());
ui->osOptionCheck->setChecked(settings->GetOsSeparator());
// Theme
ui->darkModeCheck->setChecked(MApplication::VApp()->TapeSettings()->GetDarkMode());
ui->darkModeCheck->setChecked(settings->GetDarkMode());
// Native dialogs
ui->checkBoxDontUseNativeDialog->setChecked(MApplication::VApp()->TapeSettings()->IsDontUseNativeDialog());
ui->checkBoxDontUseNativeDialog->setChecked(settings->IsDontUseNativeDialog());
//---------------------- Pattern making system
ui->systemBookValueLabel->setFixedHeight(4 * QFontMetrics(ui->systemBookValueLabel->font()).lineSpacing());
@ -77,7 +78,7 @@ TapePreferencesConfigurationPage::TapePreferencesConfigurationPage(QWidget *pare
});
// set default pattern making system
int index = ui->systemCombo->findData(MApplication::VApp()->TapeSettings()->GetPMSystemCode());
int index = ui->systemCombo->findData(settings->GetPMSystemCode());
if (index != -1)
{
ui->systemCombo->setCurrentIndex(index);
@ -88,11 +89,13 @@ TapePreferencesConfigurationPage::TapePreferencesConfigurationPage(QWidget *pare
[]() { MApplication::VApp()->TapeSettings()->SetConfirmFormatRewriting(true); });
//----------------------- Toolbar
ui->toolBarStyleCheck->setChecked(MApplication::VApp()->TapeSettings()->GetToolBarStyle());
ui->toolBarStyleCheck->setChecked(settings->GetToolBarStyle());
//----------------------- Update
ui->checkBoxAutomaticallyCheckUpdates->setChecked(
MApplication::VApp()->TapeSettings()->IsAutomaticallyCheckUpdates());
ui->checkBoxAutomaticallyCheckUpdates->setChecked(settings->IsAutomaticallyCheckUpdates());
// Tab Privacy
ui->checkBoxSendUsageStatistics->setChecked(settings->IsCollectStatistic());
}
//---------------------------------------------------------------------------------------------------------------------
@ -144,6 +147,10 @@ auto TapePreferencesConfigurationPage::Apply() -> QStringList
settings->SetAutomaticallyCheckUpdates(ui->checkBoxAutomaticallyCheckUpdates->isChecked());
}
// Tab Privacy
settings->SetCollectStatistic(ui->checkBoxSendUsageStatistics->isChecked());
VGAnalytics::Instance()->Enable(ui->checkBoxSendUsageStatistics->isChecked());
return preferences;
}

View File

@ -6,209 +6,260 @@
<rect>
<x>0</x>
<y>0</y>
<width>544</width>
<height>636</height>
<width>564</width>
<height>686</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Configuration</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable">
<bool>true</bool>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>524</width>
<height>616</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<widget class="QWidget" name="tabGeneral">
<attribute name="title">
<string>General</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Language</string>
<widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>522</width>
<height>617</height>
</rect>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>GUI language:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="langCombo"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Decimal separator parts:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="osOptionCheck">
<property name="text">
<string notr="true">&lt; With OS options &gt;</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Language</string>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>GUI language:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="langCombo"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Decimal separator parts:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="osOptionCheck">
<property name="text">
<string notr="true">&lt; With OS options &gt;</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Pattern making system</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Pattern making system:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="systemCombo"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Author:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="systemAuthorValueLabel">
<property name="text">
<string notr="true">author</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Book:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPlainTextEdit" name="systemBookValueLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>Measurements editing</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QPushButton" name="resetWarningsButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Reset warnings</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
<string>Toolbar</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QCheckBox" name="toolBarStyleCheck">
<property name="text">
<string>The text appears under the icon (recommended for beginners).</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>User Interface</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QCheckBox" name="darkModeCheck">
<property name="text">
<string>Activate dark mode</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxDontUseNativeDialog">
<property name="text">
<string>Don't use the native file dialog</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_9">
<property name="title">
<string>Update</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<widget class="QCheckBox" name="checkBoxAutomaticallyCheckUpdates">
<property name="toolTip">
<string>Automatically check for updates each time the application starts</string>
</property>
<property name="text">
<string>Automatically check for updates</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabPrivacy">
<attribute name="title">
<string>Privacy</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<widget class="QCheckBox" name="checkBoxSendUsageStatistics">
<property name="text">
<string>Send usage statistics</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Pattern making system</string>
<widget class="QLabel" name="label">
<property name="text">
<string>Please help to improve Valentina's quality by automatically sending usage statistics. Sent data contains &lt;span style=&quot; font-weight:700;&quot;&gt;no potentially sensitive information&lt;/span&gt; like user names, email addresses, file contents or file paths.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<layout class="QFormLayout" name="formLayout_2">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Pattern making system:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="systemCombo"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Author:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="systemAuthorValueLabel">
<property name="text">
<string notr="true">author</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Book:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPlainTextEdit" name="systemBookValueLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>Measurements editing</string>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QPushButton" name="resetWarningsButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Reset warnings</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
<string>Toolbar</string>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>530</height>
</size>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QCheckBox" name="toolBarStyleCheck">
<property name="text">
<string>The text appears under the icon (recommended for beginners).</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>User Interface</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QCheckBox" name="darkModeCheck">
<property name="text">
<string>Activate dark mode</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxDontUseNativeDialog">
<property name="text">
<string>Don't use the native file dialog</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_9">
<property name="title">
<string>Update</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<widget class="QCheckBox" name="checkBoxAutomaticallyCheckUpdates">
<property name="toolTip">
<string>Automatically check for updates each time the application starts</string>
</property>
<property name="text">
<string>Automatically check for updates</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</spacer>
</item>
</layout>
</widget>

View File

@ -32,6 +32,8 @@
#include "../ifc/exception/vexceptionemptyparameter.h"
#include "../ifc/exception/vexceptionobjecterror.h"
#include "../ifc/exception/vexceptionwrongid.h"
#include "../vganalytics/def.h"
#include "../vganalytics/vganalytics.h"
#include "../vmisc/projectversion.h"
#include "../vmisc/vsysexits.h"
#include "tmainwindow.h"
@ -77,6 +79,7 @@ Q_LOGGING_CATEGORY(mApp, "m.application") // NOLINT
QT_WARNING_POP
#include <QCommandLineParser>
#include <QUuid>
namespace
{
@ -318,6 +321,17 @@ MApplication::MApplication(int &argc, char **argv)
//---------------------------------------------------------------------------------------------------------------------
MApplication::~MApplication()
{
if (settings->IsCollectStatistic())
{
auto *statistic = VGAnalytics::Instance();
QString clientID = settings->GetClientID();
if (!clientID.isEmpty())
{
statistic->SendAppCloseEvent(m_uptimeTimer.elapsed());
}
}
qDeleteAll(m_mainWindows);
delete m_trVars;
@ -473,6 +487,19 @@ void MApplication::InitOptions()
}
ActivateDarkMode();
QResource::registerResource(diagramsPath());
auto *statistic = VGAnalytics::Instance();
QString clientID = settings->GetClientID();
if (clientID.isEmpty())
{
clientID = QUuid::createUuid().toString();
settings->SetClientID(clientID);
}
statistic->SetClientID(clientID);
statistic->SetGUILanguage(settings->GetLocale());
statistic->SetMeasurementId(GA_MEASUREMENT_ID);
statistic->SetApiSecret(GA_API_SECRET);
statistic->Enable(settings->IsCollectStatistic());
}
//---------------------------------------------------------------------------------------------------------------------

View File

@ -408,6 +408,15 @@ DEPENDPATH += $$PWD/../../libs/vmisc
win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vmisc/$${DESTDIR}/vmisc.lib
else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vmisc/$${DESTDIR}/libvmisc.a
# VGAnalytics static library
unix|win32: LIBS += -L$$OUT_PWD/../../libs/vganalytics/$${DESTDIR}/ -lvganalytics
INCLUDEPATH += $$PWD/../../libs/vganalytics
DEPENDPATH += $$PWD/../../libs/vganalytics
win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vganalytics/$${DESTDIR}/vganalytics.lib
else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vganalytics/$${DESTDIR}/libvganalytics.a
# QMuParser library
win32:CONFIG(release, debug|release): LIBS += -L$${OUT_PWD}/../../libs/qmuparser/$${DESTDIR} -lqmuparser2
else:win32:CONFIG(debug, debug|release): LIBS += -L$${OUT_PWD}/../../libs/qmuparser/$${DESTDIR} -lqmuparser2

View File

@ -14,6 +14,7 @@ VToolApp {
Depends { name: "VToolsLib"; }
Depends { name: "ebr" }
Depends { name: "multibundle"; }
Depends { name: "VGAnalyticsLib" }
Depends { name: "conan.XercesC"; condition: buildconfig.useConanPackages }
name: "Tape"

View File

@ -32,6 +32,7 @@
#include "../ifc/xml/vvitconverter.h"
#include "../ifc/xml/vvstconverter.h"
#include "../vmisc/compatibility.h"
#include "../vmisc/dialogs/dialogaskcollectstatistic.h"
#include "../vmisc/dialogs/dialogexporttocsv.h"
#include "../vmisc/qxtcsvmodel.h"
#include "../vmisc/vsysexits.h"
@ -56,6 +57,7 @@
#include "../vmisc/backport/qoverload.h"
#endif // QT_VERSION < QT_VERSION_CHECK(5, 7, 0)
#include "../qmuparser/qmudef.h"
#include "../vganalytics/vganalytics.h"
#include "../vmisc/dialogs/dialogselectlanguage.h"
#include "../vtools/dialogs/support/dialogeditwrongformula.h"
#include "mapplication.h" // Should be last because of definning qApp
@ -304,7 +306,7 @@ TMainWindow::TMainWindow(QWidget *parent)
if (MApplication::VApp()->IsAppInGUIMode())
{
QTimer::singleShot(V_SECONDS(1), this, &TMainWindow::SetDefaultGUILanguage);
QTimer::singleShot(V_SECONDS(1), this, &TMainWindow::AskDefaultSettings);
}
}
@ -2534,7 +2536,7 @@ void TMainWindow::DimensionCustomNames()
}
//---------------------------------------------------------------------------------------------------------------------
void TMainWindow::SetDefaultGUILanguage()
void TMainWindow::AskDefaultSettings()
{
if (MApplication::VApp()->IsAppInGUIMode())
{
@ -2552,6 +2554,38 @@ void TMainWindow::SetDefaultGUILanguage()
VAbstractApplication::VApp()->LoadTranslation(locale);
}
}
if (settings->IsAskCollectStatistic())
{
DialogAskCollectStatistic dialog(this);
if (dialog.exec() == QDialog::Accepted)
{
settings->SetCollectStatistic(dialog.CollectStatistic());
}
settings->SetAskCollectStatistic(false);
}
if (settings->IsCollectStatistic())
{
auto *statistic = VGAnalytics::Instance();
statistic->SetGUILanguage(settings->GetLocale());
QString clientID = settings->GetClientID();
bool freshID = false;
if (clientID.isEmpty())
{
clientID = QUuid::createUuid().toString();
settings->SetClientID(clientID);
statistic->SetClientID(clientID);
freshID = true;
}
statistic->Enable(true);
const qint64 uptime = MApplication::VApp()->AppUptime();
freshID ? statistic->SendAppFreshInstallEvent(uptime) : statistic->SendAppStartEvent(uptime);
}
}
}

View File

@ -146,7 +146,7 @@ private slots:
void EditDimensionLabels();
void DimensionCustomNames();
void SetDefaultGUILanguage();
void AskDefaultSettings();
private:
// cppcheck-suppress unknownMacro

View File

@ -36,10 +36,11 @@
#include "../mainwindow.h"
#include "../qmuparser/qmuparsererror.h"
#include "../version.h"
#include "../vganalytics/def.h"
#include "../vganalytics/vganalytics.h"
#include "../vmisc/qt_dispatch/qt_dispatch.h"
#include "../vmisc/vsysexits.h"
#include "../vmisc/vvalentinasettings.h"
#include <qstringliteral.h>
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
#include "../vmisc/backport/text.h"
@ -383,6 +384,18 @@ VApplication::VApplication(int &argc, char **argv)
VApplication::~VApplication()
{
qCDebug(vApp, "Application closing.");
if (settings->IsCollectStatistic())
{
auto *statistic = VGAnalytics::Instance();
QString clientID = settings->GetClientID();
if (!clientID.isEmpty())
{
statistic->SendAppCloseEvent(m_uptimeTimer.elapsed());
}
}
qInstallMessageHandler(nullptr); // Resore the message handler
delete m_trVars;
VCommandLine::Reset();
@ -695,6 +708,19 @@ void VApplication::InitOptions()
QIcon::setThemeName(QStringLiteral("win.icon.theme"));
}
ActivateDarkMode();
auto *statistic = VGAnalytics::Instance();
QString clientID = settings->GetClientID();
if (clientID.isEmpty())
{
clientID = QUuid::createUuid().toString();
settings->SetClientID(clientID);
}
statistic->SetClientID(clientID);
statistic->SetGUILanguage(settings->GetLocale());
statistic->SetMeasurementId(GA_MEASUREMENT_ID);
statistic->SetApiSecret(GA_API_SECRET);
statistic->Enable(settings->IsCollectStatistic());
}
//---------------------------------------------------------------------------------------------------------------------

View File

@ -31,12 +31,12 @@
#include "../vmisc/literals.h"
#include "../vmisc/vvalentinasettings.h"
#include "../vpatterndb/pmsystems.h"
#include "def.h"
#include "ui_preferencesconfigurationpage.h"
#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0)
#include "../vmisc/backport/qoverload.h"
#endif // QT_VERSION < QT_VERSION_CHECK(5, 7, 0)
#include "../qmuparser/qmudef.h"
#include "../vganalytics/vganalytics.h"
#include <QDir>
#include <QDirIterator>
@ -162,6 +162,9 @@ PreferencesConfigurationPage::PreferencesConfigurationPage(QWidget *parent)
ui->doubleSpinBoxAcceleration->setMinimum(VCommonSettings::scrollingAccelerationMin);
ui->doubleSpinBoxAcceleration->setMaximum(VCommonSettings::scrollingAccelerationMax);
ui->doubleSpinBoxAcceleration->setValue(settings->GetScrollingAcceleration());
// Tab Privacy
ui->checkBoxSendUsageStatistics->setChecked(settings->IsCollectStatistic());
}
//---------------------------------------------------------------------------------------------------------------------
@ -254,6 +257,10 @@ auto PreferencesConfigurationPage::Apply() -> QStringList
settings->SetWheelMouseScale(ui->doubleSpinBoxWheel->value());
settings->SetScrollingAcceleration(ui->doubleSpinBoxAcceleration->value());
// Tab Privacy
settings->SetCollectStatistic(ui->checkBoxSendUsageStatistics->isChecked());
VGAnalytics::Instance()->Enable(ui->checkBoxSendUsageStatistics->isChecked());
return preferences;
}

View File

@ -527,6 +527,43 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="tabPrivacy">
<attribute name="title">
<string>Privacy</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_9">
<item>
<widget class="QCheckBox" name="checkBoxSendUsageStatistics">
<property name="text">
<string>Send usage statistics</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_15">
<property name="text">
<string>Please help to improve Valentina's quality by automatically sending usage statistics. Sent data contains &lt;span style=&quot; font-weight:700;&quot;&gt;no potentially sensitive information&lt;/span&gt; like user names, email addresses, file contents or file paths.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>716</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>

View File

@ -38,9 +38,11 @@
#include "../ifc/xml/vvstconverter.h"
#include "../vformat/vmeasurements.h"
#include "../vformat/vpatternrecipe.h"
#include "../vganalytics/vganalytics.h"
#include "../vlayout/dialogs/watermarkwindow.h"
#include "../vmisc/customevents.h"
#include "../vmisc/def.h"
#include "../vmisc/dialogs/dialogaskcollectstatistic.h"
#include "../vmisc/qxtcsvmodel.h"
#include "../vmisc/vmodifierkey.h"
#include "../vmisc/vsysexits.h"
@ -505,7 +507,7 @@ MainWindow::MainWindow(QWidget *parent)
if (VApplication::IsGUIMode())
{
QTimer::singleShot(V_SECONDS(1), this, &MainWindow::SetDefaultGUILanguage);
QTimer::singleShot(V_SECONDS(1), this, &MainWindow::AskDefaultSettings);
}
ui->actionExportFontCorrections->setEnabled(settings->GetSingleStrokeOutlineFont());
@ -4548,7 +4550,7 @@ void MainWindow::ClearPatternMessages()
}
//---------------------------------------------------------------------------------------------------------------------
void MainWindow::SetDefaultGUILanguage()
void MainWindow::AskDefaultSettings()
{
if (VApplication::IsGUIMode())
{
@ -4565,6 +4567,38 @@ void MainWindow::SetDefaultGUILanguage()
VAbstractApplication::VApp()->LoadTranslation(locale);
}
}
if (settings->IsAskCollectStatistic())
{
DialogAskCollectStatistic dialog(this);
if (dialog.exec() == QDialog::Accepted)
{
settings->SetCollectStatistic(dialog.CollectStatistic());
}
settings->SetAskCollectStatistic(false);
}
if (settings->IsCollectStatistic())
{
auto *statistic = VGAnalytics::Instance();
statistic->SetGUILanguage(settings->GetLocale());
QString clientID = settings->GetClientID();
bool freshID = false;
if (clientID.isEmpty())
{
clientID = QUuid::createUuid().toString();
settings->SetClientID(clientID);
statistic->SetClientID(clientID);
freshID = true;
}
statistic->Enable(true);
const qint64 uptime = VAbstractValApplication::VApp()->AppUptime();
freshID ? statistic->SendAppFreshInstallEvent(uptime) : statistic->SendAppStartEvent(uptime);
}
}
}

View File

@ -237,7 +237,7 @@ private slots:
void ShowProgress();
void ClearPatternMessages();
void SetDefaultGUILanguage();
void AskDefaultSettings();
void AddBackgroundImageItem(const QUuid &id);
void DeleteBackgroundImageItem(const QUuid &id);

View File

@ -698,6 +698,15 @@ DEPENDPATH += $$PWD/../../libs/vdxf
win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vdxf/$${DESTDIR}/vdxf.lib
else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vdxf/$${DESTDIR}/libvdxf.a
# VGAnalytics static library
unix|win32: LIBS += -L$$OUT_PWD/../../libs/vganalytics/$${DESTDIR}/ -lvganalytics
INCLUDEPATH += $$PWD/../../libs/vganalytics
DEPENDPATH += $$PWD/../../libs/vganalytics
win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vganalytics/$${DESTDIR}/vganalytics.lib
else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vganalytics/$${DESTDIR}/libvganalytics.a
# QMuParser library
win32:CONFIG(release, debug|release): LIBS += -L$${OUT_PWD}/../../libs/qmuparser/$${DESTDIR} -lqmuparser2
else:win32:CONFIG(debug, debug|release): LIBS += -L$${OUT_PWD}/../../libs/qmuparser/$${DESTDIR} -lqmuparser2

View File

@ -14,6 +14,7 @@ VToolApp {
Depends { name: "VToolsLib"; }
Depends { name: "VFormatLib"; }
Depends { name: "VMiscLib"; }
Depends { name: "VGAnalyticsLib" }
Depends {
name: "Qt.xmlpatterns"

View File

@ -33,3 +33,6 @@ INCLUDEPATH += $${PWD}/vpropertyexplorer
#VTools static library
INCLUDEPATH += $${PWD}/vtest
#VGAnalytics static library
INCLUDEPATH += $${PWD}/vganalytics

View File

@ -13,4 +13,5 @@ SUBDIRS = \
vtools \
vformat \
fervor \
vtest
vtest \
vganalytics

View File

@ -14,5 +14,6 @@ Project {
"vtest/vtest.qbs",
"vtools/vtools.qbs",
"vwidgets/vwidgets.qbs",
"vganalytics/vganalytics.qbs",
]
}

View File

@ -0,0 +1,36 @@
/************************************************************************
**
** @file def.h
** @author Roman Telezhynskyi <dismine(at)gmail.com>
** @date 26 6, 2023
**
** @brief
** @copyright
** This source code is part of the Valentina project, a pattern making
** program, whose allow create and modeling patterns of clothing.
** Copyright (C) 2023 Valentina project
** <https://gitlab.com/smart-pattern/valentina> All Rights Reserved.
**
** Valentina 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 3 of the License, or
** (at your option) any later version.
**
** Valentina 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 Valentina. If not, see <http://www.gnu.org/licenses/>.
**
*************************************************************************/
#ifndef GADEF_H
#define GADEF_H
#include <QString>
const QString GA_MEASUREMENT_ID = QStringLiteral("G-KH8CCG06VJ"); // NOLINT(clazy-non-pod-global-static)
const QString GA_API_SECRET = QStringLiteral("vnXM9BlCQXC9nvaXid_lAA"); // NOLINT(clazy-non-pod-global-static)
#endif // GADEF_H

View File

@ -0,0 +1,306 @@
/************************************************************************
**
** @file vganalytics.cpp
** @author Roman Telezhynskyi <dismine(at)gmail.com>
** @date 26 6, 2023
**
** @brief
** @copyright
** This source code is part of the Valentina project, a pattern making
** program, whose allow create and modeling patterns of clothing.
** Copyright (C) 2023 Valentina project
** <https://gitlab.com/smart-pattern/valentina> All Rights Reserved.
**
** Valentina 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 3 of the License, or
** (at your option) any later version.
**
** Valentina 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 Valentina. If not, see <http://www.gnu.org/licenses/>.
**
*************************************************************************/
#include "vganalytics.h"
#include "qguiapplication.h"
#include "qstringliteral.h"
#include "vganalyticsworker.h"
#include <QDataStream>
#include <QDebug>
#include <QGuiApplication>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QLocale>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QQueue>
#include <QScreen>
#include <QSettings>
#include <QTimer>
#include <QUrlQuery>
#include <QUuid>
//---------------------------------------------------------------------------------------------------------------------
VGAnalytics::VGAnalytics(QObject *parent)
: QObject{parent},
d(new VGAnalyticsWorker(this))
{
}
//---------------------------------------------------------------------------------------------------------------------
VGAnalytics::~VGAnalytics()
{
delete d;
}
//---------------------------------------------------------------------------------------------------------------------
auto VGAnalytics::Instance() -> VGAnalytics *
{
static VGAnalytics *instance = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
if (instance == nullptr)
{
instance = new VGAnalytics();
}
return instance;
}
//---------------------------------------------------------------------------------------------------------------------
void VGAnalytics::SetLogLevel(enum VGAnalytics::LogLevel logLevel)
{
d->m_logLevel = logLevel;
}
//---------------------------------------------------------------------------------------------------------------------
auto VGAnalytics::LogLevel() const -> enum VGAnalytics::LogLevel { return d->m_logLevel; }
//---------------------------------------------------------------------------------------------------------------------
void VGAnalytics::SetRepoRevision(const QString &rev)
{
d->m_repoRevision = rev;
}
//---------------------------------------------------------------------------------------------------------------------
auto VGAnalytics::RepoRevision() const -> QString
{
return d->m_repoRevision;
}
//---------------------------------------------------------------------------------------------------------------------
void VGAnalytics::SetGUILanguage(const QString &language)
{
d->m_guiLanguage = language.toLower().replace(QChar('_'), QChar('-'));
}
//---------------------------------------------------------------------------------------------------------------------
auto VGAnalytics::GUILanguage() const -> QString
{
return d->m_guiLanguage;
}
//---------------------------------------------------------------------------------------------------------------------
void VGAnalytics::SetSendInterval(int milliseconds)
{
d->m_timer.setInterval(milliseconds);
}
//---------------------------------------------------------------------------------------------------------------------
auto VGAnalytics::SendInterval() const -> int
{
return (d->m_timer.interval());
}
//---------------------------------------------------------------------------------------------------------------------
auto VGAnalytics::IsEnabled() -> bool
{
return d->m_isEnabled;
}
//---------------------------------------------------------------------------------------------------------------------
void VGAnalytics::SetMeasurementId(const QString &measurementId)
{
d->m_measurementId = measurementId;
}
//---------------------------------------------------------------------------------------------------------------------
void VGAnalytics::SetApiSecret(const QString &apiSecret)
{
d->m_apiSecret = apiSecret;
}
//---------------------------------------------------------------------------------------------------------------------
void VGAnalytics::SetClientID(const QString &clientID)
{
d->m_clientID = clientID;
}
//---------------------------------------------------------------------------------------------------------------------
void VGAnalytics::Enable(bool state)
{
d->Enable(state);
}
//---------------------------------------------------------------------------------------------------------------------
void VGAnalytics::SetNetworkAccessManager(QNetworkAccessManager *networkAccessManager)
{
if (d->networkManager != networkAccessManager)
{
// Delete the old network manager if it was our child
if (d->networkManager && d->networkManager->parent() == this)
{
d->networkManager->deleteLater();
}
d->networkManager = networkAccessManager;
}
}
//---------------------------------------------------------------------------------------------------------------------
auto VGAnalytics::NetworkAccessManager() const -> QNetworkAccessManager *
{
return d->networkManager;
}
//---------------------------------------------------------------------------------------------------------------------
void VGAnalytics::SendAppFreshInstallEvent(qint64 engagementTimeMsec)
{
SendEvent(QStringLiteral("vapp_fresh_install"), InitAppStartEventParams(engagementTimeMsec));
}
//---------------------------------------------------------------------------------------------------------------------
void VGAnalytics::SendAppStartEvent(qint64 engagementTimeMsec)
{
SendEvent(QStringLiteral("vapp_start"), InitAppStartEventParams(engagementTimeMsec));
}
//---------------------------------------------------------------------------------------------------------------------
void VGAnalytics::SendAppCloseEvent(qint64 engagementTimeMsec)
{
QHash<QString, QJsonValue> params{
// {QStringLiteral("category"), ""},
// In order for user activity to display in standard reports like Realtime, engagement_time_msec and session_id
// must be supplied as part of the params for an event.
// https://developers.google.com/analytics/devguides/collection/protocol/ga4/sending-events?client_type=gtag#optional_parameters_for_reports
{QStringLiteral("engagement_time_msec"), engagementTimeMsec},
};
SendEvent(QStringLiteral("vapp_close"), params);
QNetworkReply *reply = d->PostMessage();
if (reply)
{
QTimer timer;
const int timeoutSeconds = 3; // Wait for 3 seconds
timer.setSingleShot(true);
timer.start(timeoutSeconds * 1000);
QEventLoop loop;
connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec(); // wait for finished
}
}
//---------------------------------------------------------------------------------------------------------------------
/**
* A query for a POST message will be created to report this event. The
* created query will be stored in a message queue.
*/
void VGAnalytics::SendEvent(const QString &eventName, const QHash<QString, QJsonValue> &params)
{
/*
// event body example
{
"client_id": "XXXXXXXXXX.YYYYYYYYYY",
"events": [{
"name": "refund",
"params": {
"currency": "USD",
"value": "9.99",
"transaction_id": "ABC-123"
}
}]
}
*/
QJsonObject root;
root[QStringLiteral("client_id")] = d->m_clientID;
QJsonObject event;
event[QStringLiteral("name")] = eventName;
QJsonObject eventParams;
auto i = params.constBegin();
while (i != params.constEnd())
{
eventParams[i.key()] = i.value();
++i;
}
// In order for user activity to display in standard reports like Realtime, engagement_time_msec and session_id must
// be supplied as part of the params for an event.
// https://developers.google.com/analytics/devguides/collection/protocol/ga4/sending-events?client_type=gtag#optional_parameters_for_reports
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast)
eventParams[QStringLiteral("session_id")] = qApp->sessionId();
event[QStringLiteral("params")] = eventParams;
QJsonArray eventArray;
eventArray.append(event);
root[QStringLiteral("events")] = eventArray;
d->EnqueQueryWithCurrentTime(root);
}
//---------------------------------------------------------------------------------------------------------------------
auto VGAnalytics::InitAppStartEventParams(qint64 engagementTimeMsec) const -> QHash<QString, QJsonValue>
{
QHash<QString, QJsonValue> params{
// {QStringLiteral("category"), ""},
{QStringLiteral("qt_version"), QT_VERSION_STR},
{QStringLiteral("app_version"), d->m_appVersion},
{QStringLiteral("word_size"), QStringLiteral("%1 bit").arg(QSysInfo::WordSize)},
{QStringLiteral("cpu_architecture"), QSysInfo::currentCpuArchitecture()},
{QStringLiteral("revision"), d->m_repoRevision},
{QStringLiteral("os_version"), QSysInfo::prettyProductName()},
{QStringLiteral("screen_size"), d->m_screenResolution},
{QStringLiteral("screen_scale_factor"), d->m_screenScaleFactor},
// In order for user activity to display in standard reports like Realtime, engagement_time_msec and session_id
// must be supplied as part of the params for an event.
// https://developers.google.com/analytics/devguides/collection/protocol/ga4/sending-events?client_type=gtag#optional_parameters_for_reports
{QStringLiteral("engagement_time_msec"), engagementTimeMsec},
{QStringLiteral("gui_language"), d->m_guiLanguage},
};
return params;
}
//---------------------------------------------------------------------------------------------------------------------
/**
* Qut stream to persist class GAnalytics.
*/
auto operator<<(QDataStream &outStream, const VGAnalytics &analytics) -> QDataStream &
{
outStream << analytics.d->PersistMessageQueue();
return outStream;
}
//---------------------------------------------------------------------------------------------------------------------
/**
* In stream to read GAnalytics from file.
*/
auto operator>>(QDataStream &inStream, VGAnalytics &analytics) -> QDataStream &
{
QList<QString> dataList;
inStream >> dataList;
analytics.d->ReadMessagesFromFile(dataList);
return inStream;
}

View File

@ -0,0 +1,99 @@
/************************************************************************
**
** @file vganalytics.h
** @author Roman Telezhynskyi <dismine(at)gmail.com>
** @date 26 6, 2023
**
** @brief
** @copyright
** This source code is part of the Valentina project, a pattern making
** program, whose allow create and modeling patterns of clothing.
** Copyright (C) 2023 Valentina project
** <https://gitlab.com/smart-pattern/valentina> All Rights Reserved.
**
** Valentina 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 3 of the License, or
** (at your option) any later version.
**
** Valentina 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 Valentina. If not, see <http://www.gnu.org/licenses/>.
**
*************************************************************************/
#ifndef VGANALYTICS_H
#define VGANALYTICS_H
#include <QObject>
class QNetworkAccessManager;
class VGAnalyticsWorker;
// https://developers.google.com/analytics/devguides/collection/protocol/ga4/verify-implementation?client_type=gtag
class VGAnalytics : public QObject
{
Q_OBJECT // NOLINT
public:
~VGAnalytics() override;
static auto Instance() -> VGAnalytics *;
enum LogLevel
{
Debug,
Info,
Error
};
Q_ENUM(LogLevel) // NOLINT
void SetMeasurementId(const QString &measurementId);
void SetApiSecret(const QString &apiSecret);
void SetClientID(const QString &clientID);
void SetLogLevel(LogLevel logLevel);
auto LogLevel() const -> LogLevel;
void SetRepoRevision(const QString &rev);
auto RepoRevision() const -> QString;
void SetGUILanguage(const QString &language);
auto GUILanguage() const -> QString;
void SetSendInterval(int milliseconds);
auto SendInterval() const -> int;
void Enable(bool state = true);
auto IsEnabled() -> bool;
/// Get or set the network access manager. If none is set, the class creates its own on the first request
void SetNetworkAccessManager(QNetworkAccessManager *networkAccessManager);
auto NetworkAccessManager() const -> QNetworkAccessManager *;
public slots:
void SendAppFreshInstallEvent(qint64 engagementTimeMsec);
void SendAppStartEvent(qint64 engagementTimeMsec);
void SendAppCloseEvent(qint64 engagementTimeMsec);
private:
Q_DISABLE_COPY_MOVE(VGAnalytics) // NOLINT
explicit VGAnalytics(QObject *parent = nullptr);
private:
VGAnalyticsWorker *d;
void SendEvent(const QString &eventName, const QHash<QString, QJsonValue> &params);
auto InitAppStartEventParams(qint64 engagementTimeMsec) const -> QHash<QString, QJsonValue>;
friend auto operator<<(QDataStream &outStream, const VGAnalytics &analytics) -> QDataStream &;
friend auto operator>>(QDataStream &inStream, VGAnalytics &analytics) -> QDataStream &;
};
#endif // VGANALYTICS_H

View File

@ -0,0 +1,8 @@
HEADERS += \
$$PWD/def.h \
$$PWD/vganalytics.h \
$$PWD/vganalyticsworker.h
SOURCES += \
$$PWD/vganalytics.cpp \
$$PWD/vganalyticsworker.cpp

View File

@ -0,0 +1,80 @@
# File with common stuff for whole project
include(../../../common.pri)
QT += network gui
# Name of library
TARGET = vganalytics
# We want to create a library
TEMPLATE = lib
CONFIG += staticlib # Making static library
# Since Q5.12 available support for C++17
equals(QT_MAJOR_VERSION, 5):greaterThan(QT_MINOR_VERSION, 11) {
CONFIG += c++17
} else {
CONFIG += c++14
}
# Use out-of-source builds (shadow builds)
CONFIG -= debug_and_release debug_and_release_target
# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
# Since Qt 5.4.0 the source code location is recorded only in debug builds.
# We need this information also in release builds. For this need define QT_MESSAGELOGCONTEXT.
DEFINES += QT_MESSAGELOGCONTEXT
include(vganalytics.pri)
# This is static library so no need in "make install"
# directory for executable file
DESTDIR = bin
# files created moc
MOC_DIR = moc
# objecs files
OBJECTS_DIR = obj
# Set using ccache. Function enable_ccache() defined in common.pri.
$$enable_ccache()
include(warnings.pri)
CONFIG(debug, debug|release){
# Debug mode
}else{
# Release mode
!*msvc*:CONFIG += silent
DEFINES += V_NO_ASSERT
!unix:*g++*{
QMAKE_CXXFLAGS += -fno-omit-frame-pointer # Need for exchndl.dll
}
noDebugSymbols{ # For enable run qmake with CONFIG+=noDebugSymbols
# do nothing
} else {
!macx:!*msvc*{
# Turn on debug symbols in release mode on Unix systems.
# On Mac OS X temporarily disabled. TODO: find way how to strip binary file.
QMAKE_CXXFLAGS_RELEASE += -g -gdwarf-3
QMAKE_CFLAGS_RELEASE += -g -gdwarf-3
QMAKE_LFLAGS_RELEASE =
}
}
}
include (../libs.pri)

View File

@ -0,0 +1,16 @@
VLib {
name: "VGAnalyticsLib"
files: [
"def.h",
"vganalytics.cpp",
"vganalytics.h",
"vganalyticsworker.cpp",
"vganalyticsworker.h",
]
Depends { name: "Qt"; submodules: ["core", "network", "gui"] }
Export {
Depends { name: "cpp" }
cpp.includePaths: [exportingProduct.sourceDirectory]
}
}

View File

@ -0,0 +1,281 @@
/************************************************************************
**
** @file vganalyticsworker.cpp
** @author Roman Telezhynskyi <dismine(at)gmail.com>
** @date 26 6, 2023
**
** @brief
** @copyright
** This source code is part of the Valentina project, a pattern making
** program, whose allow create and modeling patterns of clothing.
** Copyright (C) 2023 Valentina project
** <https://gitlab.com/smart-pattern/valentina> All Rights Reserved.
**
** Valentina 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 3 of the License, or
** (at your option) any later version.
**
** Valentina 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 Valentina. If not, see <http://www.gnu.org/licenses/>.
**
*************************************************************************/
#include "vganalyticsworker.h"
#include "vganalytics.h"
#include <QCoreApplication>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QGuiApplication>
#include <QScreen>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
const QLatin1String VGAnalyticsWorker::dateTimeFormat("yyyy,MM,dd-hh:mm::ss:zzz");
namespace
{
constexpr int fourHours = 4 * 60 * 60 * 1000;
}
//---------------------------------------------------------------------------------------------------------------------
VGAnalyticsWorker::VGAnalyticsWorker(QObject *parent)
: QObject(parent),
m_logLevel(VGAnalytics::Error)
{
m_appName = QCoreApplication::applicationName();
m_appVersion = QCoreApplication::applicationVersion();
m_request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json; charset=utf-8");
m_request.setHeader(QNetworkRequest::UserAgentHeader, GetUserAgent());
m_guiLanguage = QLocale::system().name().toLower().replace(QChar('_'), QChar('-'));
QScreen *screen = QGuiApplication::primaryScreen();
QSize size = screen->size();
m_screenResolution = QStringLiteral("%1x%2").arg(size.width()).arg(size.height());
m_screenScaleFactor = screen->logicalDotsPerInchX() / 96.0;
m_timer.setInterval(m_timerInterval);
connect(&m_timer, &QTimer::timeout, this, &VGAnalyticsWorker::PostMessage);
}
//---------------------------------------------------------------------------------------------------------------------
void VGAnalyticsWorker::Enable(bool state)
{
// state change to the same is not valid.
if (m_isEnabled == state)
{
return;
}
m_isEnabled = state;
if (m_isEnabled)
{
// enable -> start doing things :)
m_timer.start();
}
else
{
// disable -> stop the timer
m_timer.stop();
}
}
//---------------------------------------------------------------------------------------------------------------------
void VGAnalyticsWorker::LogMessage(enum VGAnalytics::LogLevel level, const QString &message) const
{
if (m_logLevel > level)
{
return;
}
qDebug() << "[Analytics]" << message;
}
//---------------------------------------------------------------------------------------------------------------------
/**
* Try to gain information about the system where this application
* is running. It needs to get the name and version of the operating
* system, the language and screen resolution.
* All this information will be send in POST messages.
* @return agent A QString with all the information formatted for a POST message.
*/
auto VGAnalyticsWorker::GetUserAgent() const -> QString
{
return QStringLiteral("%1/%2").arg(m_appName, m_appVersion);
}
//---------------------------------------------------------------------------------------------------------------------
/**
* The message queue contains a list of QueryBuffer object.
* QueryBuffer holds a QUrlQuery object and a QDateTime object.
* These both object are freed from the buffer object and
* inserted as QString objects in a QList.
* @return dataList The list with concartinated queue data.
*/
auto VGAnalyticsWorker::PersistMessageQueue() -> QList<QString>
{
QList<QString> dataList;
dataList.reserve(m_messageQueue.size() * 2);
for (const auto &buffer : m_messageQueue)
{
dataList << QJsonDocument(buffer.postQuery).toJson();
dataList << buffer.time.toString(dateTimeFormat);
}
return dataList;
}
//---------------------------------------------------------------------------------------------------------------------
/**
* Reads persistent messages from a file.
* Gets all message data as a QList<QString>.
* Two lines in the list build a QueryBuffer object.
*/
void VGAnalyticsWorker::ReadMessagesFromFile(const QList<QString> &dataList)
{
QListIterator<QString> iter(dataList);
while (iter.hasNext())
{
QString queryString = iter.next();
QString dateString = iter.next();
QDateTime dateTime = QDateTime::fromString(dateString, dateTimeFormat);
QueryBuffer buffer;
QJsonDocument jsonDocument = QJsonDocument::fromJson(queryString.toUtf8());
if (jsonDocument.isNull())
{
qDebug() << "===> please check the string " << queryString.toUtf8();
}
QJsonObject jsonObject = jsonDocument.object();
buffer.postQuery = jsonObject;
buffer.time = dateTime;
m_messageQueue.enqueue(buffer);
}
}
//---------------------------------------------------------------------------------------------------------------------
/**
* Takes a QUrlQuery object and wrapp it together with
* a QTime object into a QueryBuffer struct. These struct
* will be stored in the message queue.
*/
void VGAnalyticsWorker::EnqueQueryWithCurrentTime(const QJsonObject &query)
{
QueryBuffer buffer;
buffer.postQuery = query;
buffer.time = QDateTime::currentDateTime();
m_messageQueue.enqueue(buffer);
}
//---------------------------------------------------------------------------------------------------------------------
/**
* This function is called by a timer interval.
* The function tries to send a messages from the queue.
* If message was successfully send then this function
* will be called back to send next message.
* If message queue contains more than one message then
* the connection will kept open.
* The message POST is asyncroniously when the server
* answered a signal will be emitted.
*/
auto VGAnalyticsWorker::PostMessage() -> QNetworkReply *
{
if (m_messageQueue.isEmpty())
{
// queue empty -> try sending later
m_timer.start();
return nullptr;
}
// queue has messages -> stop timer and start sending
m_timer.stop();
QString connection = QStringLiteral("close");
if (m_messageQueue.count() > 1)
{
connection = QLatin1String("keep-alive");
}
QueryBuffer buffer = m_messageQueue.head();
QDateTime sendTime = QDateTime::currentDateTime();
qint64 timeDiff = buffer.time.msecsTo(sendTime);
if (timeDiff > fourHours)
{
// too old.
m_messageQueue.dequeue();
return PostMessage();
}
QByteArray requestJson = QJsonDocument(buffer.postQuery).toJson(QJsonDocument::Compact);
m_request.setRawHeader("Connection", connection.toUtf8());
m_request.setHeader(QNetworkRequest::ContentLengthHeader, requestJson.length());
if (m_measurementId.isEmpty())
{
LogMessage(VGAnalytics::Error, QStringLiteral("google analytics measurement id was not set!"));
}
if (m_apiSecret.isEmpty())
{
LogMessage(VGAnalytics::Error, QStringLiteral("google analytics api seceret was not set!"));
}
QString requestUrl = QStringLiteral("https://www.google-analytics.com/mp/collect?measurement_id=%1&api_secret=%2");
requestUrl = requestUrl.arg(m_measurementId, m_apiSecret);
m_request.setUrl(QUrl(requestUrl));
LogMessage(VGAnalytics::Debug, "GA Query string = " + requestJson);
// Create a new network access manager if we don't have one yet
if (networkManager == nullptr)
{
networkManager = new QNetworkAccessManager(this);
}
QNetworkReply *reply = networkManager->post(m_request, requestJson);
connect(reply, &QNetworkReply::finished, this, &VGAnalyticsWorker::PostMessageFinished);
return reply;
}
//---------------------------------------------------------------------------------------------------------------------
/**
* NetworkAccsessManager has finished to POST a message.
* If POST message was successfully send then the message
* query should be removed from queue.
* SIGNAL "postMessage" will be emitted to send next message
* if there is any.
* If message couldn't be send then next try is when the
* timer emits its signal.
*/
void VGAnalyticsWorker::PostMessageFinished()
{
auto *reply = qobject_cast<QNetworkReply *>(sender());
int httpStausCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (httpStausCode < 200 || httpStausCode > 299)
{
LogMessage(VGAnalytics::Error, QStringLiteral("Error posting message: %1").arg(reply->errorString()));
// An error ocurred. Try sending later.
m_timer.start();
return;
}
LogMessage(VGAnalytics::Debug, QStringLiteral("Message sent"));
m_messageQueue.dequeue();
PostMessage();
reply->deleteLater();
}

View File

@ -0,0 +1,97 @@
/************************************************************************
**
** @file vganalyticsworker.h
** @author Roman Telezhynskyi <dismine(at)gmail.com>
** @date 26 6, 2023
**
** @brief
** @copyright
** This source code is part of the Valentina project, a pattern making
** program, whose allow create and modeling patterns of clothing.
** Copyright (C) 2023 Valentina project
** <https://gitlab.com/smart-pattern/valentina> All Rights Reserved.
**
** Valentina 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 3 of the License, or
** (at your option) any later version.
**
** Valentina 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 Valentina. If not, see <http://www.gnu.org/licenses/>.
**
*************************************************************************/
#ifndef VGANALYTICSWORKER_H
#define VGANALYTICSWORKER_H
#include "vganalytics.h"
#include <QDateTime>
#include <QJsonObject>
#include <QNetworkRequest>
#include <QObject>
#include <QQueue>
#include <QTimer>
#include <QUrlQuery>
#include <qglobal.h>
class QNetworkReply;
struct QueryBuffer
{
QJsonObject postQuery{};
QDateTime time{};
};
class VGAnalyticsWorker : public QObject
{
Q_OBJECT // NOLINT
public:
explicit VGAnalyticsWorker(QObject *parent = nullptr);
QNetworkAccessManager *networkManager{nullptr}; // NOLINT(misc-non-private-member-variables-in-classes)
QQueue<QueryBuffer> m_messageQueue{}; // NOLINT(misc-non-private-member-variables-in-classes)
QTimer m_timer{}; // NOLINT(misc-non-private-member-variables-in-classes)
QNetworkRequest m_request{}; // NOLINT(misc-non-private-member-variables-in-classes)
enum VGAnalytics::LogLevel m_logLevel{VGAnalytics::Debug}; // NOLINT(misc-non-private-member-variables-in-classes)
QString m_measurementId{}; // NOLINT(misc-non-private-member-variables-in-classes)
QString m_apiSecret{}; // NOLINT(misc-non-private-member-variables-in-classes)
QString m_clientID{}; // NOLINT(misc-non-private-member-variables-in-classes)
QString m_userID{}; // NOLINT(misc-non-private-member-variables-in-classes)
QString m_appName{}; // NOLINT(misc-non-private-member-variables-in-classes)
QString m_appVersion{}; // NOLINT(misc-non-private-member-variables-in-classes)
QString m_guiLanguage{}; // NOLINT(misc-non-private-member-variables-in-classes)
QString m_screenResolution{}; // NOLINT(misc-non-private-member-variables-in-classes)
QString m_repoRevision{}; // NOLINT(misc-non-private-member-variables-in-classes)
bool m_isEnabled{false}; // NOLINT(misc-non-private-member-variables-in-classes)
int m_timerInterval{30000}; // NOLINT(misc-non-private-member-variables-in-classes)
qreal m_screenScaleFactor{}; // NOLINT(misc-non-private-member-variables-in-classes)
const static QLatin1String dateTimeFormat;
void LogMessage(enum VGAnalytics::LogLevel level, const QString &message) const;
auto GetUserAgent() const -> QString;
auto PersistMessageQueue() -> QList<QString>;
void ReadMessagesFromFile(const QList<QString> &dataList);
void EnqueQueryWithCurrentTime(const QJsonObject &query);
void Enable(bool state);
public slots:
QNetworkReply *PostMessage(); // NOLINT(modernize-use-trailing-return-type)
void PostMessageFinished();
private:
Q_DISABLE_COPY_MOVE(VGAnalyticsWorker)
};
#endif // VGANALYTICSWORKER_H

View File

@ -0,0 +1,82 @@
#Turn on compilers warnings.
unix {
*g++*{
QMAKE_CXXFLAGS += \
# Key -isystem disable checking errors in system headers.
-isystem "$${OUT_PWD}/$${MOC_DIR}" \
-isystem "$${OUT_PWD}/$${UI_DIR}" \
$$GCC_DEBUG_CXXFLAGS # See common.pri for more details.
checkWarnings{ # For enable run qmake with CONFIG+=checkWarnings
QMAKE_CXXFLAGS += -Werror
}
noAddressSanitizer{ # For enable run qmake with CONFIG+=noAddressSanitizer
# do nothing
} else {
CONFIG(debug, debug|release){
# Debug mode
#gccs 4.8.0 Address Sanitizer
#http://blog.qt.digia.com/blog/2013/04/17/using-gccs-4-8-0-address-sanitizer-with-qt/
QMAKE_CXXFLAGS += -fsanitize=address -fno-omit-frame-pointer
QMAKE_CFLAGS += -fsanitize=address -fno-omit-frame-pointer
QMAKE_LFLAGS += -fsanitize=address
}
}
gccUbsan{ # For enable run qmake with CONFIG+=gccUbsan
CONFIG(debug, debug|release){
# Debug mode
#gccs 4.9.0 Undefined Behavior Sanitizer (ubsan)
QMAKE_CXXFLAGS += -fsanitize=undefined
QMAKE_CFLAGS += -fsanitize=undefined
QMAKE_LFLAGS += -fsanitize=undefined
}
}
}
*clang*{
QMAKE_CXXFLAGS += \
# Key -isystem disable checking errors in system headers.
-isystem "$${OUT_PWD}/$${MOC_DIR}" \
-isystem "$${OUT_PWD}/$${UI_DIR}" \
$$CLANG_DEBUG_CXXFLAGS # See common.pri for more details.
checkWarnings{ # For enable run qmake with CONFIG+=checkWarnings
QMAKE_CXXFLAGS += -Werror
}
# -isystem key works only for headers. In some cases it's not enough. But we can't delete these warnings and
# want them in global list. Compromise decision delete them from local list.
QMAKE_CXXFLAGS -= \
-Wmissing-prototypes \
-Wundefined-reinterpret-cast
}
*-icc-*{
QMAKE_CXXFLAGS += \
-isystem "$${OUT_PWD}/$${MOC_DIR}" \
-isystem "$${OUT_PWD}/$${UI_DIR}" \
$$ICC_DEBUG_CXXFLAGS
checkWarnings{ # For enable run qmake with CONFIG+=checkWarnings
QMAKE_CXXFLAGS += -Werror
}
}
} else { # Windows
*g++*{
QMAKE_CXXFLAGS += $$GCC_DEBUG_CXXFLAGS # See common.pri for more details.
checkWarnings{ # For enable run qmake with CONFIG+=checkWarnings
QMAKE_CXXFLAGS += -Werror
}
}
*msvc*{
QMAKE_CXXFLAGS += $$MSVC_DEBUG_CXXFLAGS # See common.pri for more details.
checkWarnings{ # For enable run qmake with CONFIG+=checkWarnings
QMAKE_CXXFLAGS += -WX
}
}
}

View File

@ -0,0 +1,49 @@
/************************************************************************
**
** @file dialogaskcollectstatistic.cpp
** @author Roman Telezhynskyi <dismine(at)gmail.com>
** @date 26 6, 2023
**
** @brief
** @copyright
** This source code is part of the Valentina project, a pattern making
** program, whose allow create and modeling patterns of clothing.
** Copyright (C) 2023 Valentina project
** <https://gitlab.com/smart-pattern/valentina> All Rights Reserved.
**
** Valentina 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 3 of the License, or
** (at your option) any later version.
**
** Valentina 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 Valentina. If not, see <http://www.gnu.org/licenses/>.
**
*************************************************************************/
#include "dialogaskcollectstatistic.h"
#include "ui_dialogaskcollectstatistic.h"
//---------------------------------------------------------------------------------------------------------------------
DialogAskCollectStatistic::DialogAskCollectStatistic(QWidget *parent)
: QDialog(parent),
ui(new Ui::DialogAskCollectStatistic)
{
ui->setupUi(this);
}
//---------------------------------------------------------------------------------------------------------------------
DialogAskCollectStatistic::~DialogAskCollectStatistic()
{
delete ui;
}
//---------------------------------------------------------------------------------------------------------------------
auto DialogAskCollectStatistic::CollectStatistic() -> bool
{
return ui->checkBoxSendUsageStatistics->isChecked();
}

View File

@ -0,0 +1,57 @@
/************************************************************************
**
** @file dialogaskcollectstatistic.h
** @author Roman Telezhynskyi <dismine(at)gmail.com>
** @date 26 6, 2023
**
** @brief
** @copyright
** This source code is part of the Valentina project, a pattern making
** program, whose allow create and modeling patterns of clothing.
** Copyright (C) 2023 Valentina project
** <https://gitlab.com/smart-pattern/valentina> All Rights Reserved.
**
** Valentina 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 3 of the License, or
** (at your option) any later version.
**
** Valentina 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 Valentina. If not, see <http://www.gnu.org/licenses/>.
**
*************************************************************************/
#ifndef DIALOGASKCOLLECTSTATISTIC_H
#define DIALOGASKCOLLECTSTATISTIC_H
#include <QDialog>
#include <QtGlobal>
#if QT_VERSION < QT_VERSION_CHECK(5, 13, 0)
#include "../defglobal.h"
#endif
namespace Ui
{
class DialogAskCollectStatistic;
}
class DialogAskCollectStatistic : public QDialog
{
Q_OBJECT // NOLINT
public:
explicit DialogAskCollectStatistic(QWidget *parent = nullptr);
~DialogAskCollectStatistic() override;
auto CollectStatistic() -> bool;
private:
Q_DISABLE_COPY_MOVE(DialogAskCollectStatistic) // NOLINT
Ui::DialogAskCollectStatistic *ui;
};
#endif // DIALOGASKCOLLECTSTATISTIC_H

View File

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DialogAskCollectStatistic</class>
<widget class="QDialog" name="DialogAskCollectStatistic">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>376</width>
<height>183</height>
</rect>
</property>
<property name="windowTitle">
<string>Privacy</string>
</property>
<property name="windowIcon">
<iconset resource="../share/resources/icon.qrc">
<normaloff>:/icon/64x64/icon64x64.png</normaloff>:/icon/64x64/icon64x64.png</iconset>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="checkBoxSendUsageStatistics">
<property name="text">
<string>Send usage statistics</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Please help to improve Valentina's quality by automatically sending usage statistics. Sent data contains &lt;span style=&quot; font-weight:700;&quot;&gt;no potentially sensitive information&lt;/span&gt; like user names, email addresses, file contents or file paths.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../share/resources/icon.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>DialogAskCollectStatistic</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>DialogAskCollectStatistic</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -153,6 +153,8 @@ VAbstractApplication::VAbstractApplication(int &argc, char **argv)
#endif
connect(this, &QApplication::aboutToQuit, this, &VAbstractApplication::AboutToQuit);
m_uptimeTimer.start();
}
//---------------------------------------------------------------------------------------------------------------------
@ -433,6 +435,12 @@ void VAbstractApplication::RestartSVGFontDatabaseWatcher()
}
}
//---------------------------------------------------------------------------------------------------------------------
auto VAbstractApplication::AppUptime() const -> qint64
{
return m_uptimeTimer.elapsed();
}
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
//---------------------------------------------------------------------------------------------------------------------
auto VAbstractApplication::TextCodecCache(QStringConverter::Encoding encoding) const -> VTextCodec *

View File

@ -31,6 +31,7 @@
#include <QApplication>
#include <QCoreApplication>
#include <QElapsedTimer>
#include <QFileDialog>
#include <QLocale>
#include <QMetaObject>
@ -104,6 +105,8 @@ public:
auto SVGFontDatabase() -> VSvgFontDatabase *;
void RestartSVGFontDatabaseWatcher();
auto AppUptime() const -> qint64;
protected:
QUndoStack *undoStack;
@ -120,6 +123,8 @@ protected:
QPointer<QTranslator> appTranslator{nullptr};
QPointer<QTranslator> pmsTranslator{nullptr};
QElapsedTimer m_uptimeTimer{};
virtual void InitTrVars() = 0;
static void CheckSystemLocale();

View File

@ -233,6 +233,10 @@ Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingWatermarkEditorSize, (QLatin1Str
// NOLINTNEXTLINE
Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingWatermarkCustomColors, (QLatin1String("watermarkCustomColors")))
Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingsStatistictAskCollect, (QLatin1String("askCollect"))) // NOLINT
Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingsStatistictCollect, (QLatin1String("collect"))) // NOLINT
Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingsStatistictClientID, (QLatin1String("clientID"))) // NOLINT
// Reading settings file is very expensive, cache curve approximation to speed up getting value
qreal curveApproximationCached = -1; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
Q_GLOBAL_STATIC(QString, localeCached) // NOLINT
@ -1116,20 +1120,20 @@ auto VCommonSettings::GetCSVSeparator() const -> QChar
switch (separator)
{
case 0:
return QChar('\t');
return QChar('\t'); // NOLINT(modernize-return-braced-init-list)
case 1:
return QChar(';');
return QChar(';'); // NOLINT(modernize-return-braced-init-list)
case 2:
return QChar(' ');
return QChar(' '); // NOLINT(modernize-return-braced-init-list)
default:
return QChar(',');
return QChar(','); // NOLINT(modernize-return-braced-init-list)
}
}
//---------------------------------------------------------------------------------------------------------------------
auto VCommonSettings::GetDefCSVSeparator() -> QChar
{
return QChar(',');
return QChar(','); // NOLINT(modernize-return-braced-init-list)
}
//---------------------------------------------------------------------------------------------------------------------
@ -1751,6 +1755,51 @@ void VCommonSettings::SetWatermarkCustomColors(QVector<QColor> colors)
settings.sync();
}
//---------------------------------------------------------------------------------------------------------------------
auto VCommonSettings::IsAskCollectStatistic() const -> bool
{
QSettings settings(this->format(), this->scope(), this->organizationName(), *commonIniFilename);
return settings.value(*settingsStatistictAskCollect, 1).toBool();
}
//---------------------------------------------------------------------------------------------------------------------
void VCommonSettings::SetAskCollectStatistic(bool value)
{
QSettings settings(this->format(), this->scope(), this->organizationName(), *commonIniFilename);
settings.setValue(*settingsStatistictAskCollect, value);
settings.sync();
}
//---------------------------------------------------------------------------------------------------------------------
auto VCommonSettings::IsCollectStatistic() const -> bool
{
QSettings settings(this->format(), this->scope(), this->organizationName(), *commonIniFilename);
return settings.value(*settingsStatistictCollect, 1).toBool();
}
//---------------------------------------------------------------------------------------------------------------------
void VCommonSettings::SetCollectStatistic(bool value)
{
QSettings settings(this->format(), this->scope(), this->organizationName(), *commonIniFilename);
settings.setValue(*settingsStatistictCollect, value);
settings.sync();
}
//---------------------------------------------------------------------------------------------------------------------
auto VCommonSettings::GetClientID() const -> QString
{
QSettings settings(this->format(), this->scope(), this->organizationName(), *commonIniFilename);
return settings.value(*settingsStatistictClientID, QString()).toString();
}
//---------------------------------------------------------------------------------------------------------------------
void VCommonSettings::SetClientID(const QString &clientID)
{
QSettings settings(this->format(), this->scope(), this->organizationName(), *commonIniFilename);
settings.setValue(*settingsStatistictClientID, clientID);
settings.sync();
}
//---------------------------------------------------------------------------------------------------------------------
auto VCommonSettings::PrepareStandardFiles(const QString &currentPath, const QString &standardPath,
const QString &defPath) -> QString

View File

@ -321,6 +321,15 @@ public:
auto GetWatermarkCustomColors() const -> QVector<QColor>;
void SetWatermarkCustomColors(QVector<QColor> colors);
auto IsAskCollectStatistic() const -> bool;
void SetAskCollectStatistic(bool value);
auto IsCollectStatistic() const -> bool;
void SetCollectStatistic(bool value);
auto GetClientID() const -> QString;
void SetClientID(const QString &clientID);
signals:
void SVGFontsPathChanged(const QString &oldPath, const QString &newPath);

View File

@ -3,6 +3,7 @@
SOURCES += \
$$PWD/def.cpp \
$$PWD/dialogs/dialogaskcollectstatistic.cpp \
$$PWD/svgfont/svgdef.cpp \
$$PWD/svgfont/vsvgfont.cpp \
$$PWD/svgfont/vsvgfontdatabase.cpp \
@ -61,6 +62,7 @@ HEADERS += \
$$PWD/bpstd/utility.hpp \
$$PWD/bpstd/variant.hpp \
$$PWD/compatibility.h \
$$PWD/dialogs/dialogaskcollectstatistic.h \
$$PWD/lambdaconstants.h \
$$PWD/stable.h \
$$PWD/def.h \
@ -122,5 +124,6 @@ contains(QT_VERSION, ^5\\.[0-2]\\.[0-2]$) { # Since Qt 5.3.0
}
FORMS += \
$$PWD/dialogs/dialogaskcollectstatistic.ui \
$$PWD/dialogs/dialogexporttocsv.ui \
$$PWD/dialogs/dialogselectlanguage.ui

View File

@ -67,12 +67,15 @@ VLib {
name: "dialogs"
prefix: "dialogs/"
files: [
"dialogaskcollectstatistic.cpp",
"dialogaskcollectstatistic.h",
"dialogaskcollectstatistic.ui",
"dialogexporttocsv.cpp",
"dialogselectlanguage.cpp",
"dialogexporttocsv.h",
"dialogselectlanguage.h",
"dialogexporttocsv.ui",
"dialogselectlanguage.ui"
"dialogselectlanguage.ui",
]
}