File indexing completed on 2024-04-28 16:42:52

0001 // SPDX-FileCopyrightText: 2021 Alexey Andreyev <aa13q@ya.ru>
0002 //
0003 // SPDX-License-Identifier: LicenseRef-KDE-Accepted-GPL
0004 
0005 #include "active-call-model.h"
0006 
0007 constexpr int CALL_DURATION_UPDATE_DELAY = 1000;
0008 
0009 ActiveCallModel::ActiveCallModel(QObject *parent)
0010     : CallModel(parent)
0011 {
0012 }
0013 
0014 void ActiveCallModel::sendDtmf(const QString &tones)
0015 {
0016     if (!_callUtils) {
0017         qDebug() << Q_FUNC_INFO << "CallUtils is not initiated";
0018         return;
0019     }
0020     QString deviceUni;
0021     QString callUni;
0022     _callUtils->sendDtmf(deviceUni, callUni, tones);
0023 }
0024 
0025 void ActiveCallModel::dial(const QString &deviceUni, const QString &number)
0026 {
0027     if (!_callUtils) {
0028         qDebug() << Q_FUNC_INFO << "CallUtils is not initiated";
0029         return;
0030     }
0031     _callUtils->dial(deviceUni, number);
0032 }
0033 
0034 QString ActiveCallModel::activeCallUni()
0035 {
0036     QString activeCallUni;
0037     if (_calls.size() < 1) {
0038         qDebug() << Q_FUNC_INFO << "empty active calls list";
0039         return activeCallUni;
0040     }
0041     for (int i = 0; i < _calls.size(); i++) {
0042         const auto call = _calls.at(i);
0043         if (call.state != DialerTypes::CallState::Terminated) {
0044             return call.id;
0045         }
0046     }
0047     return activeCallUni;
0048 }
0049 
0050 QVariant ActiveCallModel::data(const QModelIndex &index, int role) const
0051 {
0052     int row = index.row();
0053     switch (role) {
0054     case Roles::EventRole:
0055         return _calls[row].id;
0056     case Roles::ProtocolRole:
0057         return _calls[row].protocol;
0058     case Roles::AccountRole:
0059         return _calls[row].account;
0060     case Roles::ProviderRole:
0061         return _calls[row].provider;
0062     case Roles::CommunicationWithRole:
0063         return _calls[row].communicationWith;
0064     case Roles::DirectionRole:
0065         return QVariant::fromValue(_calls[row].direction);
0066     case Roles::StateRole:
0067         return QVariant::fromValue(_calls[row].state);
0068     case Roles::StateReasonRole:
0069         return QVariant::fromValue(_calls[row].stateReason);
0070     case Roles::CallAttemptDurationRole:
0071         return _calls[row].callAttemptDuration;
0072     case Roles::StartedAtRole:
0073         return _calls[row].startedAt;
0074     case Roles::DurationRole:
0075         return _calls[row].duration;
0076     }
0077     return {};
0078 }
0079 
0080 int ActiveCallModel::rowCount(const QModelIndex &parent) const
0081 {
0082     Q_UNUSED(parent)
0083     return _calls.size();
0084 }
0085 
0086 void ActiveCallModel::onUtilsCallAdded(const QString &deviceUni,
0087                                        const QString &callUni,
0088                                        const DialerTypes::CallDirection &callDirection,
0089                                        const DialerTypes::CallState &callState,
0090                                        const DialerTypes::CallStateReason &callStateReason,
0091                                        const QString communicationWith)
0092 {
0093     Q_UNUSED(deviceUni);
0094     Q_UNUSED(callUni);
0095     Q_UNUSED(callDirection);
0096     Q_UNUSED(callState);
0097     Q_UNUSED(callStateReason);
0098     if (!_callUtils) {
0099         qDebug() << Q_FUNC_INFO << "CallUtils is not initiated";
0100         return;
0101     }
0102     _callUtils->fetchCalls();
0103     setCommunicationWith(communicationWith);
0104     _callsTimer.start();
0105 }
0106 
0107 void ActiveCallModel::onUtilsCallDeleted(const QString &deviceUni, const QString &callUni)
0108 {
0109     Q_UNUSED(deviceUni);
0110     Q_UNUSED(callUni);
0111     if (!_callUtils) {
0112         qDebug() << Q_FUNC_INFO << "CallUtils is not initiated";
0113         return;
0114     }
0115     _callUtils->fetchCalls();
0116     _callsTimer.stop();
0117 }
0118 
0119 void ActiveCallModel::onUtilsCallStateChanged(const DialerTypes::CallData &callData)
0120 {
0121     qDebug() << Q_FUNC_INFO << callData.state << callData.stateReason;
0122     auto callState = callData.state;
0123 
0124     if (callState == DialerTypes::CallState::Active) {
0125         _callsTimer.start();
0126     }
0127     if (callState == DialerTypes::CallState::RingingIn) {
0128         _callsTimer.start();
0129     }
0130     if (callState == DialerTypes::CallState::Terminated) {
0131         _callsTimer.stop();
0132     }
0133 
0134     // find call by id and update all the stuff including the duration
0135     for (int i = 0; i < _calls.size(); i++) {
0136         auto call = _calls.at(i);
0137         if (call.id == callData.id) {
0138             call = callData;
0139             Q_EMIT dataChanged(index(i), index(i));
0140             return;
0141         }
0142     }
0143 }
0144 
0145 void ActiveCallModel::onUtilsCallsChanged(const DialerTypes::CallDataVector &fetchedCalls)
0146 {
0147     qDebug() << Q_FUNC_INFO << _calls.size() << fetchedCalls.size();
0148     beginResetModel();
0149     _calls = fetchedCalls;
0150     endResetModel();
0151     bool active = (_calls.size() > 0);
0152     setActive(active);
0153     if (!active) {
0154         return;
0155     }
0156     bool incoming = false;
0157     for (int i = 0; i < _calls.size(); i++) {
0158         const auto call = _calls.at(i);
0159         // trying to determine current active call
0160         // should be checked could it be improved
0161         // with with DialerTypes::CallDirection
0162         if ((call.state != DialerTypes::CallState::Unknown) && (call.state != DialerTypes::CallState::Held) && (call.state != DialerTypes::CallState::Waiting)
0163             && (call.state != DialerTypes::CallState::Terminated)) {
0164             setCommunicationWith(call.communicationWith);
0165             setDuration(call.duration);
0166         }
0167         if (call.direction == DialerTypes::CallDirection::Incoming) {
0168             if (call.state == DialerTypes::CallState::RingingIn) {
0169                 incoming = true;
0170                 break;
0171             }
0172         }
0173     }
0174     setIncoming(incoming);
0175 }
0176 
0177 bool ActiveCallModel::active() const
0178 {
0179     return _active;
0180 }
0181 
0182 void ActiveCallModel::setActive(bool newActive)
0183 {
0184     if (_active == newActive)
0185         return;
0186     _active = newActive;
0187     qDebug() << Q_FUNC_INFO;
0188     Q_EMIT activeChanged();
0189 }
0190 
0191 bool ActiveCallModel::incoming() const
0192 {
0193     return _incoming;
0194 }
0195 
0196 void ActiveCallModel::setIncoming(bool newIncoming)
0197 {
0198     if (_incoming == newIncoming)
0199         return;
0200     _incoming = newIncoming;
0201     Q_EMIT incomingChanged();
0202 }
0203 
0204 QString ActiveCallModel::communicationWith() const
0205 {
0206     return _communicationWith;
0207 }
0208 
0209 void ActiveCallModel::setCommunicationWith(const QString communicationWith)
0210 {
0211     if (_communicationWith == communicationWith)
0212         return;
0213     _communicationWith = communicationWith;
0214     Q_EMIT communicationWithChanged();
0215 }
0216 
0217 qulonglong ActiveCallModel::duration() const
0218 {
0219     return _duration;
0220 }
0221 
0222 void ActiveCallModel::setDuration(qulonglong duration)
0223 {
0224     if (_duration == duration)
0225         return;
0226     _duration = duration;
0227     Q_EMIT durationChanged();
0228 }
0229 
0230 void ActiveCallModel::setCallUtils(org::kde::telephony::CallUtils *callUtils)
0231 {
0232     if (!callUtils) {
0233         qDebug() << Q_FUNC_INFO << "Could not initiate CallUtils interface";
0234         return;
0235     }
0236     _callUtils = callUtils;
0237 
0238     connect(_callUtils, &org::kde::telephony::CallUtils::callStateChanged, this, &ActiveCallModel::onUtilsCallStateChanged);
0239     connect(_callUtils, &org::kde::telephony::CallUtils::callAdded, this, &ActiveCallModel::onUtilsCallAdded);
0240     connect(_callUtils, &org::kde::telephony::CallUtils::callDeleted, this, &ActiveCallModel::onUtilsCallDeleted);
0241     connect(_callUtils, &org::kde::telephony::CallUtils::callsChanged, this, &ActiveCallModel::onUtilsCallsChanged);
0242 
0243     _callsTimer.setInterval(CALL_DURATION_UPDATE_DELAY);
0244     connect(&_callsTimer, &QTimer::timeout, this, [this]() {
0245         // minimize the number of method calls by incrementing the duration on the client side too
0246         // see also (D-Bus API Design Guidelines):
0247         // https://dbus.freedesktop.org/doc/dbus-api-design.html
0248         _updateTimers();
0249     });
0250 
0251     callUtils->fetchCalls(); // TODO: simplify sync
0252 }
0253 
0254 void ActiveCallModel::_updateTimers()
0255 {
0256     for (int i = 0; i < _calls.size(); i++) {
0257         auto call = _calls.at(i);
0258         auto callState = call.state;
0259 
0260         if (callState == DialerTypes::CallState::RingingIn) {
0261             qDebug() << "incoming call";
0262             call.callAttemptDuration++;
0263             Q_EMIT dataChanged(index(i), index(i), {CallAttemptDurationRole});
0264         }
0265         if (callState == DialerTypes::CallState::Active) {
0266             qDebug() << "call started";
0267             call.duration++;
0268             setDuration(call.duration);
0269             Q_EMIT dataChanged(index(i), index(i), {DurationRole});
0270         }
0271     }
0272 }