SipAddressesModel.cpp 22.8 KB
Newer Older
1 2
/*
 * SipAddressesModel.cpp
3
 * Copyright (C) 2017-2018  Belledonne Communications, Grenoble, France
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * 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 2
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *  Created on: February 2, 2017
 *      Author: Ronan Abhamon
 */

Ronan's avatar
Ronan committed
23
#include <QDateTime>
24
#include <QElapsedTimer>
25 26 27 28 29 30 31 32 33
#include <QUrl>

#include "components/call/CallModel.hpp"
#include "components/chat/ChatModel.hpp"
#include "components/contact/ContactModel.hpp"
#include "components/contact/VcardModel.hpp"
#include "components/contacts/ContactsListModel.hpp"
#include "components/core/CoreHandlers.hpp"
#include "components/core/CoreManager.hpp"
34
#include "components/settings/AccountSettingsModel.hpp"
35 36
#include "utils/LinphoneUtils.hpp"
#include "utils/Utils.hpp"
Ronan's avatar
Ronan committed
37

38 39 40
#include "SipAddressesModel.hpp"

// =============================================================================
Ronan's avatar
Ronan committed
41

42 43
using namespace std;

44 45 46 47 48 49 50 51 52 53 54
// -----------------------------------------------------------------------------

static inline QVariantMap buildVariantMap (const SipAddressesModel::SipAddressEntry &sipAddressEntry) {
  return QVariantMap{
    { "sipAddress", sipAddressEntry.sipAddress },
    { "contact", QVariant::fromValue(sipAddressEntry.contact) },
    { "presenceStatus", sipAddressEntry.presenceStatus },
    { "__localToConferenceEntry", QVariant::fromValue(&sipAddressEntry.localAddressToConferenceEntry) }
  };
}

Ronan's avatar
Ronan committed
55
SipAddressesModel::SipAddressesModel (QObject *parent) : QAbstractListModel(parent) {
Ronan's avatar
Ronan committed
56
  initSipAddresses();
57

58
  CoreManager *coreManager = CoreManager::getInstance();
59

60 61 62
  mCoreHandlers = coreManager->getHandlers();

  QObject::connect(coreManager, &CoreManager::chatModelCreated, this, &SipAddressesModel::handleChatModelCreated);
63

64
  ContactsListModel *contacts = CoreManager::getInstance()->getContactsListModel();
Ronan's avatar
Ronan committed
65 66
  QObject::connect(contacts, &ContactsListModel::contactAdded, this, &SipAddressesModel::handleContactAdded);
  QObject::connect(contacts, &ContactsListModel::contactRemoved, this, &SipAddressesModel::handleContactRemoved);
67 68
  QObject::connect(contacts, &ContactsListModel::sipAddressAdded, this, &SipAddressesModel::handleSipAddressAdded);
  QObject::connect(contacts, &ContactsListModel::sipAddressRemoved, this, &SipAddressesModel::handleSipAddressRemoved);
69

70 71 72 73
  CoreHandlers *coreHandlers = mCoreHandlers.get();
  QObject::connect(coreHandlers, &CoreHandlers::messageReceived, this, &SipAddressesModel::handleMessageReceived);
  QObject::connect(coreHandlers, &CoreHandlers::callStateChanged, this, &SipAddressesModel::handleCallStateChanged);
  QObject::connect(coreHandlers, &CoreHandlers::presenceReceived, this, &SipAddressesModel::handlePresenceReceived);
74
  QObject::connect(coreHandlers, &CoreHandlers::isComposingChanged, this, &SipAddressesModel::handleIsComposingChanged);
75 76 77 78 79
}

// -----------------------------------------------------------------------------

int SipAddressesModel::rowCount (const QModelIndex &) const {
80
  return mRefs.count();
81 82 83 84 85 86 87 88 89 90 91
}

QHash<int, QByteArray> SipAddressesModel::roleNames () const {
  QHash<int, QByteArray> roles;
  roles[Qt::DisplayRole] = "$sipAddress";
  return roles;
}

QVariant SipAddressesModel::data (const QModelIndex &index, int role) const {
  int row = index.row();

92
  if (!index.isValid() || row < 0 || row >= mRefs.count())
93 94 95
    return QVariant();

  if (role == Qt::DisplayRole)
96
    return buildVariantMap(*mRefs[row]);
97 98 99 100

  return QVariant();
}

