blob: 190b3839e2c0821f275abadd2ebef7ed251f7077 [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;
Sébastien Blin5f35e192020-08-06 15:24:57 -0400287 emit instance().currentAccountChanged();
Sébastien Blin1f915762020-08-03 13:27:42 -0400288 QSettings settings("jami.net", "Jami");
289 settings.setValue(SettingsKey::selectedAccount, accountId);
290
291 // Last selected account should be set as preferred.
292 accountModel().setTopAccount(accountId);
293 };
294
295 static const QString &
296 getCurrentConvUid()
297 {
298 return instance().selectedConvUid_;
299 };
300
301 static void
302 setSelectedConvId(const QString &convUid = {})
303 {
304 instance().selectedConvUid_ = convUid;
305 };
306
307 static void
308 reset(bool newInstance = false)
309 {
310 if (newInstance) {
311 instance().renderer_.reset(new RenderManager(avModel()));
312 instance().lrc_.reset(new Lrc());
313 } else {
314 instance().renderer_.reset();
315 instance().lrc_.reset();
316 }
317 };
318
319 static const int
320 getCurrentAccountIndex()
321 {
322 for (int i = 0; i < accountModel().getAccountList().size(); i++) {
323 if (accountModel().getAccountList()[i] == getCurrAccId()) {
324 return i;
325 }
326 }
327 return -1;
328 };
329
330 static const QPixmap
331 getCurrAccPixmap()
332 {
333 return instance()
334 .accountListModel_
335 .data(instance().accountListModel_.index(getCurrentAccountIndex()),
336 AccountListModel::Role::Picture)
337 .value<QPixmap>();
338 };
339
340 static void
341 setAvatarForAccount(const QPixmap &avatarPixmap, const QString &accountID)
342 {
343 QByteArray ba;
344 QBuffer bu(&ba);
345 bu.open(QIODevice::WriteOnly);
346 avatarPixmap.save(&bu, "PNG");
347 auto str = QString::fromLocal8Bit(ba.toBase64());
348 accountModel().setAvatar(accountID, str);
349 };
350
351 static void
352 setCurrAccAvatar(const QPixmap &avatarPixmap)
353 {
354 QByteArray ba;
355 QBuffer bu(&ba);
356 bu.open(QIODevice::WriteOnly);
357 avatarPixmap.save(&bu, "PNG");
358 auto str = QString::fromLocal8Bit(ba.toBase64());
359 accountModel().setAvatar(getCurrAccId(), str);
360 };
361
362 static void
363 setCurrAccAvatar(const QString &avatar)
364 {
365 accountModel().setAvatar(getCurrAccId(), avatar);
366 };
367
368 static void
369 setCurrAccDisplayName(const QString &displayName)
370 {
371 auto accountId = LRCInstance::getCurrAccId();
372 accountModel().setAlias(accountId, displayName);
373 /*
374 * Force save to .yml.
375 */
376 auto confProps = LRCInstance::accountModel().getAccountConfig(accountId);
377 LRCInstance::accountModel().setAccountConfig(accountId, confProps);
378 };
379
380 static const account::ConfProperties_t &
381 getCurrAccConfig()
382 {
383 return instance().getCurrentAccountInfo().confProperties;
384 }
385
386 static void
387 subscribeToDebugReceived()
388 {
389 instance().lrc_->subscribeToDebugReceived();
390 }
391
392 static void
393 startAudioMeter(bool async)
394 {
395 auto f = [] {
396 if (!LRCInstance::getActiveCalls().size()) {
397 LRCInstance::avModel().startAudioDevice();
398 }
399 LRCInstance::avModel().setAudioMeterState(true);
400 };
401 if (async) {
402 QtConcurrent::run(f);
403 } else {
404 f();
405 }
406 }
407
408 static void
409 stopAudioMeter(bool async)
410 {
411 auto f = [] {
412 if (!LRCInstance::getActiveCalls().size()) {
413 LRCInstance::avModel().stopAudioDevice();
414 }
415 LRCInstance::avModel().setAudioMeterState(false);
416 };
417 if (async) {
418 QtConcurrent::run(f);
419 } else {
420 f();
421 }
422 }
423
424 static QString
425 getContentDraft(const QString &convUid, const QString &accountId)
426 {
427 auto draftKey = accountId + "_" + convUid;
428 return instance().contentDrafts_[draftKey];
429 }
430
431 static void
432 setContentDraft(const QString &convUid, const QString &accountId, const QString &content)
433 {
434 auto draftKey = accountId + "_" + convUid;
435 instance().contentDrafts_[draftKey] = content;
436 }
437
438 static void
439 pushLastConferencee(const QString &confId, const QString &callId)
440 {
441 instance().lastConferencees_[confId] = callId;
442 }
443
444 static QString
445 popLastConferencee(const QString &confId)
446 {
447 QString callId = {};
448 auto iter = instance().lastConferencees_.find(confId);
449 if (iter != instance().lastConferencees_.end()) {
450 callId = iter.value();
451 instance().lastConferencees_.erase(iter);
452 }
453 return callId;
454 }
455
456signals:
457 void accountListChanged();
Sébastien Blin5f35e192020-08-06 15:24:57 -0400458 void currentAccountChanged();
Sébastien Blin1f915762020-08-03 13:27:42 -0400459
460private:
461 LRCInstance(migrateCallback willMigrateCb = {}, migrateCallback didMigrateCb = {})
462 {
463 lrc_ = std::make_unique<Lrc>(willMigrateCb, didMigrateCb);
464 renderer_ = std::make_unique<RenderManager>(lrc_->getAVModel());
465 };
466
467 std::unique_ptr<Lrc> lrc_;
468 std::unique_ptr<RenderManager> renderer_;
469 AccountListModel accountListModel_;
470 QString selectedAccountId_;
471 QString selectedConvUid_;
472 MapStringString contentDrafts_;
473 MapStringString lastConferencees_;
474};
475#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
476Q_DECLARE_METATYPE(LRCInstance *)
477#endif