Skip to content
Snippets Groups Projects
Select Git revision
  • master default protected
  • nightly/20250515.0
  • nightly/20250510.0
  • nightly/20250509.1
  • nightly/20250509.0
  • stable/20250430.1
  • stable/20250430.0
  • beta/202504301614
  • nightly/20250430.0
  • stable/20250424.1
  • beta/202504241506
  • stable/20250424.0
  • nightly/20250424.1
  • nightly/20250424.0
  • nightly/20250422.0
  • beta/202504120241
  • stable/20250411.0
  • nightly/20250411.0
  • nightly/20250331.0
  • beta/202503281438
  • stable/20250321.0
21 results

lrcinstance.h

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    lrcinstance.h 13.67 KiB
    /*!
     * 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 "appsettingsmanager.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 = static_cast<int>(profile::Type::RING);
                         i <= static_cast<int>(profile::Type::TEMPORARY);
                         ++i) {
                        auto filter = static_cast<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()
        {
            if (instance().selectedAccountId_.isEmpty()) {
                auto accountList = accountModel().getAccountList();
                if (accountList.size())
                    instance().selectedAccountId_ = accountList.at(0);
            }
            return instance().selectedAccountId_;
        }
    
        static void setSelectedAccountId(const QString& accountId = {})
        {
            if (accountId == instance().selectedAccountId_)
                return; // No need to select current selected account
    
            instance().selectedAccountId_ = 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 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();
        void restoreAppRequested();
    
    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*)