101
// -----------------------------------------------------------------------------
102

103
QVariantMap SipAddressesModel::find (const QString &sipAddress) const {
104 105 106 107 108
  auto it = mPeerAddressToSipAddressEntry.find(sipAddress);
  if (it == mPeerAddressToSipAddressEntry.end())
    return QVariantMap();

  return buildVariantMap(*it);
109 110 111 112
}

// -----------------------------------------------------------------------------

113
ContactModel *SipAddressesModel::mapSipAddressToContact (const QString &sipAddress) const {
114 115
  auto it = mPeerAddressToSipAddressEntry.find(sipAddress);
  return it == mPeerAddressToSipAddressEntry.end() ? nullptr : it->contact;
116 117
}

118 119
// -----------------------------------------------------------------------------

120 121 122 123 124 125 126 127 128 129 130 131 132
SipAddressObserver *SipAddressesModel::getSipAddressObserver (const QString &peerAddress, const QString &localAddress) {
  SipAddressObserver *model = new SipAddressObserver(peerAddress, localAddress);
  const QString cleanedPeerAddress = cleanSipAddress(peerAddress);
  const QString cleanedLocalAddress = cleanSipAddress(localAddress);

  auto it = mPeerAddressToSipAddressEntry.find(cleanedPeerAddress);
  if (it != mPeerAddressToSipAddressEntry.end()) {
    model->setContact(it->contact);
    model->setPresenceStatus(it->presenceStatus);

    auto it2 = it->localAddressToConferenceEntry.find(cleanedLocalAddress);
    if (it2 != it->localAddressToConferenceEntry.end())
      model->setUnreadMessageCount(it2->unreadMessageCount);
133
  }
134

135 136 137 138 139 140
  mObservers.insert(cleanedPeerAddress, model);
  QObject::connect(model, &SipAddressObserver::destroyed, this, [this, model, cleanedPeerAddress, cleanedLocalAddress]() {
    // Do not use `model` methods. `model` is partially destroyed here!
    if (mObservers.remove(cleanedPeerAddress, model) == 0)
      qWarning() << QStringLiteral("Unable to remove (%1, %2) from observers.")
        .arg(cleanedPeerAddress).arg(cleanedLocalAddress);
Ronan's avatar
Ronan committed
141
  });
142 143 144 145 146 147

  return model;
}

// -----------------------------------------------------------------------------

148
QString SipAddressesModel::getTransportFromSipAddress (const QString &sipAddress) {
149
  const shared_ptr<const linphone::Address> address = linphone::Factory::get()->createAddress(
Ronan's avatar
Ronan committed
150 151
    Utils::appStringToCoreString(sipAddress)
  );
152 153

  if (!address)
154
    return QString("");
155 156

  switch (address->getTransport()) {
157
    case linphone::TransportType::Udp:
158
      return QStringLiteral("UDP");
159
    case linphone::TransportType::Tcp:
160
      return QStringLiteral("TCP");
161
    case linphone::TransportType::Tls:
162
      return QStringLiteral("TLS");
163
    case linphone::TransportType::Dtls:
164 165 166
      return QStringLiteral("DTLS");
  }

167
  return QString("");
168 169
}

170
QString SipAddressesModel::addTransportToSipAddress (const QString &sipAddress, const QString &transport) {
171
  shared_ptr<linphone::Address> address = linphone::Factory::get()->createAddress(
172 173
    Utils::appStringToCoreString(sipAddress)
  );
174 175

  if (!address)
176
    return QString("");
177

178
  address->setTransport(LinphoneUtils::stringToTransportType(transport.toUpper()));
179

180
  return Utils::coreStringToAppString(address->asString());
181 182
}

183 184
// -----------------------------------------------------------------------------

185
QString SipAddressesModel::interpretSipAddress (const QString &sipAddress, bool checkUsername) {
186
  shared_ptr<linphone::Address> lAddress = CoreManager::getInstance()->getCore()->interpretUrl(
187 188
    Utils::appStringToCoreString(sipAddress)
  );
189

190
  if (lAddress && (!checkUsername || !lAddress->getUsername().empty()))
191
    return Utils::coreStringToAppString(lAddress->asStringUriOnly());
192
  return QString("");
193 194
}

