Replace postAsync functions by SafeConnection in order to make more robust thread connections and guarantee to have always caller/callee while processing signals.
Introduce ObjectCore, ObjectGui, ObjectModel. Simulate addAccount signals on adding account (in waiting for SDK callback).
This commit is contained in:
parent
3fad8ee49e
commit
cd82964b23
44 changed files with 1202 additions and 316 deletions
|
|
@ -30,8 +30,10 @@
|
||||||
#include <QQmlFileSelector>
|
#include <QQmlFileSelector>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#include "core/account/Account.hpp"
|
#include "core/account/AccountCore.hpp"
|
||||||
#include "core/account/AccountProxy.hpp"
|
#include "core/account/AccountProxy.hpp"
|
||||||
|
#include "core/call/CallCore.hpp"
|
||||||
|
#include "core/call/CallGui.hpp"
|
||||||
#include "core/logger/QtLogger.hpp"
|
#include "core/logger/QtLogger.hpp"
|
||||||
#include "core/login/LoginPage.hpp"
|
#include "core/login/LoginPage.hpp"
|
||||||
#include "core/notifier/Notifier.hpp"
|
#include "core/notifier/Notifier.hpp"
|
||||||
|
|
@ -128,8 +130,10 @@ void App::initCppInterfaces() {
|
||||||
|
|
||||||
qmlRegisterUncreatableType<PhoneNumber>(Constants::MainQmlUri, 1, 0, "PhoneNumber", QLatin1String("Uncreatable"));
|
qmlRegisterUncreatableType<PhoneNumber>(Constants::MainQmlUri, 1, 0, "PhoneNumber", QLatin1String("Uncreatable"));
|
||||||
qmlRegisterType<AccountProxy>(Constants::MainQmlUri, 1, 0, "AccountProxy");
|
qmlRegisterType<AccountProxy>(Constants::MainQmlUri, 1, 0, "AccountProxy");
|
||||||
qmlRegisterUncreatableType<Account>(Constants::MainQmlUri, 1, 0, "Account", QLatin1String("Uncreatable"));
|
qmlRegisterType<AccountGui>(Constants::MainQmlUri, 1, 0, "AccountGui");
|
||||||
qmlRegisterUncreatableType<Call>(Constants::MainQmlUri, 1, 0, "Call", QLatin1String("Uncreatable"));
|
qmlRegisterUncreatableType<AccountCore>(Constants::MainQmlUri, 1, 0, "AccountCore", QLatin1String("Uncreatable"));
|
||||||
|
qmlRegisterType<CallGui>(Constants::MainQmlUri, 1, 0, "CallGui");
|
||||||
|
qmlRegisterUncreatableType<CallCore>(Constants::MainQmlUri, 1, 0, "CallCore", QLatin1String("Uncreatable"));
|
||||||
|
|
||||||
LinphoneEnums::registerMetaTypes();
|
LinphoneEnums::registerMetaTypes();
|
||||||
}
|
}
|
||||||
|
|
@ -167,3 +171,15 @@ void App::createCommandParser() {
|
||||||
{"qt-logs-only", tr("commandLineOptionQtLogsOnly")},
|
{"qt-logs-only", tr("commandLineOptionQtLogsOnly")},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool App::notify(QObject *receiver, QEvent *event) {
|
||||||
|
bool done = true;
|
||||||
|
try {
|
||||||
|
done = QApplication::notify(receiver, event);
|
||||||
|
} catch (const std::exception &ex) {
|
||||||
|
qCritical() << "[App] Exception has been catch in notify";
|
||||||
|
} catch (...) {
|
||||||
|
qCritical() << "[App] Generic exeption has been catch in notify";
|
||||||
|
}
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ public:
|
||||||
void onLoggerInitialized();
|
void onLoggerInitialized();
|
||||||
|
|
||||||
QQmlApplicationEngine *mEngine = nullptr;
|
QQmlApplicationEngine *mEngine = nullptr;
|
||||||
|
bool notify(QObject *receiver, QEvent *event);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void createCommandParser();
|
void createCommandParser();
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
list(APPEND _LINPHONEAPP_SOURCES
|
list(APPEND _LINPHONEAPP_SOURCES
|
||||||
core/account/Account.cpp
|
core/account/AccountCore.cpp
|
||||||
|
core/account/AccountGui.cpp
|
||||||
core/account/AccountList.cpp
|
core/account/AccountList.cpp
|
||||||
core/account/AccountProxy.cpp
|
core/account/AccountProxy.cpp
|
||||||
core/App.cpp
|
core/App.cpp
|
||||||
core/call/Call.cpp
|
core/call/CallCore.cpp
|
||||||
|
core/call/CallGui.cpp
|
||||||
core/logger/QtLogger.cpp
|
core/logger/QtLogger.cpp
|
||||||
core/login/LoginPage.cpp
|
core/login/LoginPage.cpp
|
||||||
core/notifier/Notifier.cpp
|
core/notifier/Notifier.cpp
|
||||||
|
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
|
||||||
*
|
|
||||||
* This file is part of linphone-desktop
|
|
||||||
* (see https://www.linphone.org).
|
|
||||||
*
|
|
||||||
* 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 "Account.hpp"
|
|
||||||
#include "tool/Utils.hpp"
|
|
||||||
|
|
||||||
DEFINE_ABSTRACT_OBJECT(Account)
|
|
||||||
|
|
||||||
Account::Account(const std::shared_ptr<linphone::Account> &account) : QObject(nullptr) {
|
|
||||||
// Should be call from model Thread
|
|
||||||
mustBeInLinphoneThread(getClassName());
|
|
||||||
auto address = account->getContactAddress();
|
|
||||||
mContactAddress = address ? Utils::coreStringToAppString(account->getContactAddress()->asString()) : "";
|
|
||||||
auto params = account->getParams();
|
|
||||||
auto identityAddress = params->getIdentityAddress();
|
|
||||||
mIdentityAddress = identityAddress ? Utils::coreStringToAppString(identityAddress->asString()) : "";
|
|
||||||
mPictureUri = Utils::coreStringToAppString(params->getPictureUri());
|
|
||||||
mRegistrationState = LinphoneEnums::fromLinphone(account->getState());
|
|
||||||
|
|
||||||
mAccountModel = Utils::makeQObject_ptr<AccountModel>(account); // OK
|
|
||||||
connect(mAccountModel.get(), &AccountModel::registrationStateChanged, this, &Account::onRegistrationStateChanged);
|
|
||||||
connect(this, &Account::requestSetPictureUri, mAccountModel.get(), &AccountModel::setPictureUri,
|
|
||||||
Qt::QueuedConnection);
|
|
||||||
connect(mAccountModel.get(), &AccountModel::pictureUriChanged, this, &Account::onPictureUriChanged,
|
|
||||||
Qt::QueuedConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
Account::~Account() {
|
|
||||||
mustBeInMainThread("~" + getClassName());
|
|
||||||
emit mAccountModel->removeListener();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Account::getContactAddress() const {
|
|
||||||
return mContactAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Account::getIdentityAddress() const {
|
|
||||||
return mIdentityAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Account::getPictureUri() const {
|
|
||||||
return mPictureUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
LinphoneEnums::RegistrationState Account::getRegistrationState() const {
|
|
||||||
return mRegistrationState;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Account::onRegistrationStateChanged(const std::shared_ptr<linphone::Account> &account,
|
|
||||||
linphone::RegistrationState state,
|
|
||||||
const std::string &message) {
|
|
||||||
mRegistrationState = LinphoneEnums::fromLinphone(state);
|
|
||||||
emit registrationStateChanged(Utils::coreStringToAppString(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Account::setPictureUri(const QString &uri) {
|
|
||||||
if (uri != mPictureUri) {
|
|
||||||
emit requestSetPictureUri(Utils::appStringToCoreString(uri));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Account::onPictureUriChanged(std::string uri) {
|
|
||||||
mPictureUri = Utils::coreStringToAppString(uri);
|
|
||||||
emit pictureUriChanged();
|
|
||||||
}
|
|
||||||
126
Linphone/core/account/AccountCore.cpp
Normal file
126
Linphone/core/account/AccountCore.cpp
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This file is part of linphone-desktop
|
||||||
|
* (see https://www.linphone.org).
|
||||||
|
*
|
||||||
|
* 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 "AccountCore.hpp"
|
||||||
|
#include "core/App.hpp"
|
||||||
|
#include "model/core/CoreModel.hpp"
|
||||||
|
#include "tool/Utils.hpp"
|
||||||
|
#include "tool/thread/SafeConnection.hpp"
|
||||||
|
|
||||||
|
DEFINE_ABSTRACT_OBJECT(AccountCore)
|
||||||
|
|
||||||
|
QSharedPointer<AccountCore> AccountCore::create(const std::shared_ptr<linphone::Account> &account) {
|
||||||
|
auto model = QSharedPointer<AccountCore>(new AccountCore(account), &QObject::deleteLater);
|
||||||
|
model->moveToThread(App::getInstance()->thread());
|
||||||
|
model->setSelf(model);
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountCore::AccountCore(const std::shared_ptr<linphone::Account> &account) : QObject(nullptr) {
|
||||||
|
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
||||||
|
// Should be call from model Thread
|
||||||
|
mustBeInLinphoneThread(getClassName());
|
||||||
|
// Init data
|
||||||
|
auto address = account->getContactAddress();
|
||||||
|
mContactAddress = address ? Utils::coreStringToAppString(account->getContactAddress()->asString()) : "";
|
||||||
|
auto params = account->getParams();
|
||||||
|
auto identityAddress = params->getIdentityAddress();
|
||||||
|
mIdentityAddress = identityAddress ? Utils::coreStringToAppString(identityAddress->asString()) : "";
|
||||||
|
mPictureUri = Utils::coreStringToAppString(params->getPictureUri());
|
||||||
|
mRegistrationState = LinphoneEnums::fromLinphone(account->getState());
|
||||||
|
mIsDefaultAccount = CoreModel::getInstance()->getCore()->getDefaultAccount() == account;
|
||||||
|
|
||||||
|
// Add listener
|
||||||
|
mAccountModel = Utils::makeQObject_ptr<AccountModel>(account); // OK
|
||||||
|
mAccountModel->setSelf(mAccountModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountCore::~AccountCore() {
|
||||||
|
mustBeInMainThread("~" + getClassName());
|
||||||
|
emit mAccountModel->removeListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AccountCore::setSelf(QSharedPointer<AccountCore> me) {
|
||||||
|
mAccountModelConnection = QSharedPointer<SafeConnection>(
|
||||||
|
new SafeConnection(me.objectCast<QObject>(), std::dynamic_pointer_cast<QObject>(mAccountModel)));
|
||||||
|
mAccountModelConnection->makeConnect(mAccountModel.get(), &AccountModel::registrationStateChanged,
|
||||||
|
[this](const std::shared_ptr<linphone::Account> &account,
|
||||||
|
linphone::RegistrationState state, const std::string &message) {
|
||||||
|
mAccountModelConnection->invokeToCore([this, account, state, message]() {
|
||||||
|
this->onRegistrationStateChanged(account, state, message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// From Model
|
||||||
|
mAccountModelConnection->makeConnect(
|
||||||
|
mAccountModel.get(), &AccountModel::defaultAccountChanged, [this](bool isDefault) {
|
||||||
|
mAccountModelConnection->invokeToCore([this, isDefault]() { this->onDefaultAccountChanged(isDefault); });
|
||||||
|
});
|
||||||
|
|
||||||
|
mAccountModelConnection->makeConnect(mAccountModel.get(), &AccountModel::pictureUriChanged, [this](QString uri) {
|
||||||
|
mAccountModelConnection->invokeToCore([this, uri]() { this->onPictureUriChanged(uri); });
|
||||||
|
});
|
||||||
|
|
||||||
|
// From GUI
|
||||||
|
mAccountModelConnection->makeConnect(this, &AccountCore::lSetPictureUri, [this](QString uri) {
|
||||||
|
mAccountModelConnection->invokeToModel([this, uri]() { mAccountModel->setPictureUri(uri); });
|
||||||
|
});
|
||||||
|
mAccountModelConnection->makeConnect(this, &AccountCore::lSetDefaultAccount, [this]() {
|
||||||
|
mAccountModelConnection->invokeToModel([this]() { mAccountModel->setDefault(); });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QString AccountCore::getContactAddress() const {
|
||||||
|
return mContactAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString AccountCore::getIdentityAddress() const {
|
||||||
|
return mIdentityAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString AccountCore::getPictureUri() const {
|
||||||
|
return mPictureUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
LinphoneEnums::RegistrationState AccountCore::getRegistrationState() const {
|
||||||
|
return mRegistrationState;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AccountCore::getIsDefaultAccount() const {
|
||||||
|
return mIsDefaultAccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AccountCore::onRegistrationStateChanged(const std::shared_ptr<linphone::Account> &account,
|
||||||
|
linphone::RegistrationState state,
|
||||||
|
const std::string &message) {
|
||||||
|
mRegistrationState = LinphoneEnums::fromLinphone(state);
|
||||||
|
emit registrationStateChanged(Utils::coreStringToAppString(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AccountCore::onDefaultAccountChanged(bool isDefault) {
|
||||||
|
if (mIsDefaultAccount != isDefault) {
|
||||||
|
mIsDefaultAccount = isDefault;
|
||||||
|
emit defaultAccountChanged(mIsDefaultAccount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AccountCore::onPictureUriChanged(QString uri) {
|
||||||
|
mPictureUri = uri;
|
||||||
|
emit pictureUriChanged();
|
||||||
|
}
|
||||||
|
|
@ -18,54 +18,63 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef ACCOUNT_H_
|
#ifndef ACCOUNT_CORE_H_
|
||||||
#define ACCOUNT_H_
|
#define ACCOUNT_CORE_H_
|
||||||
|
|
||||||
#include "model/account/AccountModel.hpp"
|
#include "model/account/AccountModel.hpp"
|
||||||
#include "tool/LinphoneEnums.hpp"
|
#include "tool/LinphoneEnums.hpp"
|
||||||
|
#include "tool/thread/SafeConnection.hpp"
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
#include <linphone++/linphone.hh>
|
#include <linphone++/linphone.hh>
|
||||||
|
|
||||||
class Account : public QObject, public AbstractObject {
|
class AccountCore : public QObject, public AbstractObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
Q_PROPERTY(QString contactAddress READ getContactAddress CONSTANT)
|
Q_PROPERTY(QString contactAddress READ getContactAddress CONSTANT)
|
||||||
Q_PROPERTY(QString identityAddress READ getIdentityAddress CONSTANT)
|
Q_PROPERTY(QString identityAddress READ getIdentityAddress CONSTANT)
|
||||||
Q_PROPERTY(QString pictureUri READ getPictureUri WRITE setPictureUri NOTIFY pictureUriChanged)
|
Q_PROPERTY(QString pictureUri READ getPictureUri WRITE lSetPictureUri NOTIFY pictureUriChanged)
|
||||||
Q_PROPERTY(
|
Q_PROPERTY(
|
||||||
LinphoneEnums::RegistrationState registrationState READ getRegistrationState NOTIFY registrationStateChanged)
|
LinphoneEnums::RegistrationState registrationState READ getRegistrationState NOTIFY registrationStateChanged)
|
||||||
|
Q_PROPERTY(bool isDefaultAccount READ getIsDefaultAccount NOTIFY defaultAccountChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
static QSharedPointer<AccountCore> create(const std::shared_ptr<linphone::Account> &account);
|
||||||
// Should be call from model Thread. Will be automatically in App thread after initialization
|
// Should be call from model Thread. Will be automatically in App thread after initialization
|
||||||
Account(const std::shared_ptr<linphone::Account> &account);
|
AccountCore(const std::shared_ptr<linphone::Account> &account);
|
||||||
~Account();
|
~AccountCore();
|
||||||
|
void setSelf(QSharedPointer<AccountCore> me);
|
||||||
|
|
||||||
QString getContactAddress() const;
|
QString getContactAddress() const;
|
||||||
QString getIdentityAddress() const;
|
QString getIdentityAddress() const;
|
||||||
QString getPictureUri() const;
|
QString getPictureUri() const;
|
||||||
LinphoneEnums::RegistrationState getRegistrationState() const;
|
LinphoneEnums::RegistrationState getRegistrationState() const;
|
||||||
|
bool getIsDefaultAccount() const;
|
||||||
|
|
||||||
void setPictureUri(const QString &uri);
|
void onPictureUriChanged(QString uri);
|
||||||
|
|
||||||
void onPictureUriChanged(std::string uri);
|
|
||||||
void onRegistrationStateChanged(const std::shared_ptr<linphone::Account> &account,
|
void onRegistrationStateChanged(const std::shared_ptr<linphone::Account> &account,
|
||||||
linphone::RegistrationState state,
|
linphone::RegistrationState state,
|
||||||
const std::string &message);
|
const std::string &message);
|
||||||
|
|
||||||
|
void onDefaultAccountChanged(bool isDefault);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void pictureUriChanged();
|
void pictureUriChanged();
|
||||||
void registrationStateChanged(const QString &message);
|
void registrationStateChanged(const QString &message);
|
||||||
|
void defaultAccountChanged(bool isDefault);
|
||||||
|
|
||||||
// Account requests
|
// Account requests
|
||||||
void requestSetPictureUri(std::string pictureUri);
|
void lSetPictureUri(QString pictureUri);
|
||||||
|
void lSetDefaultAccount();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString mContactAddress;
|
QString mContactAddress;
|
||||||
QString mIdentityAddress;
|
QString mIdentityAddress;
|
||||||
QString mPictureUri;
|
QString mPictureUri;
|
||||||
|
bool mIsDefaultAccount = false;
|
||||||
LinphoneEnums::RegistrationState mRegistrationState;
|
LinphoneEnums::RegistrationState mRegistrationState;
|
||||||
std::shared_ptr<AccountModel> mAccountModel;
|
std::shared_ptr<AccountModel> mAccountModel;
|
||||||
|
QSharedPointer<SafeConnection> mAccountModelConnection;
|
||||||
|
|
||||||
DECLARE_ABSTRACT_OBJECT
|
DECLARE_ABSTRACT_OBJECT
|
||||||
};
|
};
|
||||||
40
Linphone/core/account/AccountGui.cpp
Normal file
40
Linphone/core/account/AccountGui.cpp
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This file is part of linphone-desktop
|
||||||
|
* (see https://www.linphone.org).
|
||||||
|
*
|
||||||
|
* 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 "AccountGui.hpp"
|
||||||
|
#include "core/App.hpp"
|
||||||
|
|
||||||
|
DEFINE_ABSTRACT_OBJECT(AccountGui)
|
||||||
|
|
||||||
|
AccountGui::AccountGui(QSharedPointer<AccountCore> core) {
|
||||||
|
mustBeInMainThread(getClassName());
|
||||||
|
qDebug() << "[AccountGui] new" << this;
|
||||||
|
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
|
||||||
|
mCore = core;
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountGui::~AccountGui() {
|
||||||
|
mustBeInMainThread("~" + getClassName());
|
||||||
|
qDebug() << "[AccountGui] delete" << this;
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountCore *AccountGui::getCore() const {
|
||||||
|
return mCore.get();
|
||||||
|
}
|
||||||
41
Linphone/core/account/AccountGui.hpp
Normal file
41
Linphone/core/account/AccountGui.hpp
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This file is part of linphone-desktop
|
||||||
|
* (see https://www.linphone.org).
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ACCOUNT_GUI_H_
|
||||||
|
#define ACCOUNT_GUI_H_
|
||||||
|
|
||||||
|
#include "AccountCore.hpp"
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
|
class AccountGui : public QObject, public AbstractObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(AccountCore *core READ getCore CONSTANT)
|
||||||
|
|
||||||
|
public:
|
||||||
|
AccountGui(QSharedPointer<AccountCore> core);
|
||||||
|
~AccountGui();
|
||||||
|
AccountCore *getCore() const;
|
||||||
|
QSharedPointer<AccountCore> mCore;
|
||||||
|
DECLARE_ABSTRACT_OBJECT
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -19,7 +19,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "AccountList.hpp"
|
#include "AccountList.hpp"
|
||||||
#include "Account.hpp"
|
#include "AccountCore.hpp"
|
||||||
|
#include "AccountGui.hpp"
|
||||||
#include "core/App.hpp"
|
#include "core/App.hpp"
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
#include <linphone++/linphone.hh>
|
#include <linphone++/linphone.hh>
|
||||||
|
|
@ -28,26 +29,80 @@
|
||||||
|
|
||||||
DEFINE_ABSTRACT_OBJECT(AccountList)
|
DEFINE_ABSTRACT_OBJECT(AccountList)
|
||||||
|
|
||||||
|
QSharedPointer<AccountList> AccountList::create() {
|
||||||
|
auto model = QSharedPointer<AccountList>(new AccountList(), &QObject::deleteLater);
|
||||||
|
model->moveToThread(App::getInstance()->thread());
|
||||||
|
model->setSelf(model);
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
AccountList::AccountList(QObject *parent) : ListProxy(parent) {
|
AccountList::AccountList(QObject *parent) : ListProxy(parent) {
|
||||||
|
qDebug() << "[AccountList] new" << this;
|
||||||
mustBeInMainThread(getClassName());
|
mustBeInMainThread(getClassName());
|
||||||
App::postModelAsync([=]() {
|
connect(CoreModel::getInstance().get(), &CoreModel::accountAdded, this, &AccountList::lUpdate);
|
||||||
QList<QSharedPointer<Account>> accounts;
|
|
||||||
// Model thread.
|
|
||||||
mustBeInLinphoneThread(getClassName());
|
|
||||||
auto linphoneAccounts = CoreModel::getInstance()->getCore()->getAccountList();
|
|
||||||
for (auto it : linphoneAccounts) {
|
|
||||||
auto model = QSharedPointer<Account>(new Account(it), &QObject::deleteLater);
|
|
||||||
model->moveToThread(this->thread());
|
|
||||||
accounts.push_back(model);
|
|
||||||
}
|
|
||||||
// Invoke for adding stuffs in caller thread
|
|
||||||
QMetaObject::invokeMethod(this, [this, accounts]() {
|
|
||||||
mustBeInMainThread(getClassName());
|
|
||||||
add(accounts);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountList::~AccountList() {
|
AccountList::~AccountList() {
|
||||||
|
qDebug() << "[AccountList] delete" << this;
|
||||||
mustBeInMainThread("~" + getClassName());
|
mustBeInMainThread("~" + getClassName());
|
||||||
|
if (mModelConnection) mModelConnection->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AccountList::setSelf(QSharedPointer<AccountList> me) {
|
||||||
|
mModelConnection = QSharedPointer<SafeConnection>(
|
||||||
|
new SafeConnection(me.objectCast<QObject>(), std::dynamic_pointer_cast<QObject>(CoreModel::getInstance())),
|
||||||
|
&QObject::deleteLater);
|
||||||
|
mModelConnection->makeConnect(this, &AccountList::lUpdate, [this]() {
|
||||||
|
mModelConnection->invokeToModel([this]() {
|
||||||
|
QList<QSharedPointer<AccountCore>> accounts;
|
||||||
|
// Model thread.
|
||||||
|
mustBeInLinphoneThread(getClassName());
|
||||||
|
auto linphoneAccounts = CoreModel::getInstance()->getCore()->getAccountList();
|
||||||
|
for (auto it : linphoneAccounts) {
|
||||||
|
auto model = AccountCore::create(it);
|
||||||
|
accounts.push_back(model);
|
||||||
|
}
|
||||||
|
mModelConnection->invokeToCore([this, accounts]() {
|
||||||
|
mustBeInMainThread(getClassName());
|
||||||
|
clearData();
|
||||||
|
add(accounts);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
lUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountGui *AccountList::getDefaultAccount() const {
|
||||||
|
for (auto it : mList) {
|
||||||
|
auto account = it.objectCast<AccountCore>();
|
||||||
|
if (account->getIsDefaultAccount()) return new AccountGui(account);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
void AccountList::update() {
|
||||||
|
App::postModelAsync([=]() {
|
||||||
|
QList<QSharedPointer<AccountCore>> accounts;
|
||||||
|
// Model thread.
|
||||||
|
mustBeInLinphoneThread(getClassName());
|
||||||
|
auto linphoneAccounts = CoreModel::getInstance()->getCore()->getAccountList();
|
||||||
|
for (auto it : linphoneAccounts) {
|
||||||
|
auto model = AccountCore::create(it);
|
||||||
|
accounts.push_back(model);
|
||||||
|
}
|
||||||
|
// Invoke for adding stuffs in caller thread
|
||||||
|
QMetaObject::invokeMethod(this, [this, accounts]() {
|
||||||
|
mustBeInMainThread(getClassName());
|
||||||
|
clearData();
|
||||||
|
add(accounts);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
QVariant AccountList::data(const QModelIndex &index, int role) const {
|
||||||
|
int row = index.row();
|
||||||
|
if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant();
|
||||||
|
if (role == Qt::DisplayRole) return QVariant::fromValue(new AccountGui(mList[row].objectCast<AccountCore>()));
|
||||||
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,15 +23,28 @@
|
||||||
|
|
||||||
#include "../proxy/ListProxy.hpp"
|
#include "../proxy/ListProxy.hpp"
|
||||||
#include "tool/AbstractObject.hpp"
|
#include "tool/AbstractObject.hpp"
|
||||||
|
#include "tool/thread/SafeConnection.hpp"
|
||||||
#include <QLocale>
|
#include <QLocale>
|
||||||
|
|
||||||
|
class AccountGui;
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
class AccountList : public ListProxy, public AbstractObject {
|
class AccountList : public ListProxy, public AbstractObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
static QSharedPointer<AccountList> create();
|
||||||
AccountList(QObject *parent = Q_NULLPTR);
|
AccountList(QObject *parent = Q_NULLPTR);
|
||||||
~AccountList();
|
~AccountList();
|
||||||
|
|
||||||
|
void setSelf(QSharedPointer<AccountList> me);
|
||||||
|
|
||||||
|
AccountGui *getDefaultAccount() const;
|
||||||
|
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
signals:
|
||||||
|
void lUpdate();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSharedPointer<SafeConnection> mModelConnection;
|
||||||
DECLARE_ABSTRACT_OBJECT
|
DECLARE_ABSTRACT_OBJECT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,15 +19,19 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "AccountProxy.hpp"
|
#include "AccountProxy.hpp"
|
||||||
#include "Account.hpp"
|
#include "AccountGui.hpp"
|
||||||
#include "AccountList.hpp"
|
#include "AccountList.hpp"
|
||||||
|
|
||||||
AccountProxy::AccountProxy(QObject *parent) : SortFilterProxy(parent) {
|
AccountProxy::AccountProxy(QObject *parent) : SortFilterProxy(parent) {
|
||||||
setSourceModel(new AccountList(this));
|
qDebug() << "[AccountProxy] new" << this;
|
||||||
|
mList = AccountList::create();
|
||||||
|
connect(mList.get(), &AccountList::countChanged, this, &AccountProxy::defaultAccountChanged);
|
||||||
|
setSourceModel(mList.get());
|
||||||
sort(0);
|
sort(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountProxy::~AccountProxy() {
|
AccountProxy::~AccountProxy() {
|
||||||
|
qDebug() << "[AccountProxy] delete" << this;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AccountProxy::getFilterText() const {
|
QString AccountProxy::getFilterText() const {
|
||||||
|
|
@ -42,6 +46,13 @@ void AccountProxy::setFilterText(const QString &filter) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AccountGui *AccountProxy::getDefaultAccount() const {
|
||||||
|
return dynamic_cast<AccountList *>(sourceModel())->getDefaultAccount();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AccountProxy::setDefaultAccount(AccountGui *account) {
|
||||||
|
}
|
||||||
|
|
||||||
bool AccountProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
|
bool AccountProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
|
||||||
bool show = (mFilterText.isEmpty() || mFilterText == "*");
|
bool show = (mFilterText.isEmpty() || mFilterText == "*");
|
||||||
if (!show) {
|
if (!show) {
|
||||||
|
|
@ -50,8 +61,8 @@ bool AccountProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourcePare
|
||||||
QRegularExpression::UseUnicodePropertiesOption);
|
QRegularExpression::UseUnicodePropertiesOption);
|
||||||
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
|
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
|
||||||
auto model = sourceModel()->data(index);
|
auto model = sourceModel()->data(index);
|
||||||
auto account = model.value<Account *>();
|
auto account = model.value<AccountGui *>();
|
||||||
show = account->getIdentityAddress().contains(search);
|
show = account->getCore()->getIdentityAddress().contains(search);
|
||||||
}
|
}
|
||||||
|
|
||||||
return show;
|
return show;
|
||||||
|
|
@ -61,5 +72,6 @@ bool AccountProxy::lessThan(const QModelIndex &left, const QModelIndex &right) c
|
||||||
auto l = sourceModel()->data(left);
|
auto l = sourceModel()->data(left);
|
||||||
auto r = sourceModel()->data(right);
|
auto r = sourceModel()->data(right);
|
||||||
|
|
||||||
return l.value<Account *>()->getIdentityAddress() < r.value<Account *>()->getIdentityAddress();
|
return l.value<AccountGui *>()->getCore()->getIdentityAddress() <
|
||||||
|
r.value<AccountGui *>()->getCore()->getIdentityAddress();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@
|
||||||
#define ACCOUNT_PROXY_H_
|
#define ACCOUNT_PROXY_H_
|
||||||
|
|
||||||
#include "../proxy/SortFilterProxy.hpp"
|
#include "../proxy/SortFilterProxy.hpp"
|
||||||
|
#include "core/account/AccountGui.hpp"
|
||||||
|
#include "core/account/AccountList.hpp"
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
|
|
@ -29,6 +31,7 @@ class AccountProxy : public SortFilterProxy {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
Q_PROPERTY(QString filterText READ getFilterText WRITE setFilterText NOTIFY filterTextChanged)
|
Q_PROPERTY(QString filterText READ getFilterText WRITE setFilterText NOTIFY filterTextChanged)
|
||||||
|
Q_PROPERTY(AccountGui *defaultAccount READ getDefaultAccount WRITE setDefaultAccount NOTIFY defaultAccountChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AccountProxy(QObject *parent = Q_NULLPTR);
|
AccountProxy(QObject *parent = Q_NULLPTR);
|
||||||
|
|
@ -37,14 +40,19 @@ public:
|
||||||
QString getFilterText() const;
|
QString getFilterText() const;
|
||||||
void setFilterText(const QString &filter);
|
void setFilterText(const QString &filter);
|
||||||
|
|
||||||
|
AccountGui *getDefaultAccount() const;
|
||||||
|
void setDefaultAccount(AccountGui *account);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void filterTextChanged();
|
void filterTextChanged();
|
||||||
|
void defaultAccountChanged();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||||
virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
|
virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
|
||||||
|
|
||||||
QString mFilterText;
|
QString mFilterText;
|
||||||
|
QSharedPointer<AccountList> mList;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
|
||||||
*
|
|
||||||
* This file is part of linphone-desktop
|
|
||||||
* (see https://www.linphone.org).
|
|
||||||
*
|
|
||||||
* 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 "Call.hpp"
|
|
||||||
#include "core/App.hpp"
|
|
||||||
#include "tool/Utils.hpp"
|
|
||||||
|
|
||||||
DEFINE_ABSTRACT_OBJECT(Call)
|
|
||||||
|
|
||||||
Call::Call(const std::shared_ptr<linphone::Call> &call) : QObject(nullptr) {
|
|
||||||
// Should be call from model Thread
|
|
||||||
mustBeInLinphoneThread(getClassName());
|
|
||||||
mCallModel = Utils::makeQObject_ptr<CallModel>(call);
|
|
||||||
connect(mCallModel.get(), &CallModel::stateChanged, this, &Call::onStateChanged);
|
|
||||||
connect(this, &Call::lAccept, mCallModel.get(), &CallModel::accept);
|
|
||||||
connect(this, &Call::lDecline, mCallModel.get(), &CallModel::decline);
|
|
||||||
connect(this, &Call::lTerminate, mCallModel.get(), &CallModel::terminate);
|
|
||||||
mCallModel->setSelf(mCallModel);
|
|
||||||
mState = LinphoneEnums::fromLinphone(call->getState());
|
|
||||||
}
|
|
||||||
|
|
||||||
Call::~Call() {
|
|
||||||
mustBeInMainThread("~" + getClassName());
|
|
||||||
emit mCallModel->removeListener();
|
|
||||||
}
|
|
||||||
|
|
||||||
LinphoneEnums::CallStatus Call::getStatus() const {
|
|
||||||
return mStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Call::setStatus(LinphoneEnums::CallStatus status) {
|
|
||||||
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
|
||||||
if (mStatus != status) {
|
|
||||||
mStatus = status;
|
|
||||||
emit statusChanged(mStatus);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LinphoneEnums::CallState Call::getState() const {
|
|
||||||
return mState;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Call::setState(LinphoneEnums::CallState state, const QString &message) {
|
|
||||||
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
|
||||||
if (mState != state) {
|
|
||||||
mState = state;
|
|
||||||
if (state == LinphoneEnums::CallState::Error) setLastErrorMessage(message);
|
|
||||||
emit stateChanged(mState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Call::onStateChanged(linphone::Call::State state, const std::string &message) {
|
|
||||||
setState(LinphoneEnums::fromLinphone(state), Utils::coreStringToAppString(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Call::getLastErrorMessage() const {
|
|
||||||
return mLastErrorMessage;
|
|
||||||
}
|
|
||||||
void Call::setLastErrorMessage(const QString &message) {
|
|
||||||
if (mLastErrorMessage != message) {
|
|
||||||
mLastErrorMessage = message;
|
|
||||||
emit lastErrorMessageChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
132
Linphone/core/call/CallCore.cpp
Normal file
132
Linphone/core/call/CallCore.cpp
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This file is part of linphone-desktop
|
||||||
|
* (see https://www.linphone.org).
|
||||||
|
*
|
||||||
|
* 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 "CallCore.hpp"
|
||||||
|
#include "core/App.hpp"
|
||||||
|
#include "model/object/VariantObject.hpp"
|
||||||
|
#include "tool/Utils.hpp"
|
||||||
|
#include "tool/thread/SafeConnection.hpp"
|
||||||
|
|
||||||
|
DEFINE_ABSTRACT_OBJECT(CallCore)
|
||||||
|
|
||||||
|
QSharedPointer<CallCore> CallCore::create(const std::shared_ptr<linphone::Call> &call) {
|
||||||
|
auto sharedPointer = QSharedPointer<CallCore>(new CallCore(call), &QObject::deleteLater);
|
||||||
|
sharedPointer->setSelf(sharedPointer);
|
||||||
|
sharedPointer->moveToThread(App::getInstance()->thread());
|
||||||
|
return sharedPointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
CallCore::CallCore(const std::shared_ptr<linphone::Call> &call) : QObject(nullptr) {
|
||||||
|
qDebug() << "[CallCore] new" << this;
|
||||||
|
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
||||||
|
// Should be call from model Thread
|
||||||
|
mustBeInLinphoneThread(getClassName());
|
||||||
|
mDuration = call->getDuration();
|
||||||
|
mMicrophoneMuted = call->getMicrophoneMuted();
|
||||||
|
mCallModel = Utils::makeQObject_ptr<CallModel>(call);
|
||||||
|
connect(mCallModel.get(), &CallModel::stateChanged, this, &CallCore::onStateChanged);
|
||||||
|
connect(this, &CallCore::lAccept, mCallModel.get(), &CallModel::accept);
|
||||||
|
connect(this, &CallCore::lDecline, mCallModel.get(), &CallModel::decline);
|
||||||
|
connect(this, &CallCore::lTerminate, mCallModel.get(), &CallModel::terminate);
|
||||||
|
mCallModel->setSelf(mCallModel);
|
||||||
|
mState = LinphoneEnums::fromLinphone(call->getState());
|
||||||
|
}
|
||||||
|
|
||||||
|
CallCore::~CallCore() {
|
||||||
|
qDebug() << "[CallCore] delete" << this;
|
||||||
|
mustBeInMainThread("~" + getClassName());
|
||||||
|
emit mCallModel->removeListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallCore::setSelf(QSharedPointer<CallCore> me) {
|
||||||
|
mAccountModelConnection = QSharedPointer<SafeConnection>(
|
||||||
|
new SafeConnection(me.objectCast<QObject>(), std::dynamic_pointer_cast<QObject>(mCallModel)),
|
||||||
|
&QObject::deleteLater);
|
||||||
|
mAccountModelConnection->makeConnect(this, &CallCore::lSetMicrophoneMuted, [this](bool isMuted) {
|
||||||
|
mAccountModelConnection->invokeToModel([this, isMuted]() { mCallModel->setMicrophoneMuted(isMuted); });
|
||||||
|
});
|
||||||
|
mAccountModelConnection->makeConnect(mCallModel.get(), &CallModel::microphoneMutedChanged, [this](bool isMuted) {
|
||||||
|
mAccountModelConnection->invokeToCore([this, isMuted]() { setMicrophoneMuted(isMuted); });
|
||||||
|
});
|
||||||
|
mAccountModelConnection->makeConnect(mCallModel.get(), &CallModel::durationChanged, [this](int duration) {
|
||||||
|
mAccountModelConnection->invokeToCore([this, duration]() { setDuration(duration); });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
LinphoneEnums::CallStatus CallCore::getStatus() const {
|
||||||
|
return mStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallCore::setStatus(LinphoneEnums::CallStatus status) {
|
||||||
|
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
||||||
|
if (mStatus != status) {
|
||||||
|
mStatus = status;
|
||||||
|
emit statusChanged(mStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LinphoneEnums::CallState CallCore::getState() const {
|
||||||
|
return mState;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallCore::setState(LinphoneEnums::CallState state, const QString &message) {
|
||||||
|
mustBeInMainThread(log().arg(Q_FUNC_INFO));
|
||||||
|
if (mState != state) {
|
||||||
|
mState = state;
|
||||||
|
if (state == LinphoneEnums::CallState::Error) setLastErrorMessage(message);
|
||||||
|
emit stateChanged(mState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallCore::onStateChanged(linphone::Call::State state, const std::string &message) {
|
||||||
|
setState(LinphoneEnums::fromLinphone(state), Utils::coreStringToAppString(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CallCore::getLastErrorMessage() const {
|
||||||
|
return mLastErrorMessage;
|
||||||
|
}
|
||||||
|
void CallCore::setLastErrorMessage(const QString &message) {
|
||||||
|
if (mLastErrorMessage != message) {
|
||||||
|
mLastErrorMessage = message;
|
||||||
|
emit lastErrorMessageChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int CallCore::getDuration() {
|
||||||
|
return mDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallCore::setDuration(int duration) {
|
||||||
|
if (mDuration != duration) {
|
||||||
|
mDuration = duration;
|
||||||
|
emit durationChanged(mDuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CallCore::getMicrophoneMuted() const {
|
||||||
|
return mMicrophoneMuted;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallCore::setMicrophoneMuted(bool isMuted) {
|
||||||
|
if (mMicrophoneMuted != isMuted) {
|
||||||
|
mMicrophoneMuted = isMuted;
|
||||||
|
emit microphoneMutedChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,8 +18,8 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef CALL_H_
|
#ifndef CALL_CORE_H_
|
||||||
#define CALL_H_
|
#define CALL_CORE_H_
|
||||||
|
|
||||||
#include "model/call/CallModel.hpp"
|
#include "model/call/CallModel.hpp"
|
||||||
#include "tool/LinphoneEnums.hpp"
|
#include "tool/LinphoneEnums.hpp"
|
||||||
|
|
@ -27,17 +27,23 @@
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
#include <linphone++/linphone.hh>
|
#include <linphone++/linphone.hh>
|
||||||
|
|
||||||
class Call : public QObject, public AbstractObject {
|
class SafeConnection;
|
||||||
|
|
||||||
|
class CallCore : public QObject, public AbstractObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
Q_PROPERTY(LinphoneEnums::CallStatus status READ getStatus NOTIFY statusChanged)
|
Q_PROPERTY(LinphoneEnums::CallStatus status READ getStatus NOTIFY statusChanged)
|
||||||
Q_PROPERTY(LinphoneEnums::CallState state READ getState NOTIFY stateChanged)
|
Q_PROPERTY(LinphoneEnums::CallState state READ getState NOTIFY stateChanged)
|
||||||
Q_PROPERTY(QString lastErrorMessage READ getLastErrorMessage NOTIFY lastErrorMessageChanged)
|
Q_PROPERTY(QString lastErrorMessage READ getLastErrorMessage NOTIFY lastErrorMessageChanged)
|
||||||
|
Q_PROPERTY(int duration READ getDuration NOTIFY durationChanged);
|
||||||
|
Q_PROPERTY(bool microphoneMuted READ getMicrophoneMuted WRITE lSetMicrophoneMuted NOTIFY microphoneMutedChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Should be call from model Thread. Will be automatically in App thread after initialization
|
// Should be call from model Thread. Will be automatically in App thread after initialization
|
||||||
Call(const std::shared_ptr<linphone::Call> &call);
|
static QSharedPointer<CallCore> create(const std::shared_ptr<linphone::Call> &call);
|
||||||
~Call();
|
CallCore(const std::shared_ptr<linphone::Call> &call);
|
||||||
|
~CallCore();
|
||||||
|
void setSelf(QSharedPointer<CallCore> me);
|
||||||
|
|
||||||
LinphoneEnums::CallStatus getStatus() const;
|
LinphoneEnums::CallStatus getStatus() const;
|
||||||
void setStatus(LinphoneEnums::CallStatus status);
|
void setStatus(LinphoneEnums::CallStatus status);
|
||||||
|
|
@ -49,43 +55,56 @@ public:
|
||||||
QString getLastErrorMessage() const;
|
QString getLastErrorMessage() const;
|
||||||
void setLastErrorMessage(const QString &message);
|
void setLastErrorMessage(const QString &message);
|
||||||
|
|
||||||
|
int getDuration();
|
||||||
|
void setDuration(int duration);
|
||||||
|
|
||||||
|
bool getMicrophoneMuted() const;
|
||||||
|
void setMicrophoneMuted(bool isMuted);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void statusChanged(LinphoneEnums::CallStatus status);
|
void statusChanged(LinphoneEnums::CallStatus status);
|
||||||
void stateChanged(LinphoneEnums::CallState state);
|
void stateChanged(LinphoneEnums::CallState state);
|
||||||
void lastErrorMessageChanged();
|
void lastErrorMessageChanged();
|
||||||
|
void durationChanged(int duration);
|
||||||
|
void microphoneMutedChanged();
|
||||||
|
|
||||||
// Linphone commands
|
// Linphone commands
|
||||||
void lAccept(bool withVideo); // Accept an incoming call
|
void lAccept(bool withVideo); // Accept an incoming call
|
||||||
void lDecline(); // Decline an incoming call
|
void lDecline(); // Decline an incoming call
|
||||||
void lTerminate(); // Hangup a call
|
void lTerminate(); // Hangup a call
|
||||||
/* TODO
|
void lSetMicrophoneMuted(bool isMuted);
|
||||||
Q_INVOKABLE void acceptWithVideo();
|
|
||||||
|
|
||||||
Q_INVOKABLE void askForTransfer();
|
/* TODO
|
||||||
Q_INVOKABLE void askForAttendedTransfer();
|
Q_INVOKABLE void acceptWithVideo();
|
||||||
Q_INVOKABLE bool transferTo(const QString &sipAddress);
|
|
||||||
Q_INVOKABLE bool transferToAnother(const QString &peerAddress);
|
|
||||||
|
|
||||||
Q_INVOKABLE bool getRemoteVideoEnabled() const;
|
Q_INVOKABLE void askForTransfer();
|
||||||
Q_INVOKABLE void acceptVideoRequest();
|
Q_INVOKABLE void askForAttendedTransfer();
|
||||||
Q_INVOKABLE void rejectVideoRequest();
|
Q_INVOKABLE bool transferTo(const QString &sipAddress);
|
||||||
|
Q_INVOKABLE bool transferToAnother(const QString &peerAddress);
|
||||||
|
|
||||||
Q_INVOKABLE void takeSnapshot();
|
Q_INVOKABLE bool getRemoteVideoEnabled() const;
|
||||||
Q_INVOKABLE void startRecording();
|
Q_INVOKABLE void acceptVideoRequest();
|
||||||
Q_INVOKABLE void stopRecording();
|
Q_INVOKABLE void rejectVideoRequest();
|
||||||
|
|
||||||
Q_INVOKABLE void sendDtmf(const QString &dtmf);
|
Q_INVOKABLE void takeSnapshot();
|
||||||
Q_INVOKABLE void verifyAuthenticationToken(bool verify);
|
Q_INVOKABLE void startRecording();
|
||||||
Q_INVOKABLE void updateStreams();
|
Q_INVOKABLE void stopRecording();
|
||||||
Q_INVOKABLE void toggleSpeakerMute();
|
|
||||||
*/
|
Q_INVOKABLE void sendDtmf(const QString &dtmf);
|
||||||
|
Q_INVOKABLE void verifyAuthenticationToken(bool verify);
|
||||||
|
Q_INVOKABLE void updateStreams();
|
||||||
|
Q_INVOKABLE void toggleSpeakerMute();
|
||||||
|
*/
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<CallModel> mCallModel;
|
std::shared_ptr<CallModel> mCallModel;
|
||||||
LinphoneEnums::CallStatus mStatus;
|
LinphoneEnums::CallStatus mStatus;
|
||||||
LinphoneEnums::CallState mState;
|
LinphoneEnums::CallState mState;
|
||||||
QString mLastErrorMessage;
|
QString mLastErrorMessage;
|
||||||
|
int mDuration = 0;
|
||||||
|
bool mMicrophoneMuted;
|
||||||
|
QSharedPointer<SafeConnection> mAccountModelConnection;
|
||||||
|
|
||||||
DECLARE_ABSTRACT_OBJECT
|
DECLARE_ABSTRACT_OBJECT
|
||||||
};
|
};
|
||||||
Q_DECLARE_METATYPE(Call *)
|
Q_DECLARE_METATYPE(CallCore *)
|
||||||
#endif
|
#endif
|
||||||
40
Linphone/core/call/CallGui.cpp
Normal file
40
Linphone/core/call/CallGui.cpp
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This file is part of linphone-desktop
|
||||||
|
* (see https://www.linphone.org).
|
||||||
|
*
|
||||||
|
* 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 "CallGui.hpp"
|
||||||
|
#include "core/App.hpp"
|
||||||
|
|
||||||
|
DEFINE_ABSTRACT_OBJECT(CallGui)
|
||||||
|
|
||||||
|
CallGui::CallGui(QSharedPointer<CallCore> core) {
|
||||||
|
mustBeInMainThread(getClassName());
|
||||||
|
qDebug() << "[CallGui] new" << this;
|
||||||
|
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
|
||||||
|
mCore = core;
|
||||||
|
}
|
||||||
|
|
||||||
|
CallGui::~CallGui() {
|
||||||
|
mustBeInMainThread("~" + getClassName());
|
||||||
|
qDebug() << "[CallGui] delete" << this;
|
||||||
|
}
|
||||||
|
|
||||||
|
CallCore *CallGui::getCore() const {
|
||||||
|
return mCore.get();
|
||||||
|
}
|
||||||
41
Linphone/core/call/CallGui.hpp
Normal file
41
Linphone/core/call/CallGui.hpp
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This file is part of linphone-desktop
|
||||||
|
* (see https://www.linphone.org).
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CALL_GUI_H_
|
||||||
|
#define CALL_GUI_H_
|
||||||
|
|
||||||
|
#include "CallCore.hpp"
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
|
class CallGui : public QObject, public AbstractObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(CallCore *core READ getCore CONSTANT)
|
||||||
|
|
||||||
|
public:
|
||||||
|
CallGui(QSharedPointer<CallCore> core);
|
||||||
|
~CallGui();
|
||||||
|
CallCore *getCore() const;
|
||||||
|
QSharedPointer<CallCore> mCore;
|
||||||
|
DECLARE_ABSTRACT_OBJECT
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
#include "Notifier.hpp"
|
#include "Notifier.hpp"
|
||||||
|
|
||||||
#include "core/App.hpp"
|
#include "core/App.hpp"
|
||||||
#include "core/call/Call.hpp"
|
#include "core/call/CallGui.hpp"
|
||||||
#include "tool/LinphoneEnums.hpp"
|
#include "tool/LinphoneEnums.hpp"
|
||||||
#include "tool/providers/ImageProvider.hpp"
|
#include "tool/providers/ImageProvider.hpp"
|
||||||
|
|
||||||
|
|
@ -274,20 +274,23 @@ void Notifier::deleteNotification(QVariant notification) {
|
||||||
|
|
||||||
void Notifier::notifyReceivedCall(const shared_ptr<linphone::Call> &call) {
|
void Notifier::notifyReceivedCall(const shared_ptr<linphone::Call> &call) {
|
||||||
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||||
auto model = new Call(call);
|
auto model = CallCore::create(call);
|
||||||
model->moveToThread(this->thread());
|
auto gui = new CallGui(model);
|
||||||
App::postCoreAsync([this, model]() {
|
gui->moveToThread(App::getInstance()->thread());
|
||||||
|
App::postCoreAsync([this, gui]() {
|
||||||
mustBeInMainThread(getClassName());
|
mustBeInMainThread(getClassName());
|
||||||
QVariantMap map;
|
QVariantMap map;
|
||||||
map["call"].setValue(model);
|
|
||||||
|
map["call"].setValue(gui);
|
||||||
CREATE_NOTIFICATION(Notifier::ReceivedCall, map)
|
CREATE_NOTIFICATION(Notifier::ReceivedCall, map)
|
||||||
|
|
||||||
QObject::connect(
|
QObject::connect(
|
||||||
model, &Call::statusChanged, notification, [this, notification](LinphoneEnums::CallStatus status) {
|
gui->getCore(), &CallCore::statusChanged, notification,
|
||||||
|
[this, notification](LinphoneEnums::CallStatus status) {
|
||||||
qInfo() << log().arg("Delete notification on call status : %1").arg(LinphoneEnums::toString(status));
|
qInfo() << log().arg("Delete notification on call status : %1").arg(LinphoneEnums::toString(status));
|
||||||
deleteNotification(QVariant::fromValue(notification));
|
deleteNotification(QVariant::fromValue(notification));
|
||||||
});
|
});
|
||||||
QObject::connect(model, &Call::destroyed, notification,
|
QObject::connect(gui->getCore(), &CallCore::destroyed, notification,
|
||||||
[this, notification]() { deleteNotification(QVariant::fromValue(notification)); });
|
[this, notification]() { deleteNotification(QVariant::fromValue(notification)); });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,10 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "core/call/Call.hpp"
|
|
||||||
#include "tool/AbstractObject.hpp"
|
#include "tool/AbstractObject.hpp"
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <linphone++/linphone.hh>
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
class QMutex;
|
class QMutex;
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ list(APPEND _LINPHONEAPP_SOURCES
|
||||||
model/logger/LoggerModel.cpp
|
model/logger/LoggerModel.cpp
|
||||||
model/logger/LoggerListener.cpp
|
model/logger/LoggerListener.cpp
|
||||||
|
|
||||||
|
model/object/SafeObject.cpp
|
||||||
model/object/VariantObject.cpp
|
model/object/VariantObject.cpp
|
||||||
|
|
||||||
model/setting/SettingsModel.cpp
|
model/setting/SettingsModel.cpp
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,7 @@ bool AccountManager::login(QString username, QString password) {
|
||||||
connect(mAccountModel.get(), &AccountModel::registrationStateChanged, this,
|
connect(mAccountModel.get(), &AccountModel::registrationStateChanged, this,
|
||||||
&AccountManager::onRegistrationStateChanged);
|
&AccountManager::onRegistrationStateChanged);
|
||||||
core->addAccount(account);
|
core->addAccount(account);
|
||||||
|
emit CoreModel::getInstance()->accountAdded();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,11 +22,16 @@
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "model/core/CoreModel.hpp"
|
||||||
|
#include "tool/Utils.hpp"
|
||||||
|
|
||||||
DEFINE_ABSTRACT_OBJECT(AccountModel)
|
DEFINE_ABSTRACT_OBJECT(AccountModel)
|
||||||
|
|
||||||
AccountModel::AccountModel(const std::shared_ptr<linphone::Account> &account, QObject *parent)
|
AccountModel::AccountModel(const std::shared_ptr<linphone::Account> &account, QObject *parent)
|
||||||
: ::Listener<linphone::Account, linphone::AccountListener>(account, parent) {
|
: ::Listener<linphone::Account, linphone::AccountListener>(account, parent) {
|
||||||
mustBeInLinphoneThread(getClassName());
|
mustBeInLinphoneThread(getClassName());
|
||||||
|
connect(CoreModel::getInstance().get(), &CoreModel::defaultAccountChanged, this,
|
||||||
|
&AccountModel::onDefaultAccountChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountModel::~AccountModel() {
|
AccountModel::~AccountModel() {
|
||||||
|
|
@ -39,11 +44,22 @@ void AccountModel::onRegistrationStateChanged(const std::shared_ptr<linphone::Ac
|
||||||
emit registrationStateChanged(account, state, message);
|
emit registrationStateChanged(account, state, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AccountModel::setPictureUri(std::string uri) {
|
void AccountModel::setPictureUri(QString uri) {
|
||||||
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||||
auto account = std::dynamic_pointer_cast<linphone::Account>(mMonitor);
|
auto account = std::dynamic_pointer_cast<linphone::Account>(mMonitor);
|
||||||
auto params = account->getParams()->clone();
|
auto params = account->getParams()->clone();
|
||||||
params->setPictureUri(uri);
|
params->setPictureUri(Utils::appStringToCoreString(uri));
|
||||||
account->setParams(params);
|
account->setParams(params);
|
||||||
emit pictureUriChanged(uri);
|
emit pictureUriChanged(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AccountModel::onDefaultAccountChanged() {
|
||||||
|
emit defaultAccountChanged(CoreModel::getInstance()->getCore()->getDefaultAccount() == mMonitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AccountModel::setDefault() {
|
||||||
|
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||||
|
auto core = CoreModel::getInstance()->getCore();
|
||||||
|
core->setDefaultAccount(mMonitor);
|
||||||
|
emit CoreModel::getInstance()->defaultAccountChanged();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,15 +38,18 @@ public:
|
||||||
virtual void onRegistrationStateChanged(const std::shared_ptr<linphone::Account> &account,
|
virtual void onRegistrationStateChanged(const std::shared_ptr<linphone::Account> &account,
|
||||||
linphone::RegistrationState state,
|
linphone::RegistrationState state,
|
||||||
const std::string &message) override;
|
const std::string &message) override;
|
||||||
|
void onDefaultAccountChanged();
|
||||||
|
|
||||||
void setPictureUri(std::string uri);
|
void setPictureUri(QString uri);
|
||||||
|
void setDefault();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void registrationStateChanged(const std::shared_ptr<linphone::Account> &account,
|
void registrationStateChanged(const std::shared_ptr<linphone::Account> &account,
|
||||||
linphone::RegistrationState state,
|
linphone::RegistrationState state,
|
||||||
const std::string &message);
|
const std::string &message);
|
||||||
|
void defaultAccountChanged(bool isDefault);
|
||||||
|
|
||||||
void pictureUriChanged(std::string uri);
|
void pictureUriChanged(QString uri);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DECLARE_ABSTRACT_OBJECT
|
DECLARE_ABSTRACT_OBJECT
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,10 @@ DEFINE_ABSTRACT_OBJECT(CallModel)
|
||||||
CallModel::CallModel(const std::shared_ptr<linphone::Call> &call, QObject *parent)
|
CallModel::CallModel(const std::shared_ptr<linphone::Call> &call, QObject *parent)
|
||||||
: ::Listener<linphone::Call, linphone::CallListener>(call, parent) {
|
: ::Listener<linphone::Call, linphone::CallListener>(call, parent) {
|
||||||
mustBeInLinphoneThread(getClassName());
|
mustBeInLinphoneThread(getClassName());
|
||||||
|
mDurationTimer.setInterval(1000);
|
||||||
|
mDurationTimer.setSingleShot(false);
|
||||||
|
connect(&mDurationTimer, &QTimer::timeout, this, [this]() { this->durationChanged(mMonitor->getDuration()); });
|
||||||
|
mDurationTimer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
CallModel::~CallModel() {
|
CallModel::~CallModel() {
|
||||||
|
|
@ -65,6 +69,11 @@ void CallModel::terminate() {
|
||||||
mMonitor->terminate();
|
mMonitor->terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CallModel::setMicrophoneMuted(bool isMuted) {
|
||||||
|
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||||
|
mMonitor->setMicrophoneMuted(isMuted);
|
||||||
|
emit microphoneMutedChanged(isMuted);
|
||||||
|
}
|
||||||
void CallModel::onDtmfReceived(const std::shared_ptr<linphone::Call> &call, int dtmf) {
|
void CallModel::onDtmfReceived(const std::shared_ptr<linphone::Call> &call, int dtmf) {
|
||||||
emit dtmfReceived(call, dtmf);
|
emit dtmfReceived(call, dtmf);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
#include "tool/AbstractObject.hpp"
|
#include "tool/AbstractObject.hpp"
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QTimer>
|
||||||
#include <linphone++/linphone.hh>
|
#include <linphone++/linphone.hh>
|
||||||
|
|
||||||
class CallModel : public ::Listener<linphone::Call, linphone::CallListener>,
|
class CallModel : public ::Listener<linphone::Call, linphone::CallListener>,
|
||||||
|
|
@ -39,7 +40,14 @@ public:
|
||||||
void decline();
|
void decline();
|
||||||
void terminate();
|
void terminate();
|
||||||
|
|
||||||
|
void setMicrophoneMuted(bool isMuted);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void microphoneMutedChanged(bool isMuted);
|
||||||
|
void durationChanged(int);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QTimer mDurationTimer;
|
||||||
DECLARE_ABSTRACT_OBJECT
|
DECLARE_ABSTRACT_OBJECT
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,10 @@ void CoreModel::setPathAfterStart() {
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void CoreModel::onAccountAdded() {
|
||||||
|
emit accountAdded();
|
||||||
|
}
|
||||||
|
|
||||||
void CoreModel::onAccountRegistrationStateChanged(const std::shared_ptr<linphone::Core> &core,
|
void CoreModel::onAccountRegistrationStateChanged(const std::shared_ptr<linphone::Core> &core,
|
||||||
const std::shared_ptr<linphone::Account> &account,
|
const std::shared_ptr<linphone::Account> &account,
|
||||||
linphone::RegistrationState state,
|
linphone::RegistrationState state,
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ public:
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void loggerInitialized();
|
void loggerInitialized();
|
||||||
|
void defaultAccountChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString mConfigPath;
|
QString mConfigPath;
|
||||||
|
|
@ -71,6 +72,7 @@ private:
|
||||||
//--------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------
|
||||||
// LINPHONE
|
// LINPHONE
|
||||||
//--------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------
|
||||||
|
virtual void onAccountAdded(); // override (Not yet implemented by SDK)
|
||||||
virtual void onAccountRegistrationStateChanged(const std::shared_ptr<linphone::Core> &core,
|
virtual void onAccountRegistrationStateChanged(const std::shared_ptr<linphone::Core> &core,
|
||||||
const std::shared_ptr<linphone::Account> &account,
|
const std::shared_ptr<linphone::Account> &account,
|
||||||
linphone::RegistrationState state,
|
linphone::RegistrationState state,
|
||||||
|
|
@ -152,6 +154,7 @@ private:
|
||||||
const std::string &url) override;
|
const std::string &url) override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
void accountAdded();
|
||||||
void accountRegistrationStateChanged(const std::shared_ptr<linphone::Core> &core,
|
void accountRegistrationStateChanged(const std::shared_ptr<linphone::Core> &core,
|
||||||
const std::shared_ptr<linphone::Account> &account,
|
const std::shared_ptr<linphone::Account> &account,
|
||||||
linphone::RegistrationState state,
|
linphone::RegistrationState state,
|
||||||
|
|
|
||||||
46
Linphone/model/object/SafeObject.cpp
Normal file
46
Linphone/model/object/SafeObject.cpp
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This file is part of linphone-desktop
|
||||||
|
* (see https://www.linphone.org).
|
||||||
|
*
|
||||||
|
* 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 "SafeObject.hpp"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QTest>
|
||||||
|
|
||||||
|
#include "core/App.hpp"
|
||||||
|
|
||||||
|
DEFINE_ABSTRACT_OBJECT(SafeObject)
|
||||||
|
|
||||||
|
SafeObject::SafeObject(QObject *parent) {
|
||||||
|
}
|
||||||
|
SafeObject::SafeObject(QVariant defaultValue, QObject *parent) : mValue(defaultValue) {
|
||||||
|
}
|
||||||
|
SafeObject::~SafeObject() {
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant SafeObject::getValue() const {
|
||||||
|
return mValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SafeObject::onSetValue(QVariant value) {
|
||||||
|
if (value != mValue) {
|
||||||
|
mValue = value;
|
||||||
|
emit valueChanged(mValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
49
Linphone/model/object/SafeObject.hpp
Normal file
49
Linphone/model/object/SafeObject.hpp
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This file is part of linphone-desktop
|
||||||
|
* (see https://www.linphone.org).
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SAFE_OBJECT_H_
|
||||||
|
#define SAFE_OBJECT_H_
|
||||||
|
|
||||||
|
#include "tool/AbstractObject.hpp"
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
class SafeObject : public QObject, public AbstractObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
SafeObject(QObject *parent = nullptr);
|
||||||
|
SafeObject(QVariant defaultValue, QObject *parent = nullptr);
|
||||||
|
~SafeObject();
|
||||||
|
|
||||||
|
QVariant getValue() const;
|
||||||
|
void onSetValue(QVariant value);
|
||||||
|
signals:
|
||||||
|
void requestValue();
|
||||||
|
void setValue(QVariant value);
|
||||||
|
void valueChanged(QVariant value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVariant mValue;
|
||||||
|
|
||||||
|
DECLARE_ABSTRACT_OBJECT
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -26,39 +26,29 @@
|
||||||
#include "core/App.hpp"
|
#include "core/App.hpp"
|
||||||
|
|
||||||
DEFINE_ABSTRACT_OBJECT(VariantObject)
|
DEFINE_ABSTRACT_OBJECT(VariantObject)
|
||||||
|
VariantObject::VariantObject(QObject *parent) : VariantObject(QVariant()) {
|
||||||
VariantObject::VariantObject(QObject *parent) {
|
|
||||||
mThreadLocation = false;
|
|
||||||
mustBeInMainThread(getClassName());
|
|
||||||
mCoreObject = nullptr;
|
|
||||||
}
|
}
|
||||||
|
VariantObject::VariantObject(QVariant defaultValue, QObject *parent) {
|
||||||
|
mCoreObject = QSharedPointer<SafeObject>::create(defaultValue);
|
||||||
|
mModelObject = QSharedPointer<SafeObject>::create();
|
||||||
|
mModelObject->moveToThread(CoreModel::getInstance()->thread());
|
||||||
|
|
||||||
VariantObject::VariantObject(QVariant value, QObject *parent) : mValue(value) {
|
mConnection = QSharedPointer<SafeConnection>(
|
||||||
mThreadLocation = true;
|
new SafeConnection(mCoreObject.objectCast<QObject>(), mModelObject.objectCast<QObject>()),
|
||||||
mustBeInMainThread(getClassName());
|
&QObject::deleteLater);
|
||||||
connect(this, &VariantObject::valueUpdated, this, &VariantObject::setValue);
|
|
||||||
mCoreObject = new VariantObject(nullptr);
|
mConnection->makeConnect(mCoreObject.get(), &SafeObject::setValue, &SafeObject::onSetValue);
|
||||||
mCoreObject->moveToThread(CoreModel::getInstance()->thread());
|
mConnection->makeConnect(mModelObject.get(), &SafeObject::setValue, &SafeObject::onSetValue);
|
||||||
connect(mCoreObject, &VariantObject::valueChanged, this, &VariantObject::setValue);
|
connect(mCoreObject.get(), &SafeObject::valueChanged, this, &VariantObject::valueChanged);
|
||||||
connect(mCoreObject, &VariantObject::valueChanged, mCoreObject, &QObject::deleteLater);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VariantObject::~VariantObject() {
|
VariantObject::~VariantObject() {
|
||||||
if (mThreadLocation) mustBeInMainThread("~" + getClassName());
|
|
||||||
else mustBeInLinphoneThread("~" + getClassName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant VariantObject::getValue() const {
|
QVariant VariantObject::getValue() const {
|
||||||
if (mThreadLocation) mustBeInMainThread(QString(gClassName) + " : " + Q_FUNC_INFO);
|
return mCoreObject->getValue();
|
||||||
else mustBeInLinphoneThread(QString(gClassName) + " : " + Q_FUNC_INFO);
|
|
||||||
return mValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VariantObject::setValue(QVariant value) {
|
void VariantObject::requestValue() {
|
||||||
if (mThreadLocation) mustBeInMainThread(QString(gClassName) + " : " + Q_FUNC_INFO);
|
emit mCoreObject->requestValue();
|
||||||
else mustBeInLinphoneThread(QString(gClassName) + " : " + Q_FUNC_INFO);
|
|
||||||
if (value != mValue) {
|
|
||||||
mValue = value;
|
|
||||||
emit valueChanged(mValue);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,35 +24,41 @@
|
||||||
#include "tool/AbstractObject.hpp"
|
#include "tool/AbstractObject.hpp"
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QSharedPointer>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
// Store the VariantObject on a propery and use value.
|
#include "SafeObject.hpp"
|
||||||
// Do not use direcly teh value like VariantObject.value : in this case if value change, VariantObject will be
|
#include "tool/thread/SafeConnection.hpp"
|
||||||
// reevaluated.
|
|
||||||
|
|
||||||
class VariantObject : public QObject, public AbstractObject {
|
class VariantObject : public QObject, public AbstractObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(QVariant value READ getValue NOTIFY valueChanged)
|
||||||
public:
|
public:
|
||||||
Q_PROPERTY(QVariant value READ getValue WRITE setValue NOTIFY valueChanged)
|
|
||||||
|
|
||||||
VariantObject(QObject *parent = nullptr);
|
VariantObject(QObject *parent = nullptr);
|
||||||
VariantObject(QVariant value, QObject *parent = nullptr);
|
VariantObject(QVariant defaultValue, QObject *parent = nullptr);
|
||||||
~VariantObject();
|
~VariantObject();
|
||||||
|
|
||||||
|
template <typename Func, typename... Args>
|
||||||
|
void makeRequest(Func &&callable, Args &&...args) {
|
||||||
|
mConnection->makeConnect(mCoreObject.get(), &SafeObject::requestValue, [this, callable, args...]() {
|
||||||
|
mConnection->invokeToModel([this, callable, args...]() { mModelObject->setValue(callable(args...)); });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
template <typename SenderClass>
|
||||||
|
void makeUpdate(const typename QtPrivate::FunctionPointer<SenderClass>::Object *sender, SenderClass signal) {
|
||||||
|
mConnection->makeConnect(sender, signal,
|
||||||
|
[this]() { mConnection->invokeToCore([this]() { mCoreObject->requestValue(); }); });
|
||||||
|
}
|
||||||
|
|
||||||
QVariant getValue() const;
|
QVariant getValue() const;
|
||||||
void setValue(QVariant value);
|
void requestValue();
|
||||||
|
|
||||||
// mCoreObject must be used to request update value : this object will be not be deleted by GUI so it is safe to use
|
|
||||||
// inside model thread. call emit updateValue() from coreObject to set value from model.
|
|
||||||
VariantObject *mCoreObject; // Ensure to use DeleteLater() after updating value
|
|
||||||
|
|
||||||
|
QSharedPointer<SafeObject> mCoreObject, mModelObject;
|
||||||
|
QSharedPointer<SafeConnection> mConnection;
|
||||||
signals:
|
signals:
|
||||||
void valueChanged(QVariant value);
|
void valueChanged(QVariant value);
|
||||||
void valueUpdated(QVariant value);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVariant mValue;
|
|
||||||
bool mThreadLocation = true; // true=Core, false=Model
|
|
||||||
DECLARE_ABSTRACT_OBJECT
|
DECLARE_ABSTRACT_OBJECT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,9 +68,9 @@ QString ToolModel::getDisplayName(QString address) {
|
||||||
return displayName.isEmpty() ? address : displayName;
|
return displayName.isEmpty() ? address : displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
Call *ToolModel::startAudioCall(const QString &sipAddress,
|
QSharedPointer<CallCore> ToolModel::startAudioCall(const QString &sipAddress,
|
||||||
const QString &prepareTransfertAddress,
|
const QString &prepareTransfertAddress,
|
||||||
const QHash<QString, QString> &headers) {
|
const QHash<QString, QString> &headers) {
|
||||||
bool waitRegistrationForCall = true; // getSettingsModel()->getWaitRegistrationForCall()
|
bool waitRegistrationForCall = true; // getSettingsModel()->getWaitRegistrationForCall()
|
||||||
std::shared_ptr<linphone::Core> core = CoreModel::getInstance()->getCore();
|
std::shared_ptr<linphone::Core> core = CoreModel::getInstance()->getCore();
|
||||||
|
|
||||||
|
|
@ -93,7 +93,7 @@ Call *ToolModel::startAudioCall(const QString &sipAddress,
|
||||||
if (core->getDefaultAccount()) params->setAccount(core->getDefaultAccount());
|
if (core->getDefaultAccount()) params->setAccount(core->getDefaultAccount());
|
||||||
// CallModel::setRecordFile(params, Utils::coreStringToAppString(address->getUsername()));
|
// CallModel::setRecordFile(params, Utils::coreStringToAppString(address->getUsername()));
|
||||||
auto call = core->inviteAddressWithParams(address, params);
|
auto call = core->inviteAddressWithParams(address, params);
|
||||||
return call ? new Call(call) : nullptr;
|
return call ? CallCore::create(call) : nullptr;
|
||||||
|
|
||||||
/* TODO transfer
|
/* TODO transfer
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
#ifndef TOOL_MODEL_H_
|
#ifndef TOOL_MODEL_H_
|
||||||
#define TOOL_MODEL_H_
|
#define TOOL_MODEL_H_
|
||||||
|
|
||||||
#include "core/call/Call.hpp"
|
#include "core/call/CallCore.hpp"
|
||||||
#include "tool/AbstractObject.hpp"
|
#include "tool/AbstractObject.hpp"
|
||||||
|
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
|
@ -39,9 +39,9 @@ public:
|
||||||
static QString getDisplayName(const std::shared_ptr<const linphone::Address> &address);
|
static QString getDisplayName(const std::shared_ptr<const linphone::Address> &address);
|
||||||
static QString getDisplayName(QString address);
|
static QString getDisplayName(QString address);
|
||||||
|
|
||||||
static Call *startAudioCall(const QString &sipAddress,
|
static QSharedPointer<CallCore> startAudioCall(const QString &sipAddress,
|
||||||
const QString &prepareTransfertAddress = "",
|
const QString &prepareTransfertAddress = "",
|
||||||
const QHash<QString, QString> &headers = {});
|
const QHash<QString, QString> &headers = {});
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DECLARE_ABSTRACT_OBJECT
|
DECLARE_ABSTRACT_OBJECT
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ list(APPEND _LINPHONEAPP_SOURCES
|
||||||
tool/Utils.cpp
|
tool/Utils.cpp
|
||||||
|
|
||||||
tool/LinphoneEnums.cpp
|
tool/LinphoneEnums.cpp
|
||||||
|
tool/thread/SafeSharedPointer.hpp
|
||||||
|
tool/thread/SafeConnection.cpp
|
||||||
tool/thread/Thread.cpp
|
tool/thread/Thread.cpp
|
||||||
tool/providers/ImageProvider.cpp
|
tool/providers/ImageProvider.cpp
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
|
|
||||||
#include "core/App.hpp"
|
#include "core/App.hpp"
|
||||||
#include "model/call/CallModel.hpp"
|
#include "core/call/CallGui.hpp"
|
||||||
#include "model/object/VariantObject.hpp"
|
#include "model/object/VariantObject.hpp"
|
||||||
#include "model/tool/ToolModel.hpp"
|
#include "model/tool/ToolModel.hpp"
|
||||||
|
|
||||||
|
|
@ -42,10 +42,11 @@ char *Utils::rstrstr(const char *a, const char *b) {
|
||||||
|
|
||||||
VariantObject *Utils::getDisplayName(const QString &address) {
|
VariantObject *Utils::getDisplayName(const QString &address) {
|
||||||
VariantObject *data = new VariantObject(address); // Scope : GUI
|
VariantObject *data = new VariantObject(address); // Scope : GUI
|
||||||
App::postModelAsync([coreObject = data->mCoreObject, address]() mutable {
|
data->makeRequest([address]() {
|
||||||
QString displayName = ToolModel::getDisplayName(address);
|
QString displayName = ToolModel::getDisplayName(address);
|
||||||
coreObject->setValue(displayName);
|
return displayName;
|
||||||
});
|
});
|
||||||
|
data->requestValue();
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,16 +54,27 @@ VariantObject *Utils::startAudioCall(const QString &sipAddress,
|
||||||
const QString &prepareTransfertAddress,
|
const QString &prepareTransfertAddress,
|
||||||
const QHash<QString, QString> &headers) {
|
const QHash<QString, QString> &headers) {
|
||||||
VariantObject *data = new VariantObject(QVariant()); // Scope : GUI
|
VariantObject *data = new VariantObject(QVariant()); // Scope : GUI
|
||||||
qDebug() << "Calling " << sipAddress;
|
|
||||||
App::postModelAsync([coreObject = data->mCoreObject, sipAddress, prepareTransfertAddress, headers]() mutable {
|
data->makeRequest([sipAddress, prepareTransfertAddress, headers]() {
|
||||||
auto call = ToolModel::startAudioCall(sipAddress, prepareTransfertAddress, headers);
|
auto call = ToolModel::startAudioCall(sipAddress, prepareTransfertAddress, headers);
|
||||||
if (call && coreObject) {
|
if (call) {
|
||||||
call->moveToThread(App::getInstance()->thread());
|
return QVariant::fromValue(new CallGui(call));
|
||||||
coreObject->setValue(QVariant::fromValue(call));
|
} else return QVariant();
|
||||||
// App::postCoreAsync([data, call]() { data->setValue(QVariant::fromValue(call)); });
|
|
||||||
// emit coreObject->valueChanged(call);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
data->requestValue();
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VariantObject *Utils::haveAccount() {
|
||||||
|
VariantObject *result = new VariantObject();
|
||||||
|
|
||||||
|
// Using connect ensure to have sender() and receiver() alive.
|
||||||
|
result->makeRequest([]() {
|
||||||
|
// Model
|
||||||
|
return CoreModel::getInstance()->getCore()->getAccountList().size() > 0;
|
||||||
|
});
|
||||||
|
result->makeUpdate(CoreModel::getInstance().get(), &CoreModel::accountAdded);
|
||||||
|
result->requestValue();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ public:
|
||||||
Q_INVOKABLE static VariantObject *startAudioCall(const QString &sipAddress,
|
Q_INVOKABLE static VariantObject *startAudioCall(const QString &sipAddress,
|
||||||
const QString &prepareTransfertAddress = "",
|
const QString &prepareTransfertAddress = "",
|
||||||
const QHash<QString, QString> &headers = {});
|
const QHash<QString, QString> &headers = {});
|
||||||
|
Q_INVOKABLE static VariantObject *haveAccount();
|
||||||
|
|
||||||
static inline QString coreStringToAppString(const std::string &str) {
|
static inline QString coreStringToAppString(const std::string &str) {
|
||||||
if (Constants::LinphoneLocaleEncoding == QString("UTF-8")) return QString::fromStdString(str);
|
if (Constants::LinphoneLocaleEncoding == QString("UTF-8")) return QString::fromStdString(str);
|
||||||
|
|
|
||||||
29
Linphone/tool/thread/SafeConnection.cpp
Normal file
29
Linphone/tool/thread/SafeConnection.cpp
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This file is part of linphone-desktop
|
||||||
|
* (see https://www.linphone.org).
|
||||||
|
*
|
||||||
|
* 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 "SafeConnection.hpp"
|
||||||
|
|
||||||
|
SafeConnection::SafeConnection(SafeSharedPointer<QObject> core, SafeSharedPointer<QObject> model)
|
||||||
|
: mCore(core), mModel(model) {
|
||||||
|
}
|
||||||
|
SafeConnection::~SafeConnection() {
|
||||||
|
if (mCore.mCountRef != 0 || mModel.mCountRef != 0)
|
||||||
|
qCritical() << "[SafeConnection] Destruction while still having references";
|
||||||
|
}
|
||||||
117
Linphone/tool/thread/SafeConnection.hpp
Normal file
117
Linphone/tool/thread/SafeConnection.hpp
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This file is part of linphone-desktop
|
||||||
|
* (see https://www.linphone.org).
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SAFE_CONNECTION_H_
|
||||||
|
#define SAFE_CONNECTION_H_
|
||||||
|
|
||||||
|
#include "SafeSharedPointer.hpp"
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
// Use this class to protect sender/receiver from being deleted while running a
|
||||||
|
// signal/call
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ObjectGui : mainAcces for GUI. Its memory is managed by JavaScript. It contains a QSharedPointer of ObjectCore.
|
||||||
|
* ObjectCore : memory is CPP managed by QSharedPointer.
|
||||||
|
* ObjectModel: memory is managed by shared pointers and is running on Model thread.
|
||||||
|
*
|
||||||
|
* => ObjectGUI have QSharedPointer<ObjectCore>
|
||||||
|
* ObjectCore have std::shared_ptr<ObjectModel> and SafeConnection.
|
||||||
|
*
|
||||||
|
* Need:
|
||||||
|
* - static QSharedPointer<ObjectCore> create(Args...args); => It set self and moveToThread.
|
||||||
|
* - In GUI constructor : App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
|
||||||
|
* - In Core/model : App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
||||||
|
* - void setSelf(QSharedPointer<ObjectCore> me); => instantiate SafeConnection with :
|
||||||
|
* me.objectCast<QObject>(), std::dynamic_pointer_cast<QObject>(<Model_stored_in_ObjectCore>));
|
||||||
|
*
|
||||||
|
* Set connections in setSelf:
|
||||||
|
* - From model
|
||||||
|
* mSafeConnection->makeConnect( mModel.get()
|
||||||
|
* , &ObjectModel::signal, [this](params) {
|
||||||
|
* mSafeConnection->invokeToCore([this, params]() { this->slot(params); });
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
*- From GUI
|
||||||
|
* mSafeConnection->makeConnect(this, &ObjectCore::lSignal, [this](params) {
|
||||||
|
* mSafeConnection->invokeToModel([this, params]() { mModel->slot(params); });
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* - Direct call can be call with only invokeToModel/invokeToCore
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
class SafeConnection : public QObject {
|
||||||
|
public:
|
||||||
|
SafeConnection(SafeSharedPointer<QObject> a, SafeSharedPointer<QObject> b);
|
||||||
|
~SafeConnection();
|
||||||
|
SafeSharedPointer<QObject> mCore, mModel;
|
||||||
|
QMutex mLocker;
|
||||||
|
|
||||||
|
template <typename Func1, typename Func2>
|
||||||
|
static inline QMetaObject::Connection makeConnect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender,
|
||||||
|
Func1 signal,
|
||||||
|
Func2 slot,
|
||||||
|
Qt::ConnectionType type = Qt::AutoConnection) {
|
||||||
|
return connect(sender, signal, sender, slot, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Func, typename... Args>
|
||||||
|
void invokeToModel(Func &&callable, Args &&...args) {
|
||||||
|
if (!tryLock()) return;
|
||||||
|
auto model = mModel.get();
|
||||||
|
QMetaObject::invokeMethod(model, [&, model, callable, args...]() { // Is async
|
||||||
|
QMetaObject::invokeMethod(model, callable, args...); // Is Sync
|
||||||
|
unlock();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Will running call in Core
|
||||||
|
template <typename Func, typename... Args>
|
||||||
|
void invokeToCore(Func &&callable, Args &&...args) {
|
||||||
|
if (!tryLock()) return;
|
||||||
|
QMetaObject::invokeMethod(mCore.get(), [&, callable, args...]() { // Is async
|
||||||
|
QMetaObject::invokeMethod(mCore.get(), callable, args...); // Is Sync
|
||||||
|
unlock();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tryLock() {
|
||||||
|
mLocker.lock();
|
||||||
|
if (!mCore.lock() || !mModel.lock()) { // Direct locking
|
||||||
|
mCore.reset();
|
||||||
|
mModel.reset();
|
||||||
|
mLocker.unlock();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mLocker.unlock();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
void unlock() {
|
||||||
|
mLocker.lock();
|
||||||
|
mCore.unlock();
|
||||||
|
mModel.unlock();
|
||||||
|
mLocker.unlock();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
78
Linphone/tool/thread/SafeSharedPointer.hpp
Normal file
78
Linphone/tool/thread/SafeSharedPointer.hpp
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2024 Belledonne Communications SARL.
|
||||||
|
*
|
||||||
|
* This file is part of linphone-desktop
|
||||||
|
* (see https://www.linphone.org).
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SAFE_SHARED_POINTER_H_
|
||||||
|
#define SAFE_SHARED_POINTER_H_
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
|
// Store a Qt/Std shared pointer
|
||||||
|
template <typename A>
|
||||||
|
class SafeSharedPointer {
|
||||||
|
public:
|
||||||
|
QSharedPointer<A> mQData;
|
||||||
|
QWeakPointer<A> mQDataWeak;
|
||||||
|
std::shared_ptr<A> mStdData;
|
||||||
|
std::weak_ptr<A> mStdDataWeak;
|
||||||
|
int mCountRef = 0;
|
||||||
|
|
||||||
|
SafeSharedPointer(QSharedPointer<A> p) : mQDataWeak(p) {
|
||||||
|
}
|
||||||
|
SafeSharedPointer(std::shared_ptr<A> p) : mStdDataWeak(p) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lock() {
|
||||||
|
if (mCountRef == 0) {
|
||||||
|
if (!mQDataWeak.isNull()) {
|
||||||
|
mQData = mQDataWeak.lock();
|
||||||
|
if (mQData) ++mCountRef;
|
||||||
|
return !mQData.isNull();
|
||||||
|
} else if (!mStdDataWeak.expired()) {
|
||||||
|
mStdData = mStdDataWeak.lock();
|
||||||
|
if (mStdData) ++mCountRef;
|
||||||
|
return mStdData != nullptr;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
++mCountRef;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void unlock() {
|
||||||
|
if (mCountRef == 0) qWarning() << "[SafeConnection] too much unlocking";
|
||||||
|
else if (--mCountRef == 0) {
|
||||||
|
mQData = nullptr;
|
||||||
|
mStdData = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void reset() {
|
||||||
|
mQData = nullptr;
|
||||||
|
mStdData = nullptr;
|
||||||
|
mCountRef = 0;
|
||||||
|
}
|
||||||
|
A *get() {
|
||||||
|
if (mQData) return mQData.get();
|
||||||
|
if (mStdData) return mStdData.get();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -84,12 +84,5 @@ Window {
|
||||||
MainLayout {
|
MainLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
|
||||||
id: ongoingCallPage
|
|
||||||
OngoingCallPage {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ list(APPEND _LINPHONEAPP_QML_FILES
|
||||||
view/Prototype/PhoneNumberPrototype.qml
|
view/Prototype/PhoneNumberPrototype.qml
|
||||||
view/Prototype/AccountsPrototype.qml
|
view/Prototype/AccountsPrototype.qml
|
||||||
view/Prototype/CallPrototype.qml
|
view/Prototype/CallPrototype.qml
|
||||||
|
view/Prototype/ItemPrototype.qml
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND _LINPHONEAPP_QML_SINGLETONS
|
list(APPEND _LINPHONEAPP_QML_SINGLETONS
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ Notification {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
readonly property var call: notificationData && notificationData.call
|
readonly property var call: notificationData && notificationData.call
|
||||||
property var state: call.state
|
property var state: call.core.state
|
||||||
onStateChanged:{
|
onStateChanged:{
|
||||||
if(state != LinphoneEnums.CallState.IncomingReceived){
|
if(state != LinphoneEnums.CallState.IncomingReceived){
|
||||||
close()
|
close()
|
||||||
|
|
@ -35,7 +35,7 @@ Notification {
|
||||||
text: 'Accept'
|
text: 'Accept'
|
||||||
Layout.rightMargin: 20
|
Layout.rightMargin: 20
|
||||||
onClicked: {
|
onClicked: {
|
||||||
notification.call.lAccept()
|
notification.call.core.lAccept(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Item{
|
Item{
|
||||||
|
|
@ -46,7 +46,7 @@ Notification {
|
||||||
text: 'Reject'
|
text: 'Reject'
|
||||||
Layout.rightMargin: 20
|
Layout.rightMargin: 20
|
||||||
onClicked: {
|
onClicked: {
|
||||||
notification.call.lDecline()
|
notification.call.core.lDecline()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,25 +12,79 @@ Window{
|
||||||
onWidthChanged: console.log(width)
|
onWidthChanged: console.log(width)
|
||||||
property var callVarObject
|
property var callVarObject
|
||||||
property var call: callVarObject ? callVarObject.value : null
|
property var call: callVarObject ? callVarObject.value : null
|
||||||
property var callState: call && call.state
|
property var callState: call && call.core.state
|
||||||
onCallStateChanged: console.log("State:" +callState)
|
onCallStateChanged: {
|
||||||
|
console.log("State:" +callState)
|
||||||
|
if(callState == LinphoneEnums.CallState.Released)
|
||||||
|
callVarObject = undefined
|
||||||
|
}
|
||||||
visible: true
|
visible: true
|
||||||
onCallChanged: console.log('New Call:' +call)
|
onCallChanged: console.log('New Call:' +call)
|
||||||
|
onClosing: {
|
||||||
|
accountStatus.defaultAccount = accountStatus
|
||||||
|
accountLayout.accounts = null
|
||||||
|
gc()
|
||||||
|
}
|
||||||
|
Component.onDestruction: gc()
|
||||||
ColumnLayout{
|
ColumnLayout{
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
id: accountLayout
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
LoginForm{
|
property AccountProxy accounts: AccountProxy{id: accountProxy}
|
||||||
|
property var haveAccountVar: UtilsCpp.haveAccount()
|
||||||
|
property var haveAccount: haveAccountVar ? haveAccountVar.value : false
|
||||||
|
onHaveAccountChanged: {
|
||||||
|
console.log("HaveAccount: " +haveAccount)
|
||||||
|
logStack.replace(haveAccount ? accountListComponent : loginComponent)
|
||||||
}
|
}
|
||||||
|
Control.StackView{
|
||||||
|
id: logStack
|
||||||
|
Layout.preferredHeight: 250
|
||||||
|
Layout.preferredWidth: 250
|
||||||
|
//initialItem: loginComponent
|
||||||
|
}
|
||||||
|
Component{
|
||||||
|
id: accountListComponent
|
||||||
|
ListView{
|
||||||
|
id: accountList
|
||||||
|
Layout.fillHeight: true
|
||||||
|
model: AccountProxy{}
|
||||||
|
delegate:Rectangle{
|
||||||
|
color: "#11111111"
|
||||||
|
height: 20
|
||||||
|
width: accountList.width
|
||||||
|
Text{
|
||||||
|
|
||||||
|
text: modelData.core.identityAddress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Component{
|
||||||
|
id: loginComponent
|
||||||
|
LoginForm{}
|
||||||
|
}
|
||||||
Rectangle{
|
Rectangle{
|
||||||
|
id: accountStatus
|
||||||
Layout.preferredWidth: 50
|
Layout.preferredWidth: 50
|
||||||
Layout.preferredHeight: 50
|
Layout.preferredHeight: 50
|
||||||
|
property var defaultAccount: accountProxy.defaultAccount
|
||||||
|
property var state: accountProxy.count > 0 && defaultAccount? defaultAccount.registrationState : LoginPageCpp.registrationState
|
||||||
|
onStateChanged: console.log("State:"+state)
|
||||||
|
|
||||||
color: LoginPageCpp.registrationState === LinphoneEnums.RegistrationState.Ok
|
color: state === LinphoneEnums.RegistrationState.Ok
|
||||||
? 'green'
|
? 'green'
|
||||||
: LoginPageCpp.registrationState === LinphoneEnums.RegistrationState.Failed || LoginPageCpp.registrationState === LinphoneEnums.RegistrationState.None
|
: state === LinphoneEnums.RegistrationState.Failed || state === LinphoneEnums.RegistrationState.None
|
||||||
? 'red'
|
? 'red'
|
||||||
: 'orange'
|
: 'orange'
|
||||||
|
MouseArea{
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked:{
|
||||||
|
logStack.replace(loginComponent)
|
||||||
|
gc()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
TextInput {
|
TextInput {
|
||||||
id: usernameToCall
|
id: usernameToCall
|
||||||
|
|
@ -41,6 +95,7 @@ Window{
|
||||||
text: 'Call'
|
text: 'Call'
|
||||||
onClicked: {
|
onClicked: {
|
||||||
mainItem.callVarObject = UtilsCpp.startAudioCall(usernameToCall.inputText + "@sip.linphone.org")
|
mainItem.callVarObject = UtilsCpp.startAudioCall(usernameToCall.inputText + "@sip.linphone.org")
|
||||||
|
proto.component1 = comp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -49,9 +104,9 @@ Window{
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: 50
|
Layout.preferredHeight: 50
|
||||||
color: call
|
color: call
|
||||||
? call.state === LinphoneEnums.CallState.StreamsRunning
|
? call.core.state === LinphoneEnums.CallState.StreamsRunning
|
||||||
? 'green'
|
? 'green'
|
||||||
: call.state === LinphoneEnums.CallState.Released
|
: call.core.state === LinphoneEnums.CallState.Released
|
||||||
? 'pink'
|
? 'pink'
|
||||||
: 'orange'
|
: 'orange'
|
||||||
: 'red'
|
: 'red'
|
||||||
|
|
@ -68,13 +123,26 @@ Window{
|
||||||
}
|
}
|
||||||
Text{
|
Text{
|
||||||
id: errorMessageText
|
id: errorMessageText
|
||||||
text: mainItem.call ? mainItem.call.lastErrorMessage : ''
|
text: mainItem.call ? mainItem.call.core.lastErrorMessage : ''
|
||||||
color: 'red'
|
color: 'red'
|
||||||
}
|
}
|
||||||
|
ItemPrototype{
|
||||||
|
id: proto
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
Item{
|
Item{
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Component{
|
||||||
|
id: comp
|
||||||
|
Rectangle{
|
||||||
|
width: 100
|
||||||
|
height: width
|
||||||
|
color: 'pink'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
54
Linphone/view/Prototype/ItemPrototype.qml
Normal file
54
Linphone/view/Prototype/ItemPrototype.qml
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Layouts 1.0
|
||||||
|
import QtQuick.Controls as Control
|
||||||
|
import Linphone
|
||||||
|
import UtilsCpp 1.0
|
||||||
|
|
||||||
|
Rectangle{
|
||||||
|
id: mainItem
|
||||||
|
property Component component1: comp1
|
||||||
|
property Component component2: comp2
|
||||||
|
property bool step: false
|
||||||
|
color: 'black'
|
||||||
|
onStepChanged: {
|
||||||
|
stack.replace(step ? component1 : component2)
|
||||||
|
}
|
||||||
|
Timer{
|
||||||
|
id: delay
|
||||||
|
interval: 1000
|
||||||
|
onTriggered: mainItem.step = !mainItem.step
|
||||||
|
repeat: true
|
||||||
|
running: true
|
||||||
|
}
|
||||||
|
Control.StackView{
|
||||||
|
id: stack
|
||||||
|
anchors.fill: parent
|
||||||
|
/*
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: parent.width/2
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: parent.width/2
|
||||||
|
anchors.bottom: parent.bottom*/
|
||||||
|
initialItem : Rectangle{width: 100
|
||||||
|
height: width
|
||||||
|
color: 'orange'}
|
||||||
|
}
|
||||||
|
Component{
|
||||||
|
id: comp1
|
||||||
|
Rectangle{
|
||||||
|
width: 100
|
||||||
|
height: width
|
||||||
|
color: 'red'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component{
|
||||||
|
id: comp2
|
||||||
|
Rectangle{
|
||||||
|
width: 100
|
||||||
|
height: width
|
||||||
|
color: 'green'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue