| /* |
| * Copyright (C) 2019-2020 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/>. |
| */ |
| |
| #pragma once |
| |
| #ifdef _MSC_VER |
| #undef ERROR |
| #endif |
| |
| #include "accountlistmodel.h" |
| #include "rendermanager.h" |
| #include "settingskey.h" |
| #include "utils.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> |
| |
| using namespace lrc::api; |
| |
| using migrateCallback = std::function<void()>; |
| using getConvPredicate = std::function<bool(const conversation::Info &conv)>; |
| |
| class LRCInstance : public QObject |
| { |
| Q_OBJECT |
| |
| public: |
| static LRCInstance & |
| instance(migrateCallback willMigrate = {}, migrateCallback didMigrate = {}) |
| { |
| static LRCInstance instance_(willMigrate, didMigrate); |
| return instance_; |
| }; |
| static void |
| init(migrateCallback willMigrate = {}, migrateCallback didMigrate = {}) |
| { |
| instance(willMigrate, didMigrate); |
| }; |
| static Lrc & |
| getAPI() |
| { |
| return *(instance().lrc_); |
| }; |
| static RenderManager * |
| renderer() |
| { |
| return instance().renderer_.get(); |
| } |
| static void |
| connectivityChanged() |
| { |
| instance().lrc_->connectivityChanged(); |
| }; |
| static NewAccountModel & |
| accountModel() |
| { |
| return instance().lrc_->getAccountModel(); |
| }; |
| static BehaviorController & |
| behaviorController() |
| { |
| return instance().lrc_->getBehaviorController(); |
| }; |
| static DataTransferModel & |
| dataTransferModel() |
| { |
| return instance().lrc_->getDataTransferModel(); |
| }; |
| static AVModel & |
| avModel() |
| { |
| return instance().lrc_->getAVModel(); |
| }; |
| static PluginModel & |
| pluginModel() |
| { |
| return instance().lrc_->getPluginModel(); |
| }; |
| static bool |
| isConnected() |
| { |
| return instance().lrc_->isConnected(); |
| }; |
| static VectorString |
| getActiveCalls() |
| { |
| return instance().lrc_->activeCalls(); |
| }; |
| static const account::Info & |
| getAccountInfo(const QString &accountId) |
| { |
| return accountModel().getAccountInfo(accountId); |
| }; |
| static const account::Info & |
| getCurrentAccountInfo() |
| { |
| return getAccountInfo(getCurrAccId()); |
| }; |
| static bool |
| hasVideoCall() |
| { |
| auto activeCalls = instance().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; |
| }; |
| static QString |
| getCallIdForConversationUid(const QString &convUid, const QString &accountId) |
| { |
| auto &accInfo = LRCInstance::getAccountInfo(accountId); |
| auto convInfo = accInfo.conversationModel->getConversationForUID(convUid); |
| if (convInfo.uid.isEmpty()) { |
| return {}; |
| } |
| return convInfo.confId.isEmpty() ? convInfo.callId : convInfo.confId; |
| } |
| static const call::Info * |
| getCallInfo(const QString &callId, const QString &accountId) |
| { |
| try { |
| auto &accInfo = LRCInstance::accountModel().getAccountInfo(accountId); |
| if (!accInfo.callModel->hasCall(callId)) { |
| return nullptr; |
| } |
| return &accInfo.callModel->getCall(callId); |
| } catch (...) { |
| return nullptr; |
| } |
| } |
| static const call::Info * |
| getCallInfoForConversation(const conversation::Info &convInfo, bool forceCallOnly = {}) |
| { |
| try { |
| auto accountId = convInfo.accountId; |
| auto &accInfo = LRCInstance::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; |
| } |
| } |
| static const conversation::Info & |
| getConversation(const QString &accountId, getConvPredicate pred = {}, bool filtered = false) |
| { |
| using namespace lrc::api; |
| static conversation::Info invalid = {}; |
| try { |
| auto &accInfo = LRCInstance::getAccountInfo(accountId); |
| auto &convModel = accInfo.conversationModel; |
| if (filtered) { |
| auto &convs = convModel->allFilteredConversations(); |
| auto conv = std::find_if(convs.begin(), convs.end(), pred); |
| if (conv != convs.end()) { |
| return *conv; |
| } |
| } else { |
| for (int i = Utils::toUnderlyingValue(profile::Type::RING); |
| i <= Utils::toUnderlyingValue(profile::Type::TEMPORARY); |
| ++i) { |
| auto filter = Utils::toEnum<profile::Type>(i); |
| auto &convs = convModel->getFilteredConversations(filter); |
| auto conv = std::find_if(convs.begin(), convs.end(), pred); |
| if (conv != convs.end()) { |
| return *conv; |
| } |
| } |
| } |
| } catch (...) { |
| } |
| return invalid; |
| } |
| static const conversation::Info & |
| getConversationFromCallId(const QString &callId, |
| const QString &accountId = {}, |
| bool filtered = false) |
| { |
| return getConversation( |
| !accountId.isEmpty() ? accountId : getCurrAccId(), |
| [&](const conversation::Info &conv) -> bool { return callId == conv.callId or callId == conv.confId; }, |
| filtered); |
| } |
| static const conversation::Info & |
| getConversationFromPeerUri(const QString &peerUri, |
| const QString &accountId = {}, |
| bool filtered = false) |
| { |
| return getConversation( |
| !accountId.isEmpty() ? accountId : getCurrAccId(), |
| [&](const conversation::Info &conv) -> bool { return peerUri == conv.participants[0]; }, |
| filtered); |
| } |
| |
| static ConversationModel * |
| getCurrentConversationModel() |
| { |
| return getCurrentAccountInfo().conversationModel.get(); |
| }; |
| |
| static NewCallModel * |
| getCurrentCallModel() |
| { |
| return getCurrentAccountInfo().callModel.get(); |
| }; |
| |
| static const QString & |
| getCurrAccId() |
| { |
| auto accountList = accountModel().getAccountList(); |
| if (instance().selectedAccountId_.isEmpty() && accountList.size()) { |
| instance().selectedAccountId_ = accountList.at(0); |
| } |
| return instance().selectedAccountId_; |
| }; |
| |
| static void |
| setSelectedAccountId(const QString &accountId = {}) |
| { |
| instance().selectedAccountId_ = accountId; |
| QSettings settings("jami.net", "Jami"); |
| settings.setValue(SettingsKey::selectedAccount, accountId); |
| |
| // Last selected account should be set as preferred. |
| accountModel().setTopAccount(accountId); |
| |
| emit instance().currentAccountChanged(); |
| }; |
| |
| static const QString & |
| getCurrentConvUid() |
| { |
| return instance().selectedConvUid_; |
| }; |
| |
| static void |
| setSelectedConvId(const QString &convUid = {}) |
| { |
| instance().selectedConvUid_ = convUid; |
| }; |
| |
| static void |
| reset(bool newInstance = false) |
| { |
| if (newInstance) { |
| instance().renderer_.reset(new RenderManager(avModel())); |
| instance().lrc_.reset(new Lrc()); |
| } else { |
| instance().renderer_.reset(); |
| instance().lrc_.reset(); |
| } |
| }; |
| |
| static const int |
| getCurrentAccountIndex() |
| { |
| for (int i = 0; i < accountModel().getAccountList().size(); i++) { |
| if (accountModel().getAccountList()[i] == getCurrAccId()) { |
| return i; |
| } |
| } |
| return -1; |
| }; |
| |
| static const QPixmap |
| getCurrAccPixmap() |
| { |
| return instance() |
| .accountListModel_ |
| .data(instance().accountListModel_.index(getCurrentAccountIndex()), |
| AccountListModel::Role::Picture) |
| .value<QPixmap>(); |
| }; |
| |
| static 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); |
| }; |
| |
| static 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); |
| }; |
| |
| static void |
| setCurrAccAvatar(const QString &avatar) |
| { |
| accountModel().setAvatar(getCurrAccId(), avatar); |
| }; |
| |
| static void |
| setCurrAccDisplayName(const QString &displayName) |
| { |
| auto accountId = LRCInstance::getCurrAccId(); |
| accountModel().setAlias(accountId, displayName); |
| /* |
| * Force save to .yml. |
| */ |
| auto confProps = LRCInstance::accountModel().getAccountConfig(accountId); |
| LRCInstance::accountModel().setAccountConfig(accountId, confProps); |
| }; |
| |
| static const account::ConfProperties_t & |
| getCurrAccConfig() |
| { |
| return instance().getCurrentAccountInfo().confProperties; |
| } |
| |
| static void |
| subscribeToDebugReceived() |
| { |
| instance().lrc_->subscribeToDebugReceived(); |
| } |
| |
| static void |
| startAudioMeter(bool async) |
| { |
| auto f = [] { |
| if (!LRCInstance::getActiveCalls().size()) { |
| LRCInstance::avModel().startAudioDevice(); |
| } |
| LRCInstance::avModel().setAudioMeterState(true); |
| }; |
| if (async) { |
| QtConcurrent::run(f); |
| } else { |
| f(); |
| } |
| } |
| |
| static void |
| stopAudioMeter(bool async) |
| { |
| auto f = [] { |
| if (!LRCInstance::getActiveCalls().size()) { |
| LRCInstance::avModel().stopAudioDevice(); |
| } |
| LRCInstance::avModel().setAudioMeterState(false); |
| }; |
| if (async) { |
| QtConcurrent::run(f); |
| } else { |
| f(); |
| } |
| } |
| |
| static QString |
| getContentDraft(const QString &convUid, const QString &accountId) |
| { |
| auto draftKey = accountId + "_" + convUid; |
| return instance().contentDrafts_[draftKey]; |
| } |
| |
| static void |
| setContentDraft(const QString &convUid, const QString &accountId, const QString &content) |
| { |
| auto draftKey = accountId + "_" + convUid; |
| instance().contentDrafts_[draftKey] = content; |
| } |
| |
| static void |
| pushLastConferencee(const QString &confId, const QString &callId) |
| { |
| instance().lastConferencees_[confId] = callId; |
| } |
| |
| static QString |
| popLastConferencee(const QString &confId) |
| { |
| QString callId = {}; |
| auto iter = instance().lastConferencees_.find(confId); |
| if (iter != instance().lastConferencees_.end()) { |
| callId = iter.value(); |
| instance().lastConferencees_.erase(iter); |
| } |
| return callId; |
| } |
| |
| signals: |
| void accountListChanged(); |
| void currentAccountChanged(); |
| |
| private: |
| LRCInstance(migrateCallback willMigrateCb = {}, migrateCallback didMigrateCb = {}) |
| { |
| lrc_ = std::make_unique<Lrc>(willMigrateCb, didMigrateCb); |
| renderer_ = std::make_unique<RenderManager>(lrc_->getAVModel()); |
| }; |
| |
| std::unique_ptr<Lrc> lrc_; |
| std::unique_ptr<RenderManager> renderer_; |
| AccountListModel accountListModel_; |
| QString selectedAccountId_; |
| QString selectedConvUid_; |
| MapStringString contentDrafts_; |
| MapStringString lastConferencees_; |
| }; |
| Q_DECLARE_METATYPE(LRCInstance *) |