195
QString SipAddressesModel::interpretSipAddress (const QUrl &sipAddress) {
196 197 198
  return sipAddress.toString();
}

199
bool SipAddressesModel::addressIsValid (const QString &address) {
200
  return !!linphone::Factory::get()->createAddress(
201
    Utils::appStringToCoreString(address)
202
  );
203 204
}

205 206
bool SipAddressesModel::sipAddressIsValid (const QString &sipAddress) {
  shared_ptr<linphone::Address> address = linphone::Factory::get()->createAddress(
207 208
    Utils::appStringToCoreString(sipAddress)
  );
209 210 211
  return address && !address->getUsername().empty();
}

212 213 214 215 216 217 218
QString SipAddressesModel::cleanSipAddress (const QString &sipAddress) {
  const int index = sipAddress.lastIndexOf('<');
  if (index == -1)
    return sipAddress;
  return sipAddress.mid(index + 1, sipAddress.lastIndexOf('>') - index - 1);
}

219 220
// -----------------------------------------------------------------------------

221 222 223 224 225 226 227
bool SipAddressesModel::removeRow (int row, const QModelIndex &parent) {
  return removeRows(row, 1, parent);
}

bool SipAddressesModel::removeRows (int row, int count, const QModelIndex &parent) {
  int limit = row + count - 1;

228
  if (row < 0 || count < 0 || limit >= mRefs.count())
229 230 231 232
    return false;

  beginRemoveRows(parent, row, limit);

233 234
  for (int i = 0; i < count; ++i)
    mPeerAddressToSipAddressEntry.remove(mRefs.takeAt(row)->sipAddress);
235 236 237 238 239 240

  endRemoveRows();

  return true;
}

241 242
// -----------------------------------------------------------------------------

Ronan's avatar
Ronan committed
243
void SipAddressesModel::handleChatModelCreated (const shared_ptr<ChatModel> &chatModel) {
244 245 246
  ChatModel *ptr = chatModel.get();

  QObject::connect(ptr, &ChatModel::allEntriesRemoved, this, [this, ptr] {
247 248 249 250
    handleAllEntriesRemoved(ptr);
  });
  QObject::connect(ptr, &ChatModel::lastEntryRemoved, this, [this, ptr] {
    handleLastEntryRemoved(ptr);
251
  });
252 253
  QObject::connect(ptr, &ChatModel::messageCountReset, this, [this, ptr] {
    handleMessageCountReset(ptr);
254
  });
255 256

  QObject::connect(ptr, &ChatModel::messageSent, this, &SipAddressesModel::handleMessageSent);
257 258
}

Ronan's avatar
Ronan committed
259
void SipAddressesModel::handleContactAdded (ContactModel *contact) {
260 261
  for (const auto &sipAddress : contact->getVcardModel()->getSipAddresses())
    addOrUpdateSipAddress(sipAddress.toString(), contact);
262
}
263

Ronan's avatar
Ronan committed
264
void SipAddressesModel::handleContactRemoved (const ContactModel *contact) {
265 266
  for (const auto &sipAddress : contact->getVcardModel()->getSipAddresses())
    removeContactOfSipAddress(sipAddress.toString());
Ronan's avatar
Ronan committed
267 268
}

269 270 271 272
void SipAddressesModel::handleSipAddressAdded (ContactModel *contact, const QString &sipAddress) {
  ContactModel *mappedContact = mapSipAddressToContact(sipAddress);
  if (mappedContact) {
    qWarning() << "Unable to map sip address" << sipAddress << "to" << contact << "- already used by" << mappedContact;
273 274 275
    return;
  }

276
  addOrUpdateSipAddress(sipAddress, contact);
277 278
}

279 280 281 282
void SipAddressesModel::handleSipAddressRemoved (ContactModel *contact, const QString &sipAddress) {
  ContactModel *mappedContact = mapSipAddressToContact(sipAddress);
  if (contact != mappedContact) {
    qWarning() << "Unable to remove sip address" << sipAddress << "of" << contact << "- already used by" << mappedContact;
283 284 285
    return;
  }

286
  removeContactOfSipAddress(sipAddress);
287 288 289
}

