1
0
Fork 0
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:
Andreas Traczyk 2020-09-17 16:08:52 -04:00
parent 9d8fd5da04
commit 3b6bbe772a
25 changed files with 884 additions and 206 deletions

View file

@ -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 &quot;del vc_redist.x64.exe; del uninstall.exe; del WinSparkle.dll;&quot;" Execute="deferred" Return="ignore" HideTarget="no" Impersonate="no" />
<CustomAction Id="removeOldJamiFiles"
Directory="APPLICATIONFOLDER"
ExeCommand="cmd /c &quot;del vc_redist.x64.exe; del uninstall.exe; del WinSparkle.dll;&quot;"
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"

View file

@ -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 \

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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")
}

View file

@ -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)
}

View file

@ -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_;

View file

@ -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.

View file

@ -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
}

View file

@ -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
View 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
View 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*)

View file

@ -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

View file

@ -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
}

View file

@ -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,

View file

@ -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()
}
}
}
}

View file

@ -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
View 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
View 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*)

View file

@ -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)
{

View file

@ -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
*/

View file

@ -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);
}

View file

@ -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_;