blob: 1da63c1f1d7c7e287b9acd4ff230c5b79df360b3 [file] [log] [blame]
/*
* 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 = 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()
{
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 *)