void SipAddressesModel::handleMessageReceived (const shared_ptr<linphone::ChatMessage> &message) {
290 291
  const QString peerAddress(Utils::coreStringToAppString(message->getChatRoom()->getPeerAddress()->asStringUriOnly()));
  addOrUpdateSipAddress(peerAddress, message);
292 293 294 295
}

void SipAddressesModel::handleCallStateChanged (
  const shared_ptr<linphone::Call> &call,
296
  linphone::Call::State state
297 298
) {
  // Ignore aborted calls.
299
  if (call->getCallLog()->getStatus() == linphone::Call::Status::Aborted)
300 301
    return;

302
  if (state == linphone::Call::State::End || state == linphone::Call::State::Error)
303
    addOrUpdateSipAddress(
304
      Utils::coreStringToAppString(call->getRemoteAddress()->asStringUriOnly()), call
305 306 307 308
    );
}

void SipAddressesModel::handlePresenceReceived (
309 310
  const QString &sipAddress,
  const shared_ptr<const linphone::PresenceModel> &presenceModel
311 312 313
) {
  Presence::PresenceStatus status;

314
  switch (presenceModel->getConsolidatedPresence()) {
315
    case linphone::ConsolidatedPresence::Online:
316 317
      status = Presence::PresenceStatus::Online;
      break;
318
    case linphone::ConsolidatedPresence::Busy:
319 320
      status = Presence::PresenceStatus::Busy;
      break;
321
    case linphone::ConsolidatedPresence::DoNotDisturb:
322 323
      status = Presence::PresenceStatus::DoNotDisturb;
      break;
324
    case linphone::ConsolidatedPresence::Offline:
325 326 327 328
      status = Presence::PresenceStatus::Offline;
      break;
  }

329 330
  auto it = mPeerAddressToSipAddressEntry.find(sipAddress);
  if (it != mPeerAddressToSipAddressEntry.end()) {
331
    qInfo() << QStringLiteral("Update presence of `%1`: %2.").arg(sipAddress).arg(status);
332
    it->presenceStatus = status;
333

334
    int row = mRefs.indexOf(&(*it));
335 336 337 338
    Q_ASSERT(row != -1);
    emit dataChanged(index(row, 0), index(row, 0));
  }

339
  updateObservers(sipAddress, status);
340 341
}

342
void SipAddressesModel::handleAllEntriesRemoved (ChatModel *chatModel) {
343 344 345 346 347 348
  auto it = mPeerAddressToSipAddressEntry.find(chatModel->getPeerAddress());
  if (it == mPeerAddressToSipAddressEntry.end())
    return;

  auto it2 = it->localAddressToConferenceEntry.find(chatModel->getLocalAddress());
  if (it2 == it->localAddressToConferenceEntry.end())
349
    return;
350
  it->localAddressToConferenceEntry.erase(it2);
351

352
  int row = mRefs.indexOf(&(*it));
353 354 355
  Q_ASSERT(row != -1);

  // No history, no contact => Remove sip address from list.
356
  if (!it->contact && it->localAddressToConferenceEntry.empty()) {
357 358 359 360 361 362 363
    removeRow(row);
    return;
  }

  emit dataChanged(index(row, 0), index(row, 0));
}

364
void SipAddressesModel::handleLastEntryRemoved (ChatModel *chatModel) {
365 366 367 368 369 370
  auto it = mPeerAddressToSipAddressEntry.find(chatModel->getPeerAddress());
  if (it == mPeerAddressToSipAddressEntry.end())
    return;

  auto it2 = it->localAddressToConferenceEntry.find(chatModel->getLocalAddress());
  if (it2 == it->localAddressToConferenceEntry.end())
371 372 373 374 375 376 377 378 379 380 381 382
    return;

  int row = mRefs.indexOf(&(*it));
  Q_ASSERT(row != -1);

  Q_ASSERT(chatModel->rowCount() > 0);
  const QVariantMap map = chatModel->data(
    chatModel->index(chatModel->rowCount() - 1, 0),
    ChatModel::ChatEntry
  ).toMap();

  // Update the timestamp with the new last chat message timestamp.
383
  it2->timestamp = map["timestamp"].toDateTime();
384
  emit dataChanged(index(row, 0), index(row, 0));
385 386
}

