diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e5b4002f78d4b83320b998b5e84bc1d49f55c12..1bb35818d3d11bb9a0aa5b9d0f3605acd8eb014f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,7 +74,8 @@ set(COMMON_SOURCES ${SRC_DIR}/moderatorlistmodel.cpp ${SRC_DIR}/screensaver.cpp ${SRC_DIR}/systemtray.cpp - ${SRC_DIR}/appsettingsmanager.cpp) + ${SRC_DIR}/appsettingsmanager.cpp + ${SRC_DIR}/lrcinstance.cpp) set(COMMON_HEADERS ${SRC_DIR}/avatarimageprovider.h @@ -86,7 +87,6 @@ set(COMMON_HEADERS ${SRC_DIR}/version.h ${SRC_DIR}/accountlistmodel.h ${SRC_DIR}/runguard.h - ${SRC_DIR}/lrcinstance.h ${SRC_DIR}/webchathelpers.h ${SRC_DIR}/rendermanager.h ${SRC_DIR}/connectivitymonitor.h @@ -127,7 +127,8 @@ set(COMMON_HEADERS ${SRC_DIR}/moderatorlistmodel.h ${SRC_DIR}/screensaver.h ${SRC_DIR}/systemtray.h - ${SRC_DIR}/appsettingsmanager.h) + ${SRC_DIR}/appsettingsmanager.h + ${SRC_DIR}/lrcinstance.h) find_package(PkgConfig REQUIRED) diff --git a/jami-qt.pro b/jami-qt.pro index 5ad3e8ff0eb26713d6fa2930667a1c1d00bca59b..9f0417c63f01568b0f9b65c6fe931117fe9c39a2 100644 --- a/jami-qt.pro +++ b/jami-qt.pro @@ -104,7 +104,6 @@ HEADERS += \ src/version.h \ src/accountlistmodel.h \ src/runguard.h \ - src/lrcinstance.h \ src/webchathelpers.h \ src/rendermanager.h \ src/connectivitymonitor.h \ @@ -139,7 +138,8 @@ HEADERS += \ src/qtutils.h \ src/utilsadapter.h \ src/systemtray.h \ - src/appsettingsmanager.h + src/appsettingsmanager.h \ + src/lrcinstance.h SOURCES += \ src/bannedlistmodel.cpp \ @@ -181,7 +181,8 @@ SOURCES += \ src/qmlregister.cpp \ src/utilsadapter.cpp \ src/systemtray.cpp \ - src/appsettingsmanager.cpp + src/appsettingsmanager.cpp \ + src/lrcinstance.cpp RESOURCES += \ resources.qrc \ diff --git a/qml.qrc b/qml.qrc index dc35418b24c6a6fa8255d5f05bde13863c5bc37b..1f5a159bad79061cfd80dbe317ba23d0b4da82a2 100644 --- a/qml.qrc +++ b/qml.qrc @@ -1,6 +1,40 @@ <RCC> <qresource prefix="/"> + <file>src/MainApplicationWindow.qml</file> + <file>src/DaemonReconnectWindow.qml</file> + <file>src/constant/JamiQmlUtils.qml</file> <file>src/constant/JamiStrings.qml</file> + <file>src/constant/JamiTheme.qml</file> + <file>src/commoncomponents/SettingParaCombobox.qml</file> + <file>src/commoncomponents/PreferenceItemDelegate.qml</file> + <file>src/commoncomponents/PasswordDialog.qml</file> + <file>src/commoncomponents/MaterialLineEdit.qml</file> + <file>src/commoncomponents/PhotoboothView.qml</file> + <file>src/commoncomponents/LookupStatusLabel.qml</file> + <file>src/commoncomponents/ListViewJami.qml</file> + <file>src/commoncomponents/DeleteAccountDialog.qml</file> + <file>src/commoncomponents/CustomBorder.qml</file> + <file>src/commoncomponents/PushButton.qml</file> + <file>src/commoncomponents/JamiFileDialog.qml</file> + <file>src/commoncomponents/TintedButton.qml</file> + <file>src/commoncomponents/GeneralMenuItem.qml</file> + <file>src/commoncomponents/GeneralMenuSeparator.qml</file> + <file>src/commoncomponents/AccountMigrationDialog.qml</file> + <file>src/commoncomponents/MaterialButton.qml</file> + <file>src/commoncomponents/ElidedTextLabel.qml</file> + <file>src/commoncomponents/js/contextmenugenerator.js</file> + <file>src/commoncomponents/BaseContextMenu.qml</file> + <file>src/commoncomponents/SpinnerButton.qml</file> + <file>src/commoncomponents/UsernameLineEdit.qml</file> + <file>src/commoncomponents/Scaffold.qml</file> + <file>src/commoncomponents/LineEditContextMenu.qml</file> + <file>src/commoncomponents/BaseDialog.qml</file> + <file>src/commoncomponents/ModalPopup.qml</file> + <file>src/commoncomponents/SimpleMessageDialog.qml</file> + <file>src/commoncomponents/ResponsiveImage.qml</file> + <file>src/commoncomponents/PresenceIndicator.qml</file> + <file>src/commoncomponents/AvatarImage.qml</file> + <file>src/commoncomponents/DaemonReconnectPopup.qml</file> <file>src/settingsview/SettingsView.qml</file> <file>src/settingsview/components/ChatviewSettings.qml</file> <file>src/settingsview/components/SettingsMenu.qml</file> @@ -42,22 +76,13 @@ <file>src/settingsview/components/SettingsComboBox.qml</file> <file>src/settingsview/components/SettingsMaterialLineEdit.qml</file> <file>src/settingsview/components/LevelMeter.qml</file> - <file>src/commoncomponents/SettingParaCombobox.qml</file> <file>src/settingsview/components/DeviceItemDelegate.qml</file> <file>src/settingsview/components/PluginItemDelegate.qml</file> - <file>src/mainview/components/PluginHandlerItemDelegate.qml</file> - <file>src/commoncomponents/PreferenceItemDelegate.qml</file> <file>src/settingsview/components/ContactItemDelegate.qml</file> <file>src/settingsview/components/MediaCodecDelegate.qml</file> <file>src/settingsview/components/NameRegistrationDialog.qml</file> <file>src/settingsview/components/LinkDeviceDialog.qml</file> <file>src/settingsview/components/RevokeDevicePasswordDialog.qml</file> - <file>src/commoncomponents/PasswordDialog.qml</file> - <file>src/commoncomponents/MaterialLineEdit.qml</file> - <file>src/commoncomponents/PhotoboothView.qml</file> - <file>src/commoncomponents/LookupStatusLabel.qml</file> - <file>src/commoncomponents/ListViewJami.qml</file> - <file>src/commoncomponents/DeleteAccountDialog.qml</file> <file>src/wizardview/WizardView.qml</file> <file>src/wizardview/components/WelcomePage.qml</file> <file>src/wizardview/components/CreateAccountPage.qml</file> @@ -68,27 +93,22 @@ <file>src/wizardview/components/ConnectToAccountManagerPage.qml</file> <file>src/wizardview/components/ProfilePage.qml</file> <file>src/wizardview/components/CollapsiblePasswordWidget.qml</file> - <file>src/MainApplicationWindow.qml</file> + <file>src/wizardview/components/AccountCreationStepIndicator.qml</file> <file>src/mainview/MainView.qml</file> - <file>src/commoncomponents/CustomBorder.qml</file> - <file>src/constant/JamiTheme.qml</file> + <file>src/mainview/components/PluginHandlerItemDelegate.qml</file> <file>src/mainview/components/AboutPopUp.qml</file> <file>src/mainview/components/SidePanel.qml</file> <file>src/mainview/components/WelcomePage.qml</file> <file>src/mainview/components/MessageWebView.qml</file> <file>src/mainview/components/MessageWebViewHeader.qml</file> - <file>src/commoncomponents/PushButton.qml</file> <file>src/mainview/components/AccountComboBox.qml</file> <file>src/mainview/components/ConversationSmartListView.qml</file> - <file>src/commoncomponents/JamiFileDialog.qml</file> <file>src/mainview/components/CallStackView.qml</file> <file>src/mainview/components/IncomingCallPage.qml</file> <file>src/mainview/components/OutgoingCallPage.qml</file> <file>src/mainview/components/AudioCallPage.qml</file> <file>src/mainview/components/CallOverlay.qml</file> - <file>src/commoncomponents/TintedButton.qml</file> <file>src/mainview/components/CallOverlayButtonGroup.qml</file> - <file>src/mainview/js/incomingcallpagecreation.js</file> <file>src/mainview/components/ContactSearchBar.qml</file> <file>src/mainview/components/VideoCallPage.qml</file> <file>src/mainview/components/ParticipantOverlay.qml</file> @@ -97,46 +117,26 @@ <file>src/mainview/components/ConversationSmartListViewItemDelegate.qml</file> <file>src/mainview/components/SidePanelTabBar.qml</file> <file>src/mainview/components/WelcomePageQrDialog.qml</file> - <file>src/commoncomponents/GeneralMenuItem.qml</file> <file>src/mainview/components/ConversationSmartListContextMenu.qml</file> <file>src/mainview/components/CallViewContextMenu.qml</file> - <file>src/commoncomponents/GeneralMenuSeparator.qml</file> <file>src/mainview/components/UserProfile.qml</file> - <file>src/mainview/js/videodevicecontextmenuitemcreation.js</file> <file>src/mainview/components/VideoCallPageContextMenuDeviceItem.qml</file> <file>src/mainview/components/SelectScreen.qml</file> - <file>src/mainview/js/selectscreenwindowcreation.js</file> <file>src/mainview/components/ScreenRubberBand.qml</file> - <file>src/mainview/js/screenrubberbandcreation.js</file> - <file>src/mainview/js/callfullscreenwindowcontainercreation.js</file> <file>src/mainview/components/VideoCallFullScreenWindowContainer.qml</file> <file>src/mainview/components/ContactPicker.qml</file> <file>src/mainview/components/PluginHandlerPicker.qml</file> - <file>src/mainview/js/contactpickercreation.js</file> - <file>src/mainview/js/pluginhandlerpickercreation.js</file> <file>src/mainview/components/ContactPickerItemDelegate.qml</file> - <file>src/commoncomponents/AccountMigrationDialog.qml</file> - <file>src/commoncomponents/MaterialButton.qml</file> <file>src/mainview/components/RecordBox.qml</file> - <file>src/commoncomponents/ElidedTextLabel.qml</file> <file>src/mainview/components/SipInputPanel.qml</file> - <file>src/commoncomponents/js/contextmenugenerator.js</file> - <file>src/commoncomponents/BaseContextMenu.qml</file> - <file>src/commoncomponents/Scaffold.qml</file> - <file>src/constant/JamiQmlUtils.qml</file> - <file>src/wizardview/components/AccountCreationStepIndicator.qml</file> - <file>src/commoncomponents/SpinnerButton.qml</file> - <file>src/commoncomponents/UsernameLineEdit.qml</file> - <file>src/mainview/components/UserInfoCallPage.qml</file> - <file>src/commoncomponents/BaseDialog.qml</file> - <file>src/commoncomponents/ModalPopup.qml</file> - <file>src/commoncomponents/SimpleMessageDialog.qml</file> - <file>src/commoncomponents/ResponsiveImage.qml</file> - <file>src/commoncomponents/PresenceIndicator.qml</file> - <file>src/commoncomponents/AvatarImage.qml</file> <file>src/mainview/components/ParticipantOverlayMenu.qml</file> - <file>src/commoncomponents/DaemonReconnectPopup.qml</file> - <file>src/DaemonReconnectWindow.qml</file> - <file>src/commoncomponents/LineEditContextMenu.qml</file> + <file>src/mainview/components/UserInfoCallPage.qml</file> + <file>src/mainview/js/videodevicecontextmenuitemcreation.js</file> + <file>src/mainview/js/incomingcallpagecreation.js</file> + <file>src/mainview/js/selectscreenwindowcreation.js</file> + <file>src/mainview/js/screenrubberbandcreation.js</file> + <file>src/mainview/js/callfullscreenwindowcontainercreation.js</file> + <file>src/mainview/js/contactpickercreation.js</file> + <file>src/mainview/js/pluginhandlerpickercreation.js</file> </qresource> </RCC> diff --git a/src/calladapter.cpp b/src/calladapter.cpp index 9a3b4495c5edfd3b3d125114c8ac42e8cb819c67..6f5cba4fa47ea2239196e068c2cde52d0bb041ad 100644 --- a/src/calladapter.cpp +++ b/src/calladapter.cpp @@ -28,6 +28,8 @@ #include "utils.h" #include <QApplication> +#include <QTimer> +#include <QJsonObject> CallAdapter::CallAdapter(SystemTray* systemTray, LRCInstance* instance, QObject* parent) : QmlAdapterBase(instance, parent) @@ -475,7 +477,7 @@ CallAdapter::connectCallModel(const QString& accountId) */ bool forceCallOnly {false}; if (!convInfo.confId.isEmpty()) { - auto callList = lrcInstance_->getAPI().getConferenceSubcalls(convInfo.confId); + auto callList = lrcInstance_->getConferenceSubcalls(convInfo.confId); if (callList.empty()) { auto lastConference = lrcInstance_->poplastConference(convInfo.confId); if (!lastConference.isEmpty()) { @@ -612,7 +614,7 @@ CallAdapter::hangupCall(const QString& uri) * so we can switch the smartlist index after termination. */ if (!convInfo.confId.isEmpty()) { - auto callList = lrcInstance_->getAPI().getConferenceSubcalls(convInfo.confId); + auto callList = lrcInstance_->getConferenceSubcalls(convInfo.confId); if (callList.size() == 2) { for (const auto& cId : callList) { if (cId != convInfo.callId) { diff --git a/src/lrcinstance.cpp b/src/lrcinstance.cpp new file mode 100644 index 0000000000000000000000000000000000000000..deed90833b61838dbfd5d3ea733f88f87ee67ecd --- /dev/null +++ b/src/lrcinstance.cpp @@ -0,0 +1,425 @@ +/* + * Copyright (C) 2019-2021 by Savoir-faire Linux + * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> + * Author: Isa Nanic <isa.nanic@savoirfairelinux.com> + * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "lrcinstance.h" + +#include <QBuffer> +#include <QMutex> +#include <QObject> +#include <QPixmap> +#include <QRegularExpression> +#include <QSettings> +#include <QtConcurrent/QtConcurrent> + +LRCInstance::LRCInstance(migrateCallback willMigrateCb, + migrateCallback didMigrateCb, + const QString& updateUrl, + ConnectivityMonitor* connectivityMonitor) + : lrc_(std::make_unique<Lrc>(willMigrateCb, didMigrateCb)) + , renderer_(std::make_unique<RenderManager>(lrc_->getAVModel())) + , updateManager_(std::make_unique<UpdateManager>(updateUrl, connectivityMonitor, this)) +{ + lrc_->holdConferences = false; +}; + +VectorString +LRCInstance::getConferenceSubcalls(const QString& callId) +{ + return lrc_->getConferenceSubcalls(callId); +} + +RenderManager* +LRCInstance::renderer() +{ + return renderer_.get(); +} + +UpdateManager* +LRCInstance::getUpdateManager() +{ + return updateManager_.get(); +} + +void +LRCInstance::connectivityChanged() +{ + lrc_->connectivityChanged(); +} + +NewAccountModel& +LRCInstance::accountModel() +{ + return lrc_->getAccountModel(); +} + +BehaviorController& +LRCInstance::behaviorController() +{ + return lrc_->getBehaviorController(); +} + +DataTransferModel& +LRCInstance::dataTransferModel() +{ + return lrc_->getDataTransferModel(); +} + +AVModel& +LRCInstance::avModel() +{ + return lrc_->getAVModel(); +} + +PluginModel& +LRCInstance::pluginModel() +{ + return lrc_->getPluginModel(); +} + +bool +LRCInstance::isConnected() +{ + return lrc_->isConnected(); +} + +VectorString +LRCInstance::getActiveCalls() +{ + return lrc_->activeCalls(); +} + +const account::Info& +LRCInstance::getAccountInfo(const QString& accountId) +{ + return accountModel().getAccountInfo(accountId); +} + +const account::Info& +LRCInstance::getCurrentAccountInfo() +{ + return getAccountInfo(getCurrAccId()); +} + +bool +LRCInstance::hasVideoCall() +{ + auto activeCalls = lrc_->activeCalls(); + auto accountList = accountModel().getAccountList(); + bool result = false; + for (const auto& callId : activeCalls) { + for (const auto& accountId : accountList) { + auto& accountInfo = accountModel().getAccountInfo(accountId); + if (accountInfo.callModel->hasCall(callId)) { + auto call = accountInfo.callModel->getCall(callId); + result |= !(call.isAudioOnly || call.videoMuted); + } + } + } + return result; +} + +QString +LRCInstance::getCallIdForConversationUid(const QString& convUid, const QString& accountId) +{ + const auto& convInfo = getConversationFromConvUid(convUid, accountId); + if (convInfo.uid.isEmpty()) { + return {}; + } + return convInfo.confId.isEmpty() ? convInfo.callId : convInfo.confId; +} + +const call::Info* +LRCInstance::getCallInfo(const QString& callId, const QString& accountId) +{ + try { + auto& accInfo = accountModel().getAccountInfo(accountId); + if (!accInfo.callModel->hasCall(callId)) { + return nullptr; + } + return &accInfo.callModel->getCall(callId); + } catch (...) { + return nullptr; + } +} + +const call::Info* +LRCInstance::getCallInfoForConversation(const conversation::Info& convInfo, bool forceCallOnly) +{ + try { + auto accountId = convInfo.accountId; + auto& accInfo = accountModel().getAccountInfo(accountId); + auto callId = forceCallOnly + ? convInfo.callId + : (convInfo.confId.isEmpty() ? convInfo.callId : convInfo.confId); + if (!accInfo.callModel->hasCall(callId)) { + return nullptr; + } + return &accInfo.callModel->getCall(callId); + } catch (...) { + return nullptr; + } +} + +const conversation::Info& +LRCInstance::getConversationFromConvUid(const QString& convUid, const QString& accountId) +{ + auto& accInfo = accountModel().getAccountInfo(!accountId.isEmpty() ? accountId : getCurrAccId()); + auto& convModel = accInfo.conversationModel; + return convModel->getConversationForUid(convUid).value_or(invalid); +} + +const conversation::Info& +LRCInstance::getConversationFromPeerUri(const QString& peerUri, const QString& accountId) +{ + auto& accInfo = accountModel().getAccountInfo(!accountId.isEmpty() ? accountId : getCurrAccId()); + auto& convModel = accInfo.conversationModel; + return convModel->getConversationForPeerUri(peerUri).value_or(invalid); +} + +const conversation::Info& +LRCInstance::getConversationFromCallId(const QString& callId, const QString& accountId) +{ + auto& accInfo = accountModel().getAccountInfo(!accountId.isEmpty() ? accountId : getCurrAccId()); + auto& convModel = accInfo.conversationModel; + return convModel->getConversationForCallId(callId).value_or(invalid); +} + +ConversationModel* +LRCInstance::getCurrentConversationModel() +{ + return getCurrentAccountInfo().conversationModel.get(); +} + +NewCallModel* +LRCInstance::getCurrentCallModel() +{ + return getCurrentAccountInfo().callModel.get(); +} + +const QString& +LRCInstance::getCurrAccId() +{ + if (selectedAccountId_.isEmpty()) { + auto accountList = accountModel().getAccountList(); + if (accountList.size()) + selectedAccountId_ = accountList.at(0); + } + return selectedAccountId_; +} + +void +LRCInstance::setSelectedAccountId(const QString& accountId) +{ + if (accountId == selectedAccountId_) + return; // No need to select current selected account + + selectedAccountId_ = accountId; + + // Last selected account should be set as preferred. + accountModel().setTopAccount(accountId); + + Q_EMIT currentAccountChanged(); +} + +const QString& +LRCInstance::getCurrentConvUid() +{ + return selectedConvUid_; +} + +void +LRCInstance::setSelectedConvId(const QString& convUid) +{ + selectedConvUid_ = convUid; +} + +int +LRCInstance::getCurrentAccountIndex() +{ + for (int i = 0; i < accountModel().getAccountList().size(); i++) { + if (accountModel().getAccountList()[i] == getCurrAccId()) { + return i; + } + } + return -1; +} + +void +LRCInstance::setAvatarForAccount(const QPixmap& avatarPixmap, const QString& accountID) +{ + QByteArray ba; + QBuffer bu(&ba); + bu.open(QIODevice::WriteOnly); + avatarPixmap.save(&bu, "PNG"); + auto str = QString::fromLocal8Bit(ba.toBase64()); + accountModel().setAvatar(accountID, str); +} + +void +LRCInstance::setCurrAccAvatar(const QPixmap& avatarPixmap) +{ + QByteArray ba; + QBuffer bu(&ba); + bu.open(QIODevice::WriteOnly); + avatarPixmap.save(&bu, "PNG"); + auto str = QString::fromLocal8Bit(ba.toBase64()); + accountModel().setAvatar(getCurrAccId(), str); +} + +void +LRCInstance::setCurrAccAvatar(const QString& avatar) +{ + accountModel().setAvatar(getCurrAccId(), avatar); +} + +void +LRCInstance::setCurrAccDisplayName(const QString& displayName) +{ + auto accountId = getCurrAccId(); + accountModel().setAlias(accountId, displayName); + /* + * Force save to .yml. + */ + auto confProps = accountModel().getAccountConfig(accountId); + accountModel().setAccountConfig(accountId, confProps); +} + +const account::ConfProperties_t& +LRCInstance::getCurrAccConfig() +{ + return getCurrentAccountInfo().confProperties; +} + +void +LRCInstance::subscribeToDebugReceived() +{ + lrc_->subscribeToDebugReceived(); +} + +void +LRCInstance::startAudioMeter(bool async) +{ + auto f = [this] { + if (!getActiveCalls().size()) { + avModel().startAudioDevice(); + } + avModel().setAudioMeterState(true); + }; + if (async) { + QtConcurrent::run(f); + } else { + f(); + } +} + +void +LRCInstance::stopAudioMeter(bool async) +{ + auto f = [this] { + if (!getActiveCalls().size()) { + avModel().stopAudioDevice(); + } + avModel().setAudioMeterState(false); + }; + if (async) { + QtConcurrent::run(f); + } else { + f(); + } +} + +QString +LRCInstance::getContentDraft(const QString& convUid, const QString& accountId) +{ + auto draftKey = accountId + "_" + convUid; + return contentDrafts_[draftKey]; +} + +void +LRCInstance::setContentDraft(const QString& convUid, + const QString& accountId, + const QString& content) +{ + auto draftKey = accountId + "_" + convUid; + contentDrafts_[draftKey] = content; +} + +void +LRCInstance::pushlastConference(const QString& confId, const QString& callId) +{ + lastConferences_[confId] = callId; +} + +QString +LRCInstance::poplastConference(const QString& confId) +{ + QString callId = {}; + auto iter = lastConferences_.find(confId); + if (iter != lastConferences_.end()) { + callId = iter.value(); + lastConferences_.erase(iter); + } + return callId; +} + +void +LRCInstance::selectConversation(const QString& accountId, const QString& convUid) +{ + const auto& convInfo = getConversationFromConvUid(convUid, accountId); + + if (getCurrentConvUid() != convInfo.uid || convInfo.participants.size() > 0) { + // If the account is not currently selected, do that first, then + // proceed to select the conversation. + auto selectConversation = [this, accountId, convUid = convInfo.uid] { + const auto& convInfo = getConversationFromConvUid(convUid, accountId); + if (convInfo.uid.isEmpty()) { + return; + } + auto& accInfo = getAccountInfo(convInfo.accountId); + setSelectedConvId(convInfo.uid); + accInfo.conversationModel->clearUnreadInteractions(convInfo.uid); + + try { + // Set contact filter (for conversation tab selection) + auto& contact = accInfo.contactModel->getContact(convInfo.participants.front()); + setProperty("currentTypeFilter", QVariant::fromValue(contact.profileInfo.type)); + } catch (const std::out_of_range& e) { + qDebug() << e.what(); + } + }; + if (convInfo.accountId != getCurrAccId()) { + Utils::oneShotConnect(this, &LRCInstance::currentAccountChanged, [selectConversation] { + selectConversation(); + }); + setSelectedConvId(); + setSelectedAccountId(convInfo.accountId); + } else { + selectConversation(); + } + } + Q_EMIT conversationSelected(); +} + +void +LRCInstance::finish() +{ + renderer_.reset(); + lrc_.reset(); +} diff --git a/src/lrcinstance.h b/src/lrcinstance.h index e5d8ccdc36068a88c7cb94e6774e7f9310b8f119..f50ba8688d87ad0d56f4b5be09aed35cb582f5ec 100644 --- a/src/lrcinstance.h +++ b/src/lrcinstance.h @@ -1,5 +1,5 @@ -/*! - * Copyright (C) 2019-2020 by Savoir-faire Linux +/* + * Copyright (C) 2019-2021 by Savoir-faire Linux * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> * Author: Isa Nanic <isa.nanic@savoirfairelinux.com> * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com> @@ -29,29 +29,18 @@ #include "qtutils.h" #include "utils.h" +#include "api/lrc.h" #include "api/account.h" #include "api/avmodel.h" -#include "api/pluginmodel.h" #include "api/behaviorcontroller.h" #include "api/contact.h" #include "api/contactmodel.h" #include "api/conversation.h" #include "api/conversationmodel.h" -#include "api/datatransfermodel.h" -#include "api/lrc.h" #include "api/newaccountmodel.h" #include "api/newcallmodel.h" -#include "api/newcodecmodel.h" -#include "api/newdevicemodel.h" -#include "api/peerdiscoverymodel.h" -#include <QBuffer> -#include <QMutex> #include <QObject> -#include <QPixmap> -#include <QRegularExpression> -#include <QSettings> -#include <QtConcurrent/QtConcurrent> #include <memory> @@ -67,369 +56,65 @@ class LRCInstance : public QObject Q_OBJECT public: - LRCInstance(migrateCallback willMigrateCb = {}, - migrateCallback didMigrateCb = {}, - const QString& updateUrl = {}, - ConnectivityMonitor* connectivityMonitor = {}) - { - lrc_ = std::make_unique<Lrc>(willMigrateCb, didMigrateCb); - renderer_ = std::make_unique<RenderManager>(lrc_->getAVModel()); - updateManager_ = std::make_unique<UpdateManager>(updateUrl, connectivityMonitor, this); - }; - - Lrc& getAPI() - { - return *(lrc_); - } - - RenderManager* renderer() - { - return renderer_.get(); - } - - UpdateManager* getUpdateManager() - { - return updateManager_.get(); - } - - void connectivityChanged() - { - lrc_->connectivityChanged(); - } - - NewAccountModel& accountModel() - { - return lrc_->getAccountModel(); - } - - BehaviorController& behaviorController() - { - return lrc_->getBehaviorController(); - } - - DataTransferModel& dataTransferModel() - { - return lrc_->getDataTransferModel(); - } - - AVModel& avModel() - { - return lrc_->getAVModel(); - } - - PluginModel& pluginModel() - { - return lrc_->getPluginModel(); - } - - bool isConnected() - { - return lrc_->isConnected(); - } - - VectorString getActiveCalls() - { - return lrc_->activeCalls(); - } - - const account::Info& getAccountInfo(const QString& accountId) - { - return accountModel().getAccountInfo(accountId); - } - - const account::Info& getCurrentAccountInfo() - { - return getAccountInfo(getCurrAccId()); - } - - bool hasVideoCall() - { - auto activeCalls = lrc_->activeCalls(); - auto accountList = accountModel().getAccountList(); - bool result = false; - for (const auto& callId : activeCalls) { - for (const auto& accountId : accountList) { - auto& accountInfo = accountModel().getAccountInfo(accountId); - if (accountInfo.callModel->hasCall(callId)) { - auto call = accountInfo.callModel->getCall(callId); - result |= !(call.isAudioOnly || call.videoMuted); - } - } - } - return result; - } - - QString getCallIdForConversationUid(const QString& convUid, const QString& accountId) - { - const auto& convInfo = getConversationFromConvUid(convUid, accountId); - if (convInfo.uid.isEmpty()) { - return {}; - } - return convInfo.confId.isEmpty() ? convInfo.callId : convInfo.confId; - } - - const call::Info* getCallInfo(const QString& callId, const QString& accountId) - { - try { - auto& accInfo = accountModel().getAccountInfo(accountId); - if (!accInfo.callModel->hasCall(callId)) { - return nullptr; - } - return &accInfo.callModel->getCall(callId); - } catch (...) { - return nullptr; - } - } - + explicit LRCInstance(migrateCallback willMigrateCb = {}, + migrateCallback didMigrateCb = {}, + const QString& updateUrl = {}, + ConnectivityMonitor* connectivityMonitor = {}); + ~LRCInstance() = default; + + void finish(); + + RenderManager* renderer(); + UpdateManager* getUpdateManager(); + + NewAccountModel& accountModel(); + ConversationModel* getCurrentConversationModel(); + NewCallModel* getCurrentCallModel(); + AVModel& avModel(); + PluginModel& pluginModel(); + BehaviorController& behaviorController(); + DataTransferModel& dataTransferModel(); + + void subscribeToDebugReceived(); + bool isConnected(); + void connectivityChanged(); + VectorString getActiveCalls(); + + const account::Info& getAccountInfo(const QString& accountId); + const account::Info& getCurrentAccountInfo(); + QString getCallIdForConversationUid(const QString& convUid, const QString& accountId); + const call::Info* getCallInfo(const QString& callId, const QString& accountId); const call::Info* getCallInfoForConversation(const conversation::Info& convInfo, - bool forceCallOnly = {}) - { - try { - auto accountId = convInfo.accountId; - auto& accInfo = accountModel().getAccountInfo(accountId); - auto callId = forceCallOnly - ? convInfo.callId - : (convInfo.confId.isEmpty() ? convInfo.callId : convInfo.confId); - if (!accInfo.callModel->hasCall(callId)) { - return nullptr; - } - return &accInfo.callModel->getCall(callId); - } catch (...) { - return nullptr; - } - } - + bool forceCallOnly = {}); const conversation::Info& getConversationFromConvUid(const QString& convUid, - const QString& accountId = {}) - { - auto& accInfo = accountModel().getAccountInfo(!accountId.isEmpty() ? accountId - : getCurrAccId()); - auto& convModel = accInfo.conversationModel; - return convModel->getConversationForUid(convUid).value_or(invalid); - } - + const QString& accountId = {}); const conversation::Info& getConversationFromPeerUri(const QString& peerUri, - const QString& accountId = {}) - { - auto& accInfo = accountModel().getAccountInfo(!accountId.isEmpty() ? accountId - : getCurrAccId()); - auto& convModel = accInfo.conversationModel; - return convModel->getConversationForPeerUri(peerUri).value_or(invalid); - } - + const QString& accountId = {}); const conversation::Info& getConversationFromCallId(const QString& callId, - const QString& accountId = {}) - { - auto& accInfo = accountModel().getAccountInfo(!accountId.isEmpty() ? accountId - : getCurrAccId()); - auto& convModel = accInfo.conversationModel; - return convModel->getConversationForCallId(callId).value_or(invalid); - } - - ConversationModel* getCurrentConversationModel() - { - return getCurrentAccountInfo().conversationModel.get(); - } - - NewCallModel* getCurrentCallModel() - { - return getCurrentAccountInfo().callModel.get(); - } - - const QString& getCurrAccId() - { - if (selectedAccountId_.isEmpty()) { - auto accountList = accountModel().getAccountList(); - if (accountList.size()) - selectedAccountId_ = accountList.at(0); - } - return selectedAccountId_; - } - - void setSelectedAccountId(const QString& accountId = {}) - { - if (accountId == selectedAccountId_) - return; // No need to select current selected account - - selectedAccountId_ = accountId; - - // Last selected account should be set as preferred. - accountModel().setTopAccount(accountId); - - Q_EMIT currentAccountChanged(); - } - - const QString& getCurrentConvUid() - { - return selectedConvUid_; - } - - void setSelectedConvId(const QString& convUid = {}) - { - selectedConvUid_ = convUid; - } - - void reset(bool newInstance = false) - { - if (newInstance) { - renderer_.reset(new RenderManager(avModel())); - lrc_.reset(new Lrc()); - } else { - renderer_.reset(); - lrc_.reset(); - } - } - - int getCurrentAccountIndex() - { - for (int i = 0; i < accountModel().getAccountList().size(); i++) { - if (accountModel().getAccountList()[i] == getCurrAccId()) { - return i; - } - } - return -1; - } - - void setAvatarForAccount(const QPixmap& avatarPixmap, const QString& accountID) - { - QByteArray ba; - QBuffer bu(&ba); - bu.open(QIODevice::WriteOnly); - avatarPixmap.save(&bu, "PNG"); - auto str = QString::fromLocal8Bit(ba.toBase64()); - accountModel().setAvatar(accountID, str); - } - - void setCurrAccAvatar(const QPixmap& avatarPixmap) - { - QByteArray ba; - QBuffer bu(&ba); - bu.open(QIODevice::WriteOnly); - avatarPixmap.save(&bu, "PNG"); - auto str = QString::fromLocal8Bit(ba.toBase64()); - accountModel().setAvatar(getCurrAccId(), str); - } - - void setCurrAccAvatar(const QString& avatar) - { - accountModel().setAvatar(getCurrAccId(), avatar); - } - - void setCurrAccDisplayName(const QString& displayName) - { - auto accountId = getCurrAccId(); - accountModel().setAlias(accountId, displayName); - /* - * Force save to .yml. - */ - auto confProps = accountModel().getAccountConfig(accountId); - accountModel().setAccountConfig(accountId, confProps); - } - - const account::ConfProperties_t& getCurrAccConfig() - { - return getCurrentAccountInfo().confProperties; - } - - void subscribeToDebugReceived() - { - lrc_->subscribeToDebugReceived(); - } - - void startAudioMeter(bool async) - { - auto f = [this] { - if (!getActiveCalls().size()) { - avModel().startAudioDevice(); - } - avModel().setAudioMeterState(true); - }; - if (async) { - QtConcurrent::run(f); - } else { - f(); - } - } - - void stopAudioMeter(bool async) - { - auto f = [this] { - if (!getActiveCalls().size()) { - avModel().stopAudioDevice(); - } - avModel().setAudioMeterState(false); - }; - if (async) { - QtConcurrent::run(f); - } else { - f(); - } - } - - QString getContentDraft(const QString& convUid, const QString& accountId) - { - auto draftKey = accountId + "_" + convUid; - return contentDrafts_[draftKey]; - } - - void setContentDraft(const QString& convUid, const QString& accountId, const QString& content) - { - auto draftKey = accountId + "_" + convUid; - contentDrafts_[draftKey] = content; - } - - void pushlastConference(const QString& confId, const QString& callId) - { - lastConferences_[confId] = callId; - } - - QString poplastConference(const QString& confId) - { - QString callId = {}; - auto iter = lastConferences_.find(confId); - if (iter != lastConferences_.end()) { - callId = iter.value(); - lastConferences_.erase(iter); - } - return callId; - } - - void selectConversation(const QString& accountId, const QString& convUid) - { - const auto& convInfo = getConversationFromConvUid(convUid, accountId); - - if (getCurrentConvUid() != convInfo.uid || convInfo.participants.size() > 0) { - // If the account is not currently selected, do that first, then - // proceed to select the conversation. - auto selectConversation = [this, accountId, convUid = convInfo.uid] { - const auto& convInfo = getConversationFromConvUid(convUid, accountId); - if (convInfo.uid.isEmpty()) { - return; - } - auto& accInfo = getAccountInfo(convInfo.accountId); - setSelectedConvId(convInfo.uid); - accInfo.conversationModel->clearUnreadInteractions(convInfo.uid); - - try { - // Set contact filter (for conversation tab selection) - auto& contact = accInfo.contactModel->getContact(convInfo.participants.front()); - setProperty("currentTypeFilter", QVariant::fromValue(contact.profileInfo.type)); - } catch (const std::out_of_range& e) { - qDebug() << e.what(); - } - }; - if (convInfo.accountId != getCurrAccId()) { - Utils::oneShotConnect(this, - &LRCInstance::currentAccountChanged, - [selectConversation] { selectConversation(); }); - setSelectedConvId(); - setSelectedAccountId(convInfo.accountId); - } else { - selectConversation(); - } - } - Q_EMIT conversationSelected(); - } + const QString& accountId = {}); + + const QString& getCurrAccId(); + void setSelectedAccountId(const QString& accountId = {}); + const QString& getCurrentConvUid(); + void selectConversation(const QString& accountId, const QString& convUid); + void setSelectedConvId(const QString& convUid = {}); + int getCurrentAccountIndex(); + void setAvatarForAccount(const QPixmap& avatarPixmap, const QString& accountID); + void setCurrAccAvatar(const QPixmap& avatarPixmap); + void setCurrAccAvatar(const QString& avatar); + void setCurrAccDisplayName(const QString& displayName); + const account::ConfProperties_t& getCurrAccConfig(); + + void startAudioMeter(bool async); + void stopAudioMeter(bool async); + + QString getContentDraft(const QString& convUid, const QString& accountId); + void setContentDraft(const QString& convUid, const QString& accountId, const QString& content); + + bool hasVideoCall(); + void pushlastConference(const QString& confId, const QString& callId); + QString poplastConference(const QString& confId); + VectorString getConferenceSubcalls(const QString& callId); Q_SIGNALS: void accountListChanged(); diff --git a/src/mainapplication.cpp b/src/mainapplication.cpp index 76e22e9cef4c4f260f34bffb14ba9c91997d81b9..7a7aebe69d17503a46a4ce0f4bf5d38df6917938 100644 --- a/src/mainapplication.cpp +++ b/src/mainapplication.cpp @@ -46,6 +46,9 @@ #include <QFontDatabase> #include <QMenu> #include <QQmlContext> +#include <QResource> +#include <QTranslator> +#include <QLibraryInfo> #include <locale.h> #include <thread> @@ -339,7 +342,6 @@ MainApplication::initLrc(const QString& downloadUrl, ConnectivityMonitor* cm) downloadUrl, cm)); lrcInstance_->subscribeToDebugReceived(); - lrcInstance_->getAPI().holdConferences = false; } const QVariantMap diff --git a/src/mediacodeclistmodel.cpp b/src/mediacodeclistmodel.cpp index 1887fba61051d2eb742ffcf39db94a15f824bf3d..fb4cec22a3d08a825ccf9bfb1ca52ac0fda6fde8 100644 --- a/src/mediacodeclistmodel.cpp +++ b/src/mediacodeclistmodel.cpp @@ -23,6 +23,7 @@ #include "api/account.h" #include "api/contact.h" #include "api/conversation.h" +#include "api/newcodecmodel.h" #include "api/newdevicemodel.h" MediaCodecListModel::MediaCodecListModel(QObject* parent) diff --git a/src/messagesadapter.cpp b/src/messagesadapter.cpp index 1cdf09ee5aceb301366f75b2322d36d066c0a003..a5c6db97b04834b6d4fb1fe3e03b8aa19e445958 100644 --- a/src/messagesadapter.cpp +++ b/src/messagesadapter.cpp @@ -35,6 +35,8 @@ #include <QImageReader> #include <QList> #include <QUrl> +#include <QMimeData> +#include <QBuffer> MessagesAdapter::MessagesAdapter(AppSettingsManager* settingsManager, LRCInstance* instance, diff --git a/src/pluginlistpreferencemodel.h b/src/pluginlistpreferencemodel.h index 1ce6f6e0677c03eb750eab120b49012bb05fdf7a..614eb4284508e2136cdb04d57ae0f963567f3b2b 100644 --- a/src/pluginlistpreferencemodel.h +++ b/src/pluginlistpreferencemodel.h @@ -20,6 +20,8 @@ #include "abstractitemmodelbase.h" +#include "api/pluginmodel.h" + class PluginListPreferenceModel : public AbstractListModelBase { Q_OBJECT diff --git a/src/qmlregister.cpp b/src/qmlregister.cpp index 78adb0ce6433c8715d8db06242838572c7c93795..47a2d06bbab739ba5602cbc30cd5f8ec3518236f 100644 --- a/src/qmlregister.cpp +++ b/src/qmlregister.cpp @@ -39,6 +39,12 @@ #include "videoformatresolutionmodel.h" #include "videoinputdevicemodel.h" +#include "api/peerdiscoverymodel.h" +#include "api/newcodecmodel.h" +#include "api/newdevicemodel.h" +#include "api/datatransfermodel.h" +#include "api/pluginmodel.h" + #include <QMetaType> #include <QQmlEngine> diff --git a/src/settingsadapter.cpp b/src/settingsadapter.cpp index a2ea168b68431f5fca3fc86e22d982e4a35b58d4..8f770c31df9db8996bc50063d69a83d04988e5fd 100644 --- a/src/settingsadapter.cpp +++ b/src/settingsadapter.cpp @@ -18,6 +18,7 @@ #include "settingsadapter.h" +#include "api/newcodecmodel.h" #include "api/newdevicemodel.h" SettingsAdapter::SettingsAdapter(AppSettingsManager* settingsManager, diff --git a/src/updatemanager.cpp b/src/updatemanager.cpp index 68f98a6abd86174f760b698a239f75be6cc0f402..1d5b3e9234bcfb51c7cf7c5948c6c75fe5b493d3 100644 --- a/src/updatemanager.cpp +++ b/src/updatemanager.cpp @@ -128,7 +128,7 @@ UpdateManager::applyUpdates(bool beta) get( downloadUrl, [this, downloadUrl](const QString&) { - lrcInstance_->reset(); + lrcInstance_->finish(); Q_EMIT lrcInstance_->quitEngineRequested(); auto args = QString(" /passive /norestart WIXNONUILAUNCH=1"); QProcess process; diff --git a/src/utilsadapter.cpp b/src/utilsadapter.cpp index dafd36a0901a660a7258973e66a61bd7c825ef34..3b983086104ea150a8e74d9d026024092bee43aa 100644 --- a/src/utilsadapter.cpp +++ b/src/utilsadapter.cpp @@ -27,6 +27,8 @@ #include "utils.h" #include "version.h" +#include "api/pluginmodel.h" + #include <QApplication> #include <QClipboard> #include <QFileInfo>