mirror of
https://git.jami.net/savoirfairelinux/jami-client-qt.git
synced 2025-04-21 21:52:03 +02:00
misc: implement update system
- re-introduce a genericized NetworkManager - isolate update logic into a qml accessible class derived from NetworkManager - fix QtWebEngineProcess missing when re-installing over existing version - provide a command line option to override the base url for testing local and remote updates - clean-up manual update-check UI Gitlab: #101 Change-Id: I9c8d2badae59ec31cab12d38b8470edf2bcad401
This commit is contained in:
parent
9d8fd5da04
commit
3b6bbe772a
25 changed files with 884 additions and 206 deletions
|
@ -4,7 +4,7 @@
|
|||
<Product Id="*" Name="$(var.Name)" Language="1033" Version="$(fun.AutoVersion(1.0))" Manufacturer="$(var.Manufacturer)" UpgradeCode="7c45b52b-0390-4fe8-947a-3f13e82dd346">
|
||||
<Package InstallerVersion="301" Compressed="yes" InstallScope="perMachine" />
|
||||
|
||||
<MajorUpgrade AllowSameVersionUpgrades="yes" DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
|
||||
<MajorUpgrade Schedule="afterInstallInitialize" AllowDowngrades="yes"/>
|
||||
<MediaTemplate EmbedCab="yes" CompressionLevel="high" />
|
||||
|
||||
<!--Icon File should be in release folder(not wix project), otherwise cannot be read-->
|
||||
|
@ -12,6 +12,9 @@
|
|||
<Property Id="ARPPRODUCTICON" Value="icon.ico" />
|
||||
<Property Id="ARPNOMODIFY" Value="1" />
|
||||
|
||||
<!-- It seems that QtWebEngineProcess.exe versioning requires us to force reinstall. -->
|
||||
<Property Id="REINSTALLMODE" Value="dm" />
|
||||
|
||||
<Feature Id="ProductFeature" Title="Main" Level="1" Absent="disallow">
|
||||
<ComponentGroupRef Id="StandardComponents" Primary="yes" />
|
||||
<ComponentGroupRef Id="HeatGenerated" />
|
||||
|
@ -38,7 +41,13 @@
|
|||
<WixVariable Id="WixUIDialogBmp" Value="main-banner.bmp" />
|
||||
<WixVariable Id="WixUISupportPerUser" Value="0" />
|
||||
|
||||
<CustomAction Id="removeOldJamiFiles" Directory="APPLICATIONFOLDER" ExeCommand="cmd /c "del vc_redist.x64.exe; del uninstall.exe; del WinSparkle.dll;"" Execute="deferred" Return="ignore" HideTarget="no" Impersonate="no" />
|
||||
<CustomAction Id="removeOldJamiFiles"
|
||||
Directory="APPLICATIONFOLDER"
|
||||
ExeCommand="cmd /c "del vc_redist.x64.exe; del uninstall.exe; del WinSparkle.dll;""
|
||||
Execute="deferred"
|
||||
Return="ignore"
|
||||
HideTarget="no"
|
||||
Impersonate="no"/>
|
||||
|
||||
<Property Id="QtExecCmdLine" Value='"[WindowsFolder]\System32\taskkill.exe" /F /IM QtWebEngineProcess.exe /IM Jami.exe'/>
|
||||
<CustomAction Id="JamiProcesses.TaskKill"
|
||||
|
|
|
@ -111,7 +111,9 @@ unix {
|
|||
|
||||
# Input
|
||||
HEADERS += \
|
||||
src/networkmanager.h \
|
||||
src/smartlistmodel.h \
|
||||
src/updatemanager.h \
|
||||
src/utils.h \
|
||||
src/bannedlistmodel.h \
|
||||
src/version.h \
|
||||
|
@ -159,7 +161,9 @@ HEADERS += \
|
|||
SOURCES += \
|
||||
src/bannedlistmodel.cpp \
|
||||
src/accountlistmodel.cpp \
|
||||
src/networkmanager.cpp \
|
||||
src/runguard.cpp \
|
||||
src/updatemanager.cpp \
|
||||
src/webchathelpers.cpp \
|
||||
src/main.cpp \
|
||||
src/smartlistmodel.cpp \
|
||||
|
|
|
@ -15,6 +15,8 @@ import "commoncomponents"
|
|||
ApplicationWindow {
|
||||
id: root
|
||||
|
||||
property ApplicationWindow appWindow: root
|
||||
|
||||
AccountMigrationDialog{
|
||||
id: accountMigrationDialog
|
||||
|
||||
|
@ -25,11 +27,11 @@ ApplicationWindow {
|
|||
}
|
||||
}
|
||||
|
||||
function close() {
|
||||
function close(force = false) {
|
||||
// If we're in the onboarding wizard or 'MinimizeOnClose'
|
||||
// is set, then we can quit
|
||||
if (!SettingsAdapter.getAppValue(Settings.MinimizeOnClose) ||
|
||||
!UtilsAdapter.getAccountListSize()) {
|
||||
if (force || !SettingsAdapter.getAppValue(Settings.MinimizeOnClose) ||
|
||||
!UtilsAdapter.getAccountListSize()) {
|
||||
Qt.quit()
|
||||
} else {
|
||||
// hide to the systray
|
||||
|
|
|
@ -35,27 +35,29 @@ BaseDialog {
|
|||
property var buttonTitles: []
|
||||
property var buttonCallBacks: []
|
||||
property var buttonStyles: []
|
||||
property alias description: descriptionText.text
|
||||
property alias infoText: infoText.text
|
||||
property alias innerContentData: innerContent.data
|
||||
|
||||
function openWithParameters(title, info) {
|
||||
function openWithParameters(title, info = "") {
|
||||
root.title = title
|
||||
descriptionText.text = info
|
||||
if (info !== "")
|
||||
root.infoText = info
|
||||
open()
|
||||
}
|
||||
|
||||
contentItem: Rectangle {
|
||||
id: simpleMessageDialogContentRect
|
||||
id: container
|
||||
|
||||
implicitWidth: Math.max(JamiTheme.preferredDialogWidth,
|
||||
buttonTitles.length * (JamiTheme.preferredFieldWidth / 2
|
||||
+ JamiTheme.preferredMarginSize))
|
||||
implicitHeight: JamiTheme.preferredDialogHeight / 2
|
||||
implicitHeight: JamiTheme.preferredDialogHeight / 2 - JamiTheme.preferredMarginSize
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
Label {
|
||||
id: descriptionText
|
||||
id: infoText
|
||||
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
Layout.preferredWidth: JamiTheme.preferredDialogWidth - JamiTheme.preferredMarginSize
|
||||
|
@ -67,6 +69,13 @@ BaseDialog {
|
|||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
Item {
|
||||
id: innerContent
|
||||
Layout.topMargin: JamiTheme.preferredMarginSize / 2
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: JamiTheme.preferredMarginSize
|
||||
|
||||
|
|
|
@ -18,9 +18,8 @@
|
|||
|
||||
#include "connectivitymonitor.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <QDebug>
|
||||
#include <atlbase.h>
|
||||
#include <netlistmgr.h>
|
||||
|
||||
|
@ -159,4 +158,4 @@ ConnectivityMonitor::~ConnectivityMonitor()
|
|||
destroy();
|
||||
CoUninitialize();
|
||||
}
|
||||
#endif // Q_OS_WIN
|
||||
#endif // Q_OS_WIN
|
||||
|
|
|
@ -21,10 +21,9 @@
|
|||
#include <QObject>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
class ConnectivityMonitor : public QObject
|
||||
class ConnectivityMonitor final : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConnectivityMonitor(QObject* parent = 0);
|
||||
~ConnectivityMonitor();
|
||||
|
@ -43,4 +42,22 @@ private:
|
|||
class NetworkEventHandler* netEventHandler_;
|
||||
unsigned long cookie_;
|
||||
};
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
#else
|
||||
// Dummy implementation for non-Windows platforms.
|
||||
// TODO: platform implementations should be in the daemon.
|
||||
|
||||
// clang-format off
|
||||
class ConnectivityMonitor final : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ConnectivityMonitor(QObject* parent = 0) : QObject(parent) {};
|
||||
~ConnectivityMonitor() = default;
|
||||
|
||||
bool isOnline() { return false; };
|
||||
signals:
|
||||
void connectivityChanged();
|
||||
};
|
||||
// clang-format on
|
||||
#endif // Q_OS_WIN
|
||||
|
|
|
@ -268,9 +268,22 @@ Item {
|
|||
property string tipChooseDownloadFolder: qsTr("Choose download directory")
|
||||
property string recordCall: qsTr("Record call")
|
||||
|
||||
// UpdateSettings
|
||||
// Updates
|
||||
property string betaInstall: qsTr("Install beta version")
|
||||
property string checkForUpdates: qsTr("Check for updates now")
|
||||
property string enableAutoUpdates: qsTr("Enable/Disable automatic updates")
|
||||
property string tipAutoUpdate: qsTr("toggle automatic updates")
|
||||
property string updatesTitle: qsTr("Updates")
|
||||
property string updateDialogTitle: qsTr("Update")
|
||||
property string updateFound: qsTr("A new version of Jami was found\n Would you like to update now?")
|
||||
property string updateNotFound: qsTr("No new version of Jami was found")
|
||||
property string updateCheckError: qsTr("An error occured when checking for a new version")
|
||||
property string updateDownloadNetworkError: qsTr("Installer download failed due to a network error")
|
||||
property string updateDownloadCanceled: qsTr("Installer download canceled")
|
||||
property string updateDownloading: "Downloading"
|
||||
property string confirmBeta: qsTr("This will uninstall your current Release version and you can always download the latest Release version on our website")
|
||||
property string networkDisconnected: qsTr("Network disconnected")
|
||||
property string genericError: qsTr("Something went wrong")
|
||||
|
||||
// Recording Settings
|
||||
property string tipRecordFolder: qsTr("Select a record directory")
|
||||
|
@ -365,4 +378,8 @@ Item {
|
|||
|
||||
// Update settings
|
||||
property string update: qsTr("Automatically check for updates")
|
||||
|
||||
// Generic dialog options
|
||||
property string optionOk: qsTr("Ok")
|
||||
property string optionCancel: qsTr("Cancel")
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ Item {
|
|||
property string notificationRed: "#ff3b30"
|
||||
property string unPresenceOrange: "orange"
|
||||
property string backgroundColor: lightGrey_
|
||||
property string backgroundDarkColor: lightGreyTab_
|
||||
property string backgroundDarkColor: rgb256(220, 220, 220)
|
||||
|
||||
property string screenSelectionBorderGreen: "green"
|
||||
|
||||
|
@ -97,28 +97,21 @@ Item {
|
|||
property int preferredDialogWidth: 400
|
||||
property int preferredDialogHeight: 300
|
||||
|
||||
// Misc.
|
||||
property color white: "white"
|
||||
property color darkGrey: rgb256(63, 63, 63)
|
||||
|
||||
// Jami theme colors
|
||||
function rgb256(r, g, b) {
|
||||
return Qt.rgba(r / 256, g / 256, b / 256, 1)
|
||||
return Qt.rgba(r / 255, g / 255, b / 255, 1.0)
|
||||
}
|
||||
|
||||
property color blue_: "#109ede"
|
||||
property color wizardBlueButtons: "#28b1ed"
|
||||
property color blueLogo_: rgb256(0, 7, 71)
|
||||
property color lightBlue_: "#c1ebf0"
|
||||
property color lightGrey_: rgb256(242, 242, 242)
|
||||
property color lightGreyTab_: rgb256(220, 220, 220)
|
||||
property color imGrey_: "#dedee0"
|
||||
property color imBlue_: "#cfebf5"
|
||||
property color lightBlack_: rgb256(63, 63, 63)
|
||||
property color grey_: rgb256(160, 160, 160)
|
||||
property color red_: rgb256(251, 72, 71)
|
||||
property color lightRed_: rgb256(252, 91, 90)
|
||||
property color darkRed_: rgb256(219, 55, 54)
|
||||
property color notificationRed_: rgb256(255, 59, 48)
|
||||
property color urgentOrange_: rgb256(255, 165, 0)
|
||||
property color green_: rgb256(127, 255, 0)
|
||||
property color presenceGreen_: rgb256(76, 217, 100)
|
||||
property color smartlistSelection_: rgb256(240, 240, 240)
|
||||
property color smartlistHighlight_: rgb256(245, 245, 245)
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#endif
|
||||
|
||||
#include "accountlistmodel.h"
|
||||
#include "updatemanager.h"
|
||||
#include "rendermanager.h"
|
||||
#include "appsettingsmanager.h"
|
||||
#include "utils.h"
|
||||
|
@ -55,6 +56,8 @@
|
|||
|
||||
#include <memory>
|
||||
|
||||
class ConnectivityMonitor;
|
||||
|
||||
using namespace lrc::api;
|
||||
|
||||
using migrateCallback = std::function<void()>;
|
||||
|
@ -65,15 +68,21 @@ class LRCInstance : public QObject
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static LRCInstance& instance(migrateCallback willMigrate = {}, migrateCallback didMigrate = {})
|
||||
static LRCInstance& instance(migrateCallback willMigrate = {},
|
||||
migrateCallback didMigrate = {},
|
||||
const QString& updateUrl = {},
|
||||
ConnectivityMonitor* connectivityMonitor = {})
|
||||
{
|
||||
static LRCInstance instance_(willMigrate, didMigrate);
|
||||
static LRCInstance instance_(willMigrate, didMigrate, updateUrl, connectivityMonitor);
|
||||
return instance_;
|
||||
}
|
||||
|
||||
static void init(migrateCallback willMigrate = {}, migrateCallback didMigrate = {})
|
||||
static void init(migrateCallback willMigrate = {},
|
||||
migrateCallback didMigrate = {},
|
||||
const QString& updateUrl = {},
|
||||
ConnectivityMonitor* connectivityMonitor = {})
|
||||
{
|
||||
instance(willMigrate, didMigrate);
|
||||
instance(willMigrate, didMigrate, updateUrl, connectivityMonitor);
|
||||
}
|
||||
|
||||
static Lrc& getAPI()
|
||||
|
@ -86,6 +95,11 @@ public:
|
|||
return instance().renderer_.get();
|
||||
}
|
||||
|
||||
static UpdateManager* getUpdateManager()
|
||||
{
|
||||
return instance().updateManager_.get();
|
||||
}
|
||||
|
||||
static void connectivityChanged()
|
||||
{
|
||||
instance().lrc_->connectivityChanged();
|
||||
|
@ -443,16 +457,22 @@ signals:
|
|||
void restoreAppRequested();
|
||||
void notificationClicked(bool forceToTop = false);
|
||||
void updateSmartList();
|
||||
void quitEngineRequested();
|
||||
|
||||
private:
|
||||
LRCInstance(migrateCallback willMigrateCb = {}, migrateCallback didMigrateCb = {})
|
||||
LRCInstance(migrateCallback willMigrateCb = {},
|
||||
migrateCallback didMigrateCb = {},
|
||||
const QString& updateUrl = {},
|
||||
ConnectivityMonitor* connectivityMonitor = {})
|
||||
{
|
||||
lrc_ = std::make_unique<Lrc>(willMigrateCb, didMigrateCb);
|
||||
renderer_ = std::make_unique<RenderManager>(lrc_->getAVModel());
|
||||
updateManager_ = std::make_unique<UpdateManager>(updateUrl, connectivityMonitor);
|
||||
};
|
||||
|
||||
std::unique_ptr<Lrc> lrc_;
|
||||
std::unique_ptr<RenderManager> renderer_;
|
||||
std::unique_ptr<UpdateManager> updateManager_;
|
||||
AccountListModel accountListModel_;
|
||||
QString selectedAccountId_;
|
||||
QString selectedConvUid_;
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "mainapplication.h"
|
||||
#include "runguard.h"
|
||||
#include "version.h"
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QtWebEngine>
|
||||
|
@ -60,14 +61,12 @@ main(int argc, char* argv[])
|
|||
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
QApplication::setQuitOnLastWindowClosed(false);
|
||||
QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
|
||||
QCoreApplication::setApplicationVersion(QString(VERSION_STRING));
|
||||
QApplication::setHighDpiScaleFactorRoundingPolicy(
|
||||
Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor);
|
||||
QtWebEngine::initialize();
|
||||
|
||||
// Allow QtWebEngine to load local resources.
|
||||
char ARG_DISABLE_WEB_SECURITY[] = "--disable-web-security";
|
||||
auto newArgv = parseInputArgument(argc, argv, ARG_DISABLE_WEB_SECURITY);
|
||||
MainApplication app(argc, newArgv);
|
||||
MainApplication app(argc, argv);
|
||||
|
||||
/*
|
||||
* Runguard to make sure that only one instance runs at a time.
|
||||
|
|
|
@ -22,14 +22,18 @@
|
|||
#include "mainapplication.h"
|
||||
|
||||
#include "appsettingsmanager.h"
|
||||
#include "globalinstances.h"
|
||||
#include "connectivitymonitor.h"
|
||||
#include "globalsystemtray.h"
|
||||
#include "qmlregister.h"
|
||||
#include "qrimageprovider.h"
|
||||
#include "pixbufmanipulator.h"
|
||||
#include "tintedbuttonimageprovider.h"
|
||||
|
||||
#include "globalinstances.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QCommandLineParser>
|
||||
#include <QCoreApplication>
|
||||
#include <QFontDatabase>
|
||||
#include <QMenu>
|
||||
#include <QQmlContext>
|
||||
|
@ -45,6 +49,15 @@
|
|||
#include <gnutls/gnutls.h>
|
||||
#endif
|
||||
|
||||
namespace opts {
|
||||
// Keys used to store command-line options.
|
||||
constexpr static const char STARTMINIMIZED[] = "STARTMINIMIZED";
|
||||
constexpr static const char DEBUG[] = "DEBUG";
|
||||
constexpr static const char DEBUGCONSOLE[] = "DEBUGCONSOLE";
|
||||
constexpr static const char DEBUGFILE[] = "DEBUGFILE";
|
||||
constexpr static const char UPDATEURL[] = "UPDATEURL";
|
||||
} // namespace opts
|
||||
|
||||
static void
|
||||
consoleDebug()
|
||||
{
|
||||
|
@ -106,6 +119,7 @@ fileDebug(QFile* debugFile)
|
|||
MainApplication::MainApplication(int& argc, char** argv)
|
||||
: QApplication(argc, argv)
|
||||
, engine_(new QQmlApplicationEngine())
|
||||
, connectivityMonitor_(new ConnectivityMonitor(this))
|
||||
{
|
||||
QObject::connect(this, &QApplication::aboutToQuit, [this] { cleanup(); });
|
||||
}
|
||||
|
@ -120,10 +134,10 @@ MainApplication::init()
|
|||
setenv("QT_QPA_PLATFORMTHEME", "gtk3", true);
|
||||
#endif
|
||||
|
||||
for (auto string : QCoreApplication::arguments()) {
|
||||
if (string == "-d" || string == "--debug") {
|
||||
consoleDebug();
|
||||
}
|
||||
auto results = parseArguments();
|
||||
|
||||
if (results[opts::DEBUG].toBool()) {
|
||||
consoleDebug();
|
||||
}
|
||||
|
||||
Utils::removeOldVersions();
|
||||
|
@ -135,31 +149,33 @@ MainApplication::init()
|
|||
#endif
|
||||
|
||||
GlobalInstances::setPixmapManipulator(std::make_unique<PixbufManipulator>());
|
||||
initLrc();
|
||||
initLrc(results[opts::UPDATEURL].toString(), connectivityMonitor_);
|
||||
|
||||
initConnectivityMonitor();
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
QObject::connect(&LRCInstance::instance(), &LRCInstance::notificationClicked, [] {
|
||||
for (QWindow* appWindow : qApp->allWindows()) {
|
||||
if (appWindow->objectName().compare("mainViewWindow"))
|
||||
continue;
|
||||
// clang-format off
|
||||
::SetWindowPos((HWND) appWindow->winId(),
|
||||
HWND_TOPMOST, 0, 0, 0, 0,
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
|
||||
::SetWindowPos((HWND) appWindow->winId(),
|
||||
HWND_NOTOPMOST, 0, 0, 0, 0,
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
|
||||
// clang-format on
|
||||
return;
|
||||
}
|
||||
#ifdef Q_OS_WIN
|
||||
connect(connectivityMonitor_, &ConnectivityMonitor::connectivityChanged, [] {
|
||||
LRCInstance::connectivityChanged();
|
||||
});
|
||||
#endif
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
bool startMinimized {false};
|
||||
parseArguments(startMinimized);
|
||||
QObject::connect(
|
||||
&LRCInstance::instance(),
|
||||
&LRCInstance::quitEngineRequested,
|
||||
this,
|
||||
[this] { engine_->quit(); },
|
||||
Qt::DirectConnection);
|
||||
|
||||
if (results[opts::DEBUGFILE].toBool()) {
|
||||
debugFile_.reset(new QFile(getDebugFilePath()));
|
||||
debugFile_->open(QIODevice::WriteOnly | QIODevice::Truncate);
|
||||
debugFile_->close();
|
||||
fileDebug(debugFile_.get());
|
||||
}
|
||||
|
||||
if (results[opts::DEBUGCONSOLE].toBool()) {
|
||||
vsConsoleDebug();
|
||||
}
|
||||
|
||||
connectForceWindowToTop();
|
||||
initSettings();
|
||||
initSystray();
|
||||
initQmlEngine();
|
||||
|
@ -205,7 +221,7 @@ MainApplication::loadTranslations()
|
|||
}
|
||||
|
||||
void
|
||||
MainApplication::initLrc()
|
||||
MainApplication::initLrc(const QString& downloadUrl, ConnectivityMonitor* cm)
|
||||
{
|
||||
/*
|
||||
* Init mainwindow and finish splash when mainwindow shows up.
|
||||
|
@ -226,51 +242,59 @@ MainApplication::initLrc()
|
|||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
isMigrating = false;
|
||||
});
|
||||
},
|
||||
downloadUrl,
|
||||
cm);
|
||||
LRCInstance::subscribeToDebugReceived();
|
||||
LRCInstance::getAPI().holdConferences = false;
|
||||
}
|
||||
|
||||
void
|
||||
MainApplication::initConnectivityMonitor()
|
||||
const QVariantMap
|
||||
MainApplication::parseArguments()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
connectivityMonitor_.reset(new ConnectivityMonitor(this));
|
||||
connect(connectivityMonitor_.get(), &ConnectivityMonitor::connectivityChanged, [] {
|
||||
LRCInstance::connectivityChanged();
|
||||
});
|
||||
#endif // Q_OS_WIN
|
||||
}
|
||||
QVariantMap results;
|
||||
QCommandLineParser parser;
|
||||
parser.addHelpOption();
|
||||
parser.addVersionOption();
|
||||
|
||||
void
|
||||
MainApplication::parseArguments(bool& startMinimized)
|
||||
{
|
||||
QString uri = "";
|
||||
QCommandLineOption minimizedOption(QStringList() << "m"
|
||||
<< "minimized",
|
||||
"Start minimized.");
|
||||
parser.addOption(minimizedOption);
|
||||
|
||||
QCommandLineOption debugOption(QStringList() << "d"
|
||||
<< "debug",
|
||||
"Debug out.");
|
||||
parser.addOption(debugOption);
|
||||
|
||||
for (auto string : QCoreApplication::arguments()) {
|
||||
if (string.startsWith("jami:")) {
|
||||
uri = string;
|
||||
} else {
|
||||
if (string == "-m" || string == "--minimized") {
|
||||
startMinimized = true;
|
||||
}
|
||||
#ifdef Q_OS_WINDOWS
|
||||
debugFile_.reset(new QFile(getDebugFilePath()));
|
||||
auto dbgFile = string == "-f" || string == "--file";
|
||||
auto dbgConsole = string == "-c" || string == "--vsconsole";
|
||||
if (dbgFile || dbgConsole) {
|
||||
if (dbgFile) {
|
||||
debugFile_->open(QIODevice::WriteOnly | QIODevice::Truncate);
|
||||
debugFile_->close();
|
||||
fileDebug(debugFile_.get());
|
||||
}
|
||||
if (dbgConsole) {
|
||||
vsConsoleDebug();
|
||||
}
|
||||
}
|
||||
QCommandLineOption debugConsoleOption(QStringList() << "c"
|
||||
<< "console",
|
||||
"Debug out to IDE console.");
|
||||
parser.addOption(debugConsoleOption);
|
||||
|
||||
QCommandLineOption debugFileOption(QStringList() << "f"
|
||||
<< "file",
|
||||
"Debug to file.");
|
||||
parser.addOption(debugFileOption);
|
||||
|
||||
QCommandLineOption updateUrlOption(QStringList() << "u"
|
||||
<< "url",
|
||||
"Reference <url> for client versioning.",
|
||||
"url");
|
||||
parser.addOption(updateUrlOption);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
parser.process(*this);
|
||||
|
||||
results[opts::STARTMINIMIZED] = parser.isSet(minimizedOption);
|
||||
results[opts::DEBUG] = parser.isSet(debugOption);
|
||||
#ifdef Q_OS_WINDOWS
|
||||
results[opts::DEBUGCONSOLE] = parser.isSet(debugConsoleOption);
|
||||
results[opts::DEBUGFILE] = parser.isSet(debugFileOption);
|
||||
results[opts::UPDATEURL] = parser.value(updateUrlOption);
|
||||
#endif
|
||||
return results;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -333,3 +357,25 @@ MainApplication::cleanup()
|
|||
#endif
|
||||
QApplication::exit(0);
|
||||
}
|
||||
|
||||
void
|
||||
MainApplication::connectForceWindowToTop()
|
||||
{
|
||||
#ifdef Q_OS_WINDOWS
|
||||
QObject::connect(&LRCInstance::instance(), &LRCInstance::notificationClicked, [] {
|
||||
for (QWindow* appWindow : qApp->allWindows()) {
|
||||
if (appWindow->objectName().compare("mainViewWindow"))
|
||||
continue;
|
||||
// clang-format off
|
||||
::SetWindowPos((HWND) appWindow->winId(),
|
||||
HWND_TOPMOST, 0, 0, 0, 0,
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
|
||||
::SetWindowPos((HWND) appWindow->winId(),
|
||||
HWND_NOTOPMOST, 0, 0, 0, 0,
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
|
||||
// clang-format on
|
||||
return;
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/*
|
||||
/*!
|
||||
* Copyright (C) 2020 by Savoir-faire Linux
|
||||
* Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>
|
||||
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
|
||||
|
@ -20,8 +20,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "connectivitymonitor.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QApplication>
|
||||
#include <QQmlApplicationEngine>
|
||||
|
@ -29,6 +27,8 @@
|
|||
|
||||
#include <memory>
|
||||
|
||||
class ConnectivityMonitor;
|
||||
|
||||
class MainApplication : public QApplication
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -41,19 +41,17 @@ public:
|
|||
|
||||
private:
|
||||
void loadTranslations();
|
||||
void initLrc();
|
||||
void initConnectivityMonitor();
|
||||
void parseArguments(bool& startMinimized);
|
||||
void initLrc(const QString& downloadUrl, ConnectivityMonitor* cm);
|
||||
const QVariantMap parseArguments();
|
||||
void setApplicationFont();
|
||||
void initQmlEngine();
|
||||
void initSettings();
|
||||
void initSystray();
|
||||
void cleanup();
|
||||
void connectForceWindowToTop();
|
||||
|
||||
private:
|
||||
#ifdef Q_OS_WIN
|
||||
QScopedPointer<ConnectivityMonitor> connectivityMonitor_;
|
||||
#endif // Q_OS_WIN
|
||||
QScopedPointer<QFile> debugFile_;
|
||||
QQmlApplicationEngine* engine_;
|
||||
ConnectivityMonitor* connectivityMonitor_;
|
||||
};
|
||||
|
|
166
src/networkmanager.cpp
Normal file
166
src/networkmanager.cpp
Normal file
|
@ -0,0 +1,166 @@
|
|||
/*!
|
||||
* Copyright (C) 2019-2020 by Savoir-faire Linux
|
||||
* Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
|
||||
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "networkmanager.h"
|
||||
|
||||
#include "connectivitymonitor.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <QMetaEnum>
|
||||
#include <QtNetwork>
|
||||
|
||||
NetWorkManager::NetWorkManager(ConnectivityMonitor* cm, QObject* parent)
|
||||
: QObject(parent)
|
||||
, manager_(new QNetworkAccessManager(this))
|
||||
, reply_(nullptr)
|
||||
, connectivityMonitor_(cm)
|
||||
{
|
||||
emit statusChanged(GetStatus::IDLE);
|
||||
connect(connectivityMonitor_, &ConnectivityMonitor::connectivityChanged, [this] {
|
||||
auto connected = connectivityMonitor_->isOnline();
|
||||
if (connected && !lastConnectionState_) {
|
||||
manager_->deleteLater();
|
||||
manager_ = new QNetworkAccessManager(this);
|
||||
qWarning() << "connectivity changed, reset QNetworkAccessManager";
|
||||
}
|
||||
lastConnectionState_ = connected;
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
NetWorkManager::get(const QUrl& url, const DoneCallBack& doneCb, const QString& path)
|
||||
{
|
||||
if (!connectivityMonitor_->isOnline()) {
|
||||
emit errorOccured(GetError::DISCONNECTED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (reply_ && reply_->isRunning()) {
|
||||
qWarning() << Q_FUNC_INFO << "currently downloading";
|
||||
return;
|
||||
} else if (url.isEmpty()) {
|
||||
qWarning() << Q_FUNC_INFO << "missing url";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!path.isEmpty()) {
|
||||
QFileInfo fileInfo(url.path());
|
||||
QString fileName = fileInfo.fileName();
|
||||
|
||||
file_.reset(new QFile(path + "/" + fileName));
|
||||
if (!file_->open(QIODevice::WriteOnly)) {
|
||||
emit errorOccured(GetError::ACCESS_DENIED);
|
||||
file_.reset(nullptr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QNetworkRequest request(url);
|
||||
reply_ = manager_->get(request);
|
||||
|
||||
emit statusChanged(GetStatus::STARTED);
|
||||
|
||||
connect(reply_,
|
||||
QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::errorOccurred),
|
||||
[this, doneCb, path](QNetworkReply::NetworkError error) {
|
||||
reply_->disconnect();
|
||||
reset(true);
|
||||
qWarning() << Q_FUNC_INFO << "NetworkError: "
|
||||
<< QMetaEnum::fromType<QNetworkReply::NetworkError>().valueToKey(error);
|
||||
emit errorOccured(GetError::NETWORK_ERROR);
|
||||
});
|
||||
|
||||
connect(reply_, &QNetworkReply::finished, [this, doneCb, path]() {
|
||||
reply_->disconnect();
|
||||
QString response = {};
|
||||
if (path.isEmpty())
|
||||
response = QString(reply_->readAll());
|
||||
reset(!path.isEmpty());
|
||||
emit statusChanged(GetStatus::FINISHED);
|
||||
if (doneCb)
|
||||
doneCb(response);
|
||||
});
|
||||
|
||||
connect(reply_,
|
||||
&QNetworkReply::downloadProgress,
|
||||
this,
|
||||
&NetWorkManager::downloadProgressChanged);
|
||||
|
||||
connect(reply_, &QNetworkReply::readyRead, this, &NetWorkManager::onHttpReadyRead);
|
||||
|
||||
#if QT_CONFIG(ssl)
|
||||
connect(reply_,
|
||||
SIGNAL(sslErrors(const QList<QSslError>&)),
|
||||
this,
|
||||
SLOT(onSslErrors(QList<QSslError>)),
|
||||
Qt::UniqueConnection);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
NetWorkManager::reset(bool flush)
|
||||
{
|
||||
reply_->deleteLater();
|
||||
reply_ = nullptr;
|
||||
if (file_ && flush) {
|
||||
file_->flush();
|
||||
file_->close();
|
||||
file_.reset(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NetWorkManager::onSslErrors(const QList<QSslError>& sslErrors)
|
||||
{
|
||||
#if QT_CONFIG(ssl)
|
||||
QString errorsString;
|
||||
for (const QSslError& error : sslErrors) {
|
||||
if (errorsString.length() > 0) {
|
||||
errorsString += "\n";
|
||||
}
|
||||
errorsString += error.errorString();
|
||||
}
|
||||
emit errorOccured(GetError::SSL_ERROR, errorsString);
|
||||
return;
|
||||
#else
|
||||
Q_UNUSED(sslErrors);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
NetWorkManager::onHttpReadyRead()
|
||||
{
|
||||
/*
|
||||
* This slot gets called every time the QNetworkReply has new data.
|
||||
* We read all of its new data and write it into the file.
|
||||
* That way we use less RAM than when reading it at the finished()
|
||||
* signal of the QNetworkReply
|
||||
*/
|
||||
if (file_)
|
||||
file_->write(reply_->readAll());
|
||||
}
|
||||
|
||||
void
|
||||
NetWorkManager::cancelRequest()
|
||||
{
|
||||
if (reply_) {
|
||||
reply_->abort();
|
||||
emit errorOccured(GetError::CANCELED);
|
||||
}
|
||||
}
|
76
src/networkmanager.h
Normal file
76
src/networkmanager.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*!
|
||||
* Copyright (C) 2019-2020 by Savoir-faire Linux
|
||||
* Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
|
||||
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QFile>
|
||||
#include <QSslError>
|
||||
#include <QNetworkReply>
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class ConnectivityMonitor;
|
||||
|
||||
class NetWorkManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit NetWorkManager(ConnectivityMonitor* cm, QObject* parent = nullptr);
|
||||
virtual ~NetWorkManager() = default;
|
||||
|
||||
enum GetStatus { IDLE, STARTED, FINISHED };
|
||||
|
||||
enum GetError { DISCONNECTED, NETWORK_ERROR, ACCESS_DENIED, SSL_ERROR, CANCELED };
|
||||
Q_ENUM(GetError)
|
||||
|
||||
using DoneCallBack = std::function<void(const QString&)>;
|
||||
|
||||
/*!
|
||||
* using qt get request to store the reply in file
|
||||
* @param url - network address
|
||||
* @param doneCb - done callback
|
||||
* @param path - optional file saving path, if empty
|
||||
* a string will be passed as the second paramter of doneCb
|
||||
*/
|
||||
void get(const QUrl& url, const DoneCallBack& doneCb = {}, const QString& path = {});
|
||||
|
||||
/*!
|
||||
* manually abort the current request
|
||||
*/
|
||||
Q_INVOKABLE void cancelRequest();
|
||||
|
||||
signals:
|
||||
void statusChanged(GetStatus error);
|
||||
void downloadProgressChanged(qint64 bytesRead, qint64 totalBytes);
|
||||
void errorOccured(GetError error, const QString& msg = {});
|
||||
|
||||
private slots:
|
||||
void onSslErrors(const QList<QSslError>& sslErrors);
|
||||
void onHttpReadyRead();
|
||||
|
||||
private:
|
||||
void reset(bool flush = true);
|
||||
|
||||
QNetworkAccessManager* manager_;
|
||||
QNetworkReply* reply_;
|
||||
QScopedPointer<QFile> file_;
|
||||
ConnectivityMonitor* connectivityMonitor_;
|
||||
bool lastConnectionState_;
|
||||
};
|
||||
Q_DECLARE_METATYPE(NetWorkManager*)
|
|
@ -35,6 +35,7 @@
|
|||
#include "mediahandleritemlistmodel.h"
|
||||
#include "messagesadapter.h"
|
||||
#include "namedirectory.h"
|
||||
#include "updatemanager.h"
|
||||
#include "preferenceitemlistmodel.h"
|
||||
#include "pluginitemlistmodel.h"
|
||||
#include "pluginlistpreferencemodel.h"
|
||||
|
@ -137,6 +138,7 @@ registerTypes()
|
|||
QML_REGISTERSINGLETONTYPE_CUSTOM("net.jami.Models", AVModel, 1, 0, &LRCInstance::avModel())
|
||||
QML_REGISTERSINGLETONTYPE_CUSTOM("net.jami.Models", PluginModel, 1, 0, &LRCInstance::pluginModel())
|
||||
QML_REGISTERSINGLETONTYPE_CUSTOM("net.jami.Models", RenderManager, 1, 0, LRCInstance::renderer())
|
||||
QML_REGISTERSINGLETONTYPE_CUSTOM("net.jami.Models", UpdateManager, 1, 0, LRCInstance::getUpdateManager())
|
||||
|
||||
/*
|
||||
* Qml singleton components
|
||||
|
@ -179,5 +181,6 @@ registerTypes()
|
|||
* Enums
|
||||
*/
|
||||
QML_REGISTERUNCREATABLE("net.jami.Enums", Settings, 1, 0);
|
||||
QML_REGISTERUNCREATABLE("net.jami.Enums", NetWorkManager, 1, 0);
|
||||
}
|
||||
// clang-format on
|
||||
|
|
|
@ -87,6 +87,7 @@ Rectangle {
|
|||
UpdateSettings {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: JamiTheme.preferredMarginSize
|
||||
Layout.rightMargin: JamiTheme.preferredMarginSize
|
||||
Layout.bottomMargin: JamiTheme.preferredMarginSize
|
||||
visible: Qt.platform.os == "windows"? true : false
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ ColumnLayout {
|
|||
property string idOfDev: ""
|
||||
|
||||
title: qsTr("Remove Device")
|
||||
description: qsTr("Are you sure you wish to remove this device?")
|
||||
infoText: qsTr("Are you sure you wish to remove this device?")
|
||||
|
||||
buttonTitles: [qsTr("Ok"), qsTr("Cancel")]
|
||||
buttonStyles: [SimpleMessageDialog.ButtonStyle.TintedBlue,
|
||||
|
|
|
@ -72,6 +72,7 @@ ColumnLayout {
|
|||
|
||||
ToggleSwitch {
|
||||
id: alwaysRecordingCheckBox
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: JamiTheme.preferredMarginSize
|
||||
|
||||
|
@ -85,6 +86,7 @@ ColumnLayout {
|
|||
|
||||
ToggleSwitch {
|
||||
id: recordPreviewCheckBox
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: JamiTheme.preferredMarginSize
|
||||
|
||||
|
@ -185,4 +187,4 @@ ColumnLayout {
|
|||
onClicked: recordPathDialog.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* Copyright (C) 2020 by Savoir-faire Linux
|
||||
* Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
|
||||
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -17,30 +18,23 @@
|
|||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Window 2.14
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import QtQuick.Controls.Universal 2.12
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtGraphicalEffects 1.14
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import net.jami.Models 1.0
|
||||
import net.jami.Adapters 1.0
|
||||
import Qt.labs.platform 1.1
|
||||
import net.jami.Enums 1.0
|
||||
import net.jami.Models 1.0
|
||||
import "../../commoncomponents"
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
//TODO: complete check for update and check for Beta slot functions
|
||||
function checkForUpdateSlot() {}
|
||||
function installBetaSlot() {}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: JamiTheme.preferredFieldHeight
|
||||
|
||||
text: qsTr("Updates")
|
||||
text: JamiStrings.updatesTitle
|
||||
font.pointSize: JamiTheme.headerFontSize
|
||||
font.kerning: true
|
||||
|
||||
|
@ -51,48 +45,205 @@ ColumnLayout {
|
|||
ToggleSwitch {
|
||||
id: autoUpdateCheckBox
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: JamiTheme.preferredMarginSize
|
||||
|
||||
checked: SettingsAdapter.getAppValue(Settings.Key.AutoUpdate)
|
||||
|
||||
labelText: JamiStrings.update
|
||||
tooltipText: JamiStrings.enableAutoUpdates
|
||||
fontPointSize: JamiTheme.settingsFontSize
|
||||
|
||||
tooltipText: JamiStrings.enableAutoUpdates
|
||||
|
||||
onSwitchToggled: SettingsAdapter.setAppValue(Settings.Key.AutoUpdate, checked)
|
||||
onSwitchToggled: {
|
||||
SettingsAdapter.setAppValue(Settings.Key.AutoUpdate, checked)
|
||||
UpdateManager.setAutoUpdateCheck(checked)
|
||||
}
|
||||
}
|
||||
|
||||
HoverableRadiusButton {
|
||||
MaterialButton {
|
||||
id: checkUpdateButton
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.preferredWidth: JamiTheme.preferredFieldWidth
|
||||
Layout.preferredHeight: JamiTheme.preferredFieldHeight
|
||||
|
||||
radius: height / 2
|
||||
color: enabled? JamiTheme.buttonTintedBlack : JamiTheme.buttonTintedGrey
|
||||
hoveredColor: JamiTheme.buttonTintedBlackHovered
|
||||
pressedColor: JamiTheme.buttonTintedBlackPressed
|
||||
outlined: true
|
||||
|
||||
toolTipText: qsTr("Check for updates now")
|
||||
text: qsTr("Updates")
|
||||
fontPointSize: JamiTheme.buttonFontSize
|
||||
toolTipText: JamiStrings.checkForUpdates
|
||||
text: JamiStrings.checkForUpdates
|
||||
|
||||
onClicked: {
|
||||
checkForUpdateSlot()
|
||||
}
|
||||
onClicked: UpdateManager.checkForUpdates()
|
||||
}
|
||||
|
||||
HoverableRadiusButton {
|
||||
MaterialButton {
|
||||
id: installBetaButton
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.preferredWidth: JamiTheme.preferredFieldWidth
|
||||
Layout.preferredHeight: JamiTheme.preferredFieldHeight
|
||||
|
||||
radius: height / 2
|
||||
color: enabled? JamiTheme.buttonTintedBlack : JamiTheme.buttonTintedGrey
|
||||
hoveredColor: JamiTheme.buttonTintedBlackHovered
|
||||
pressedColor: JamiTheme.buttonTintedBlackPressed
|
||||
outlined: true
|
||||
|
||||
toolTipText: qsTr("Install the latest beta version")
|
||||
toolTipText: JamiStrings.betaInstall
|
||||
text: JamiStrings.betaInstall
|
||||
fontPointSize: JamiTheme.buttonFontSize
|
||||
|
||||
onClicked: {
|
||||
installBetaSlot()
|
||||
confirmInstallDialog.beta = true
|
||||
confirmInstallDialog.openWithParameters(JamiStrings.updateDialogTitle,
|
||||
JamiStrings.confirmBeta)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
// Quiet check for updates on start if set to.
|
||||
if (SettingsAdapter.getAppValue(Settings.AutoUpdate)) {
|
||||
UpdateManager.checkForUpdates(true)
|
||||
UpdateManager.setAutoUpdateCheck(true)
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: UpdateManager
|
||||
|
||||
function errorToString(error) {
|
||||
switch(error){
|
||||
case NetWorkManager.ACCESS_DENIED:
|
||||
return JamiStrings.genericError
|
||||
case NetWorkManager.DISCONNECTED:
|
||||
return JamiStrings.networkDisconnected
|
||||
case NetWorkManager.NETWORK_ERROR:
|
||||
case NetWorkManager.SSL_ERROR:
|
||||
return JamiStrings.updateDownloadNetworkError
|
||||
case NetWorkManager.CANCELED:
|
||||
return JamiStrings.updateDownloadCanceled
|
||||
default: return {}
|
||||
}
|
||||
}
|
||||
|
||||
function onUpdateCheckReplyReceived(ok, found) {
|
||||
if (!ok) {
|
||||
issueDialog.openWithParameters(JamiStrings.updateDialogTitle,
|
||||
JamiStrings.updateCheckError)
|
||||
return
|
||||
}
|
||||
if (!found) {
|
||||
issueDialog.openWithParameters(JamiStrings.updateDialogTitle,
|
||||
JamiStrings.updateNotFound)
|
||||
} else {
|
||||
confirmInstallDialog.openWithParameters(JamiStrings.updateDialogTitle,
|
||||
JamiStrings.updateFound)
|
||||
}
|
||||
}
|
||||
|
||||
function onUpdateCheckErrorOccurred(error) {
|
||||
issueDialog.openWithParameters(JamiStrings.updateDialogTitle,
|
||||
errorToString(error))
|
||||
}
|
||||
|
||||
function onUpdateDownloadStarted() {
|
||||
downloadDialog.setDownloadProgress(0, 0)
|
||||
downloadDialog.openWithParameters(JamiStrings.updateDialogTitle)
|
||||
}
|
||||
|
||||
function onUpdateDownloadProgressChanged(bytesRead, totalBytes) {
|
||||
downloadDialog.setDownloadProgress(bytesRead, totalBytes)
|
||||
}
|
||||
|
||||
function onUpdateDownloadErrorOccurred(error) {
|
||||
downloadDialog.close()
|
||||
issueDialog.openWithParameters(JamiStrings.updateDialogTitle,
|
||||
errorToString(error))
|
||||
}
|
||||
|
||||
function onUpdateDownloadFinished() { downloadDialog.close() }
|
||||
}
|
||||
|
||||
SimpleMessageDialog {
|
||||
id: confirmInstallDialog
|
||||
|
||||
property bool beta: false
|
||||
|
||||
buttonTitles: [JamiStrings.optionOk, JamiStrings.optionCancel]
|
||||
buttonStyles: [
|
||||
SimpleMessageDialog.ButtonStyle.TintedBlue,
|
||||
SimpleMessageDialog.ButtonStyle.TintedBlue
|
||||
]
|
||||
buttonCallBacks: [function() {UpdateManager.applyUpdates(beta)}]
|
||||
}
|
||||
|
||||
SimpleMessageDialog {
|
||||
id: issueDialog
|
||||
|
||||
buttonTitles: [JamiStrings.optionOk]
|
||||
buttonStyles: [SimpleMessageDialog.ButtonStyle.TintedBlue]
|
||||
buttonCallBacks: []
|
||||
}
|
||||
|
||||
SimpleMessageDialog {
|
||||
id: downloadDialog
|
||||
|
||||
property int bytesRead: 0
|
||||
property int totalBytes: 0
|
||||
property string hSizeRead: UtilsAdapter.humanFileSize(bytesRead)
|
||||
property string hTotalBytes: UtilsAdapter.humanFileSize(totalBytes)
|
||||
property alias progressBarValue: progressBar.value
|
||||
|
||||
function setDownloadProgress(bytesRead, totalBytes) {
|
||||
downloadDialog.bytesRead = bytesRead
|
||||
downloadDialog.totalBytes = totalBytes
|
||||
}
|
||||
|
||||
infoText: JamiStrings.updateDownloading +
|
||||
" (%1 / %2)".arg(hSizeRead).arg(hTotalBytes)
|
||||
|
||||
innerContentData: ProgressBar {
|
||||
id: progressBar
|
||||
|
||||
value: downloadDialog.bytesRead /
|
||||
downloadDialog.totalBytes
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: JamiTheme.preferredMarginSize
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: JamiTheme.preferredMarginSize
|
||||
|
||||
background: Rectangle {
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: 24
|
||||
color: JamiTheme.darkGrey
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: 22
|
||||
|
||||
Rectangle {
|
||||
width: progressBar.visualPosition * parent.width
|
||||
height: parent.height
|
||||
color: JamiTheme.selectionBlue
|
||||
}
|
||||
Label {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
color: JamiTheme.white
|
||||
font.bold: true
|
||||
font.pointSize: JamiTheme.textFontSize + 1
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
text: Math.ceil(progressBar.value * 100).toString() + "%"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buttonTitles: [JamiStrings.optionCancel]
|
||||
buttonStyles: [SimpleMessageDialog.ButtonStyle.TintedBlue]
|
||||
buttonCallBacks: [function() {UpdateManager.cancelUpdate()}]
|
||||
}
|
||||
}
|
||||
|
|
159
src/updatemanager.cpp
Normal file
159
src/updatemanager.cpp
Normal file
|
@ -0,0 +1,159 @@
|
|||
/*!
|
||||
* Copyright (C) 2020 by Savoir-faire Linux
|
||||
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "updatemanager.h"
|
||||
|
||||
#include "appsettingsmanager.h"
|
||||
#include "lrcinstance.h"
|
||||
#include "utils.h"
|
||||
#include "version.h"
|
||||
|
||||
#include <QProcess>
|
||||
#include <QTimer>
|
||||
|
||||
#ifdef BETA
|
||||
static constexpr bool isBeta = true;
|
||||
#else
|
||||
static constexpr bool isBeta = false;
|
||||
#endif
|
||||
|
||||
static constexpr int updatePeriod = 1000 * 60 * 60 * 24; // one day in millis
|
||||
|
||||
UpdateManager::UpdateManager(const QString& url, ConnectivityMonitor* cm, QObject* parent)
|
||||
: NetWorkManager(cm, parent)
|
||||
, baseUrl_(url.isEmpty() ? "https://dl.jami.net/windows" : url.toLatin1())
|
||||
, tempPath_(Utils::WinGetEnv("TEMP"))
|
||||
, updateTimer_(new QTimer(this))
|
||||
{
|
||||
connect(updateTimer_, &QTimer::timeout, [this] {
|
||||
// Quiet period update check.
|
||||
checkForUpdates(true);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
UpdateManager::setAutoUpdateCheck(bool state)
|
||||
{
|
||||
// Quiet check for updates periodically, if set to.
|
||||
if (!state) {
|
||||
updateTimer_->stop();
|
||||
return;
|
||||
}
|
||||
updateTimer_->start(updatePeriod);
|
||||
}
|
||||
|
||||
void
|
||||
UpdateManager::checkForUpdates(bool quiet)
|
||||
{
|
||||
disconnect();
|
||||
|
||||
// Fail without UI if this is a programmatic check.
|
||||
if (!quiet)
|
||||
connect(this, &NetWorkManager::errorOccured, this, &UpdateManager::updateCheckErrorOccurred);
|
||||
|
||||
cleanUpdateFiles();
|
||||
QUrl versionUrl {isBeta ? QUrl::fromEncoded(baseUrl_ + "/beta/version")
|
||||
: QUrl::fromEncoded(baseUrl_ + "/version")};
|
||||
|
||||
get(versionUrl, [this, quiet](const QString& latestVersionString) {
|
||||
if (latestVersionString.isEmpty()) {
|
||||
qWarning() << "Error checking version";
|
||||
if (!quiet)
|
||||
emit updateCheckReplyReceived(false);
|
||||
return;
|
||||
}
|
||||
auto currentVersion = QString(VERSION_STRING).toULongLong();
|
||||
auto latestVersion = latestVersionString.toULongLong();
|
||||
qDebug() << "latest: " << latestVersion << " current: " << currentVersion;
|
||||
if (latestVersion > currentVersion) {
|
||||
qDebug() << "New version found";
|
||||
emit updateCheckReplyReceived(true, true);
|
||||
} else {
|
||||
qDebug() << "No new version found";
|
||||
if (!quiet)
|
||||
emit updateCheckReplyReceived(true, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
UpdateManager::applyUpdates(bool beta)
|
||||
{
|
||||
disconnect();
|
||||
connect(this, &NetWorkManager::errorOccured, this, &UpdateManager::updateDownloadErrorOccurred);
|
||||
connect(this, &NetWorkManager::statusChanged, [this](GetStatus status) {
|
||||
switch (status) {
|
||||
case GetStatus::STARTED:
|
||||
connect(this,
|
||||
&NetWorkManager::downloadProgressChanged,
|
||||
this,
|
||||
&UpdateManager::updateDownloadProgressChanged);
|
||||
emit updateDownloadStarted();
|
||||
break;
|
||||
case GetStatus::FINISHED:
|
||||
emit updateDownloadFinished();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
QUrl downloadUrl {(beta || isBeta) ? QUrl::fromEncoded(baseUrl_ + "/beta/jami.beta.x64.msi")
|
||||
: QUrl::fromEncoded(baseUrl_ + "/jami.release.x64.msi")};
|
||||
|
||||
get(
|
||||
downloadUrl,
|
||||
[this, downloadUrl](const QString&) {
|
||||
LRCInstance::reset();
|
||||
emit LRCInstance::instance().quitEngineRequested();
|
||||
auto args = QString(" /passive /norestart WIXNONUILAUNCH=1");
|
||||
QProcess process;
|
||||
process.start("powershell ",
|
||||
QStringList() << tempPath_ + "\\" + downloadUrl.fileName() << "/L*V"
|
||||
<< tempPath_ + "\\jami_x64_install.log" + args);
|
||||
process.waitForFinished();
|
||||
},
|
||||
tempPath_);
|
||||
}
|
||||
|
||||
void
|
||||
UpdateManager::cancelUpdate()
|
||||
{
|
||||
cancelRequest();
|
||||
}
|
||||
|
||||
void
|
||||
UpdateManager::cleanUpdateFiles()
|
||||
{
|
||||
/*
|
||||
* Delete all logs and msi in the %TEMP% directory before launching.
|
||||
*/
|
||||
QString dir = QString(Utils::WinGetEnv("TEMP"));
|
||||
QDir log_dir(dir, {"jami*.log"});
|
||||
for (const QString& filename : log_dir.entryList()) {
|
||||
log_dir.remove(filename);
|
||||
}
|
||||
QDir msi_dir(dir, {"jami*.msi"});
|
||||
for (const QString& filename : msi_dir.entryList()) {
|
||||
msi_dir.remove(filename);
|
||||
}
|
||||
QDir version_dir(dir, {"version"});
|
||||
for (const QString& filename : version_dir.entryList()) {
|
||||
version_dir.remove(filename);
|
||||
}
|
||||
}
|
54
src/updatemanager.h
Normal file
54
src/updatemanager.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*!
|
||||
* Copyright (C) 2020 by Savoir-faire Linux
|
||||
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "networkmanager.h"
|
||||
|
||||
class ConnectivityMonitor;
|
||||
class QTimer;
|
||||
|
||||
class UpdateManager final : public NetWorkManager
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit UpdateManager(const QString& url, ConnectivityMonitor* cm, QObject* parent = nullptr);
|
||||
~UpdateManager() = default;
|
||||
|
||||
Q_INVOKABLE void checkForUpdates(bool quiet = false);
|
||||
Q_INVOKABLE void applyUpdates(bool beta = false);
|
||||
Q_INVOKABLE void cancelUpdate();
|
||||
Q_INVOKABLE void setAutoUpdateCheck(bool state);
|
||||
|
||||
signals:
|
||||
void updateCheckReplyReceived(bool ok, bool found = false);
|
||||
void updateCheckErrorOccurred(GetError error);
|
||||
void updateDownloadStarted();
|
||||
void updateDownloadProgressChanged(qint64 bytesRead, qint64 totalBytes);
|
||||
void updateDownloadErrorOccurred(GetError error);
|
||||
void updateDownloadFinished();
|
||||
void appCloseRequested();
|
||||
|
||||
private:
|
||||
QByteArray baseUrl_;
|
||||
QString tempPath_;
|
||||
QTimer* updateTimer_;
|
||||
|
||||
void cleanUpdateFiles();
|
||||
};
|
||||
Q_DECLARE_METATYPE(UpdateManager*)
|
|
@ -403,47 +403,6 @@ Utils::getProjectCredits()
|
|||
return credits;
|
||||
}
|
||||
|
||||
void
|
||||
Utils::cleanUpdateFiles()
|
||||
{
|
||||
/*
|
||||
* Delete all logs and msi in the %TEMP% directory before launching.
|
||||
*/
|
||||
QString dir = QString(Utils::WinGetEnv("TEMP"));
|
||||
QDir log_dir(dir, {"jami*.log"});
|
||||
for (const QString& filename : log_dir.entryList()) {
|
||||
log_dir.remove(filename);
|
||||
}
|
||||
QDir msi_dir(dir, {"jami*.msi"});
|
||||
for (const QString& filename : msi_dir.entryList()) {
|
||||
msi_dir.remove(filename);
|
||||
}
|
||||
QDir version_dir(dir, {"version"});
|
||||
for (const QString& filename : version_dir.entryList()) {
|
||||
version_dir.remove(filename);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Utils::checkForUpdates(bool withUI, QWidget* parent)
|
||||
{
|
||||
Q_UNUSED(withUI)
|
||||
Q_UNUSED(parent)
|
||||
/*
|
||||
* TODO: check update logic.
|
||||
*/
|
||||
}
|
||||
|
||||
void
|
||||
Utils::applyUpdates(bool updateToBeta, QWidget* parent)
|
||||
{
|
||||
Q_UNUSED(updateToBeta)
|
||||
Q_UNUSED(parent)
|
||||
/*
|
||||
* TODO: update logic.
|
||||
*/
|
||||
}
|
||||
|
||||
inline QString
|
||||
removeEndlines(const QString& str)
|
||||
{
|
||||
|
|
12
src/utils.h
12
src/utils.h
|
@ -77,18 +77,6 @@ QString getChangeLog();
|
|||
QString getProjectCredits();
|
||||
void removeOldVersions();
|
||||
|
||||
/*
|
||||
* Updates
|
||||
*/
|
||||
#ifdef BETA
|
||||
static constexpr bool isBeta = true;
|
||||
#else
|
||||
static constexpr bool isBeta = false;
|
||||
#endif
|
||||
void cleanUpdateFiles();
|
||||
void checkForUpdates(bool withUI, QWidget* parent = nullptr);
|
||||
void applyUpdates(bool updateToBeta, QWidget* parent = nullptr);
|
||||
|
||||
/*
|
||||
* LRC helpers
|
||||
*/
|
||||
|
|
|
@ -234,11 +234,11 @@ UtilsAdapter::hasVideoCall()
|
|||
}
|
||||
|
||||
bool
|
||||
UtilsAdapter::hasCall(const QString &accountId)
|
||||
UtilsAdapter::hasCall(const QString& accountId)
|
||||
{
|
||||
auto activeCalls = LRCInstance::getActiveCalls();
|
||||
for (const auto &callId : activeCalls) {
|
||||
auto &accountInfo = LRCInstance::accountModel().getAccountInfo(accountId);
|
||||
for (const auto& callId : activeCalls) {
|
||||
auto& accountInfo = LRCInstance::accountModel().getAccountInfo(accountId);
|
||||
if (accountInfo.callModel->hasCall(callId)) {
|
||||
return true;
|
||||
}
|
||||
|
@ -247,11 +247,11 @@ UtilsAdapter::hasCall(const QString &accountId)
|
|||
}
|
||||
|
||||
const QString
|
||||
UtilsAdapter::getCallConvForAccount(const QString &accountId)
|
||||
UtilsAdapter::getCallConvForAccount(const QString& accountId)
|
||||
{
|
||||
// TODO: Currently returning first call, establish priority according to state?
|
||||
for (const auto &callId : LRCInstance::getActiveCalls()) {
|
||||
auto &accountInfo = LRCInstance::accountModel().getAccountInfo(accountId);
|
||||
for (const auto& callId : LRCInstance::getActiveCalls()) {
|
||||
auto& accountInfo = LRCInstance::accountModel().getAccountInfo(accountId);
|
||||
if (accountInfo.callModel->hasCall(callId)) {
|
||||
return LRCInstance::getConversationFromCallId(callId, accountId).uid;
|
||||
}
|
||||
|
@ -278,10 +278,9 @@ UtilsAdapter::getCallId(const QString& accountId, const QString& convUid)
|
|||
}
|
||||
|
||||
int
|
||||
UtilsAdapter::getCallStatus(const QString &callId)
|
||||
UtilsAdapter::getCallStatus(const QString& callId)
|
||||
{
|
||||
const auto callStatus = LRCInstance::getCallInfo(
|
||||
callId, LRCInstance::getCurrAccId());
|
||||
const auto callStatus = LRCInstance::getCallInfo(callId, LRCInstance::getCurrAccId());
|
||||
return static_cast<int>(callStatus->status);
|
||||
}
|
||||
|
||||
|
@ -400,3 +399,9 @@ UtilsAdapter::isImage(const QString& fileExt)
|
|||
{
|
||||
return Utils::isImage(fileExt);
|
||||
}
|
||||
|
||||
QString
|
||||
UtilsAdapter::humanFileSize(qint64 fileSize)
|
||||
{
|
||||
return Utils::humanFileSize(fileSize);
|
||||
}
|
||||
|
|
|
@ -64,10 +64,10 @@ public:
|
|||
Q_INVOKABLE void startPreviewing(bool force);
|
||||
Q_INVOKABLE void stopPreviewing();
|
||||
Q_INVOKABLE bool hasVideoCall();
|
||||
Q_INVOKABLE bool hasCall(const QString &accountId);
|
||||
Q_INVOKABLE const QString getCallConvForAccount(const QString &accountId);
|
||||
Q_INVOKABLE bool hasCall(const QString& accountId);
|
||||
Q_INVOKABLE const QString getCallConvForAccount(const QString& accountId);
|
||||
Q_INVOKABLE const QString getCallId(const QString& accountId, const QString& convUid);
|
||||
Q_INVOKABLE int getCallStatus(const QString &callId);
|
||||
Q_INVOKABLE int getCallStatus(const QString& callId);
|
||||
Q_INVOKABLE const QString getCallStatusStr(int statusInt);
|
||||
Q_INVOKABLE QString getStringUTF8(QString string);
|
||||
Q_INVOKABLE bool validateRegNameForm(const QString& regName);
|
||||
|
@ -83,6 +83,7 @@ public:
|
|||
Q_INVOKABLE QString fileName(const QString& path);
|
||||
Q_INVOKABLE QString getExt(const QString& path);
|
||||
Q_INVOKABLE bool isImage(const QString& fileExt);
|
||||
Q_INVOKABLE QString humanFileSize(qint64 fileSize);
|
||||
|
||||
private:
|
||||
QClipboard* clipboard_;
|
||||
|
|
Loading…
Add table
Reference in a new issue