387 388 389 390 391
void SipAddressesModel::handleMessageCountReset (ChatModel *chatModel) {
  const QString &peerAddress = chatModel->getPeerAddress();
  auto it = mPeerAddressToSipAddressEntry.find(peerAddress);
  if (it == mPeerAddressToSipAddressEntry.end())
    return;
392

393 394 395 396 397 398 399 400 401 402
  const QString &localAddress = chatModel->getLocalAddress();
  auto it2 = it->localAddressToConferenceEntry.find(localAddress);
  if (it2 == it->localAddressToConferenceEntry.end())
    return;

  it2->unreadMessageCount = 0;

  int row = mRefs.indexOf(&(*it));
  Q_ASSERT(row != -1);
  emit dataChanged(index(row, 0), index(row, 0));
403

404
  updateObservers(peerAddress, localAddress, 0);
405 406
}

407
void SipAddressesModel::handleMessageSent (const shared_ptr<linphone::ChatMessage> &message) {
408 409
  const QString localAddress(Utils::coreStringToAppString(message->getChatRoom()->getLocalAddress()->asStringUriOnly()));
  addOrUpdateSipAddress(localAddress, message);
410 411
}

412
void SipAddressesModel::handleIsComposingChanged (const shared_ptr<linphone::ChatRoom> &chatRoom) {
413 414 415 416 417
  auto it = mPeerAddressToSipAddressEntry.find(
    Utils::coreStringToAppString(chatRoom->getPeerAddress()->asStringUriOnly())
  );
  if (it == mPeerAddressToSipAddressEntry.end())
    return;
418

419 420 421 422 423 424 425 426 427 428 429
  auto it2 = it->localAddressToConferenceEntry.find(
    Utils::coreStringToAppString(chatRoom->getLocalAddress()->asStringUriOnly())
  );
  if (it2 == it->localAddressToConferenceEntry.end())
    return;

  it2->isComposing = chatRoom->isRemoteComposing();

  int row = mRefs.indexOf(&(*it));
  Q_ASSERT(row != -1);
  emit dataChanged(index(row, 0), index(row, 0));
430 431
}

432
// -----------------------------------------------------------------------------
433

434 435
void SipAddressesModel::addOrUpdateSipAddress (SipAddressEntry &sipAddressEntry, ContactModel *contact) {
  const QString &sipAddress = sipAddressEntry.sipAddress;
436 437

  if (contact)
438 439
    sipAddressEntry.contact = contact;
  else if (!sipAddressEntry.contact)
440 441 442
    qWarning() << QStringLiteral("`contact` field is empty on sip address: `%1`.").arg(sipAddress);

  updateObservers(sipAddress, contact);
443 444
}

445
void SipAddressesModel::addOrUpdateSipAddress (SipAddressEntry &sipAddressEntry, const shared_ptr<linphone::Call> &call) {
446
  const shared_ptr<linphone::CallLog> callLog = call->getCallLog();
447 448 449
  sipAddressEntry.localAddressToConferenceEntry[
    Utils::coreStringToAppString(callLog->getLocalAddress()->asStringUriOnly())
  ].timestamp = callLog->getStatus() == linphone::Call::Status::Success
450 451
    ? QDateTime::fromMSecsSinceEpoch((callLog->getStartDate() + callLog->getDuration()) * 1000)
    : QDateTime::fromMSecsSinceEpoch(callLog->getStartDate() * 1000);
452 453
}

454
void SipAddressesModel::addOrUpdateSipAddress (SipAddressEntry &sipAddressEntry, const shared_ptr<linphone::ChatMessage> &message) {
455 456
  shared_ptr<linphone::ChatRoom> chatRoom(message->getChatRoom());
  int count = chatRoom->getUnreadMessagesCount();
457

458 459 460 461 462 463 464
  QString localAddress(Utils::coreStringToAppString(
    message->isOutgoing()
      ? chatRoom->getPeerAddress()->asStringUriOnly()
      : chatRoom->getLocalAddress()->asStringUriOnly()
  ));
  qInfo() << QStringLiteral("Update (`%1`, `%2`) from chat message.").arg(sipAddressEntry.sipAddress, localAddress);

465 466 467
  ConferenceEntry &conferenceEntry = sipAddressEntry.localAddressToConferenceEntry[localAddress];
  conferenceEntry.timestamp = QDateTime::fromMSecsSinceEpoch(message->getTime() * 1000);
  conferenceEntry.unreadMessageCount = count;
468

469
  updateObservers(sipAddressEntry.sipAddress, localAddress, count);
470 471 472
}

