blob: 16762f14b93187ce662b0ebd853968fe83c2cbc4 [file] [log] [blame]
Sébastien Blin1f915762020-08-03 13:27:42 -04001/*
2 * Copyright (C) 2019-2020 by Savoir-faire Linux
3 * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
4 * Author: Isa Nanic <isa.nanic@savoirfairelinux.com>
5 * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#pragma once
22
23#ifdef _MSC_VER
24#undef ERROR
25#endif
26
27#include "accountlistmodel.h"
28#include "rendermanager.h"
29#include "settingskey.h"
30#include "utils.h"
31
32#include "api/account.h"
33#include "api/avmodel.h"
34#include "api/pluginmodel.h"
35#include "api/behaviorcontroller.h"
36#include "api/contact.h"
37#include "api/contactmodel.h"
38#include "api/conversation.h"
39#include "api/conversationmodel.h"
40#include "api/datatransfermodel.h"
41#include "api/lrc.h"
42#include "api/newaccountmodel.h"
43#include "api/newcallmodel.h"
44#include "api/newcodecmodel.h"
45#include "api/newdevicemodel.h"
46#include "api/peerdiscoverymodel.h"
47
48#include <QBuffer>
49#include <QMutex>
50#include <QObject>
51#include <QPixmap>
52#include <QRegularExpression>
53#include <QSettings>
54#include <QtConcurrent/QtConcurrent>
55
56#include <memory>
57
58using namespace lrc::api;
59
60using migrateCallback = std::function<void()>;
61using getConvPredicate = std::function<bool(const conversation::Info &conv)>;
62
63class LRCInstance : public QObject
64{
65 Q_OBJECT
66
67public:
68 static LRCInstance &
69 instance(migrateCallback willMigrate = {}, migrateCallback didMigrate = {})
70 {
71 static LRCInstance instance_(willMigrate, didMigrate);
72 return instance_;
73 };
74 static void
75 init(migrateCallback willMigrate = {}, migrateCallback didMigrate = {})
76 {
77 instance(willMigrate, didMigrate);
78 };
79 static Lrc &
80 getAPI()
81 {
82 return *(instance().lrc_);
83 };
84 static RenderManager *
85 renderer()
86 {
87 return instance().renderer_.get();
88 }
89 static void
90 connectivityChanged()
91 {
92 instance().lrc_->connectivityChanged();
93 };
94 static NewAccountModel &
95 accountModel()
96 {
97 return instance().lrc_->getAccountModel();
98 };
99 static BehaviorController &
100 behaviorController()
101 {
102 return instance().lrc_->getBehaviorController();
103 };
104 static DataTransferModel &
105 dataTransferModel()
106 {
107 return instance().lrc_->getDataTransferModel();
108 };
109 static AVModel &
110 avModel()
111 {
112 return instance().lrc_->getAVModel();
113 };
114 static PluginModel &
115 pluginModel()
116 {
117 return instance().lrc_->getPluginModel();
118 };
119 static bool
120 isConnected()
121 {
122 return instance().lrc_->isConnected();
123 };
124 static VectorString
125 getActiveCalls()
126 {
127 return instance().lrc_->activeCalls();
128 };
129 static const account::Info &
130 getAccountInfo(const QString &accountId)
131 {
132 return accountModel().getAccountInfo(accountId);
133 };
134 static const account::Info &
135 getCurrentAccountInfo()
136 {
137 return getAccountInfo(getCurrAccId());
138 };
139 static bool
140 hasVideoCall()
141 {
142 auto activeCalls = instance().lrc_->activeCalls();
143 auto accountList = accountModel().getAccountList();
144 bool result = false;
145 for (const auto &callId : activeCalls) {
146 for (const auto &accountId : accountList) {
147 auto &accountInfo = accountModel().getAccountInfo(accountId);
148 if (accountInfo.callModel->hasCall(callId)) {
149 auto call = accountInfo.callModel->getCall(callId);
150 result |= !(call.isAudioOnly || call.videoMuted);
151 }
152 }
153 }
154 return result;
155 };
156 static QString
157 getCallIdForConversationUid(const QString &convUid, const QString &accountId)
158 {
159 auto convInfo = LRCInstance::getConversationFromConvUid(convUid, accountId);
160 if (convInfo.uid.isEmpty()) {
161 return {};
162 }
163 return convInfo.confId.isEmpty() ? convInfo.callId : convInfo.confId;
164 }
165 static const call::Info *
166 getCallInfo(const QString &callId, const QString &accountId)
167 {
168 try {
169 auto &accInfo = LRCInstance::accountModel().getAccountInfo(accountId);
170 if (!accInfo.callModel->hasCall(callId)) {
171 return nullptr;
172 }
173 return &accInfo.callModel->getCall(callId);
174 } catch (...) {
175 return nullptr;
176 }
177 }
178 static const call::Info *
179 getCallInfoForConversation(const conversation::Info &convInfo, bool forceCallOnly = {})
180 {
181 try {
182 auto accountId = convInfo.accountId;
183 auto &accInfo = LRCInstance::accountModel().getAccountInfo(accountId);
184 auto callId = forceCallOnly
185 ? convInfo.callId
186 : (convInfo.confId.isEmpty() ? convInfo.callId : convInfo.confId);
187 if (!accInfo.callModel->hasCall(callId)) {
188 return nullptr;
189 }
190 return &accInfo.callModel->getCall(callId);
191 } catch (...) {
192 return nullptr;
193 }
194 }
195 static const conversation::Info &
196 getConversation(const QString &accountId, getConvPredicate pred = {}, bool filtered = false)
197 {
198 using namespace lrc::api;
199 static conversation::Info invalid = {};
200 try {
201 auto &accInfo = LRCInstance::getAccountInfo(accountId);
202 auto &convModel = accInfo.conversationModel;
203 if (filtered) {
204 auto &convs = convModel->allFilteredConversations();
205 auto conv = std::find_if(convs.begin(), convs.end(), pred);
206 if (conv != convs.end()) {
207 return *conv;
208 }
209 } else {
210 for (int i = Utils::toUnderlyingValue(profile::Type::RING);
211 i <= Utils::toUnderlyingValue(profile::Type::TEMPORARY);
212 ++i) {
213 auto filter = Utils::toEnum<profile::Type>(i);
214 auto &convs = convModel->getFilteredConversations(filter);
215 auto conv = std::find_if(convs.begin(), convs.end(), pred);
216 if (conv != convs.end()) {
217 return *conv;
218 }
219 }
220 }
221 } catch (...) {
222 }
223 return invalid;
224 }
225 static const conversation::Info &
226 getConversationFromCallId(const QString &callId,
227 const QString &accountId = {},
228 bool filtered = false)
229 {
230 return getConversation(
231 !accountId.isEmpty() ? accountId : getCurrAccId(),
232 [&](const conversation::Info &conv) -> bool { return callId == conv.callId; },
233 filtered);
234 }
235 static const conversation::Info &
236 getConversationFromConvUid(const QString &convUid,
237 const QString &accountId = {},
238 bool filtered = false)
239 {
240 return getConversation(
241 !accountId.isEmpty() ? accountId : getCurrAccId(),
242 [&](const conversation::Info &conv) -> bool { return convUid == conv.uid; },
243 filtered);
244 }
245 static const conversation::Info &
246 getConversationFromPeerUri(const QString &peerUri,
247 const QString &accountId = {},
248 bool filtered = false)
249 {
250 return getConversation(
251 !accountId.isEmpty() ? accountId : getCurrAccId(),
252 [&](const conversation::Info &conv) -> bool { return peerUri == conv.participants[0]; },
253 filtered);
254 }
255 static const conversation::Info &
256 getCurrentConversation()
257 {
258 return getConversationFromConvUid(getCurrentConvUid());
259 }
260
261 static ConversationModel *
262 getCurrentConversationModel()
263 {
264 return getCurrentAccountInfo().conversationModel.get();
265 };
266
267 static NewCallModel *
268 getCurrentCallModel()
269 {
270 return getCurrentAccountInfo().callModel.get();
271 };
272
273 static const QString &
274 getCurrAccId()
275 {
276 auto accountList = accountModel().getAccountList();
277 if (instance().selectedAccountId_.isEmpty() && accountList.size()) {
278 instance().selectedAccountId_ = accountList.at(0);
279 }
280 return instance().selectedAccountId_;
281 };
282
283 static void
284 setSelectedAccountId(const QString &accountId = {})
285 {
286 instance().selectedAccountId_ = accountId;
287 QSettings settings("jami.net", "Jami");
288 settings.setValue(SettingsKey::selectedAccount, accountId);
289
290 // Last selected account should be set as preferred.
291 accountModel().setTopAccount(accountId);
292 };
293
294 static const QString &
295 getCurrentConvUid()
296 {
297 return instance().selectedConvUid_;
298 };
299
300 static void
301 setSelectedConvId(const QString &convUid = {})
302 {
303 instance().selectedConvUid_ = convUid;
304 };
305
306 static void
307 reset(bool newInstance = false)
308 {
309 if (newInstance) {
310 instance().renderer_.reset(new RenderManager(avModel()));
311 instance().lrc_.reset(new Lrc());
312 } else {
313 instance().renderer_.reset();
314 instance().lrc_.reset();
315 }
316 };
317
318 static const int
319 getCurrentAccountIndex()
320 {
321 for (int i = 0; i < accountModel().getAccountList().size(); i++) {
322 if (accountModel().getAccountList()[i] == getCurrAccId()) {
323 return i;
324 }
325 }
326 return -1;
327 };
328
329 static const QPixmap
330 getCurrAccPixmap()
331 {
332 return instance()
333 .accountListModel_
334 .data(instance().accountListModel_.index(getCurrentAccountIndex()),
335 AccountListModel::Role::Picture)
336 .value<QPixmap>();
337 };
338
339 static void
340 setAvatarForAccount(const QPixmap &avatarPixmap, const QString &accountID)
341 {
342 QByteArray ba;
343 QBuffer bu(&ba);
344 bu.open(QIODevice::WriteOnly);
345 avatarPixmap.save(&bu, "PNG");
346 auto str = QString::fromLocal8Bit(ba.toBase64());
347 accountModel().setAvatar(accountID, str);
348 };
349
350 static void
351 setCurrAccAvatar(const QPixmap &avatarPixmap)
352 {
353 QByteArray ba;
354 QBuffer bu(&ba);
355 bu.open(QIODevice::WriteOnly);
356 avatarPixmap.save(&bu, "PNG");
357 auto str = QString::fromLocal8Bit(ba.toBase64());
358 accountModel().setAvatar(getCurrAccId(), str);
359 };
360
361 static void
362 setCurrAccAvatar(const QString &avatar)
363 {
364 accountModel().setAvatar(getCurrAccId(), avatar);
365 };
366
367 static void
368 setCurrAccDisplayName(const QString &displayName)
369 {
370 auto accountId = LRCInstance::getCurrAccId();
371 accountModel().setAlias(accountId, displayName);
372 /*
373 * Force save to .yml.
374 */
375 auto confProps = LRCInstance::accountModel().getAccountConfig(accountId);
376 LRCInstance::accountModel().setAccountConfig(accountId, confProps);
377 };
378
379 static const account::ConfProperties_t &
380 getCurrAccConfig()
381 {
382 return instance().getCurrentAccountInfo().confProperties;
383 }
384
385 static void
386 subscribeToDebugReceived()
387 {
388 instance().lrc_->subscribeToDebugReceived();
389 }
390
391 static void
392 startAudioMeter(bool async)
393 {
394 auto f = [] {
395 if (!LRCInstance::getActiveCalls().size()) {
396 LRCInstance::avModel().startAudioDevice();
397 }
398 LRCInstance::avModel().setAudioMeterState(true);
399 };
400 if (async) {
401 QtConcurrent::run(f);
402 } else {
403 f();
404 }
405 }
406
407 static void
408 stopAudioMeter(bool async)
409 {
410 auto f = [] {
411 if (!LRCInstance::getActiveCalls().size()) {
412 LRCInstance::avModel().stopAudioDevice();
413 }
414 LRCInstance::avModel().setAudioMeterState(false);
415 };
416 if (async) {
417 QtConcurrent::run(f);
418 } else {
419 f();
420 }
421 }
422
423 static QString
424 getContentDraft(const QString &convUid, const QString &accountId)
425 {
426 auto draftKey = accountId + "_" + convUid;
427 return instance().contentDrafts_[draftKey];
428 }
429
430 static void
431 setContentDraft(const QString &convUid, const QString &accountId, const QString &content)
432 {
433 auto draftKey = accountId + "_" + convUid;
434 instance().contentDrafts_[draftKey] = content;
435 }
436
437 static void
438 pushLastConferencee(const QString &confId, const QString &callId)
439 {
440 instance().lastConferencees_[confId] = callId;
441 }
442
443 static QString
444 popLastConferencee(const QString &confId)
445 {
446 QString callId = {};
447 auto iter = instance().lastConferencees_.find(confId);
448 if (iter != instance().lastConferencees_.end()) {
449 callId = iter.value();
450 instance().lastConferencees_.erase(iter);
451 }
452 return callId;
453 }
454
455signals:
456 void accountListChanged();
457
458private:
459 LRCInstance(migrateCallback willMigrateCb = {}, migrateCallback didMigrateCb = {})
460 {
461 lrc_ = std::make_unique<Lrc>(willMigrateCb, didMigrateCb);
462 renderer_ = std::make_unique<RenderManager>(lrc_->getAVModel());
463 };
464
465 std::unique_ptr<Lrc> lrc_;
466 std::unique_ptr<RenderManager> renderer_;
467 AccountListModel accountListModel_;
468 QString selectedAccountId_;
469 QString selectedConvUid_;
470 MapStringString contentDrafts_;
471 MapStringString lastConferencees_;
472};
473#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
474Q_DECLARE_METATYPE(LRCInstance *)
475#endif