blob: eafa7324dc7a0dacc96e721b723edf7ffd0a6631 [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 {
ababi0b686642020-08-18 17:21:28 +0200159 auto &accInfo = LRCInstance::getAccountInfo(accountId);
160 auto convInfo = accInfo.conversationModel->getConversationForUID(convUid);
Sébastien Blin1f915762020-08-03 13:27:42 -0400161 if (convInfo.uid.isEmpty()) {
162 return {};
163 }
164 return convInfo.confId.isEmpty() ? convInfo.callId : convInfo.confId;
165 }
166 static const call::Info *
167 getCallInfo(const QString &callId, const QString &accountId)
168 {
169 try {
170 auto &accInfo = LRCInstance::accountModel().getAccountInfo(accountId);
171 if (!accInfo.callModel->hasCall(callId)) {
172 return nullptr;
173 }
174 return &accInfo.callModel->getCall(callId);
175 } catch (...) {
176 return nullptr;
177 }
178 }
179 static const call::Info *
180 getCallInfoForConversation(const conversation::Info &convInfo, bool forceCallOnly = {})
181 {
182 try {
183 auto accountId = convInfo.accountId;
184 auto &accInfo = LRCInstance::accountModel().getAccountInfo(accountId);
185 auto callId = forceCallOnly
186 ? convInfo.callId
187 : (convInfo.confId.isEmpty() ? convInfo.callId : convInfo.confId);
188 if (!accInfo.callModel->hasCall(callId)) {
189 return nullptr;
190 }
191 return &accInfo.callModel->getCall(callId);
192 } catch (...) {
193 return nullptr;
194 }
195 }
196 static const conversation::Info &
197 getConversation(const QString &accountId, getConvPredicate pred = {}, bool filtered = false)
198 {
199 using namespace lrc::api;
200 static conversation::Info invalid = {};
201 try {
202 auto &accInfo = LRCInstance::getAccountInfo(accountId);
203 auto &convModel = accInfo.conversationModel;
204 if (filtered) {
205 auto &convs = convModel->allFilteredConversations();
206 auto conv = std::find_if(convs.begin(), convs.end(), pred);
207 if (conv != convs.end()) {
208 return *conv;
209 }
210 } else {
211 for (int i = Utils::toUnderlyingValue(profile::Type::RING);
212 i <= Utils::toUnderlyingValue(profile::Type::TEMPORARY);
213 ++i) {
214 auto filter = Utils::toEnum<profile::Type>(i);
215 auto &convs = convModel->getFilteredConversations(filter);
216 auto conv = std::find_if(convs.begin(), convs.end(), pred);
217 if (conv != convs.end()) {
218 return *conv;
219 }
220 }
221 }
222 } catch (...) {
223 }
224 return invalid;
225 }
226 static const conversation::Info &
227 getConversationFromCallId(const QString &callId,
228 const QString &accountId = {},
229 bool filtered = false)
230 {
231 return getConversation(
232 !accountId.isEmpty() ? accountId : getCurrAccId(),
Sébastien Blin6607e0e2020-07-24 15:15:47 -0400233 [&](const conversation::Info &conv) -> bool { return callId == conv.callId or callId == conv.confId; },
Sébastien Blin1f915762020-08-03 13:27:42 -0400234 filtered);
235 }
236 static const conversation::Info &
Sébastien Blin1f915762020-08-03 13:27:42 -0400237 getConversationFromPeerUri(const QString &peerUri,
238 const QString &accountId = {},
239 bool filtered = false)
240 {
241 return getConversation(
242 !accountId.isEmpty() ? accountId : getCurrAccId(),
243 [&](const conversation::Info &conv) -> bool { return peerUri == conv.participants[0]; },
244 filtered);
245 }
Sébastien Blin1f915762020-08-03 13:27:42 -0400246
247 static ConversationModel *
248 getCurrentConversationModel()
249 {
250 return getCurrentAccountInfo().conversationModel.get();
251 };
252
253 static NewCallModel *
254 getCurrentCallModel()
255 {
256 return getCurrentAccountInfo().callModel.get();
257 };
258
259 static const QString &
260 getCurrAccId()
261 {
262 auto accountList = accountModel().getAccountList();
263 if (instance().selectedAccountId_.isEmpty() && accountList.size()) {
264 instance().selectedAccountId_ = accountList.at(0);
265 }
266 return instance().selectedAccountId_;
267 };
268
269 static void
270 setSelectedAccountId(const QString &accountId = {})
271 {
272 instance().selectedAccountId_ = accountId;
273 QSettings settings("jami.net", "Jami");
274 settings.setValue(SettingsKey::selectedAccount, accountId);
275
276 // Last selected account should be set as preferred.
277 accountModel().setTopAccount(accountId);
Ming Rui Zhangb07f7af2020-08-27 16:46:22 -0400278
279 emit instance().currentAccountChanged();
Sébastien Blin1f915762020-08-03 13:27:42 -0400280 };
281
282 static const QString &
283 getCurrentConvUid()
284 {
285 return instance().selectedConvUid_;
286 };
287
288 static void
289 setSelectedConvId(const QString &convUid = {})
290 {
291 instance().selectedConvUid_ = convUid;
292 };
293
294 static void
295 reset(bool newInstance = false)
296 {
297 if (newInstance) {
298 instance().renderer_.reset(new RenderManager(avModel()));
299 instance().lrc_.reset(new Lrc());
300 } else {
301 instance().renderer_.reset();
302 instance().lrc_.reset();
303 }
304 };
305
306 static const int
307 getCurrentAccountIndex()
308 {
309 for (int i = 0; i < accountModel().getAccountList().size(); i++) {
310 if (accountModel().getAccountList()[i] == getCurrAccId()) {
311 return i;
312 }
313 }
314 return -1;
315 };
316
317 static const QPixmap
318 getCurrAccPixmap()
319 {
320 return instance()
321 .accountListModel_
322 .data(instance().accountListModel_.index(getCurrentAccountIndex()),
323 AccountListModel::Role::Picture)
324 .value<QPixmap>();
325 };
326
327 static void
328 setAvatarForAccount(const QPixmap &avatarPixmap, const QString &accountID)
329 {
330 QByteArray ba;
331 QBuffer bu(&ba);
332 bu.open(QIODevice::WriteOnly);
333 avatarPixmap.save(&bu, "PNG");
334 auto str = QString::fromLocal8Bit(ba.toBase64());
335 accountModel().setAvatar(accountID, str);
336 };
337
338 static void
339 setCurrAccAvatar(const QPixmap &avatarPixmap)
340 {
341 QByteArray ba;
342 QBuffer bu(&ba);
343 bu.open(QIODevice::WriteOnly);
344 avatarPixmap.save(&bu, "PNG");
345 auto str = QString::fromLocal8Bit(ba.toBase64());
346 accountModel().setAvatar(getCurrAccId(), str);
347 };
348
349 static void
350 setCurrAccAvatar(const QString &avatar)
351 {
352 accountModel().setAvatar(getCurrAccId(), avatar);
353 };
354
355 static void
356 setCurrAccDisplayName(const QString &displayName)
357 {
358 auto accountId = LRCInstance::getCurrAccId();
359 accountModel().setAlias(accountId, displayName);
360 /*
361 * Force save to .yml.
362 */
363 auto confProps = LRCInstance::accountModel().getAccountConfig(accountId);
364 LRCInstance::accountModel().setAccountConfig(accountId, confProps);
365 };
366
367 static const account::ConfProperties_t &
368 getCurrAccConfig()
369 {
370 return instance().getCurrentAccountInfo().confProperties;
371 }
372
373 static void
374 subscribeToDebugReceived()
375 {
376 instance().lrc_->subscribeToDebugReceived();
377 }
378
379 static void
380 startAudioMeter(bool async)
381 {
382 auto f = [] {
383 if (!LRCInstance::getActiveCalls().size()) {
384 LRCInstance::avModel().startAudioDevice();
385 }
386 LRCInstance::avModel().setAudioMeterState(true);
387 };
388 if (async) {
389 QtConcurrent::run(f);
390 } else {
391 f();
392 }
393 }
394
395 static void
396 stopAudioMeter(bool async)
397 {
398 auto f = [] {
399 if (!LRCInstance::getActiveCalls().size()) {
400 LRCInstance::avModel().stopAudioDevice();
401 }
402 LRCInstance::avModel().setAudioMeterState(false);
403 };
404 if (async) {
405 QtConcurrent::run(f);
406 } else {
407 f();
408 }
409 }
410
411 static QString
412 getContentDraft(const QString &convUid, const QString &accountId)
413 {
414 auto draftKey = accountId + "_" + convUid;
415 return instance().contentDrafts_[draftKey];
416 }
417
418 static void
419 setContentDraft(const QString &convUid, const QString &accountId, const QString &content)
420 {
421 auto draftKey = accountId + "_" + convUid;
422 instance().contentDrafts_[draftKey] = content;
423 }
424
425 static void
426 pushLastConferencee(const QString &confId, const QString &callId)
427 {
428 instance().lastConferencees_[confId] = callId;
429 }
430
431 static QString
432 popLastConferencee(const QString &confId)
433 {
434 QString callId = {};
435 auto iter = instance().lastConferencees_.find(confId);
436 if (iter != instance().lastConferencees_.end()) {
437 callId = iter.value();
438 instance().lastConferencees_.erase(iter);
439 }
440 return callId;
441 }
442
443signals:
444 void accountListChanged();
Sébastien Blin5f35e192020-08-06 15:24:57 -0400445 void currentAccountChanged();
Sébastien Blin1f915762020-08-03 13:27:42 -0400446
447private:
448 LRCInstance(migrateCallback willMigrateCb = {}, migrateCallback didMigrateCb = {})
449 {
450 lrc_ = std::make_unique<Lrc>(willMigrateCb, didMigrateCb);
451 renderer_ = std::make_unique<RenderManager>(lrc_->getAVModel());
452 };
453
454 std::unique_ptr<Lrc> lrc_;
455 std::unique_ptr<RenderManager> renderer_;
456 AccountListModel accountListModel_;
457 QString selectedAccountId_;
458 QString selectedConvUid_;
459 MapStringString contentDrafts_;
460 MapStringString lastConferencees_;
461};
Sébastien Blin1f915762020-08-03 13:27:42 -0400462Q_DECLARE_METATYPE(LRCInstance *)