template<typename T>
473
void SipAddressesModel::addOrUpdateSipAddress (const QString &sipAddress, T data) {
474 475
  auto it = mPeerAddressToSipAddressEntry.find(sipAddress);
  if (it != mPeerAddressToSipAddressEntry.end()) {
476
    addOrUpdateSipAddress(*it, data);
477

478
    int row = mRefs.indexOf(&(*it));
Ronan's avatar
Ronan committed
479 480
    Q_ASSERT(row != -1);
    emit dataChanged(index(row, 0), index(row, 0));
481

Ronan's avatar
Ronan committed
482 483
    return;
  }
484

485 486
  SipAddressEntry sipAddressEntry{ sipAddress, nullptr, Presence::Offline, {} };
  addOrUpdateSipAddress(sipAddressEntry, data);
487

488
  int row = mRefs.count();
489

Ronan's avatar
Ronan committed
490
  beginInsertRows(QModelIndex(), row, row);
491

492 493
  mPeerAddressToSipAddressEntry[sipAddress] = move(sipAddressEntry);
  mRefs << &mPeerAddressToSipAddressEntry[sipAddress];
494

Ronan's avatar
Ronan committed
495
  endInsertRows();
496 497
}

498 499
// -----------------------------------------------------------------------------

500
void SipAddressesModel::removeContactOfSipAddress (const QString &sipAddress) {
501 502
  auto it = mPeerAddressToSipAddressEntry.find(sipAddress);
  if (it == mPeerAddressToSipAddressEntry.end()) {
503
    qWarning() << QStringLiteral("Unable to remove unavailable sip address: `%1`.").arg(sipAddress);
504 505 506
    return;
  }

507 508 509
  // Try to map other contact on this sip address.
  ContactModel *contactModel = CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(sipAddress);
  updateObservers(sipAddress, contactModel);
510

511 512
  qInfo() << QStringLiteral("Map new contact on sip address: `%1`.").arg(sipAddress) << contactModel;
  addOrUpdateSipAddress(*it, contactModel);
513

514
  int row = mRefs.indexOf(&(*it));
515 516
  Q_ASSERT(row != -1);

517 518
  // History or contact exists, signal changes.
  if (!it->localAddressToConferenceEntry.empty() || contactModel) {
519
    emit dataChanged(index(row, 0), index(row, 0));
520
    return;
521
  }
522 523 524

  // Remove sip address if no history.
  removeRow(row);
525 526
}

Ronan's avatar
Ronan committed
527 528
// -----------------------------------------------------------------------------

Ronan's avatar
Ronan committed
529
void SipAddressesModel::initSipAddresses () {
530 531 532
  QElapsedTimer timer;
  timer.start();

Ronan's avatar
Ronan committed
533 534 535 536
  initSipAddressesFromChat();
  initSipAddressesFromCalls();
  initRefs();
  initSipAddressesFromContacts();
537 538

  qInfo() << "Sip addresses model initialized in:" << timer.elapsed() << "ms.";
Ronan's avatar
Ronan committed
539
}
Ronan's avatar
Ronan committed
540

Ronan's avatar
Ronan committed
541 542
void SipAddressesModel::initSipAddressesFromChat () {
  for (const auto &chatRoom : CoreManager::getInstance()->getCore()->getChatRooms()) {
543
    list<shared_ptr<linphone::ChatMessage>> history(chatRoom->getHistory(1));
Ronan's avatar
Ronan committed
544
    if (history.empty())
Ronan's avatar
Ronan committed
545 546
      continue;

547 548
    QString peerAddress(Utils::coreStringToAppString(chatRoom->getPeerAddress()->asStringUriOnly()));
    QString localAddress(Utils::coreStringToAppString(chatRoom->getLocalAddress()->asStringUriOnly()));
Ronan's avatar
Ronan committed
549

550 551 552 553 554
    getSipAddressEntry(peerAddress)->localAddressToConferenceEntry[localAddress] = {
      chatRoom->getUnreadMessagesCount(),
      false,
      QDateTime::fromMSecsSinceEpoch(history.back()->getTime() * 1000)
    };
Ronan's avatar
Ronan committed
555
  }
Ronan's avatar
Ronan committed
556
}
Ronan's avatar
Ronan committed
557

