From a43430fa340a98c910e5b22d6a3cbc6e3501a0e3 Mon Sep 17 00:00:00 2001 From: Julien Wadel Date: Wed, 6 Dec 2023 10:07:08 +0100 Subject: [PATCH] Fix crashes on SafeConnection. Display missed notifications and fit text size from content. Add account from profile menu and display Login Page with back button. Default account selection and change avatar from accounts list. Remove deprecated function on Friends DB. Return username by default on display name computation. Update SDK. --- Linphone/core/account/AccountCore.cpp | 52 ++++++++++++++------- Linphone/core/account/AccountCore.hpp | 8 +++- Linphone/core/account/AccountGui.cpp | 2 - Linphone/core/account/AccountList.cpp | 40 ++++++++++------ Linphone/core/account/AccountList.hpp | 5 +- Linphone/core/account/AccountProxy.cpp | 5 +- Linphone/core/call/CallCore.cpp | 54 +++++++++++---------- Linphone/core/call/CallCore.hpp | 5 +- Linphone/core/friend/FriendCore.cpp | 57 ++++++++++++----------- Linphone/core/friend/FriendCore.hpp | 10 ++-- Linphone/core/path/Paths.cpp | 8 ---- Linphone/core/path/Paths.hpp | 1 - Linphone/core/search/MagicSearchList.cpp | 49 ++++++++++--------- Linphone/core/search/MagicSearchList.hpp | 5 +- Linphone/model/account/AccountModel.cpp | 9 ++++ Linphone/model/account/AccountModel.hpp | 2 + Linphone/model/core/CoreModel.cpp | 21 +++++---- Linphone/model/core/CoreModel.hpp | 1 + Linphone/model/listener/Listener.hpp | 1 - Linphone/model/object/VariantObject.cpp | 11 ++--- Linphone/model/object/VariantObject.hpp | 14 +++--- Linphone/tool/CMakeLists.txt | 2 +- Linphone/tool/Constants.cpp | 1 - Linphone/tool/Constants.hpp | 1 - Linphone/tool/Utils.cpp | 4 +- Linphone/tool/thread/SafeConnection.cpp | 32 ------------- Linphone/tool/thread/SafeConnection.hpp | 42 +++++++++++++---- Linphone/view/App/Layout/MainLayout.qml | 3 ++ Linphone/view/App/Main.qml | 5 +- Linphone/view/Item/Account/Accounts.qml | 28 +++++++++-- Linphone/view/Item/Contact/Avatar.qml | 4 +- Linphone/view/Item/Contact/Contact.qml | 34 +++++++------- Linphone/view/Page/Login/LoginPage.qml | 25 ++++++++-- Linphone/view/Page/Login/SIPLoginPage.qml | 4 +- external/linphone-sdk | 2 +- 35 files changed, 313 insertions(+), 234 deletions(-) delete mode 100644 Linphone/tool/thread/SafeConnection.cpp diff --git a/Linphone/core/account/AccountCore.cpp b/Linphone/core/account/AccountCore.cpp index f27d92543..7345b9c81 100644 --- a/Linphone/core/account/AccountCore.cpp +++ b/Linphone/core/account/AccountCore.cpp @@ -46,6 +46,8 @@ AccountCore::AccountCore(const std::shared_ptr &account) : QO mPictureUri = Utils::coreStringToAppString(params->getPictureUri()); mRegistrationState = LinphoneEnums::fromLinphone(account->getState()); mIsDefaultAccount = CoreModel::getInstance()->getCore()->getDefaultAccount() == account; + // mUnreadNotifications = account->getUnreadChatMessageCount() + account->getMissedCallsCount(); // TODO + mUnreadNotifications = account->getMissedCallsCount(); // Add listener mAccountModel = Utils::makeQObject_ptr(account); // OK @@ -58,30 +60,34 @@ AccountCore::~AccountCore() { } void AccountCore::setSelf(QSharedPointer me) { - mAccountModelConnection = QSharedPointer( - new SafeConnection(me.objectCast(), std::dynamic_pointer_cast(mAccountModel))); - mAccountModelConnection->makeConnect(mAccountModel.get(), &AccountModel::registrationStateChanged, - [this](const std::shared_ptr &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 = QSharedPointer>( + new SafeConnection(me, mAccountModel)); + mAccountModelConnection->makeConnectToModel( + &AccountModel::registrationStateChanged, [this](const std::shared_ptr &account, + linphone::RegistrationState state, const std::string &message) { + mAccountModelConnection->invokeToCore( + [this, account, state, message]() { this->onRegistrationStateChanged(account, state, message); }); }); + // From Model + mAccountModelConnection->makeConnectToModel(&AccountModel::defaultAccountChanged, [this](bool isDefault) { + mAccountModelConnection->invokeToCore([this, isDefault]() { this->onDefaultAccountChanged(isDefault); }); + }); - mAccountModelConnection->makeConnect(mAccountModel.get(), &AccountModel::pictureUriChanged, [this](QString uri) { + mAccountModelConnection->makeConnectToModel(&AccountModel::pictureUriChanged, [this](QString uri) { mAccountModelConnection->invokeToCore([this, uri]() { this->onPictureUriChanged(uri); }); }); + mAccountModelConnection->makeConnectToModel( + &AccountModel::unreadNotificationsChanged, [this](int unreadMessagesCount, int unreadCallsCount) { + mAccountModelConnection->invokeToCore([this, unreadMessagesCount, unreadCallsCount]() { + this->setUnreadNotifications(unreadMessagesCount + unreadCallsCount); + }); + }); // From GUI - mAccountModelConnection->makeConnect(this, &AccountCore::lSetPictureUri, [this](QString uri) { + mAccountModelConnection->makeConnectToCore(&AccountCore::lSetPictureUri, [this](QString uri) { mAccountModelConnection->invokeToModel([this, uri]() { mAccountModel->setPictureUri(uri); }); }); - mAccountModelConnection->makeConnect(this, &AccountCore::lSetDefaultAccount, [this]() { + mAccountModelConnection->makeConnectToCore(&AccountCore::lSetDefaultAccount, [this]() { mAccountModelConnection->invokeToModel([this]() { mAccountModel->setDefault(); }); }); } @@ -106,6 +112,16 @@ bool AccountCore::getIsDefaultAccount() const { return mIsDefaultAccount; } +int AccountCore::getUnreadNotifications() const { + return mUnreadNotifications; +} +void AccountCore::setUnreadNotifications(int unread) { + if (mUnreadNotifications != unread) { + mUnreadNotifications = unread; + emit unreadNotificationsChanged(unread); + } +} + void AccountCore::onRegistrationStateChanged(const std::shared_ptr &account, linphone::RegistrationState state, const std::string &message) { @@ -124,3 +140,7 @@ void AccountCore::onPictureUriChanged(QString uri) { mPictureUri = uri; emit pictureUriChanged(); } + +void AccountCore::removeAccount() { + mAccountModelConnection->invokeToModel([this]() { mAccountModel->removeAccount(); }); +} diff --git a/Linphone/core/account/AccountCore.hpp b/Linphone/core/account/AccountCore.hpp index fa1de1184..50e17c72e 100644 --- a/Linphone/core/account/AccountCore.hpp +++ b/Linphone/core/account/AccountCore.hpp @@ -37,6 +37,7 @@ class AccountCore : public QObject, public AbstractObject { Q_PROPERTY( LinphoneEnums::RegistrationState registrationState READ getRegistrationState NOTIFY registrationStateChanged) Q_PROPERTY(bool isDefaultAccount READ getIsDefaultAccount NOTIFY defaultAccountChanged) + Q_PROPERTY(int unreadNotifications READ getUnreadNotifications NOTIFY unreadNotificationsChanged) public: static QSharedPointer create(const std::shared_ptr &account); @@ -50,6 +51,8 @@ public: QString getPictureUri() const; LinphoneEnums::RegistrationState getRegistrationState() const; bool getIsDefaultAccount() const; + int getUnreadNotifications() const; + void setUnreadNotifications(int unread); void onPictureUriChanged(QString uri); void onRegistrationStateChanged(const std::shared_ptr &account, @@ -57,11 +60,13 @@ public: const std::string &message); void onDefaultAccountChanged(bool isDefault); + Q_INVOKABLE void removeAccount(); signals: void pictureUriChanged(); void registrationStateChanged(const QString &message); void defaultAccountChanged(bool isDefault); + void unreadNotificationsChanged(int unreadNotifications); // Account requests void lSetPictureUri(QString pictureUri); @@ -73,8 +78,9 @@ private: QString mPictureUri; bool mIsDefaultAccount = false; LinphoneEnums::RegistrationState mRegistrationState; + int mUnreadNotifications = 0; std::shared_ptr mAccountModel; - QSharedPointer mAccountModelConnection; + QSharedPointer> mAccountModelConnection; DECLARE_ABSTRACT_OBJECT }; diff --git a/Linphone/core/account/AccountGui.cpp b/Linphone/core/account/AccountGui.cpp index 7ec4c92fa..52b64d1fb 100644 --- a/Linphone/core/account/AccountGui.cpp +++ b/Linphone/core/account/AccountGui.cpp @@ -24,7 +24,6 @@ DEFINE_ABSTRACT_OBJECT(AccountGui) AccountGui::AccountGui(QSharedPointer core) { - qDebug() << "[AccountGui] new" << this; App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); mCore = core; if (isInLinphoneThread()) moveToThread(App::getInstance()->thread()); @@ -32,7 +31,6 @@ AccountGui::AccountGui(QSharedPointer core) { AccountGui::~AccountGui() { mustBeInMainThread("~" + getClassName()); - qDebug() << "[AccountGui] delete" << this; } AccountCore *AccountGui::getCore() const { diff --git a/Linphone/core/account/AccountList.cpp b/Linphone/core/account/AccountList.cpp index 5cc089a90..3b837b5ed 100644 --- a/Linphone/core/account/AccountList.cpp +++ b/Linphone/core/account/AccountList.cpp @@ -37,51 +37,54 @@ QSharedPointer AccountList::create() { } AccountList::AccountList(QObject *parent) : ListProxy(parent) { - qDebug() << "[AccountList] new" << this; mustBeInMainThread(getClassName()); connect(CoreModel::getInstance().get(), &CoreModel::accountAdded, this, &AccountList::lUpdate); connect(CoreModel::getInstance().get(), &CoreModel::accountRemoved, this, &AccountList::lUpdate); } AccountList::~AccountList() { - qDebug() << "[AccountList] delete" << this; mustBeInMainThread("~" + getClassName()); + mModelConnection = nullptr; } void AccountList::setSelf(QSharedPointer me) { - mModelConnection = QSharedPointer( - new SafeConnection(me.objectCast(), std::dynamic_pointer_cast(CoreModel::getInstance())), - &QObject::deleteLater); - mModelConnection->makeConnect(this, &AccountList::lUpdate, [this]() { + mModelConnection = QSharedPointer>( + new SafeConnection(me, CoreModel::getInstance()), &QObject::deleteLater); + + mModelConnection->makeConnectToCore(&AccountList::lUpdate, [this]() { mModelConnection->invokeToModel([this]() { + // Avoid copy to lambdas QList> *accounts = new QList>(); - // Model thread. mustBeInLinphoneThread(getClassName()); auto linphoneAccounts = CoreModel::getInstance()->getCore()->getAccountList(); + auto defaultAccount = CoreModel::getInstance()->getCore()->getDefaultAccount(); + QSharedPointer defaultAccountCore; for (auto it : linphoneAccounts) { auto model = AccountCore::create(it); + if (it == defaultAccount) defaultAccountCore = AccountCore::create(defaultAccount); accounts->push_back(model); } - mModelConnection->invokeToCore([this, accounts]() { + mModelConnection->invokeToCore([this, accounts, defaultAccountCore]() { mustBeInMainThread(getClassName()); resetData(); add(*accounts); setHaveAccount(accounts->size() > 0); + setDefaultAccount(defaultAccountCore); delete accounts; }); }); }); - mModelConnection->makeConnect(CoreModel::getInstance().get(), &CoreModel::defaultAccountChanged, - [this]() { mModelConnection->invokeToCore([this]() { defaultAccountChanged(); }); }); + mModelConnection->makeConnectToModel( + &CoreModel::defaultAccountChanged, + [this](const std::shared_ptr &core, const std::shared_ptr &account) { + auto model = AccountCore::create(account); + mModelConnection->invokeToCore([this, model]() { setDefaultAccount(model); }); + }); lUpdate(); } QSharedPointer AccountList::getDefaultAccountCore() const { - for (auto it : mList) { - auto account = it.objectCast(); - if (account->getIsDefaultAccount()) return account; - } - return nullptr; + return mDefaultAccount; } AccountGui *AccountList::getDefaultAccount() const { @@ -89,6 +92,13 @@ AccountGui *AccountList::getDefaultAccount() const { if (account) return new AccountGui(account); else return nullptr; } + +void AccountList::setDefaultAccount(QSharedPointer account) { + if (mDefaultAccount != account) { + mDefaultAccount = account; + emit defaultAccountChanged(); + } +} bool AccountList::getHaveAccount() const { return mHaveAccount; } diff --git a/Linphone/core/account/AccountList.hpp b/Linphone/core/account/AccountList.hpp index f99fb1efe..7b8febce2 100644 --- a/Linphone/core/account/AccountList.hpp +++ b/Linphone/core/account/AccountList.hpp @@ -28,6 +28,7 @@ class AccountGui; class AccountCore; +class CoreModel; // ============================================================================= class AccountList : public ListProxy, public AbstractObject { @@ -41,6 +42,7 @@ public: AccountGui *getDefaultAccount() const; QSharedPointer getDefaultAccountCore() const; + void setDefaultAccount(QSharedPointer account); bool getHaveAccount() const; void setHaveAccount(bool haveAccount); @@ -53,7 +55,8 @@ signals: private: bool mHaveAccount = false; - QSharedPointer mModelConnection; + QSharedPointer mDefaultAccount; + QSharedPointer> mModelConnection; DECLARE_ABSTRACT_OBJECT }; diff --git a/Linphone/core/account/AccountProxy.cpp b/Linphone/core/account/AccountProxy.cpp index 256dfb275..d6e36aa76 100644 --- a/Linphone/core/account/AccountProxy.cpp +++ b/Linphone/core/account/AccountProxy.cpp @@ -23,7 +23,6 @@ #include "AccountList.hpp" AccountProxy::AccountProxy(QObject *parent) : SortFilterProxy(parent) { - qDebug() << "[AccountProxy] new" << this; mList = AccountList::create(); connect(mList.get(), &AccountList::countChanged, this, &AccountProxy::resetDefaultAccount); connect(mList.get(), &AccountList::defaultAccountChanged, this, &AccountProxy::resetDefaultAccount); @@ -33,7 +32,7 @@ AccountProxy::AccountProxy(QObject *parent) : SortFilterProxy(parent) { } AccountProxy::~AccountProxy() { - qDebug() << "[AccountProxy] delete" << this; + setSourceModel(nullptr); } QString AccountProxy::getFilterText() const { @@ -59,7 +58,7 @@ void AccountProxy::setDefaultAccount(AccountGui *account) { // Reset the default account to let UI build its new object if needed. void AccountProxy::resetDefaultAccount() { mDefaultAccount = nullptr; - this->defaultAccountChanged(); // Warn the UI + emit this->defaultAccountChanged(); // Warn the UI } bool AccountProxy::getHaveAccount() const { diff --git a/Linphone/core/call/CallCore.cpp b/Linphone/core/call/CallCore.cpp index f20e2ed3a..8c6667f63 100644 --- a/Linphone/core/call/CallCore.cpp +++ b/Linphone/core/call/CallCore.cpp @@ -67,13 +67,12 @@ CallCore::~CallCore() { } void CallCore::setSelf(QSharedPointer me) { - mAccountModelConnection = QSharedPointer( - new SafeConnection(me.objectCast(), std::dynamic_pointer_cast(mCallModel)), - &QObject::deleteLater); - mAccountModelConnection->makeConnect(this, &CallCore::lSetMicrophoneMuted, [this](bool isMuted) { + mAccountModelConnection = QSharedPointer>( + new SafeConnection(me, mCallModel), &QObject::deleteLater); + mAccountModelConnection->makeConnectToCore(&CallCore::lSetMicrophoneMuted, [this](bool isMuted) { mAccountModelConnection->invokeToModel([this, isMuted]() { mCallModel->setMicrophoneMuted(isMuted); }); }); - mAccountModelConnection->makeConnect(mCallModel.get(), &CallModel::microphoneMutedChanged, [this](bool isMuted) { + mAccountModelConnection->makeConnectToModel(&CallModel::microphoneMutedChanged, [this](bool isMuted) { mAccountModelConnection->invokeToCore([this, isMuted]() { setMicrophoneMuted(isMuted); }); }); // mAccountModelConnection->makeConnect(this, &CallCore::lSetSpeakerMuted, [this](bool isMuted) { @@ -82,37 +81,37 @@ void CallCore::setSelf(QSharedPointer me) { // mAccountModelConnection->makeConnect(mCallModel.get(), &CallModel::speakerMutedChanged, [this](bool isMuted) { // mAccountModelConnection->invokeToCore([this, isMuted]() { setSpeakerMuted(isMuted); }); // }); - mAccountModelConnection->makeConnect(this, &CallCore::lSetCameraEnabled, [this](bool enabled) { + mAccountModelConnection->makeConnectToCore(&CallCore::lSetCameraEnabled, [this](bool enabled) { mAccountModelConnection->invokeToModel([this, enabled]() { mCallModel->setCameraEnabled(enabled); }); }); - mAccountModelConnection->makeConnect(mCallModel.get(), &CallModel::cameraEnabledChanged, [this](bool enabled) { + mAccountModelConnection->makeConnectToModel(&CallModel::cameraEnabledChanged, [this](bool enabled) { mAccountModelConnection->invokeToCore([this, enabled]() { setCameraEnabled(enabled); }); }); - mAccountModelConnection->makeConnect(mCallModel.get(), &CallModel::durationChanged, [this](int duration) { + mAccountModelConnection->makeConnectToModel(&CallModel::durationChanged, [this](int duration) { mAccountModelConnection->invokeToCore([this, duration]() { setDuration(duration); }); }); - connect(mCallModel.get(), &CallModel::stateChanged, this, - [this](linphone::Call::State state, const std::string &message) { - mAccountModelConnection->invokeToCore([this, state, message]() { - setState(LinphoneEnums::fromLinphone(state), Utils::coreStringToAppString(message)); - }); - }); - connect(mCallModel.get(), &CallModel::statusChanged, this, [this](linphone::Call::Status status) { + mAccountModelConnection->makeConnectToModel( + &CallModel::stateChanged, [this](linphone::Call::State state, const std::string &message) { + mAccountModelConnection->invokeToCore([this, state, message]() { + setState(LinphoneEnums::fromLinphone(state), Utils::coreStringToAppString(message)); + }); + }); + mAccountModelConnection->makeConnectToModel(&CallModel::statusChanged, [this](linphone::Call::Status status) { mAccountModelConnection->invokeToCore([this, status]() { setStatus(LinphoneEnums::fromLinphone(status)); }); }); - mAccountModelConnection->makeConnect(this, &CallCore::lSetPaused, [this](bool paused) { + mAccountModelConnection->makeConnectToCore(&CallCore::lSetPaused, [this](bool paused) { mAccountModelConnection->invokeToModel([this, paused]() { mCallModel->setPaused(paused); }); }); - mAccountModelConnection->makeConnect(mCallModel.get(), &CallModel::pausedChanged, [this](bool paused) { + mAccountModelConnection->makeConnectToModel(&CallModel::pausedChanged, [this](bool paused) { mAccountModelConnection->invokeToCore([this, paused]() { setPaused(paused); }); }); - mAccountModelConnection->makeConnect(this, &CallCore::lTransferCall, [this](const QString &address) { + mAccountModelConnection->makeConnectToCore(&CallCore::lTransferCall, [this](const QString &address) { mAccountModelConnection->invokeToModel( [this, address]() { mCallModel->transferTo(ToolModel::interpretUrl(address)); }); }); - mAccountModelConnection->makeConnect( - mCallModel.get(), &CallModel::transferStateChanged, + mAccountModelConnection->makeConnectToModel( + &CallModel::transferStateChanged, [this](const std::shared_ptr &call, linphone::Call::State state) { mAccountModelConnection->invokeToCore([this, state]() { QString message; @@ -122,8 +121,8 @@ void CallCore::setSelf(QSharedPointer me) { setTransferState(LinphoneEnums::fromLinphone(state), message); }); }); - mAccountModelConnection->makeConnect( - mCallModel.get(), &CallModel::encryptionChanged, + mAccountModelConnection->makeConnectToModel( + &CallModel::encryptionChanged, [this](const std::shared_ptr &call, bool on, const std::string &authenticationToken) { auto encryption = LinphoneEnums::fromLinphone(call->getCurrentParams()->getMediaEncryption()); auto tokenVerified = mCallModel->getAuthenticationTokenVerified(); @@ -133,13 +132,12 @@ void CallCore::setSelf(QSharedPointer me) { encryption == LinphoneEnums::MediaEncryption::Dtls); }); }); - mAccountModelConnection->makeConnect(this, &CallCore::lAccept, [this](bool withVideo) { + mAccountModelConnection->makeConnectToCore(&CallCore::lAccept, [this](bool withVideo) { mAccountModelConnection->invokeToModel([this, withVideo]() { mCallModel->accept(withVideo); }); }); - mAccountModelConnection->makeConnect(this, &CallCore::lDecline, [this]() { - mAccountModelConnection->invokeToModel([this]() { mCallModel->decline(); }); - }); - mAccountModelConnection->makeConnect(this, &CallCore::lTerminate, [this]() { + mAccountModelConnection->makeConnectToCore( + &CallCore::lDecline, [this]() { mAccountModelConnection->invokeToModel([this]() { mCallModel->decline(); }); }); + mAccountModelConnection->makeConnectToCore(&CallCore::lTerminate, [this]() { mAccountModelConnection->invokeToModel([this]() { mCallModel->terminate(); }); }); } @@ -255,4 +253,4 @@ void CallCore::setTransferState(LinphoneEnums::CallState state, const QString &m if (state == LinphoneEnums::CallState::Error) setLastErrorMessage(message); emit transferStateChanged(); } -} \ No newline at end of file +} diff --git a/Linphone/core/call/CallCore.hpp b/Linphone/core/call/CallCore.hpp index de056d003..48a1d7646 100644 --- a/Linphone/core/call/CallCore.hpp +++ b/Linphone/core/call/CallCore.hpp @@ -23,12 +23,11 @@ #include "model/call/CallModel.hpp" #include "tool/LinphoneEnums.hpp" +#include "tool/thread/SafeConnection.hpp" #include #include #include -class SafeConnection; - class CallCore : public QObject, public AbstractObject { Q_OBJECT @@ -138,7 +137,7 @@ private: bool mMicrophoneMuted; bool mCameraEnabled; bool mPaused = false; - QSharedPointer mAccountModelConnection; + QSharedPointer> mAccountModelConnection; DECLARE_ABSTRACT_OBJECT }; diff --git a/Linphone/core/friend/FriendCore.cpp b/Linphone/core/friend/FriendCore.cpp index b9b465929..9c0653bde 100644 --- a/Linphone/core/friend/FriendCore.cpp +++ b/Linphone/core/friend/FriendCore.cpp @@ -56,35 +56,36 @@ FriendCore::FriendCore(const FriendCore &friendCore) { FriendCore::~FriendCore() { } -void FriendCore::setSelf(QSharedPointer me) { - setSelf(me.objectCast()); +void FriendCore::setSelf(SafeSharedPointer me) { + setSelf(me.mQDataWeak.lock()); } - -void FriendCore::setSelf(SafeSharedPointer me) { - if (mFriendModel) { - mFriendModelConnection = QSharedPointer( - new SafeConnection(me, std::dynamic_pointer_cast(mFriendModel)), &QObject::deleteLater); - mFriendModelConnection->makeConnect( - mFriendModel.get(), &FriendModel::presenceReceived, - [this](LinphoneEnums::ConsolidatedPresence consolidatedPresence, QDateTime presenceTimestamp) { - mFriendModelConnection->invokeToCore([this, consolidatedPresence, presenceTimestamp]() { - setConsolidatedPresence(consolidatedPresence); - setPresenceTimestamp(presenceTimestamp); +void FriendCore::setSelf(QSharedPointer me) { + if (me) { + if (mFriendModel) { + mCoreModelConnection = nullptr; // No more needed + mFriendModelConnection = QSharedPointer>( + new SafeConnection(me, mFriendModel), &QObject::deleteLater); + mFriendModelConnection->makeConnectToModel( + &FriendModel::presenceReceived, + [this](LinphoneEnums::ConsolidatedPresence consolidatedPresence, QDateTime presenceTimestamp) { + mFriendModelConnection->invokeToCore([this, consolidatedPresence, presenceTimestamp]() { + setConsolidatedPresence(consolidatedPresence); + setPresenceTimestamp(presenceTimestamp); + }); }); - }); - mFriendModelConnection->makeConnect(mFriendModel.get(), &FriendModel::pictureUriChanged, [this](QString uri) { - mFriendModelConnection->invokeToCore([this, uri]() { this->onPictureUriChanged(uri); }); - }); + mFriendModelConnection->makeConnectToModel(&FriendModel::pictureUriChanged, [this](QString uri) { + mFriendModelConnection->invokeToCore([this, uri]() { this->onPictureUriChanged(uri); }); + }); - // From GUI - mFriendModelConnection->makeConnect(this, &FriendCore::lSetPictureUri, [this](QString uri) { - mFriendModelConnection->invokeToModel([this, uri]() { mFriendModel->setPictureUri(uri); }); - }); + // From GUI + mFriendModelConnection->makeConnectToCore(&FriendCore::lSetPictureUri, [this](QString uri) { + mFriendModelConnection->invokeToModel([this, uri]() { mFriendModel->setPictureUri(uri); }); + }); - } else { // Create - mFriendModelConnection = QSharedPointer( - new SafeConnection(me, std::dynamic_pointer_cast(CoreModel::getInstance())), - &QObject::deleteLater); + } else { // Create + mCoreModelConnection = QSharedPointer>( + new SafeConnection(me, CoreModel::getInstance()), &QObject::deleteLater); + } } } @@ -200,7 +201,7 @@ void FriendCore::save() { // Save Value mFriendModelConnection->invokeToCore([this]() { saved(); }); }); } else { // Creation - mFriendModelConnection->invokeToModel([this, thisCopy]() { + mCoreModelConnection->invokeToModel([this, thisCopy]() { auto core = CoreModel::getInstance()->getCore(); auto contact = core->createFriend(); thisCopy->writeInto(contact); @@ -212,8 +213,8 @@ void FriendCore::save() { // Save Value core->getDefaultFriendList()->updateSubscriptions(); } emit CoreModel::getInstance()->friendAdded(); - mFriendModelConnection->invokeToCore([this, created]() { - if (created) setSelf(mFriendModelConnection->mCore); + mCoreModelConnection->invokeToCore([this, created]() { + if (created) setSelf(mCoreModelConnection->mCore); setIsSaved(created); }); }); diff --git a/Linphone/core/friend/FriendCore.hpp b/Linphone/core/friend/FriendCore.hpp index f9d0e6884..eb42d8f06 100644 --- a/Linphone/core/friend/FriendCore.hpp +++ b/Linphone/core/friend/FriendCore.hpp @@ -23,18 +23,19 @@ #include "model/friend/FriendModel.hpp" #include "tool/LinphoneEnums.hpp" +#include "tool/thread/SafeConnection.hpp" #include "tool/thread/SafeSharedPointer.hpp" #include #include #include #include -class SafeConnection; - // This object is defferent from usual Core. It set internal data from directly from GUI. // Values are saved on request. // This allow revert feature. +class CoreModel; + class FriendCore : public QObject, public AbstractObject { Q_OBJECT @@ -53,7 +54,7 @@ public: FriendCore(const FriendCore &friendCore); ~FriendCore(); void setSelf(QSharedPointer me); - void setSelf(SafeSharedPointer me); + void setSelf(SafeSharedPointer me); void reset(const FriendCore &contact); QString getName() const; @@ -106,7 +107,8 @@ protected: QString mPictureUri; bool mIsSaved; std::shared_ptr mFriendModel; - QSharedPointer mFriendModelConnection; + QSharedPointer> mFriendModelConnection; + QSharedPointer> mCoreModelConnection; DECLARE_ABSTRACT_OBJECT }; diff --git a/Linphone/core/path/Paths.cpp b/Linphone/core/path/Paths.cpp index f63293097..670566ef9 100644 --- a/Linphone/core/path/Paths.cpp +++ b/Linphone/core/path/Paths.cpp @@ -162,10 +162,6 @@ static inline QString getAppRootCaFilePath() { return ""; } -static inline QString getAppFriendsFilePath() { - return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + Constants::PathFriendsList; -} - static inline QString getAppMessageHistoryFilePath() { return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + Constants::PathMessageHistoryList; } @@ -243,10 +239,6 @@ QString Paths::getFactoryConfigFilePath() { return getReadableFilePath(getAppFactoryConfigFilePath()); } -QString Paths::getFriendsListFilePath() { - return getWritableFilePath(getAppFriendsFilePath()); -} - QString Paths::getDownloadDirPath() { return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + QDir::separator()); } diff --git a/Linphone/core/path/Paths.hpp b/Linphone/core/path/Paths.hpp index 57b84469b..94d3b3abb 100644 --- a/Linphone/core/path/Paths.hpp +++ b/Linphone/core/path/Paths.hpp @@ -39,7 +39,6 @@ QString getConfigFilePath(const QString &configPath = QString(), bool writable = QString getDatabaseFilePath(); QString getDownloadDirPath(); QString getFactoryConfigFilePath(); -QString getFriendsListFilePath(); QString getLimeDatabasePath(); QString getLogsDirPath(); QString getMessageHistoryFilePath(); diff --git a/Linphone/core/search/MagicSearchList.cpp b/Linphone/core/search/MagicSearchList.cpp index a23cc5008..f304d05fa 100644 --- a/Linphone/core/search/MagicSearchList.cpp +++ b/Linphone/core/search/MagicSearchList.cpp @@ -57,38 +57,37 @@ MagicSearchList::~MagicSearchList() { } void MagicSearchList::setSelf(QSharedPointer me) { - mModelConnection = QSharedPointer( - new SafeConnection(me.objectCast(), std::dynamic_pointer_cast(mMagicSearch)), - &QObject::deleteLater); - mModelConnection->makeConnect(this, &MagicSearchList::lSearch, [this](QString filter) { + mModelConnection = QSharedPointer>( + new SafeConnection(me, mMagicSearch), &QObject::deleteLater); + mModelConnection->makeConnectToCore(&MagicSearchList::lSearch, [this](QString filter) { mModelConnection->invokeToModel([this, filter]() { mMagicSearch->search(filter); }); }); - mModelConnection->makeConnect(this, &MagicSearchList::lSetSourceFlags, [this](int flags) { + mModelConnection->makeConnectToCore(&MagicSearchList::lSetSourceFlags, [this](int flags) { mModelConnection->invokeToModel([this, flags]() { mMagicSearch->setSourceFlags(flags); }); }); - mModelConnection->makeConnect(mMagicSearch.get(), &MagicSearchModel::sourceFlagsChanged, [this](int flags) { + mModelConnection->makeConnectToModel(&MagicSearchModel::sourceFlagsChanged, [this](int flags) { mModelConnection->invokeToCore([this, flags]() { setSourceFlags(flags); }); }); - mModelConnection->makeConnect(mMagicSearch.get(), &MagicSearchModel::aggregationFlagChanged, - [this](LinphoneEnums::MagicSearchAggregation flag) { - mModelConnection->invokeToCore([this, flag]() { setAggregationFlag(flag); }); - }); + mModelConnection->makeConnectToModel( + &MagicSearchModel::aggregationFlagChanged, [this](LinphoneEnums::MagicSearchAggregation flag) { + mModelConnection->invokeToCore([this, flag]() { setAggregationFlag(flag); }); + }); - mModelConnection->makeConnect(mMagicSearch.get(), &MagicSearchModel::searchResultsReceived, - [this](const std::list> &results) { - auto *contacts = new QList>(); - for (auto it : results) { - QSharedPointer contact; - if (it->getFriend()) { - contact = FriendCore::create(it->getFriend()); - contacts->append(contact); - } - } - mModelConnection->invokeToCore([this, contacts]() { - setResults(*contacts); - delete contacts; - }); - }); + mModelConnection->makeConnectToModel(&MagicSearchModel::searchResultsReceived, + [this](const std::list> &results) { + auto *contacts = new QList>(); + for (auto it : results) { + QSharedPointer contact; + if (it->getFriend()) { + contact = FriendCore::create(it->getFriend()); + contacts->append(contact); + } + } + mModelConnection->invokeToCore([this, contacts]() { + setResults(*contacts); + delete contacts; + }); + }); } void MagicSearchList::setResults(const QList> &contacts) { diff --git a/Linphone/core/search/MagicSearchList.hpp b/Linphone/core/search/MagicSearchList.hpp index 672b8bafd..743eafef4 100644 --- a/Linphone/core/search/MagicSearchList.hpp +++ b/Linphone/core/search/MagicSearchList.hpp @@ -28,6 +28,7 @@ #include class FriendCore; +class CoreModel; // ============================================================================= // Return FriendGui list to Ui @@ -63,8 +64,8 @@ private: LinphoneEnums::MagicSearchAggregation mAggregationFlag; std::shared_ptr mMagicSearch; - QSharedPointer mModelConnection; - QSharedPointer mCoreModelConnection; + QSharedPointer> mModelConnection; + QSharedPointer> mCoreModelConnection; DECLARE_ABSTRACT_OBJECT }; diff --git a/Linphone/model/account/AccountModel.cpp b/Linphone/model/account/AccountModel.cpp index 5b4376055..0dfb4b819 100644 --- a/Linphone/model/account/AccountModel.cpp +++ b/Linphone/model/account/AccountModel.cpp @@ -38,6 +38,11 @@ AccountModel::AccountModel(const std::shared_ptr &account, QO // Hack because Account doesn't provide callbacks on updated data connect(this, &AccountModel::defaultAccountChanged, this, [this]() { emit pictureUriChanged(Utils::coreStringToAppString(mMonitor->getParams()->getPictureUri())); }); + + connect(CoreModel::getInstance().get(), &CoreModel::unreadNotificationsChanged, this, [this]() { + emit unreadNotificationsChanged(0 /*mMonitor->getUnreadChatMessageCount()*/, + mMonitor->getMissedCallsCount()); // TODO + }); } AccountModel::~AccountModel() { @@ -80,3 +85,7 @@ void AccountModel::setDefault() { auto core = CoreModel::getInstance()->getCore(); core->setDefaultAccount(mMonitor); } + +void AccountModel::removeAccount() { + CoreModel::getInstance()->getCore()->removeAccount(mMonitor); +} diff --git a/Linphone/model/account/AccountModel.hpp b/Linphone/model/account/AccountModel.hpp index bc648f9c3..940cb4021 100644 --- a/Linphone/model/account/AccountModel.hpp +++ b/Linphone/model/account/AccountModel.hpp @@ -42,6 +42,7 @@ public: void setPictureUri(QString uri); void setDefault(); + void removeAccount(); signals: void registrationStateChanged(const std::shared_ptr &account, @@ -50,6 +51,7 @@ signals: void defaultAccountChanged(bool isDefault); void pictureUriChanged(QString uri); + void unreadNotificationsChanged(int unreadMessagesCount, int unreadCallsCount); private: DECLARE_ABSTRACT_OBJECT diff --git a/Linphone/model/core/CoreModel.cpp b/Linphone/model/core/CoreModel.cpp index 1f44bc02d..4550ae7bf 100644 --- a/Linphone/model/core/CoreModel.cpp +++ b/Linphone/model/core/CoreModel.cpp @@ -63,18 +63,23 @@ std::shared_ptr CoreModel::create(const QString &configPath, QThread void CoreModel::start() { mIterateTimer = new QTimer(this); mIterateTimer->setInterval(30); - connect(mIterateTimer, &QTimer::timeout, [this]() { mCore->iterate(); }); + connect(mIterateTimer, &QTimer::timeout, [this]() { + static int iterateCount = 0; + if (iterateCount != 0) qCritical() << log().arg("Multi Iterate ! "); + ++iterateCount; + mCore->iterate(); + --iterateCount; + }); setPathBeforeCreation(); mCore = linphone::Factory::get()->createCore(Utils::appStringToCoreString(Paths::getConfigFilePath(mConfigPath)), Utils::appStringToCoreString(Paths::getFactoryConfigFilePath()), nullptr); setMonitor(mCore); setPathsAfterCreation(); - mCore->start(); - setPathAfterStart(); mCore->enableFriendListSubscription(true); mCore->enableRecordAware(true); - mCore->getCallsNb(); + mCore->start(); + setPathAfterStart(); mIterateTimer->start(); } // ----------------------------------------------------------------------------- @@ -92,7 +97,7 @@ void CoreModel::setConfigPath(QString path) { if (mConfigPath != path) { mConfigPath = path; if (!mCore) { - qWarning() << "[CoreModel] Setting config path after core creation is not yet supported"; + qWarning() << log().arg("Setting config path after core creation is not yet supported"); } } } @@ -118,9 +123,6 @@ void CoreModel::setPathBeforeCreation() { } void CoreModel::setPathsAfterCreation() { - QString friendsDb = Paths::getFriendsListFilePath(); - qInfo() << QStringLiteral("[CoreModel] Set Database `Friends` path: `%1`").arg(friendsDb); - mCore->setFriendsDatabasePath(Utils::appStringToCoreString(friendsDb)); } void CoreModel::setPathAfterStart() { @@ -169,6 +171,7 @@ void CoreModel::onCallEncryptionChanged(const std::shared_ptr &c } void CoreModel::onCallLogUpdated(const std::shared_ptr &core, const std::shared_ptr &callLog) { + if (callLog && callLog->getStatus() == linphone::Call::Status::Missed) emit unreadNotificationsChanged(); emit callLogUpdated(core, callLog); } void CoreModel::onCallStateChanged(const std::shared_ptr &core, @@ -242,11 +245,13 @@ void CoreModel::onLogCollectionUploadProgressIndication(const std::shared_ptr
  • &core, const std::shared_ptr &room, const std::shared_ptr &message) { + emit unreadNotificationsChanged(); emit messageReceived(core, room, message); } void CoreModel::onMessagesReceived(const std::shared_ptr &core, const std::shared_ptr &room, const std::list> &messages) { + emit unreadNotificationsChanged(); emit messagesReceived(core, room, messages); } diff --git a/Linphone/model/core/CoreModel.hpp b/Linphone/model/core/CoreModel.hpp index 721df83bb..8c145cd40 100644 --- a/Linphone/model/core/CoreModel.hpp +++ b/Linphone/model/core/CoreModel.hpp @@ -58,6 +58,7 @@ signals: void loggerInitialized(); void friendAdded(); void friendRemoved(); + void unreadNotificationsChanged(); private: QString mConfigPath; diff --git a/Linphone/model/listener/Listener.hpp b/Linphone/model/listener/Listener.hpp index ef9885149..64399febe 100644 --- a/Linphone/model/listener/Listener.hpp +++ b/Linphone/model/listener/Listener.hpp @@ -44,7 +44,6 @@ public: setMonitor(monitor); } ~Listener() { - qDebug() << "Destroying Listener"; setSelf(nullptr); } virtual void onRemoveListener() { diff --git a/Linphone/model/object/VariantObject.cpp b/Linphone/model/object/VariantObject.cpp index 684cda536..b12d87561 100644 --- a/Linphone/model/object/VariantObject.cpp +++ b/Linphone/model/object/VariantObject.cpp @@ -33,17 +33,16 @@ VariantObject::VariantObject(QVariant defaultValue, QObject *parent) { mModelObject = QSharedPointer::create(); mModelObject->moveToThread(CoreModel::getInstance()->thread()); - mConnection = QSharedPointer( - new SafeConnection(mCoreObject.objectCast(), mModelObject.objectCast()), - &QObject::deleteLater); + mConnection = QSharedPointer>( + new SafeConnection(mCoreObject, mModelObject), &QObject::deleteLater); - mConnection->makeConnect(mCoreObject.get(), &SafeObject::setValue, [this](QVariant value) { + mConnection->makeConnectToCore(&SafeObject::setValue, [this](QVariant value) { mConnection->invokeToModel([this, value]() { mModelObject->onSetValue(value); }); }); - mConnection->makeConnect(mModelObject.get(), &SafeObject::setValue, [this](QVariant value) { + mConnection->makeConnectToModel(&SafeObject::setValue, [this](QVariant value) { mConnection->invokeToCore([this, value]() { mCoreObject->onSetValue(value); }); }); - mConnection->makeConnect(mModelObject.get(), &SafeObject::valueChanged, [this](QVariant value) { + mConnection->makeConnectToModel(&SafeObject::valueChanged, [this](QVariant value) { mConnection->invokeToCore([this, value]() { mCoreObject->valueChanged(value); }); }); connect(mCoreObject.get(), &SafeObject::valueChanged, this, &VariantObject::valueChanged); diff --git a/Linphone/model/object/VariantObject.hpp b/Linphone/model/object/VariantObject.hpp index bb0546d28..b86a60b5c 100644 --- a/Linphone/model/object/VariantObject.hpp +++ b/Linphone/model/object/VariantObject.hpp @@ -30,6 +30,8 @@ #include "SafeObject.hpp" #include "tool/thread/SafeConnection.hpp" +class CoreModel; + class VariantObject : public QObject, public AbstractObject { Q_OBJECT Q_PROPERTY(QVariant value READ getValue NOTIFY valueChanged) @@ -40,21 +42,21 @@ public: template void makeRequest(Func &&callable, Args &&...args) { - mConnection->makeConnect(mCoreObject.get(), &SafeObject::requestValue, [this, callable, args...]() { + mConnection->makeConnectToCore(&SafeObject::requestValue, [this, callable, args...]() { mConnection->invokeToModel([this, callable, args...]() { mModelObject->setValue(callable(args...)); }); }); } - template - void makeUpdate(const typename QtPrivate::FunctionPointer::Object *sender, SenderClass signal) { - mConnection->makeConnect(sender, signal, - [this]() { mConnection->invokeToCore([this]() { mCoreObject->requestValue(); }); }); + template + void makeUpdate(Sender sender, SenderClass signal) { + mConnection->makeConnectToModel( + sender, signal, [this]() { mConnection->invokeToCore([this]() { mCoreObject->requestValue(); }); }); } QVariant getValue() const; void requestValue(); QSharedPointer mCoreObject, mModelObject; - QSharedPointer mConnection; + QSharedPointer> mConnection; signals: void valueChanged(QVariant value); diff --git a/Linphone/tool/CMakeLists.txt b/Linphone/tool/CMakeLists.txt index 5b1a32b3e..6328ed747 100644 --- a/Linphone/tool/CMakeLists.txt +++ b/Linphone/tool/CMakeLists.txt @@ -5,7 +5,7 @@ list(APPEND _LINPHONEAPP_SOURCES tool/LinphoneEnums.cpp tool/thread/SafeSharedPointer.hpp - tool/thread/SafeConnection.cpp + tool/thread/SafeConnection.hpp tool/thread/Thread.cpp tool/providers/AvatarProvider.cpp tool/providers/ImageProvider.cpp diff --git a/Linphone/tool/Constants.cpp b/Linphone/tool/Constants.cpp index 6d377f0e3..7614fa4c1 100644 --- a/Linphone/tool/Constants.cpp +++ b/Linphone/tool/Constants.cpp @@ -75,7 +75,6 @@ constexpr char Constants::PathConfig[]; constexpr char Constants::PathDatabase[]; constexpr char Constants::PathFactoryConfig[]; constexpr char Constants::PathRootCa[]; -constexpr char Constants::PathFriendsList[]; constexpr char Constants::PathLimeDatabase[]; constexpr char Constants::PathMessageHistoryList[]; constexpr char Constants::PathZrtpSecrets[]; diff --git a/Linphone/tool/Constants.hpp b/Linphone/tool/Constants.hpp index 71c921a1a..3c76b4f6b 100644 --- a/Linphone/tool/Constants.hpp +++ b/Linphone/tool/Constants.hpp @@ -146,7 +146,6 @@ public: static constexpr char PathDatabase[] = "/linphone.db"; static constexpr char PathFactoryConfig[] = "/" EXECUTABLE_NAME "/linphonerc-factory"; static constexpr char PathRootCa[] = "/" EXECUTABLE_NAME "/rootca.pem"; - static constexpr char PathFriendsList[] = "/friends.db"; static constexpr char PathLimeDatabase[] = "/x3dh.c25519.sqlite3"; static constexpr char PathMessageHistoryList[] = "/message-history.db"; static constexpr char PathZrtpSecrets[] = "/zidcache"; diff --git a/Linphone/tool/Utils.cpp b/Linphone/tool/Utils.cpp index a9ccef066..ff80b9f68 100644 --- a/Linphone/tool/Utils.cpp +++ b/Linphone/tool/Utils.cpp @@ -45,7 +45,9 @@ char *Utils::rstrstr(const char *a, const char *b) { } VariantObject *Utils::getDisplayName(const QString &address) { - VariantObject *data = new VariantObject(address); // Scope : GUI + QStringList splitted = address.split(":"); + if (splitted.size() > 0 && splitted[0] == "sip") splitted.removeFirst(); + VariantObject *data = new VariantObject(splitted.first().split("@").first()); // Scope : GUI data->makeRequest([address]() { QString displayName = ToolModel::getDisplayName(address); return displayName; diff --git a/Linphone/tool/thread/SafeConnection.cpp b/Linphone/tool/thread/SafeConnection.cpp deleted file mode 100644 index 00f315812..000000000 --- a/Linphone/tool/thread/SafeConnection.cpp +++ /dev/null @@ -1,32 +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 . - */ - -#include "SafeConnection.hpp" - -SafeConnection::SafeConnection(SafeSharedPointer core, SafeSharedPointer model) - : mCore(core), mModel(model) { -} -SafeConnection::~SafeConnection() { - mLocker.lock(); - if (mCore.mCountRef != 0 || mModel.mCountRef != 0) - qCritical() << "[SafeConnection] Destruction while still having references. CoreRef=" << mCore.mCountRef - << "ModelRef=" << mModel.mCountRef; - mLocker.unlock(); -} diff --git a/Linphone/tool/thread/SafeConnection.hpp b/Linphone/tool/thread/SafeConnection.hpp index 6b5198533..710f2ae03 100644 --- a/Linphone/tool/thread/SafeConnection.hpp +++ b/Linphone/tool/thread/SafeConnection.hpp @@ -22,6 +22,7 @@ #define SAFE_CONNECTION_H_ #include "SafeSharedPointer.hpp" +#include "model/core/CoreModel.hpp" #include #include @@ -60,19 +61,40 @@ * */ +template class SafeConnection : public QObject { public: - SafeConnection(SafeSharedPointer a, SafeSharedPointer b); - ~SafeConnection(); - SafeSharedPointer mCore, mModel; + // SafeConnection(SafeSharedPointer a, SafeSharedPointer b); + SafeConnection(QSharedPointer a, std::shared_ptr b) + : mCore(a), mModel(b), mCoreObject(a.get()), mModelObject(b.get()) { + } + SafeConnection(QSharedPointer a, QSharedPointer b) + : mCore(a), mModel(b), mCoreObject(a.get()), mModelObject(b.get()) { + } + ~SafeConnection() { + mLocker.lock(); + if (mCore.mCountRef != 0 || mModel.mCountRef != 0) + qCritical() << "[SafeConnection] Destruction while still having references. CoreRef=" << mCore.mCountRef + << "ModelRef=" << mModel.mCountRef; + mCore.reset(); + mModel.reset(); + mLocker.unlock(); + } + SafeSharedPointer mCore; + SafeSharedPointer mModel; QMutex mLocker; template - static inline QMetaObject::Connection makeConnect(const typename QtPrivate::FunctionPointer::Object *sender, - Func1 signal, - Func2 slot, - Qt::ConnectionType type = Qt::AutoConnection) { - return connect(sender, signal, sender, slot, type); + inline QMetaObject::Connection makeConnectToModel(Func1 signal, Func2 slot) { + return connect(mModelObject, signal, mCoreObject, slot, Qt::DirectConnection); + } + template + inline QMetaObject::Connection makeConnectToModel(Sender sender, Func1 signal, Func2 slot) { + return connect(sender, signal, mCoreObject, slot, Qt::DirectConnection); + } + template + inline QMetaObject::Connection makeConnectToCore(Func1 signal, Func2 slot) { + return connect(mCoreObject, signal, mModelObject, slot, Qt::DirectConnection); } template @@ -112,6 +134,10 @@ public: mModel.unlock(); mLocker.unlock(); } + +protected: + A *mCoreObject = nullptr; + B *mModelObject = nullptr; // Use only for makeConnects }; #endif diff --git a/Linphone/view/App/Layout/MainLayout.qml b/Linphone/view/App/Layout/MainLayout.qml index 4f8a9a0d9..5c684ecc8 100644 --- a/Linphone/view/App/Layout/MainLayout.qml +++ b/Linphone/view/App/Layout/MainLayout.qml @@ -13,6 +13,8 @@ import UtilsCpp Item { id: mainItem + + signal addAccountRequest() RowLayout { anchors.fill: parent @@ -79,6 +81,7 @@ Item { contentHeight: accounts.height Accounts{ id: accounts + onAddAccountRequest: mainItem.addAccountRequest() } } } diff --git a/Linphone/view/App/Main.qml b/Linphone/view/App/Main.qml index b9fcc0966..ce0a8aae6 100644 --- a/Linphone/view/App/Main.qml +++ b/Linphone/view/App/Main.qml @@ -36,6 +36,8 @@ Window { Component { id: loginPage LoginPage { + showBackButton: accountProxy.haveAccount + onGoBack: mainWindowStackView.replace(mainPage) onUseSIPButtonClicked: mainWindowStackView.push(sipLoginPage) onGoToRegister: mainWindowStackView.replace(registerPage) onConnectionSucceed: { @@ -50,7 +52,7 @@ Window { Component { id: sipLoginPage SIPLoginPage { - onReturnToLogin: mainWindowStackView.pop() + onGoBack: mainWindowStackView.pop() onGoToRegister: mainWindowStackView.replace(registerPage) onConnectionSucceed: { @@ -90,6 +92,7 @@ Window { Component { id: mainPage MainLayout { + onAddAccountRequest: mainWindowStackView.replace(loginPage) } } } diff --git a/Linphone/view/Item/Account/Accounts.qml b/Linphone/view/Item/Account/Accounts.qml index c37ff2491..a452ba59a 100644 --- a/Linphone/view/Item/Account/Accounts.qml +++ b/Linphone/view/Item/Account/Accounts.qml @@ -1,6 +1,8 @@ -import QtQuick 2.15 -import QtQuick.Layouts 1.3 -import QtQuick.Controls 2.2 as Control +import QtCore +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls as Control +import QtQuick.Dialogs import Linphone import UtilsCpp @@ -13,6 +15,9 @@ Item { readonly property int leftPadding: 32 * DefaultStyle.dp readonly property int rightPadding: 32 * DefaultStyle.dp readonly property int spacing: 16 * DefaultStyle.dp + + signal addAccountRequest() + implicitHeight: list.contentHeight + topPadding + bottomPadding + 32 * DefaultStyle.dp + 1 + newAccountArea.height ColumnLayout{ anchors.top: parent.top @@ -28,8 +33,21 @@ Item { spacing: mainItem.spacing model: AccountProxy{} delegate: Contact{ + id: contactItem width: list.width account: modelData + onAvatarClicked: fileDialog.open() + onBackgroundClicked: modelData.core.lSetDefaultAccount() + FileDialog { + id: fileDialog + currentFolder: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0] + onAccepted: { + var avatarPath = UtilsCpp.createAvatar( selectedFile ) + if(avatarPath){ + modelData.core.pictureUri = avatarPath + } + } + } } } Rectangle{ @@ -40,10 +58,10 @@ Item { height: 1 color: DefaultStyle.main2_300 } - MouseArea{ // TODO + MouseArea{ Layout.fillWidth: true Layout.preferredHeight: 32 * DefaultStyle.dp - onClicked: console.log('New!') + onClicked: mainItem.addAccountRequest() RowLayout{ id: newAccountArea anchors.fill: parent diff --git a/Linphone/view/Item/Contact/Avatar.qml b/Linphone/view/Item/Contact/Avatar.qml index a83e37c9f..8ceb5c9f0 100644 --- a/Linphone/view/Item/Contact/Avatar.qml +++ b/Linphone/view/Item/Contact/Avatar.qml @@ -13,11 +13,11 @@ StackView{ id: mainItem property FriendGui contact property AccountGui account - onAccountChanged: if(account) replace(avatar, StackView.Immediate) property string address: account ? account.core.identityAddress : '' property var displayNameObj: UtilsCpp.getDisplayName(address) property bool haveAvatar: (account && account.core.pictureUri ) || (contact && contact.core.pictureUri) + onHaveAvatarChanged: replace(haveAvatar ? avatar : initials, StackView.Immediate) initialItem: haveAvatar ? avatar : initials Component{ @@ -25,12 +25,10 @@ StackView{ Rectangle { id: initialItem property string initials: UtilsCpp.getInitials(mainItem.displayNameObj.value) - onInitialsChanged: console.log("newInit:"+initials) radius: width / 2 color: DefaultStyle.main2_200 height: mainItem.height width: height - Component.onCompleted: console.log("init:"+initials) Text { anchors.fill: parent anchors.centerIn: parent diff --git a/Linphone/view/Item/Contact/Contact.qml b/Linphone/view/Item/Contact/Contact.qml index f7993c4fc..ec103225a 100644 --- a/Linphone/view/Item/Contact/Contact.qml +++ b/Linphone/view/Item/Contact/Contact.qml @@ -1,7 +1,6 @@ -import QtCore import QtQuick import QtQuick.Effects -import QtQuick.Dialogs + import QtQuick.Layouts @@ -11,7 +10,14 @@ import UtilsCpp Rectangle{ id: mainItem property AccountGui account + signal avatarClicked() + signal backgroundClicked() + height: 45 * DefaultStyle.dp + MouseArea{ + anchors.fill: parent + onClicked: mainItem.backgroundClicked() + } RowLayout{ anchors.fill: parent spacing: 0 @@ -22,17 +28,7 @@ Rectangle{ account: mainItem.account MouseArea{ anchors.fill: parent - onClicked: fileDialog.open() - } - FileDialog { - id: fileDialog - currentFolder: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0] - onAccepted: { - var avatarPath = UtilsCpp.createAvatar( selectedFile ) - if(avatarPath){ - mainItem.account.core.pictureUri = avatarPath - } - } + onClicked: mainItem.avatarClicked() } } ContactDescription{ @@ -95,7 +91,7 @@ Rectangle{ } } } - Item{ // TODO + Item{ Layout.preferredWidth: 100 * DefaultStyle.dp Layout.fillHeight: true Rectangle{ @@ -103,6 +99,7 @@ Rectangle{ anchors.left: parent.left anchors.leftMargin: 10 * DefaultStyle.dp anchors.verticalCenter: parent.verticalCenter + visible: unreadCount.text > 0 width: 22 * DefaultStyle.dp height: 22 * DefaultStyle.dp radius: width/2 @@ -112,10 +109,15 @@ Rectangle{ Text{ id: unreadCount anchors.fill: parent + anchors.margins: 2 * DefaultStyle.dp verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter color: DefaultStyle.grey_0 - text: '2' + minimumPixelSize: 5 + fontSizeMode: Text.Fit + font.pixelSize: 20 * DefaultStyle.dp + property var unread: mainItem.account.core.unreadNotifications + text: unread > 100 ? '+' : unread } } } @@ -130,7 +132,7 @@ Rectangle{ colorizationColor: DefaultStyle.main2_500main MouseArea{ // TODO anchors.fill: parent - onClicked: console.log('Manage!') + onClicked: console.log('TODO: Manage!') } } } diff --git a/Linphone/view/Page/Login/LoginPage.qml b/Linphone/view/Page/Login/LoginPage.qml index d2b6bdf9c..1f015173b 100644 --- a/Linphone/view/Page/Login/LoginPage.qml +++ b/Linphone/view/Page/Login/LoginPage.qml @@ -5,11 +5,28 @@ import Linphone LoginLayout { id: mainItem + property bool showBackButton: false + signal goBack() signal useSIPButtonClicked() signal goToRegister() signal connectionSucceed() titleContent: RowLayout { + Control.Button { + Layout.preferredHeight: 40 * DefaultStyle.dp + Layout.preferredWidth: height + visible: mainItem.showBackButton + icon.width: width + icon.height: height + icon.source: AppIcons.returnArrow + background: Rectangle { + color: "transparent" + } + onClicked: { + console.debug("[LoginLayout] User: return") + mainItem.goBack() + } + } Image { fillMode: Image.PreserveAspectFit source: AppIcons.profile @@ -24,7 +41,7 @@ LoginLayout { Layout.fillWidth: true } Text { - Layout.rightMargin: 15 + Layout.rightMargin: 15 * DefaultStyle.dp text: "No account yet ?" font.pointSize: DefaultStyle.indicatorMessageTextSize } @@ -47,7 +64,7 @@ LoginLayout { onConnectionSucceed: mainItem.connectionSucceed() } Button { - Layout.topMargin: 40 + Layout.topMargin: 40 * DefaultStyle.dp inversedColors: true text: "Use SIP Account" onClicked: {mainItem.useSIPButtonClicked()} @@ -57,8 +74,8 @@ LoginLayout { Layout.fillWidth: true } Image { - Layout.rightMargin: 40 - Layout.preferredWidth: 300 + Layout.rightMargin: 40 * DefaultStyle.dp + Layout.preferredWidth: 300 * DefaultStyle.dp fillMode: Image.PreserveAspectFit source: AppIcons.loginImage } diff --git a/Linphone/view/Page/Login/SIPLoginPage.qml b/Linphone/view/Page/Login/SIPLoginPage.qml index 64197bd2d..b4898af14 100644 --- a/Linphone/view/Page/Login/SIPLoginPage.qml +++ b/Linphone/view/Page/Login/SIPLoginPage.qml @@ -6,7 +6,7 @@ import ConstantsCpp 1.0 LoginLayout { id: mainItem - signal returnToLogin() + signal goBack() signal goToRegister() signal connectionSucceed() @@ -22,7 +22,7 @@ LoginLayout { } onClicked: { console.debug("[SIPLoginPage] User: return") - mainItem.returnToLogin() + mainItem.goBack() } } Image { diff --git a/external/linphone-sdk b/external/linphone-sdk index bf9106ee5..d07e85507 160000 --- a/external/linphone-sdk +++ b/external/linphone-sdk @@ -1 +1 @@ -Subproject commit bf9106ee57b8a32aea2693f0fc69c2397a66d570 +Subproject commit d07e85507cb6ddba7fe397f5fc699835516945aa