mirror of
https://gitlab.linphone.org/BC/public/linphone-desktop.git
synced 2026-01-20 04:58:09 +00:00
257 lines
7.8 KiB
C++
257 lines
7.8 KiB
C++
/*
|
|
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
|
*
|
|
* This file is part of linphone-desktop
|
|
* (see https://www.linphone.org).
|
|
*
|
|
* 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/>.
|
|
*/
|
|
|
|
#include <algorithm>
|
|
|
|
#include <QDateTime>
|
|
#include <QDesktopServices>
|
|
#include <QElapsedTimer>
|
|
#include <QFileInfo>
|
|
#include <QMimeDatabase>
|
|
#include <QTimer>
|
|
#include <QUuid>
|
|
#include <QMessageBox>
|
|
#include <QUrlQuery>
|
|
#include <QImageReader>
|
|
|
|
#include "app/App.hpp"
|
|
#include "app/paths/Paths.hpp"
|
|
#include "app/providers/ThumbnailProvider.hpp"
|
|
#include "components/core/CoreHandlers.hpp"
|
|
#include "components/core/CoreManager.hpp"
|
|
#include "components/notifier/Notifier.hpp"
|
|
#include "components/settings/SettingsModel.hpp"
|
|
#include "utils/QExifImageHeader.hpp"
|
|
#include "utils/Utils.hpp"
|
|
|
|
#include "HistoryModel.hpp"
|
|
|
|
// =============================================================================
|
|
|
|
using namespace std;
|
|
|
|
static inline void fillCallStartEntry (QVariantMap &dest, const shared_ptr<linphone::CallLog> &callLog) {
|
|
dest["type"] = HistoryModel::CallEntry;
|
|
dest["timestamp"] = QDateTime::fromMSecsSinceEpoch(callLog->getStartDate() * 1000);
|
|
dest["isOutgoing"] = callLog->getDir() == linphone::Call::Dir::Outgoing;
|
|
dest["status"] = static_cast<HistoryModel::CallStatus>(callLog->getStatus());
|
|
dest["isStart"] = true;
|
|
dest["sipAddress"] = Utils::coreStringToAppString(callLog->getRemoteAddress()->asString());
|
|
}
|
|
|
|
static inline void fillCallEndEntry (QVariantMap &dest, const shared_ptr<linphone::CallLog> &callLog) {
|
|
dest["type"] = HistoryModel::CallEntry;
|
|
dest["timestamp"] = QDateTime::fromMSecsSinceEpoch((callLog->getStartDate() + callLog->getDuration()) * 1000);
|
|
dest["isOutgoing"] = callLog->getDir() == linphone::Call::Dir::Outgoing;
|
|
dest["status"] = static_cast<HistoryModel::CallStatus>(callLog->getStatus());
|
|
dest["isStart"] = false;
|
|
dest["sipAddress"] = Utils::coreStringToAppString(callLog->getRemoteAddress()->asString());
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
HistoryModel::HistoryModel (QObject *parent) :QAbstractListModel(parent){
|
|
CoreManager *coreManager = CoreManager::getInstance();
|
|
|
|
mCoreHandlers = coreManager->getHandlers();
|
|
|
|
setSipAddresses();
|
|
CoreHandlers *coreHandlers = mCoreHandlers.get();
|
|
QObject::connect(coreHandlers, &CoreHandlers::callStateChanged, this, &HistoryModel::handleCallStateChanged);
|
|
|
|
}
|
|
|
|
HistoryModel::~HistoryModel () {
|
|
}
|
|
|
|
QHash<int, QByteArray> HistoryModel::roleNames () const {
|
|
QHash<int, QByteArray> roles;
|
|
roles[Roles::HistoryEntry] = "$historyEntry";
|
|
roles[Roles::SectionDate] = "$sectionDate";
|
|
return roles;
|
|
}
|
|
|
|
int HistoryModel::rowCount (const QModelIndex &) const {
|
|
return mEntries.count();
|
|
}
|
|
|
|
QVariant HistoryModel::data (const QModelIndex &index, int role) const {
|
|
int row = index.row();
|
|
|
|
if (!index.isValid() || row < 0 || row >= mEntries.count())
|
|
return QVariant();
|
|
|
|
switch (role) {
|
|
case Roles::HistoryEntry: {
|
|
auto &data = mEntries[row].first;
|
|
return QVariant::fromValue(data);
|
|
}
|
|
case Roles::SectionDate:
|
|
return QVariant::fromValue(mEntries[row].first["timestamp"].toDate());
|
|
}
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
bool HistoryModel::removeRow (int row, const QModelIndex &) {
|
|
return removeRows(row, 1);
|
|
}
|
|
|
|
bool HistoryModel::removeRows (int row, int count, const QModelIndex &parent) {
|
|
int limit = row + count - 1;
|
|
|
|
if (row < 0 || count < 0 || limit >= mEntries.count())
|
|
return false;
|
|
|
|
beginRemoveRows(parent, row, limit);
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
removeEntry(mEntries[row]);
|
|
mEntries.removeAt(row);
|
|
}
|
|
|
|
endRemoveRows();
|
|
|
|
if (mEntries.count() == 0)
|
|
emit allEntriesRemoved();
|
|
else if (limit == mEntries.count())
|
|
emit lastEntryRemoved();
|
|
emit focused();// Removing rows is like having focus. Don't wait asynchronous events.
|
|
return true;
|
|
}
|
|
|
|
void HistoryModel::setSipAddresses () {
|
|
shared_ptr<linphone::Core> core = CoreManager::getInstance()->getCore();
|
|
mEntries.clear();
|
|
|
|
QElapsedTimer timer;
|
|
timer.start();
|
|
|
|
// Get calls.
|
|
for (auto &callLog : core->getCallLogs())
|
|
insertCall(callLog);
|
|
|
|
qInfo() << QStringLiteral("HistoryModel loaded in %3 milliseconds.").arg(timer.elapsed());
|
|
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void HistoryModel::removeEntry (int id) {
|
|
qInfo() << QStringLiteral("Removing call entry: %1.").arg(id);
|
|
|
|
if (!removeRow(id))
|
|
qWarning() << QStringLiteral("Unable to remove call entry: %1").arg(id);
|
|
}
|
|
|
|
void HistoryModel::removeAllEntries () {
|
|
qInfo() << QStringLiteral("Removing all call entries.");
|
|
|
|
beginResetModel();
|
|
|
|
for (auto &entry : mEntries)
|
|
removeEntry(entry);
|
|
|
|
mEntries.clear();
|
|
|
|
endResetModel();
|
|
|
|
emit allEntriesRemoved();
|
|
emit focused();// Removing all entries is like having focus. Don't wait asynchronous events.
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void HistoryModel::removeEntry (HistoryEntryData &entry) {
|
|
int type = entry.first["type"].toInt();
|
|
|
|
switch (type) {
|
|
|
|
case HistoryModel::CallEntry: {
|
|
if (entry.first["status"].toInt() == CallStatusSuccess) {
|
|
// WARNING: Unable to remove symmetric call here. (start/end)
|
|
// We are between `beginRemoveRows` and `endRemoveRows`.
|
|
// A solution is to schedule a `removeEntry` call in the Qt main loop.
|
|
shared_ptr<void> linphonePtr = entry.second;
|
|
QTimer::singleShot(0, this, [this, linphonePtr]() {
|
|
auto it = find_if(mEntries.begin(), mEntries.end(), [linphonePtr](const HistoryEntryData &entry) {
|
|
return entry.second == linphonePtr;
|
|
});
|
|
|
|
if (it != mEntries.end())
|
|
removeEntry(int(distance(mEntries.begin(), it)));
|
|
});
|
|
}
|
|
|
|
CoreManager::getInstance()->getCore()->removeCallLog(static_pointer_cast<linphone::CallLog>(entry.second));
|
|
break;
|
|
}
|
|
|
|
default:
|
|
qWarning() << QStringLiteral("Unknown history entry type: %1.").arg(type);
|
|
}
|
|
}
|
|
|
|
void HistoryModel::insertCall (const shared_ptr<linphone::CallLog> &callLog) {
|
|
linphone::Call::Status status = callLog->getStatus();
|
|
|
|
auto insertEntry = [this](
|
|
const HistoryEntryData &entry,
|
|
const QList<HistoryEntryData>::iterator *start = nullptr
|
|
) {
|
|
auto it = lower_bound(start ? *start : mEntries.begin(), mEntries.end(), entry, [](const HistoryEntryData &a, const HistoryEntryData &b) {
|
|
return a.first["timestamp"] < b.first["timestamp"];
|
|
});
|
|
|
|
int row = int(distance(mEntries.begin(), it));
|
|
|
|
beginInsertRows(QModelIndex(), row, row);
|
|
it = mEntries.insert(it, entry);
|
|
endInsertRows();
|
|
emit layoutChanged();
|
|
|
|
return it;
|
|
};
|
|
|
|
// Add start call.
|
|
QVariantMap start;
|
|
fillCallStartEntry(start, callLog);
|
|
auto it = insertEntry(qMakePair(start, static_pointer_cast<void>(callLog)));
|
|
|
|
if (status == linphone::Call::Status::Success) {
|
|
QVariantMap end;
|
|
fillCallEndEntry(end, callLog);
|
|
insertEntry(qMakePair(end, static_pointer_cast<void>(callLog)), &it);
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void HistoryModel::resetMessageCount () {
|
|
emit callCountReset();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void HistoryModel::handleCallStateChanged (const shared_ptr<linphone::Call> &call, linphone::Call::State state) {
|
|
if (state == linphone::Call::State::End || state == linphone::Call::State::Error)
|
|
insertCall(call->getCallLog());
|
|
}
|
|
|