Ronan's avatar
Ronan committed
558
void SipAddressesModel::initSipAddressesFromCalls () {
559 560
  using ConferenceId = QPair<QString, QString>;
  QSet<ConferenceId> conferenceDone;
Ronan's avatar
Ronan committed
561
  for (const auto &callLog : CoreManager::getInstance()->getCore()->getCallLogs()) {
562 563
    const QString peerAddress(Utils::coreStringToAppString(callLog->getRemoteAddress()->asStringUriOnly()));
    const QString localAddress(Utils::coreStringToAppString(callLog->getLocalAddress()->asStringUriOnly()));
564

565 566 567 568 569 570 571 572 573 574 575 576 577 578
    switch (callLog->getStatus()) {
      case linphone::Call::Status::Aborted:
      case linphone::Call::Status::EarlyAborted:
        return; // Ignore aborted calls.

      case linphone::Call::Status::AcceptedElsewhere:
      case linphone::Call::Status::DeclinedElsewhere:
        return; // Ignore accepted calls on other device.

      case linphone::Call::Status::Success:
      case linphone::Call::Status::Missed:
      case linphone::Call::Status::Declined:
        break;
    }
579

580 581 582 583
    ConferenceId conferenceId{ peerAddress, localAddress };
    if (conferenceDone.contains(conferenceId))
      continue; // Already used.
    conferenceDone << conferenceId;
584 585

    // The duration can be wrong if status is not success.
586
    QDateTime timestamp(callLog->getStatus() == linphone::Call::Status::Success
587
      ? QDateTime::fromMSecsSinceEpoch((callLog->getStartDate() + callLog->getDuration()) * 1000)
588 589 590 591 592 593 594 595
      : QDateTime::fromMSecsSinceEpoch(callLog->getStartDate() * 1000));

    auto &localToConferenceEntry = getSipAddressEntry(peerAddress)->localAddressToConferenceEntry;
    auto it = localToConferenceEntry.find(localAddress);
    if (it == localToConferenceEntry.end())
      localToConferenceEntry[localAddress] = { 0, false, move(timestamp) };
    else if (it->timestamp.isNull() || timestamp > it->timestamp)
      it->timestamp = move(timestamp);
Ronan's avatar
Ronan committed
596
  }
Ronan's avatar
Ronan committed
597
}
Ronan's avatar
Ronan committed
598

Ronan's avatar
Ronan committed
599
void SipAddressesModel::initSipAddressesFromContacts () {
600
  for (auto &contact : CoreManager::getInstance()->getContactsListModel()->mList)
Ronan's avatar
Ronan committed
601
    handleContactAdded(contact);
Ronan's avatar
Ronan committed
602
}
603

Ronan's avatar
Ronan committed
604
void SipAddressesModel::initRefs () {
605 606
  for (const auto &sipAddressEntry : mPeerAddressToSipAddressEntry)
    mRefs << &sipAddressEntry;
Ronan's avatar
Ronan committed
607 608
}

609 610
// -----------------------------------------------------------------------------

611 612
void SipAddressesModel::updateObservers (const QString &sipAddress, ContactModel *contact) {
  for (auto &observer : mObservers.values(sipAddress))
613 614 615
    observer->setContact(contact);
}

616 617 618
void SipAddressesModel::updateObservers (const QString &sipAddress, const Presence::PresenceStatus &presenceStatus) {
  for (auto &observer : mObservers.values(sipAddress))
    observer->setPresenceStatus(presenceStatus);
619
}
620

621 622 623 624 625 626
void SipAddressesModel::updateObservers (const QString &peerAddress, const QString &localAddress, int messageCount) {
  for (auto &observer : mObservers.values(peerAddress))
    if (observer->getLocalAddress() == localAddress) {
      observer->setUnreadMessageCount(messageCount);
      return;
    }
627
}