diff --git a/linphone-app/CMakeLists.txt b/linphone-app/CMakeLists.txt index 8654b7ca2..172fd81a5 100644 --- a/linphone-app/CMakeLists.txt +++ b/linphone-app/CMakeLists.txt @@ -174,6 +174,7 @@ set(SOURCES src/components/chat-events/ChatMessageListener.cpp src/components/chat-events/ChatMessageModel.cpp src/components/chat-events/ChatNoticeModel.cpp + src/components/chat-room/ChatRoomInitializer.cpp src/components/chat-room/ChatRoomListener.cpp src/components/chat-room/ChatRoomModel.cpp src/components/chat-room/ChatRoomProxyModel.cpp @@ -265,7 +266,6 @@ set(SOURCES src/utils/MediastreamerUtils.cpp src/utils/QExifImageHeader.cpp src/utils/Utils.cpp - src/utils/hacks/ChatRoomInitializer.cpp src/utils/plugins/PluginsManager.cpp ) set(PLUGIN_SOURCES src/utils/plugins/PluginDataAPI.cpp @@ -307,6 +307,7 @@ set(HEADERS src/components/chat-events/ChatMessageListener.hpp src/components/chat-events/ChatMessageModel.hpp src/components/chat-events/ChatNoticeModel.hpp + src/components/chat-room/ChatRoomInitializer.hpp src/components/chat-room/ChatRoomListener.hpp src/components/chat-room/ChatRoomModel.hpp src/components/chat-room/ChatRoomProxyModel.hpp @@ -400,7 +401,6 @@ set(HEADERS src/utils/MediastreamerUtils.hpp src/utils/QExifImageHeader.hpp src/utils/Utils.hpp - src/utils/hacks/ChatRoomInitializer.hpp src/utils/plugins/PluginsManager.hpp ) set(PLUGIN_HEADERS diff --git a/linphone-app/src/components/call/CallModel.cpp b/linphone-app/src/components/call/CallModel.cpp index 0ed57f6a9..c2f0cf0a4 100644 --- a/linphone-app/src/components/call/CallModel.cpp +++ b/linphone-app/src/components/call/CallModel.cpp @@ -27,6 +27,7 @@ #include "app/App.hpp" #include "CallListener.hpp" #include "components/calls/CallsListModel.hpp" +#include "components/chat-room/ChatRoomInitializer.hpp" #include "components/chat-room/ChatRoomListener.hpp" #include "components/chat-room/ChatRoomModel.hpp" #include "components/conference/ConferenceModel.hpp" @@ -55,10 +56,6 @@ void CallModel::connectTo(CallListener * listener){ connect(listener, &CallListener::remoteRecording, this, &CallModel::onRemoteRecording); } -void CallModel::connectTo(ChatRoomListener * listener){ - connect(listener, &ChatRoomListener::stateChanged, this, &CallModel::onChatRoomStateChanged); -} - CallModel::CallModel (shared_ptr call){ CoreManager *coreManager = CoreManager::getInstance(); SettingsModel *settings = coreManager->getSettingsModel(); @@ -81,8 +78,6 @@ CallModel::CallModel (shared_ptr call){ }else settings->setCameraMode(settings->getCallCameraMode()); } - mDelayedCreationChatRoom.first = std::make_shared(); - connectTo(mDelayedCreationChatRoom.first.get()); // Deal with auto-answer. if (!isOutgoing()) { @@ -175,9 +170,9 @@ ChatRoomModel * CallModel::getChatRoomModel(){ auto currentParams = mCall->getCurrentParams(); bool isEncrypted = currentParams->getMediaEncryption() != linphone::MediaEncryption::None; SettingsModel * settingsModel = CoreManager::getInstance()->getSettingsModel(); - if(mDelayedCreationChatRoom.second){// We already created a chat room. - if( mDelayedCreationChatRoom.second->getState() == linphone::ChatRoom::State::Created) - return CoreManager::getInstance()->getTimelineListModel()->getChatRoomModel(mDelayedCreationChatRoom.second, true).get(); + if(mChatRoom){// We already created a chat room. + if( mChatRoom->getState() == linphone::ChatRoom::State::Created) + return CoreManager::getInstance()->getTimelineListModel()->getChatRoomModel(mChatRoom, true).get(); else// Chat room is not yet created. return nullptr; } @@ -209,8 +204,10 @@ ChatRoomModel * CallModel::getChatRoomModel(){ if(chatRoom) return CoreManager::getInstance()->getTimelineListModel()->getChatRoomModel(chatRoom, true).get(); else{// Wait for creation. Secure chat rooms cannot be used before being created. - mDelayedCreationChatRoom.second = CoreManager::getInstance()->getCore()->createChatRoom(params, callLocalAddress, participants);// The result cannot be used. - mDelayedCreationChatRoom.second->addListener(mDelayedCreationChatRoom.first); + mChatRoom = CoreManager::getInstance()->getCore()->createChatRoom(params, callLocalAddress, participants); + auto initializer = ChatRoomInitializer::create(mChatRoom); + connect(initializer.get(), &ChatRoomInitializer::finished, this, &CallModel::onChatRoomInitialized, Qt::DirectConnection); + ChatRoomInitializer::start(initializer); return nullptr; } }else @@ -937,9 +934,9 @@ void CallModel::onRemoteRecording(const std::shared_ptr & call, emit remoteRecordingChanged(recording); } -void CallModel::onChatRoomStateChanged(const std::shared_ptr & chatRoom, linphone::ChatRoom::State newState){ - if(newState == linphone::ChatRoom::State::Created)// A chat room has been created for the call. Warn listeners that the model changed. - emit chatRoomModelChanged(); +void CallModel::onChatRoomInitialized(int state){ + qInfo() << "[CallModel] Chat room initialized with state : " << state; + emit chatRoomModelChanged(); } void CallModel::setRemoteDisplayName(const std::string& name){ diff --git a/linphone-app/src/components/call/CallModel.hpp b/linphone-app/src/components/call/CallModel.hpp index c08313cde..3662a02f2 100644 --- a/linphone-app/src/components/call/CallModel.hpp +++ b/linphone-app/src/components/call/CallModel.hpp @@ -33,7 +33,6 @@ class CallListener; class ConferenceInfoModel; class ConferenceModel; class ContactModel; -class ChatRoomListener; class ChatRoomModel; class CallModel : public QObject { @@ -186,7 +185,7 @@ public: std::shared_ptr mCall; std::shared_ptr mCallListener; // This is passed to linpĥone object and must be in shared_ptr - QPair, std::shared_ptr> mDelayedCreationChatRoom; // For secure chat rooms, we need to wait till it is created by keeping a ref and by using alistener. + std::shared_ptr mChatRoom; // Used chat room for the call. std::shared_ptr mRemoteAddress; std::shared_ptr mMagicSearch; @@ -195,7 +194,7 @@ public slots: void searchReceived(std::list> results); void endCall(); void onRemoteRecording(const std::shared_ptr & call, bool recording); - void onChatRoomStateChanged(const std::shared_ptr & chatRoom, linphone::ChatRoom::State newState); + void onChatRoomInitialized(int state); signals: void callErrorChanged (const QString &callError); @@ -300,7 +299,6 @@ public: private: void connectTo(CallListener * listener); - void connectTo(ChatRoomListener * listener); bool mIsInConference = false; diff --git a/linphone-app/src/components/calls/CallsListModel.cpp b/linphone-app/src/components/calls/CallsListModel.cpp index d6673c8df..c1793b2fe 100644 --- a/linphone-app/src/components/calls/CallsListModel.cpp +++ b/linphone-app/src/components/calls/CallsListModel.cpp @@ -24,6 +24,7 @@ #include "app/App.hpp" #include "components/call/CallModel.hpp" +#include "components/chat-room/ChatRoomInitializer.hpp" #include "components/conference/ConferenceAddModel.hpp" #include "components/conference/ConferenceHelperModel.hpp" #include "components/conference/ConferenceModel.hpp" @@ -38,7 +39,7 @@ #include "CallsListModel.hpp" -#include "utils/hacks/ChatRoomInitializer.hpp" + // ============================================================================= @@ -340,12 +341,15 @@ QVariantMap CallsListModel::createChatRoom(const QString& subject, const int& se params->setSubject(subject != ""?Utils::appStringToCoreString(subject):"Dummy Subject"); if( !chatRoom) { chatRoom = core->createChatRoom(params, localAddress, chatRoomParticipants); - if(chatRoom != nullptr && admins.size() > 0) - ChatRoomInitializer::setAdminsAsync(params->getSubject(), params->getBackend(), params->groupEnabled(), admins ); + if(chatRoom != nullptr && admins.size() > 0){ + auto initializer = ChatRoomInitializer::create(chatRoom); + initializer->setAdminsData(admins); + ChatRoomInitializer::start(initializer); + } timeline = timelineList->getTimeline(chatRoom, false); }else{ if(admins.size() > 0){ - ChatRoomInitializer::setAdminsSync(chatRoom, admins); + ChatRoomInitializer::create(chatRoom)->setAdmins(admins); } timeline = timelineList->getTimeline(chatRoom, true); } diff --git a/linphone-app/src/components/chat-room/ChatRoomInitializer.cpp b/linphone-app/src/components/chat-room/ChatRoomInitializer.cpp new file mode 100644 index 000000000..076d318a6 --- /dev/null +++ b/linphone-app/src/components/chat-room/ChatRoomInitializer.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2021 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 "ChatRoomInitializer.hpp" + +#include +#include + +#include "ChatRoomListener.hpp" +#include "components/core/CoreManager.hpp" +#include "components/core/CoreHandlers.hpp" + +// ============================================================================= + +void ChatRoomInitializer::connectTo(ChatRoomListener * listener){ + connect(listener, &ChatRoomListener::conferenceJoined, this, &ChatRoomInitializer::onConferenceJoined); + connect(listener, &ChatRoomListener::stateChanged, this, &ChatRoomInitializer::onStateChanged); +} + +// ============================================================================= + +ChatRoomInitializer::ChatRoomInitializer(std::shared_ptr chatRoom){ + mChatRoomListener = std::make_shared(); + connectTo(mChatRoomListener.get()); + if( chatRoom){ + mChatRoom = chatRoom; + mChatRoom->addListener(mChatRoomListener); + } +} +ChatRoomInitializer::~ChatRoomInitializer(){ + if(mChatRoom) + mChatRoom->removeListener(mChatRoomListener); +} + +QSharedPointer ChatRoomInitializer::create(std::shared_ptr chatRoom){ + QSharedPointer initializer = QSharedPointer::create(chatRoom); + return initializer; +} + + + +void ChatRoomInitializer::setAdminsData(QList< std::shared_ptr> admins){ + mAdmins = admins; + mAdminsSet = false; +} + +void ChatRoomInitializer::setAdmins(QList< std::shared_ptr> admins){ + if( admins.size() > 0) { + std::list> participants = mChatRoom->getParticipants(); + int count = 0; + for(auto participant : participants){ + auto address = participant->getAddress(); + auto isAdmin = std::find_if(admins.begin(), admins.end(), [address](std::shared_ptr addr){ + return addr->weakEqual(address); + }); + if( isAdmin != admins.end()){ + ++count; + mChatRoom->setParticipantAdminStatus(participant, true); + } + } + mAdminsSet = true; + qInfo() << "[ChatRoomInitializer] '" << admins.size() << "' admin(s) specified in addition of Me, " << count << " set."; + checkInitialization(); + } +} + +void ChatRoomInitializer::start(QSharedPointer initializer){ + QObject * context = new QObject(); + QObject::connect(initializer.get(), &ChatRoomInitializer::finished, context, [context, initializer](int state){ + qInfo() << "[ChatRoomInitializer] initialized"; + context->deleteLater();// This will destroy context and initializer + }); +} + +void ChatRoomInitializer::checkInitialization(){ + if( mAdmins.size() > 0 && !mAdminsSet) + return; + + emit finished((int)mChatRoom->getState()); +} + +void ChatRoomInitializer::onConferenceJoined(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) { + qInfo() << "[ChatRoomInitializer] Conference has been set"; + setAdmins(mAdmins); +} + +void ChatRoomInitializer::onStateChanged(const std::shared_ptr & chatRoom, linphone::ChatRoom::State newState) { + qInfo() << "[ChatRoomInitializer] State : " << (int)newState; + if( newState >= linphone::ChatRoom::State::Created || newState == linphone::ChatRoom::State::Instantiated) { + checkInitialization(); + } +} \ No newline at end of file diff --git a/linphone-app/src/components/chat-room/ChatRoomInitializer.hpp b/linphone-app/src/components/chat-room/ChatRoomInitializer.hpp new file mode 100644 index 000000000..3e2efd470 --- /dev/null +++ b/linphone-app/src/components/chat-room/ChatRoomInitializer.hpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2022 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 . + */ + +#ifndef CHAT_ROOM_INITIALIZER_H_ +#define CHAT_ROOM_INITIALIZER_H_ + + +#include +#include "ChatRoomInitializer.hpp" + + +#include + +#include +// ============================================================================= + +class ChatRoomListener; + +class ChatRoomInitializer : public QObject{ +Q_OBJECT +public: + ChatRoomInitializer(std::shared_ptr chatRoom); + ~ChatRoomInitializer(); +// DATA to set + QList< std::shared_ptr> mAdmins; + bool mAdminsSet = false; + + static QSharedPointer create(std::shared_ptr chatRoom); // Return a shared pointer to pass to start function (if delayed creation is needed) + + void setAdminsData(QList< std::shared_ptr> admins);// Call it to initialize admins for delayed creation. + void setAdmins(QList< std::shared_ptr> admins);// set admins directly in chat room from list + + static void start(QSharedPointer initializer);// Keep a ref and remove it when initialization is finished. + +// Linphone callbacks + virtual void onConferenceJoined(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); + virtual void onStateChanged(const std::shared_ptr & chatRoom, linphone::ChatRoom::State newState); + +signals: + void finished(int state); // this signal is emit before deletion and give the current linphone::ChatRoom:State of the chat room. + +private: + void connectTo(ChatRoomListener * listener); + + void checkInitialization();// Will send finished() if all are done + + std::shared_ptr mChatRoom; + std::shared_ptr mChatRoomListener; // This need to be a shared_ptr because of adding it to linphone +}; +#endif diff --git a/linphone-app/src/utils/hacks/ChatRoomInitializer.cpp b/linphone-app/src/utils/hacks/ChatRoomInitializer.cpp deleted file mode 100644 index 523568dad..000000000 --- a/linphone-app/src/utils/hacks/ChatRoomInitializer.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2021 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 "ChatRoomInitializer.hpp" - -#include -#include - -#include "components/core/CoreManager.hpp" -#include "components/core/CoreHandlers.hpp" - -// ============================================================================= - -ChatRoomInitializer::ChatRoomInitializer(){} -ChatRoomInitializer::~ChatRoomInitializer(){} - -void ChatRoomInitializer::onConferenceJoined(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) { - qInfo() << "[ChatRoomInitializer] Conference has been set"; - if(mAdmins.size() > 0){ - setAdminsSync(chatRoom, mAdmins); - } - chatRoom->removeListener(mSelf); - mSelf = nullptr; -} - -void ChatRoomInitializer::setAdminsSync(const std::shared_ptr & chatRoom, QList< std::shared_ptr> admins){ - std::list> chatRoomParticipants = chatRoom->getParticipants(); - int count = 0; - for(auto participant : chatRoomParticipants){ - auto address = participant->getAddress(); - auto isAdmin = std::find_if(admins.begin(), admins.end(), [address](std::shared_ptr addr){ - return addr->weakEqual(address); - }); - if( isAdmin != admins.end()){ - ++count; - chatRoom->setParticipantAdminStatus(participant, true); - } - } - qInfo() << "[ChatRoomInitializer] '" << admins.size() << "' admin(s) specified in addition of Me, " << count << " set."; -} - -void ChatRoomInitializer::setAdminsAsync(const std::string& subject, const linphone::ChatRoomBackend& backend, const bool& groupEnabled, QList< std::shared_ptr> admins){ - QObject * context = new QObject(); - QObject::connect(CoreManager::getInstance()->getHandlers().get(), &CoreHandlers::chatRoomStateChanged, - context,[context, admins, subject, backend, groupEnabled](const std::shared_ptr &chatRoomEvent,linphone::ChatRoom::State state){ - auto params = chatRoomEvent->getCurrentParams(); - if( subject == chatRoomEvent->getSubject() && backend == params->getBackend() && groupEnabled == params->groupEnabled()){ - if( state == linphone::ChatRoom::State::Created){ - std::shared_ptr init = std::make_shared(); - init->mAdmins = admins; - init->mSelf = init; - chatRoomEvent->addListener(init); - context->deleteLater(); - }else if( state > linphone::ChatRoom::State::Created){// The chat room could be completed. Delete the bind. - context->deleteLater(); - } - } - }); -} diff --git a/linphone-app/src/utils/hacks/ChatRoomInitializer.hpp b/linphone-app/src/utils/hacks/ChatRoomInitializer.hpp deleted file mode 100644 index 12c285dd8..000000000 --- a/linphone-app/src/utils/hacks/ChatRoomInitializer.hpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2021 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 . - */ - -#ifndef CHAT_ROOM_INITIALIZER_H_ -#define CHAT_ROOM_INITIALIZER_H_ - -// Update Admin. We need this hack in order to set parameters without touching on ChatRoom from createChatRoom. -// If something protect the return ChatRoom (like with shared pointer), the future behaviour will be unstable (2 internal instances, wrong ChatRoom objects from callbacks and crash on deletion) -// Thus, we cannot bind to ChatRoom callbacks at the moment of creation and we need to wait for onChatRoomStateChanged from Core Listener and then, react to linphone::ChatRoom::State::Created from the new ChatRoom. -// As we cannot compare exactly the right ChatRoom, we test on subject and parameters that should be enough to be unique at the moment of the creation. -// This is not a 100% (we may protect with a one-time creation) but this workaround should be enough. - -// Used on Core::createChatRoom() - -#include - -#include - -// ============================================================================= - - -class ChatRoomInitializer : public linphone::ChatRoomListener{ -public: - ChatRoomInitializer(); - ~ChatRoomInitializer(); - QList< std::shared_ptr> mAdmins; - std::shared_ptr mSelf; - - virtual void onConferenceJoined(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; - - // Sync : Set Admins to chat room without binding anything (eg. do not wait for any callback and use ChatRoom directly) - static void setAdminsSync(const std::shared_ptr & chatRoom, QList< std::shared_ptr> admins); - - // Async : Bind to core for onChatRoomStateChanged event and then wait of linphone::ChatRoom::State::Created from ChatRoom listener. - static void setAdminsAsync(const std::string& subject, const linphone::ChatRoomBackend& backend, const bool& groupEnabled, QList< std::shared_ptr> admins); -}; -#endif diff --git a/linphone-app/ui/views/App/Calls/CallsWindow.qml b/linphone-app/ui/views/App/Calls/CallsWindow.qml index 0dd919bfa..7f68500ac 100644 --- a/linphone-app/ui/views/App/Calls/CallsWindow.qml +++ b/linphone-app/ui/views/App/Calls/CallsWindow.qml @@ -36,6 +36,7 @@ Window { readonly property bool chatIsOpened: !rightPaned.isClosed() readonly property bool callsIsOpened: !mainPaned.isClosed() + readonly property bool haveChat: rightPane.sourceComponent @@ -267,6 +268,7 @@ Window { id: rightPane anchors.fill: parent sourceComponent: window.call && window.call.chatRoomModel ? chat : null + onSourceComponentChanged: if(!sourceComponent) window.closeChat() } } } diff --git a/linphone-app/ui/views/App/Calls/Incall.qml b/linphone-app/ui/views/App/Calls/Incall.qml index 35e949775..206e31e2d 100644 --- a/linphone-app/ui/views/App/Calls/Incall.qml +++ b/linphone-app/ui/views/App/Calls/Incall.qml @@ -478,7 +478,7 @@ Rectangle { isCustom: true backgroundRadius: width/2 colorSet: IncallStyle.buttons.chat - visible: (SettingsModel.standardChatEnabled || SettingsModel.secureChatEnabled) && callModel && !callModel.isConference + visible: window.haveChat && (SettingsModel.standardChatEnabled || SettingsModel.secureChatEnabled) && callModel && !callModel.isConference toggled: window.chatIsOpened onClicked: { if (window.chatIsOpened) {