Select Git revision
CreationUsingNew.hpp
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
call.cpp 59.30 KiB
/****************************************************************************
* Copyright (C) 2009-2014 by Savoir-Faire Linux *
* Author : Jérémy Quentin <jeremy.quentin@savoirfairelinux.com> *
* Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library 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 *
* Lesser 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/>. *
***************************************************************************/
//Parent
#include "call.h"
//C include
#include <time.h>
//Qt
#include <QtCore/QFile>
#include <QtCore/QTimer>
//SFLPhone library
#include "dbus/callmanager.h"
#include "dbus/configurationmanager.h"
#include "abstractitembackend.h"
#include "contact.h"
#include "account.h"
#include "accountlistmodel.h"
#include "videomodel.h"
#include "historymodel.h"
#include "instantmessagingmodel.h"
#include "useractionmodel.h"
#include "callmodel.h"
#include "numbercategory.h"
#include "phonedirectorymodel.h"
#include "phonenumber.h"
#include "videorenderer.h"
#include "tlsmethodmodel.h"
#include "audiosettingsmodel.h"
#include "contactmodel.h"
const TypedStateMachine< TypedStateMachine< Call::State , Call::Action> , Call::State> Call::actionPerformedStateMap =
{{
// ACCEPT REFUSE TRANSFER HOLD RECORD /**/
/*INCOMING */ {{Call::State::INCOMING , Call::State::INCOMING , Call::State::ERROR , Call::State::INCOMING , Call::State::INCOMING }},/**/
/*RINGING */ {{Call::State::ERROR , Call::State::RINGING , Call::State::ERROR , Call::State::ERROR , Call::State::RINGING }},/**/
/*CURRENT */ {{Call::State::ERROR , Call::State::CURRENT , Call::State::TRANSFERRED , Call::State::CURRENT , Call::State::CURRENT }},/**/
/*DIALING */ {{Call::State::DIALING , Call::State::OVER , Call::State::ERROR , Call::State::ERROR , Call::State::ERROR }},/**/
/*HOLD */ {{Call::State::ERROR , Call::State::HOLD , Call::State::TRANSF_HOLD , Call::State::HOLD , Call::State::HOLD }},/**/
/*FAILURE */ {{Call::State::ERROR , Call::State::FAILURE , Call::State::ERROR , Call::State::ERROR , Call::State::ERROR }},/**/
/*BUSY */ {{Call::State::ERROR , Call::State::BUSY , Call::State::ERROR , Call::State::ERROR , Call::State::ERROR }},/**/
/*TRANSFER */ {{Call::State::TRANSFERRED, Call::State::TRANSFERRED , Call::State::CURRENT , Call::State::TRANSFERRED , Call::State::TRANSFERRED }},/**/
/*TRANSF_HOLD */ {{Call::State::TRANSF_HOLD, Call::State::TRANSF_HOLD , Call::State::HOLD , Call::State::TRANSF_HOLD , Call::State::TRANSF_HOLD }},/**/
/*OVER */ {{Call::State::ERROR , Call::State::ERROR , Call::State::ERROR , Call::State::ERROR , Call::State::ERROR }},/**/
/*ERROR */ {{Call::State::ERROR , Call::State::ERROR , Call::State::ERROR , Call::State::ERROR , Call::State::ERROR }},/**/
/*CONF */ {{Call::State::ERROR , Call::State::CURRENT , Call::State::TRANSFERRED , Call::State::CURRENT , Call::State::CURRENT }},/**/
/*CONF_HOLD */ {{Call::State::ERROR , Call::State::HOLD , Call::State::TRANSF_HOLD , Call::State::HOLD , Call::State::HOLD }},/**/
}};//
const TypedStateMachine< TypedStateMachine< function , Call::Action > , Call::State > Call::actionPerformedFunctionMap =
{{
// ACCEPT REFUSE TRANSFER HOLD RECORD /**/
/*INCOMING */ {{&Call::accept , &Call::refuse , &Call::acceptTransf , &Call::acceptHold , &Call::setRecord }},/**/
/*RINGING */ {{&Call::nothing , &Call::hangUp , &Call::nothing , &Call::nothing , &Call::setRecord }},/**/
/*CURRENT */ {{&Call::nothing , &Call::hangUp , &Call::nothing , &Call::hold , &Call::setRecord }},/**/
/*DIALING */ {{&Call::call , &Call::cancel , &Call::nothing , &Call::nothing , &Call::nothing }},/**/
/*HOLD */ {{&Call::nothing , &Call::hangUp , &Call::nothing , &Call::unhold , &Call::setRecord }},/**/
/*FAILURE */ {{&Call::nothing , &Call::hangUp , &Call::nothing , &Call::nothing , &Call::nothing }},/**/
/*BUSY */ {{&Call::nothing , &Call::hangUp , &Call::nothing , &Call::nothing , &Call::nothing }},/**/
/*TRANSFERT */ {{&Call::transfer , &Call::hangUp , &Call::transfer , &Call::hold , &Call::setRecord }},/**/
/*TRANSFERT_HOLD */ {{&Call::transfer , &Call::hangUp , &Call::transfer , &Call::unhold , &Call::setRecord }},/**/
/*OVER */ {{&Call::nothing , &Call::nothing , &Call::nothing , &Call::nothing , &Call::nothing }},/**/
/*ERROR */ {{&Call::nothing , &Call::remove , &Call::nothing , &Call::nothing , &Call::nothing }},/**/
/*CONF */ {{&Call::nothing , &Call::hangUp , &Call::nothing , &Call::hold , &Call::setRecord }},/**/
/*CONF_HOLD */ {{&Call::nothing , &Call::hangUp , &Call::nothing , &Call::unhold , &Call::setRecord }},/**/
}};//
const TypedStateMachine< TypedStateMachine< Call::State , Call::DaemonState> , Call::State> Call::stateChangedStateMap =
{{
// RINGING CURRENT BUSY HOLD HUNGUP FAILURE /**/
/*INCOMING */ {{Call::State::INCOMING , Call::State::CURRENT , Call::State::BUSY , Call::State::HOLD , Call::State::OVER , Call::State::FAILURE }},/**/
/*RINGING */ {{Call::State::RINGING , Call::State::CURRENT , Call::State::BUSY , Call::State::HOLD , Call::State::OVER , Call::State::FAILURE }},/**/
/*CURRENT */ {{Call::State::CURRENT , Call::State::CURRENT , Call::State::BUSY , Call::State::HOLD , Call::State::OVER , Call::State::FAILURE }},/**/
/*DIALING */ {{Call::State::RINGING , Call::State::CURRENT , Call::State::BUSY , Call::State::HOLD , Call::State::OVER , Call::State::FAILURE }},/**/
/*HOLD */ {{Call::State::HOLD , Call::State::CURRENT , Call::State::BUSY , Call::State::HOLD , Call::State::OVER , Call::State::FAILURE }},/**/
/*FAILURE */ {{Call::State::FAILURE , Call::State::FAILURE , Call::State::BUSY , Call::State::FAILURE , Call::State::OVER , Call::State::FAILURE }},/**/
/*BUSY */ {{Call::State::BUSY , Call::State::CURRENT , Call::State::BUSY , Call::State::BUSY , Call::State::OVER , Call::State::FAILURE }},/**/
/*TRANSFER */ {{Call::State::TRANSFERRED , Call::State::TRANSFERRED, Call::State::BUSY , Call::State::TRANSF_HOLD , Call::State::OVER , Call::State::FAILURE }},/**/
/*TRANSF_HOLD */ {{Call::State::TRANSF_HOLD , Call::State::TRANSFERRED, Call::State::BUSY , Call::State::TRANSF_HOLD , Call::State::OVER , Call::State::FAILURE }},/**/
/*OVER */ {{Call::State::OVER , Call::State::OVER , Call::State::OVER , Call::State::OVER , Call::State::OVER , Call::State::OVER }},/**/
/*ERROR */ {{Call::State::ERROR , Call::State::ERROR , Call::State::ERROR , Call::State::ERROR , Call::State::ERROR , Call::State::ERROR }},/**/
/*CONF */ {{Call::State::CURRENT , Call::State::CURRENT , Call::State::BUSY , Call::State::HOLD , Call::State::OVER , Call::State::FAILURE }},/**/
/*CONF_HOLD */ {{Call::State::HOLD , Call::State::CURRENT , Call::State::BUSY , Call::State::HOLD , Call::State::OVER , Call::State::FAILURE }},/**/
}};//
const TypedStateMachine< TypedStateMachine< function , Call::DaemonState > , Call::State > Call::stateChangedFunctionMap =
{{
// RINGING CURRENT BUSY HOLD HUNGUP FAILURE /**/
/*INCOMING */ {{&Call::nothing , &Call::start , &Call::startWeird , &Call::startWeird , &Call::startStop , &Call::start }},/**/
/*RINGING */ {{&Call::nothing , &Call::start , &Call::start , &Call::start , &Call::startStop , &Call::start }},/**/
/*CURRENT */ {{&Call::nothing , &Call::nothing , &Call::warning , &Call::nothing , &Call::stop , &Call::nothing }},/**/
/*DIALING */ {{&Call::nothing , &Call::warning , &Call::warning , &Call::warning , &Call::stop , &Call::warning }},/**/
/*HOLD */ {{&Call::nothing , &Call::nothing , &Call::warning , &Call::nothing , &Call::stop , &Call::nothing }},/**/
/*FAILURE */ {{&Call::nothing , &Call::warning , &Call::warning , &Call::warning , &Call::stop , &Call::nothing }},/**/
/*BUSY */ {{&Call::nothing , &Call::nothing , &Call::nothing , &Call::warning , &Call::stop , &Call::nothing }},/**/
/*TRANSFERT */ {{&Call::nothing , &Call::nothing , &Call::warning , &Call::nothing , &Call::stop , &Call::nothing }},/**/
/*TRANSFERT_HOLD */ {{&Call::nothing , &Call::nothing , &Call::warning , &Call::nothing , &Call::stop , &Call::nothing }},/**/
/*OVER */ {{&Call::nothing , &Call::warning , &Call::warning , &Call::warning , &Call::stop , &Call::warning }},/**/
/*ERROR */ {{&Call::error , &Call::error , &Call::error , &Call::error , &Call::stop , &Call::error }},/**/
/*CONF */ {{&Call::nothing , &Call::nothing , &Call::warning , &Call::nothing , &Call::stop , &Call::nothing }},/**/
/*CONF_HOLD */ {{&Call::nothing , &Call::nothing , &Call::warning , &Call::nothing , &Call::stop , &Call::nothing }},/**/
}};//
QDebug LIB_EXPORT operator<<(QDebug dbg, const Call::State& c)
{
dbg.nospace() << QString(Call::toHumanStateName(c));
return dbg.space();
}
QDebug LIB_EXPORT operator<<(QDebug dbg, const Call::DaemonState& c)
{
dbg.nospace() << static_cast<int>(c);
return dbg.space();
}
QDebug LIB_EXPORT operator<<(QDebug dbg, const Call::Action& c)
{
dbg.nospace() << static_cast<int>(c);
return dbg.space();
}
///Constructor
Call::Call(Call::State startState, const QString& callId, const QString& peerName, PhoneNumber* number, Account* account)
: QObject(CallModel::instance()), m_isConference(false),m_pStopTimeStamp(0),
m_pImModel(nullptr),m_pTimer(nullptr),m_Recording(false),m_Account(nullptr),
m_PeerName(peerName),m_pPeerPhoneNumber(number),m_HistoryConst(HistoryTimeCategoryModel::HistoryConst::Never),
m_CallId(callId),m_CurrentState(startState),m_pStartTimeStamp(0),m_pDialNumber(nullptr),m_pTransferNumber(nullptr),
m_History(false),m_Missed(false),m_Direction(Call::Direction::OUTGOING),m_pBackend(nullptr)
{
m_Account = account;
Q_ASSERT(!callId.isEmpty());
setObjectName("Call:"+callId);
changeCurrentState(startState);
m_pUserActionModel = new UserActionModel(this);
emit changed();
emit changed(this);
}
///Destructor
Call::~Call()
{
if (m_pTimer) delete m_pTimer;
this->disconnect();
if ( m_pTransferNumber ) delete m_pTransferNumber;
if ( m_pDialNumber ) delete m_pDialNumber;
}
///Constructor
Call::Call(const QString& confId, const QString& account): QObject(CallModel::instance()),
m_pStopTimeStamp(0),m_pStartTimeStamp(0),m_pImModel(nullptr),m_ConfId(confId),
m_Account(AccountListModel::instance()->getAccountById(account)),m_CurrentState(Call::State::CONFERENCE),
m_pTimer(nullptr), m_isConference(false),m_pPeerPhoneNumber(nullptr),m_pDialNumber(nullptr),m_pTransferNumber(nullptr),
m_HistoryConst(HistoryTimeCategoryModel::HistoryConst::Never),m_History(false),m_Missed(false),
m_Direction(Call::Direction::OUTGOING),m_pBackend(nullptr)
{
setObjectName("Conf:"+confId);
m_isConference = !m_ConfId.isEmpty();
m_pUserActionModel = new UserActionModel(this);
if (m_isConference) {
time_t curTime;
::time(&curTime);
setStartTimeStamp(curTime);
m_pTimer = new QTimer(this);
m_pTimer->setInterval(1000);
connect(m_pTimer,SIGNAL(timeout()),this,SLOT(updated()));
m_pTimer->start();
CallManagerInterface& callManager = DBus::CallManager::instance();
MapStringString details = callManager.getConferenceDetails(m_ConfId) ;
m_CurrentState = confStatetoCallState(details["CONF_STATE"]);
emit stateChanged();
}
}
/*****************************************************************************
* *
* Call builder *
* *
****************************************************************************/
///Build a call from its ID
Call* Call::buildExistingCall(const QString& callId)
{
CallManagerInterface& callManager = DBus::CallManager::instance();
MapStringString details = callManager.getCallDetails(callId).value();
//Too noisy
//qDebug() << "Constructing existing call with details : " << details;
const QString peerNumber = details[ Call::DetailsMapFields::PEER_NUMBER ];
const QString peerName = details[ Call::DetailsMapFields::PEER_NAME ];
const QString account = details[ Call::DetailsMapFields::ACCOUNT_ID ];
Call::State startState = startStateFromDaemonCallState(details[Call::DetailsMapFields::STATE], details[Call::DetailsMapFields::TYPE]);
Account* acc = AccountListModel::instance()->getAccountById(account);
PhoneNumber* nb = PhoneDirectoryModel::instance()->getNumber(peerNumber,acc);
Call* call = new Call(startState, callId, peerName, nb, acc);
call->m_Recording = callManager.getIsRecording(callId);
call->m_HistoryState = historyStateFromType(details[Call::HistoryMapFields::STATE]);
if (!details[ Call::DetailsMapFields::TIMESTAMP_START ].isEmpty())
call->setStartTimeStamp(details[ Call::DetailsMapFields::TIMESTAMP_START ].toInt());
else {
time_t curTime;
::time(&curTime);
call->setStartTimeStamp(curTime);
}
call->m_pTimer = new QTimer(CallModel::instance());
call->m_pTimer->setInterval(1000);
connect(call->m_pTimer,SIGNAL(timeout()),call,SLOT(updated()));
call->m_pTimer->start();
if (call->peerPhoneNumber()) {
call->peerPhoneNumber()->addCall(call);
}
return call;
} //buildExistingCall
///Build a call from a dialing call (a call that is about to exist)
Call* Call::buildDialingCall(const QString& callId, const QString & peerName, Account* account)
{
Call* call = new Call(Call::State::DIALING, callId, peerName, nullptr, account);
call->m_HistoryState = Call::LegacyHistoryState::NONE;
call->m_Direction = Call::Direction::OUTGOING;
if (AudioSettingsModel::instance()->isRoomToneEnabled()) {
AudioSettingsModel::instance()->playRoomTone();
}
return call;
}
///Build a call from a dbus event
Call* Call::buildIncomingCall(const QString& callId)
{
CallManagerInterface & callManager = DBus::CallManager::instance();
MapStringString details = callManager.getCallDetails(callId).value();
const QString from = details[ Call::DetailsMapFields::PEER_NUMBER ];
const QString account = details[ Call::DetailsMapFields::ACCOUNT_ID ];
const QString peerName = details[ Call::DetailsMapFields::PEER_NAME ];
Account* acc = AccountListModel::instance()->getAccountById(account);
PhoneNumber* nb = PhoneDirectoryModel::instance()->getNumber(from,acc);
Call* call = new Call(Call::State::INCOMING, callId, peerName, nb, acc);
call->m_HistoryState = Call::LegacyHistoryState::MISSED;
call->m_Direction = Call::Direction::INCOMING;
if (call->peerPhoneNumber()) {
call->peerPhoneNumber()->addCall(call);
}
return call;
} //buildIncomingCall
///Build a ringing call (from dbus)
Call* Call::buildRingingCall(const QString & callId)
{
CallManagerInterface& callManager = DBus::CallManager::instance();
MapStringString details = callManager.getCallDetails(callId).value();
const QString from = details[ Call::DetailsMapFields::PEER_NUMBER ];
const QString account = details[ Call::DetailsMapFields::ACCOUNT_ID ];
const QString peerName = details[ Call::DetailsMapFields::PEER_NAME ];
Account* acc = AccountListModel::instance()->getAccountById(account);
PhoneNumber* nb = PhoneDirectoryModel::instance()->getNumber(from,acc);
Call* call = new Call(Call::State::RINGING, callId, peerName, nb, acc);
call->m_HistoryState = LegacyHistoryState::OUTGOING;
call->m_Direction = Call::Direction::OUTGOING;
if (call->peerPhoneNumber()) {
call->peerPhoneNumber()->addCall(call);
}
return call;
} //buildRingingCall
/*****************************************************************************
* *
* History *
* *
****************************************************************************/
///Build a call that is already over
Call* Call::buildHistoryCall(const QMap<QString,QString>& hc)
{
const QString& callId = hc[ Call::HistoryMapFields::CALLID ] ;
time_t startTimeStamp = hc[ Call::HistoryMapFields::TIMESTAMP_START ].toUInt() ;
time_t stopTimeStamp = hc[ Call::HistoryMapFields::TIMESTAMP_STOP ].toUInt() ;
const QString& accId = hc[ Call::HistoryMapFields::ACCOUNT_ID ] ;
const QString& name = hc[ Call::HistoryMapFields::DISPLAY_NAME ] ;
const QString& number = hc[ Call::HistoryMapFields::PEER_NUMBER ] ;
const QString& type = hc[ Call::HistoryMapFields::STATE ] ;
const QString& direction = hc[ Call::HistoryMapFields::DIRECTION ] ;
const bool missed = hc[ Call::HistoryMapFields::MISSED ] == "true";
Account* acc = AccountListModel::instance()->getAccountById(accId);
PhoneNumber* nb = PhoneDirectoryModel::instance()->getNumber(number,acc);
Call* call = new Call(Call::State::OVER, callId, (name == "empty")?QString():name, nb, acc );
call->m_pStopTimeStamp = stopTimeStamp ;
call->m_History = true;
call->setStartTimeStamp(startTimeStamp);
call->m_HistoryState = historyStateFromType(type);
call->m_Account = AccountListModel::instance()->getAccountById(accId);
//BEGIN In ~2015, remove the old logic and clean this
if (missed || call->m_HistoryState == Call::LegacyHistoryState::MISSED) {
call->m_Missed = true;
call->m_HistoryState = Call::LegacyHistoryState::MISSED;
}
if (!direction.isEmpty()) {
if (direction == HistoryStateName::INCOMING) {
call->m_Direction = Call::Direction::INCOMING ;
call->m_HistoryState = Call::LegacyHistoryState::INCOMING;
}
else if (direction == HistoryStateName::OUTGOING) {
call->m_Direction = Call::Direction::OUTGOING ;
call->m_HistoryState = Call::LegacyHistoryState::OUTGOING;
}
}
else if (call->m_HistoryState == Call::LegacyHistoryState::INCOMING)
call->m_Direction = Call::Direction::INCOMING ;
else if (call->m_HistoryState == Call::LegacyHistoryState::OUTGOING)
call->m_Direction = Call::Direction::OUTGOING ;
else //BUG Pick one, even if it is the wrong one
call->m_Direction = Call::Direction::OUTGOING ;
if (missed)
call->m_HistoryState = Call::LegacyHistoryState::MISSED;
//END
call->setObjectName("History:"+call->m_CallId);
if (call->peerPhoneNumber()) {
call->peerPhoneNumber()->addCall(call);
connect(call->peerPhoneNumber(),SIGNAL(presentChanged(bool)),call,SLOT(updated()));
}
return call;
}
///Get the history state from the type (see Call.cpp header)
Call::LegacyHistoryState Call::historyStateFromType(const QString& type)
{
if(type == Call::HistoryStateName::MISSED )
return Call::LegacyHistoryState::MISSED ;
else if(type == Call::HistoryStateName::OUTGOING )
return Call::LegacyHistoryState::OUTGOING ;
else if(type == Call::HistoryStateName::INCOMING )
return Call::LegacyHistoryState::INCOMING ;
return Call::LegacyHistoryState::NONE ;
}
///Get the start sate from the daemon state
Call::State Call::startStateFromDaemonCallState(const QString& daemonCallState, const QString& daemonCallType)
{
if(daemonCallState == Call::DaemonStateInit::CURRENT )
return Call::State::CURRENT ;
else if(daemonCallState == Call::DaemonStateInit::HOLD )
return Call::State::HOLD ;
else if(daemonCallState == Call::DaemonStateInit::BUSY )
return Call::State::BUSY ;
else if(daemonCallState == Call::DaemonStateInit::INACTIVE && daemonCallType == Call::CallType::INCOMING )
return Call::State::INCOMING ;
else if(daemonCallState == Call::DaemonStateInit::INACTIVE && daemonCallType == Call::CallType::OUTGOING )
return Call::State::RINGING ;
else if(daemonCallState == Call::DaemonStateInit::INCOMING )
return Call::State::INCOMING ;
else if(daemonCallState == Call::DaemonStateInit::RINGING )
return Call::State::RINGING ;
else
return Call::State::FAILURE ;
} //getStartStateFromDaemonCallState
/*****************************************************************************
* *
* Getters *
* *
****************************************************************************/
///Transfer state from internal to daemon internal syntaz
Call::DaemonState Call::toDaemonCallState(const QString& stateName)
{
if(stateName == Call::StateChange::HUNG_UP )
return Call::DaemonState::HUNG_UP ;
if(stateName == Call::StateChange::RINGING )
return Call::DaemonState::RINGING ;
if(stateName == Call::StateChange::CURRENT )
return Call::DaemonState::CURRENT ;
if(stateName == Call::StateChange::UNHOLD_CURRENT )
return Call::DaemonState::CURRENT ;
if(stateName == Call::StateChange::HOLD )
return Call::DaemonState::HOLD ;
if(stateName == Call::StateChange::BUSY )
return Call::DaemonState::BUSY ;
if(stateName == Call::StateChange::FAILURE )
return Call::DaemonState::FAILURE ;
qDebug() << "stateChanged signal received with unknown state.";
return Call::DaemonState::FAILURE ;
} //toDaemonCallState
///Transform a conference call state to a proper call state
Call::State Call::confStatetoCallState(const QString& stateName)
{
if ( stateName == Call::ConferenceStateChange::HOLD )
return Call::State::CONFERENCE_HOLD;
else if ( stateName == Call::ConferenceStateChange::ACTIVE )
return Call::State::CONFERENCE;
else
return Call::State::ERROR; //Well, this may bug a little
}
///Transform a backend state into a translated string
const QString Call::toHumanStateName(const Call::State cur)
{
switch (cur) {
case Call::State::INCOMING:
return tr( "Ringing (in)" );
break;
case Call::State::RINGING:
return tr( "Ringing (out)" );
break;
case Call::State::CURRENT:
return tr( "Talking" );
break;
case Call::State::DIALING:
return tr( "Dialing" );
break;
case Call::State::HOLD:
return tr( "Hold" );
break;
case Call::State::FAILURE:
return tr( "Failed" );
break;
case Call::State::BUSY:
return tr( "Busy" );
break;
case Call::State::TRANSFERRED:
return tr( "Transfer" );
break;
case Call::State::TRANSF_HOLD:
return tr( "Transfer hold" );
break;
case Call::State::OVER:
return tr( "Over" );
break;
case Call::State::ERROR:
return tr( "Error" );
break;
case Call::State::CONFERENCE:
return tr( "Conference" );
break;
case Call::State::CONFERENCE_HOLD:
return tr( "Conference (hold)" );
case Call::State::COUNT:
return tr( "ERROR" );
default:
return QString::number(static_cast<int>(cur));
}
}
QString Call::toHumanStateName() const
{
return toHumanStateName(state());
}
///Get the time (second from 1 jan 1970) when the call ended
time_t Call::stopTimeStamp() const
{
return m_pStopTimeStamp;
}
///Get the time (second from 1 jan 1970) when the call started
time_t Call::startTimeStamp() const
{
return m_pStartTimeStamp;
}
///Get the number where the call have been transferred
const QString Call::transferNumber() const
{
return m_pTransferNumber?m_pTransferNumber->uri():QString();
}
///Get the call / peer number
const QString Call::dialNumber() const
{
if (m_CurrentState != Call::State::DIALING) return QString();
if (!m_pDialNumber) {
const_cast<Call*>(this)->m_pDialNumber = new TemporaryPhoneNumber();
}
return m_pDialNumber->uri();
}
///Return the call id
const QString Call::id() const
{
return m_CallId;
}
PhoneNumber* Call::peerPhoneNumber() const
{
if (m_CurrentState == Call::State::DIALING) {
if (!m_pTransferNumber) {
const_cast<Call*>(this)->m_pTransferNumber = new TemporaryPhoneNumber(m_pPeerPhoneNumber);
}
if (!m_pDialNumber)
const_cast<Call*>(this)->m_pDialNumber = new TemporaryPhoneNumber(m_pPeerPhoneNumber);
return m_pDialNumber;
}
return m_pPeerPhoneNumber?m_pPeerPhoneNumber:const_cast<PhoneNumber*>(PhoneNumber::BLANK());
}
///Get the peer name
const QString Call::peerName() const
{
return m_PeerName;
}
///Generate the best possible peer name
const QString Call::formattedName() const
{
if (isConference())
return tr("Conference");
else if (!peerPhoneNumber())
return "Error";
else if (peerPhoneNumber()->contact() && !peerPhoneNumber()->contact()->formattedName().isEmpty())
return peerPhoneNumber()->contact()->formattedName();
else if (!peerName().isEmpty())
return m_PeerName;
else if (peerPhoneNumber())
return peerPhoneNumber()->uri();
else
return tr("Unknown");
}
///If the call have a valid record
bool Call::hasRecording() const
{
return !recordingPath().isEmpty() && QFile::exists(recordingPath());
}
///Generate an human readable string from the difference between StartTimeStamp and StopTimeStamp (or 'now')
QString Call::length() const
{
if (m_pStartTimeStamp == m_pStopTimeStamp) return QString(); //Invalid
int nsec =0;
if (m_pStopTimeStamp)
nsec = stopTimeStamp() - startTimeStamp();//If the call is over
else { //Time to now
time_t curTime;
::time(&curTime);
nsec = curTime - m_pStartTimeStamp;
}
if (nsec/3600)
return QString("%1:%2:%3 ").arg((nsec%(3600*24))/3600).arg(((nsec%(3600*24))%3600)/60,2,10,QChar('0')).arg(((nsec%(3600*24))%3600)%60,2,10,QChar('0'));
else
return QString("%1:%2 ").arg(nsec/60,2,10,QChar('0')).arg(nsec%60,2,10,QChar('0'));
}
///Is this call part of history
bool Call::isHistory()
{
if (m_CurrentState == Call::State::OVER && !m_History)
m_History = true;
return m_History;
}
///Is this call missed
bool Call::isMissed() const
{
return m_Missed;
}
///Is the call incoming or outgoing
Call::Direction Call::direction() const
{
return m_Direction;
}
///Return the backend used to serialize this call
AbstractHistoryBackend* Call::backend() const
{
return m_pBackend;
}
///Does this call currently has video
bool Call::hasVideo() const
{
#ifdef ENABLE_VIDEO
return VideoModel::instance()->getRenderer(this) != nullptr;
#else
return false;
#endif
}
///Get the current state
Call::State Call::state() const
{
return m_CurrentState;
}
///Get the call recording
bool Call::isRecording() const
{
return m_Recording;
}
///Get the call account id
Account* Call::account() const
{
return m_Account;
}
///Is this call a conference
bool Call::isConference() const
{
return m_isConference;
}
///Get the conference ID
const QString Call::confId() const
{
return m_ConfId;
}
///Get the recording path
const QString Call::recordingPath() const
{
return m_RecordingPath;
}
///Get the history state
Call::LegacyHistoryState Call::historyState() const
{
return m_HistoryState;
}
///This function could also be called mayBeSecure or haveChancesToBeEncryptedButWeCantTell.
bool Call::isSecure() const
{
if (!m_Account) {
qDebug() << "Account not set, can't check security";
return false;
}
//BUG this doesn't work
return m_Account && ((m_Account->isTlsEnable()) || (m_Account->tlsMethod() != TlsMethodModel::Type::DEFAULT));
} //isSecure
///Return the renderer associated with this call or nullptr
VideoRenderer* Call::videoRenderer() const
{
#ifdef ENABLE_VIDEO
return VideoModel::instance()->getRenderer(this);
#else
return nullptr;
#endif
}
/*****************************************************************************
* *
* Setters *
* *
****************************************************************************/
///Set the transfer number
void Call::setTransferNumber(const QString& number)
{
if (!m_pTransferNumber) {
m_pTransferNumber = new TemporaryPhoneNumber();
}
m_pTransferNumber->setUri(number);
}
///This call is a conference
void Call::setConference(bool value)
{
m_isConference = value;
}
///Set the call number
void Call::setDialNumber(const QString& number)
{
//This is not supposed to happen, but this is not a serious issue if it does
if (m_CurrentState != Call::State::DIALING) {
qDebug() << "Trying to set a dial number to a non-dialing call, doing nothing";
return;
}
if (!m_pDialNumber) {
m_pDialNumber = new TemporaryPhoneNumber();
}
m_pDialNumber->setUri(number);
emit dialNumberChanged(m_pDialNumber->uri());
emit changed();
emit changed(this);
}
///Set the dial number from a full phone number
void Call::setDialNumber(const PhoneNumber* number)
{
if (m_CurrentState == Call::State::DIALING && !m_pDialNumber) {
m_pDialNumber = new TemporaryPhoneNumber(number);
}
if (m_pDialNumber)
m_pDialNumber->setUri(number->uri());
emit dialNumberChanged(m_pDialNumber->uri());
emit changed();
emit changed(this);
}
///Set the conference ID
void Call::setConfId(const QString& value)
{
m_ConfId = value;
}
///Set the recording path
void Call::setRecordingPath(const QString& path)
{
m_RecordingPath = path;
if (!m_RecordingPath.isEmpty()) {
CallManagerInterface & callManager = DBus::CallManager::instance();
connect(&callManager,SIGNAL(recordPlaybackStopped(QString)), this, SLOT(stopPlayback(QString)) );
connect(&callManager,SIGNAL(updatePlaybackScale(QString,int,int)) , this, SLOT(updatePlayback(QString,int,int)));
}
}
///Set peer name
void Call::setPeerName(const QString& name)
{
m_PeerName = name;
}
///Set the account (DIALING only, may be ignored)
void Call::setAccount( Account* account)
{
if (state() == Call::State::DIALING)
m_Account = account;
}
/// Set the backend to save this call to. It is currently impossible to migrate.
void Call::setBackend(AbstractHistoryBackend* backend)
{
m_pBackend = backend;
}
/*****************************************************************************
* *
* Mutator *
* *
****************************************************************************/
///The call state just changed
Call::State Call::stateChanged(const QString& newStateName)
{
Call::State previousState = m_CurrentState;
if (!m_isConference) {
Call::DaemonState dcs = toDaemonCallState(newStateName);
if (dcs == Call::DaemonState::COUNT || m_CurrentState == Call::State::COUNT) {
qDebug() << "Error: Invalid state change";
return Call::State::FAILURE;
}
try {
changeCurrentState(stateChangedStateMap[m_CurrentState][dcs]);
}
catch(Call::State& state) {
qDebug() << "State change failed (stateChangedStateMap)" << state;
m_CurrentState = Call::State::ERROR;
emit stateChanged();
emit changed();
return m_CurrentState;
}
catch(Call::DaemonState& state) {
qDebug() << "State change failed (stateChangedStateMap)" << state;
m_CurrentState = Call::State::ERROR;
emit stateChanged();
emit changed();
return m_CurrentState;
}
catch (...) {
qDebug() << "State change failed (stateChangedStateMap) other";;
m_CurrentState = Call::State::ERROR;
emit stateChanged();
emit changed();
return m_CurrentState;
}
CallManagerInterface & callManager = DBus::CallManager::instance();
MapStringString details = callManager.getCallDetails(m_CallId).value();
if (details[Call::DetailsMapFields::PEER_NAME] != m_PeerName)
m_PeerName = details[Call::DetailsMapFields::PEER_NAME];
try {
(this->*(stateChangedFunctionMap[previousState][dcs]))();
}
catch(Call::State& state) {
qDebug() << "State change failed (stateChangedFunctionMap)" << state;
m_CurrentState = Call::State::ERROR;
emit stateChanged();
emit changed();
return m_CurrentState;
}
catch(Call::DaemonState& state) {
qDebug() << "State change failed (stateChangedFunctionMap)" << state;
m_CurrentState = Call::State::ERROR;
emit stateChanged();
emit changed();
return m_CurrentState;
}
catch (...) {
qDebug() << "State change failed (stateChangedFunctionMap) other";;
m_CurrentState = Call::State::ERROR;
emit stateChanged();
emit changed();
return m_CurrentState;
}
}
else {
//Until now, it does not worth using stateChangedStateMap, conferences are quite simple
m_CurrentState = confStatetoCallState(newStateName);
emit stateChanged();
}
if ((m_CurrentState == Call::State::HOLD || m_CurrentState == Call::State::CURRENT) && !m_pTimer) {
m_pTimer = new QTimer(this);
m_pTimer->setInterval(1000);
connect(m_pTimer,SIGNAL(timeout()),this,SLOT(updated()));
m_pTimer->start();
}
if (m_CurrentState != Call::State::DIALING && m_pDialNumber) {
if (!m_pPeerPhoneNumber)
m_pPeerPhoneNumber = PhoneDirectoryModel::instance()->fromTemporary(m_pDialNumber);
delete m_pDialNumber;
m_pDialNumber = nullptr;
}
emit changed();
emit changed(this);
qDebug() << "Calling stateChanged " << newStateName << " -> " << toDaemonCallState(newStateName) << " on call with state " << previousState << ". Become " << m_CurrentState;
return m_CurrentState;
} //stateChanged
///An account have been performed
Call::State Call::performAction(Call::Action action)
{
const Call::State previousState = m_CurrentState;
//update the state
try {
changeCurrentState(actionPerformedStateMap[previousState][action]);
}
catch(Call::State& state) {
qDebug() << "State change failed (actionPerformedStateMap)" << state;
m_CurrentState = Call::State::ERROR;
emit stateChanged();
emit changed();
return Call::State::ERROR;
}
catch (...) {
qDebug() << "State change failed (actionPerformedStateMap) other";;
m_CurrentState = Call::State::ERROR;
emit stateChanged();
emit changed();
return m_CurrentState;
}
//execute the action associated with this transition
try {
(this->*(actionPerformedFunctionMap[previousState][action]))();
}
catch(Call::State& state) {
qDebug() << "State change failed (actionPerformedFunctionMap)" << state;
m_CurrentState = Call::State::ERROR;
emit stateChanged();
emit changed();
return Call::State::ERROR;
}
catch(Call::Action& action) {
qDebug() << "State change failed (actionPerformedFunctionMap)" << action;
m_CurrentState = Call::State::ERROR;
emit stateChanged();
emit changed();
return Call::State::ERROR;
}
catch (...) {
qDebug() << "State change failed (actionPerformedFunctionMap) other";;
m_CurrentState = Call::State::ERROR;
emit stateChanged();
emit changed();
return m_CurrentState;
}
qDebug() << "Calling action " << action << " on call with state " << previousState << ". Become " << m_CurrentState;
return m_CurrentState;
} //actionPerformed
///Change the state
void Call::changeCurrentState(Call::State newState)
{
if (newState == Call::State::COUNT) {
qDebug() << "Error: Call reach invalid state";
m_CurrentState = Call::State::ERROR;
throw newState;
}
m_CurrentState = newState;
emit stateChanged();
emit changed();
emit changed(this);
if (m_CurrentState == Call::State::OVER)
emit isOver(this);
}
///Set the start timestamp and update the cache
void Call::setStartTimeStamp(time_t stamp)
{
m_pStartTimeStamp = stamp;
//While the HistoryConst is not directly related to the call concept,
//It is called to often to ignore
m_HistoryConst = HistoryTimeCategoryModel::timeToHistoryConst(m_pStartTimeStamp);
}
///Send a text message
void Call::sendTextMessage(const QString& message)
{
CallManagerInterface& callManager = DBus::CallManager::instance();
Q_NOREPLY callManager.sendTextMessage(isConference()?m_ConfId:m_CallId,message);
if (!m_pImModel) {
m_pImModel = InstantMessagingModelManager::instance()->getModel(this);
}
m_pImModel->addOutgoingMessage(message);
}
/*****************************************************************************
* *
* Automate function *
* *
****************************************************************************/
///@warning DO NOT TOUCH THAT, THEY ARE CALLED FROM AN ARRAY, HIGH FRAGILITY
///Do nothing (literally)
void Call::nothing()
{
//nop
}
void Call::error()
{
if (videoRenderer()) {
//Well, in this case we have no choice, it still doesn't belong here
videoRenderer()->stopRendering();
}
throw QString("There was an error handling your call, please restart SFLPhone.Is you encounter this problem often, \
please open SFLPhone-KDE in a terminal and send this last 100 lines before this message in a bug report at \
https://projects.savoirfairelinux.com/projects/sflphone/issues");
}
///Accept the call
void Call::accept()
{
CallManagerInterface & callManager = DBus::CallManager::instance();
qDebug() << "Accepting call. callId : " << m_CallId << "ConfId:" << m_ConfId;
Q_NOREPLY callManager.accept(m_CallId);
time_t curTime;
::time(&curTime);
setStartTimeStamp(curTime);
this->m_HistoryState = LegacyHistoryState::INCOMING;
m_Direction = Call::Direction::INCOMING;
if (!m_pTimer) {
m_pTimer = new QTimer(this);
m_pTimer->setInterval(1000);
connect(m_pTimer,SIGNAL(timeout()),this,SLOT(updated()));
m_pTimer->start();
}
}
///Refuse the call
void Call::refuse()
{
CallManagerInterface & callManager = DBus::CallManager::instance();
qDebug() << "Refusing call. callId : " << m_CallId << "ConfId:" << m_ConfId;
Q_NOREPLY callManager.refuse(m_CallId);
time_t curTime;
::time(&curTime);
setStartTimeStamp(curTime);
this->m_HistoryState = Call::LegacyHistoryState::MISSED;
m_Missed = true;
}
///Accept the transfer
void Call::acceptTransf()
{
if (!m_pTransferNumber) {
qDebug() << "Trying to transfer to no one";
return;
}
CallManagerInterface & callManager = DBus::CallManager::instance();
qDebug() << "Accepting call and transferring it to number : " << m_pTransferNumber->uri() << ". callId : " << m_CallId << "ConfId:" << m_ConfId;
callManager.accept(m_CallId);
Q_NOREPLY callManager.transfer(m_CallId, m_pTransferNumber->uri());
}
///Put the call on hold
void Call::acceptHold()
{
CallManagerInterface & callManager = DBus::CallManager::instance();
qDebug() << "Accepting call and holding it. callId : " << m_CallId << "ConfId:" << m_ConfId;
callManager.accept(m_CallId);
Q_NOREPLY callManager.hold(m_CallId);
this->m_HistoryState = LegacyHistoryState::INCOMING;
m_Direction = Call::Direction::INCOMING;
}
///Hang up
void Call::hangUp()
{
CallManagerInterface & callManager = DBus::CallManager::instance();
time_t curTime;
::time(&curTime);
m_pStopTimeStamp = curTime;
qDebug() << "Hanging up call. callId : " << m_CallId << "ConfId:" << m_ConfId;
bool ret;
if (videoRenderer()) { //TODO remove, cheap hack
videoRenderer()->stopRendering();
}
if (!isConference())
ret = callManager.hangUp(m_CallId);
else
ret = callManager.hangUpConference(m_ConfId);
if (!ret) { //Can happen if the daemon crash and open again
qDebug() << "Error: Invalid call, the daemon may have crashed";
m_CurrentState = Call::State::OVER;
emit stateChanged();
emit changed();
emit changed(this);
}
if (m_pTimer)
m_pTimer->stop();
}
///Remove the call without contacting the daemon
void Call::remove()
{
m_CurrentState = Call::State::OVER;
emit stateChanged();
emit changed();
emit changed(this);
}
///Cancel this call
void Call::cancel()
{
CallManagerInterface & callManager = DBus::CallManager::instance();
qDebug() << "Canceling call. callId : " << m_CallId << "ConfId:" << m_ConfId;
emit dialNumberChanged(QString());
Q_NOREPLY callManager.hangUp(m_CallId);
}
///Put on hold
void Call::hold()
{
CallManagerInterface & callManager = DBus::CallManager::instance();
qDebug() << "Holding call. callId : " << m_CallId << "ConfId:" << m_ConfId;
if (!isConference())
Q_NOREPLY callManager.hold(m_CallId);
else
Q_NOREPLY callManager.holdConference(m_ConfId);
}
///Start the call
void Call::call()
{
CallManagerInterface& callManager = DBus::CallManager::instance();
qDebug() << "account = " << m_Account;
if(!m_Account) {
qDebug() << "Account is not set, taking the first registered.";
this->m_Account = AccountListModel::currentAccount();
}
//Calls to empty URI should not be allowed, sflphoned will go crazy
if (m_pDialNumber->uri().isEmpty()) {
qDebug() << "Trying to call an empty URI";
m_CurrentState = Call::State::FAILURE;
if (m_pDialNumber)
emit dialNumberChanged(QString());
setPeerName(tr("Failure"));
emit stateChanged();
emit changed();
delete m_pDialNumber;
m_pDialNumber = nullptr;
}
//Normal case
else if(m_Account) {
qDebug() << "Calling " << peerPhoneNumber()->uri() << " with account " << m_Account << ". callId : " << m_CallId << "ConfId:" << m_ConfId;
callManager.placeCall(m_Account->id(), m_CallId, m_pDialNumber->uri());
this->m_pPeerPhoneNumber = PhoneDirectoryModel::instance()->getNumber(m_pDialNumber->uri(),account());
if (ContactModel::instance()->hasBackends()) {
if (peerPhoneNumber()->contact())
m_PeerName = peerPhoneNumber()->contact()->formattedName();
}
connect(peerPhoneNumber(),SIGNAL(presentChanged(bool)),this,SLOT(updated()));
time_t curTime;
::time(&curTime);
setStartTimeStamp(curTime);
this->m_HistoryState = LegacyHistoryState::OUTGOING;
m_Direction = Call::Direction::OUTGOING;
if (peerPhoneNumber()) {
peerPhoneNumber()->addCall(this);
}
if (m_pDialNumber)
emit dialNumberChanged(QString());
delete m_pDialNumber;
m_pDialNumber = nullptr;
}
else {
qDebug() << "Trying to call " << (m_pTransferNumber?m_pTransferNumber->uri():"ERROR")
<< " with no account registered . callId : " << m_CallId << "ConfId:" << m_ConfId;
this->m_HistoryState = LegacyHistoryState::NONE;
throw tr("No account registered!");
}
}
///Trnasfer the call
void Call::transfer()
{
if (m_pTransferNumber) {
CallManagerInterface & callManager = DBus::CallManager::instance();
qDebug() << "Transferring call to number : " << m_pTransferNumber->uri() << ". callId : " << m_CallId;
Q_NOREPLY callManager.transfer(m_CallId, m_pTransferNumber->uri());
time_t curTime;
::time(&curTime);
m_pStopTimeStamp = curTime;
}
}
///Unhold the call
void Call::unhold()
{
CallManagerInterface & callManager = DBus::CallManager::instance();
qDebug() << "Unholding call. callId : " << m_CallId << "ConfId:" << m_ConfId;
if (!isConference())
Q_NOREPLY callManager.unhold(m_CallId);
else
Q_NOREPLY callManager.unholdConference(m_ConfId);
}
///Record the call
void Call::setRecord()
{
CallManagerInterface & callManager = DBus::CallManager::instance();
qDebug() << "Setting record " << !m_Recording << " for call. callId : " << m_CallId << "ConfId:" << m_ConfId;
callManager.toggleRecording((!m_isConference)?m_CallId:m_ConfId);
}
///Start the timer
void Call::start()
{
qDebug() << "Starting call. callId : " << m_CallId << "ConfId:" << m_ConfId;
time_t curTime;
::time(&curTime);
emit changed();
emit changed(this);
if (m_pDialNumber) {
if (!m_pPeerPhoneNumber)
m_pPeerPhoneNumber = PhoneDirectoryModel::instance()->fromTemporary(m_pDialNumber);
delete m_pDialNumber;
m_pDialNumber = nullptr;
}
setStartTimeStamp(curTime);
}
///Toggle the timer
void Call::startStop()
{
qDebug() << "Starting and stoping call. callId : " << m_CallId << "ConfId:" << m_ConfId;
time_t curTime;
::time(&curTime);
setStartTimeStamp(curTime);
m_pStopTimeStamp = curTime;
}
///Stop the timer
void Call::stop()
{
qDebug() << "Stoping call. callId : " << m_CallId << "ConfId:" << m_ConfId;
if (videoRenderer()) { //TODO remove, cheap hack
videoRenderer()->stopRendering();
}
time_t curTime;
::time(&curTime);
m_pStopTimeStamp = curTime;
}
///Handle error instead of crashing
void Call::startWeird()
{
qDebug() << "Starting call. callId : " << m_CallId << "ConfId:" << m_ConfId;
time_t curTime;
::time(&curTime);
setStartTimeStamp(curTime);
qDebug() << "Warning : call " << m_CallId << " had an unexpected transition of state at its start.";
}
///Print a warning
void Call::warning()
{
qWarning() << "Warning : call " << m_CallId << " had an unexpected transition of state.(" << m_CurrentState << ")";
switch (m_CurrentState) {
case Call::State::FAILURE :
case Call::State::ERROR :
case Call::State::COUNT :
//If not stopped, then the counter will keep going
//Getting here indicate something wrong happened
//It can be normal, aka, an invalid URI such as '><'
// or an SFLPhone-KDE bug
stop();
break;
case Call::State::TRANSFERRED :
case Call::State::TRANSF_HOLD :
case Call::State::DIALING :
case Call::State::INCOMING :
case Call::State::RINGING :
case Call::State::CURRENT :
case Call::State::HOLD :
case Call::State::BUSY :
case Call::State::OVER :
case Call::State::CONFERENCE :
case Call::State::CONFERENCE_HOLD:
default:
break;
}
}
/*****************************************************************************
* *
* Keyboard handling *
* *
****************************************************************************/
///Input text on the call item
void Call::appendText(const QString& str)
{
TemporaryPhoneNumber* editNumber = nullptr;
switch (m_CurrentState) {
case Call::State::TRANSFERRED :
case Call::State::TRANSF_HOLD :
editNumber = m_pTransferNumber;
break;
case Call::State::DIALING :
editNumber = m_pDialNumber;
break;
case Call::State::INCOMING:
case Call::State::RINGING:
case Call::State::CURRENT:
case Call::State::HOLD:
case Call::State::FAILURE:
case Call::State::BUSY:
case Call::State::OVER:
case Call::State::ERROR:
case Call::State::CONFERENCE:
case Call::State::CONFERENCE_HOLD:
case Call::State::COUNT:
default:
qDebug() << "Backspace on call not editable. Doing nothing.";
return;
}
if (editNumber) {
editNumber->setUri(editNumber->uri()+str);
if (state() == Call::State::DIALING)
emit dialNumberChanged(editNumber->uri());
}
else
qDebug() << "TemporaryPhoneNumber not defined";
emit changed();
emit changed(this);
}
///Remove the last character
void Call::backspaceItemText()
{
TemporaryPhoneNumber* editNumber = nullptr;
switch (m_CurrentState) {
case Call::State::TRANSFERRED :
case Call::State::TRANSF_HOLD :
editNumber = m_pTransferNumber;
break;
case Call::State::DIALING :
editNumber = m_pDialNumber;
break;
case Call::State::INCOMING:
case Call::State::RINGING:
case Call::State::CURRENT:
case Call::State::HOLD:
case Call::State::FAILURE:
case Call::State::BUSY:
case Call::State::OVER:
case Call::State::ERROR:
case Call::State::CONFERENCE:
case Call::State::CONFERENCE_HOLD:
case Call::State::COUNT:
default :
qDebug() << "Backspace on call not editable. Doing nothing.";
return;
}
if (editNumber) {
QString text = editNumber->uri();
const int textSize = text.size();
if(textSize > 0) {
editNumber->setUri(text.remove(textSize-1, 1));
emit changed();
emit changed(this);
}
else {
changeCurrentState(Call::State::OVER);
}
}
else
qDebug() << "TemporaryPhoneNumber not defined";
}
///Reset the string a dialing or transfer call
void Call::reset()
{
TemporaryPhoneNumber* editNumber = nullptr;
switch (m_CurrentState) {
case Call::State::TRANSFERRED :
case Call::State::TRANSF_HOLD :
editNumber = m_pTransferNumber;
break;
case Call::State::DIALING :
editNumber = m_pDialNumber;
break;
case Call::State::INCOMING :
case Call::State::RINGING :
case Call::State::CURRENT :
case Call::State::HOLD :
case Call::State::FAILURE :
case Call::State::BUSY :
case Call::State::OVER :
case Call::State::ERROR :
case Call::State::CONFERENCE :
case Call::State::CONFERENCE_HOLD :
case Call::State::COUNT:
default :
qDebug() << "Cannot reset" << m_CurrentState << "calls";
return;
}
if (editNumber) {
editNumber->setUri(QString());
}
}
/*****************************************************************************
* *
* SLOTS *
* *
****************************************************************************/
void Call::updated()
{
emit changed();
emit changed(this);
}
///Play the record, if any
void Call::playRecording()
{
CallManagerInterface& callManager = DBus::CallManager::instance();
const bool retval = callManager.startRecordedFilePlayback(recordingPath());
if (retval)
emit playbackStarted();
}
///Stop the record, if any
void Call::stopRecording()
{
CallManagerInterface& callManager = DBus::CallManager::instance();
Q_NOREPLY callManager.stopRecordedFilePlayback(recordingPath());
emit playbackStopped(); //TODO remove this, it is a workaround for bug #11942
}
///seek the record, if any
void Call::seekRecording(double position)
{
CallManagerInterface& callManager = DBus::CallManager::instance();
Q_NOREPLY callManager.recordPlaybackSeek(position);
}
///Daemon record playback stopped
void Call::stopPlayback(const QString& filePath)
{
if (filePath == recordingPath()) {
emit playbackStopped();
}
}
///Daemon playback position chnaged
void Call::updatePlayback(const QString& path, int position,int size)
{
if (path == m_RecordingPath) {
emit playbackPositionChanged(position,size);
}
}
UserActionModel* Call::userActionModel() const
{
return m_pUserActionModel;
}
///Common source for model data roles
QVariant Call::roleData(int role) const
{
const Contact* ct = peerPhoneNumber()?peerPhoneNumber()->contact():nullptr;
switch (role) {
case Call::Role::Name:
case Qt::DisplayRole:
if (isConference())
return tr("Conference");
else if (state() == Call::State::DIALING)
return dialNumber();
else if (m_PeerName.isEmpty())
return ct?ct->formattedName():peerPhoneNumber()?peerPhoneNumber()->uri():dialNumber();
else
return formattedName();
break;
case Qt::EditRole:
return dialNumber();
case Call::Role::Number:
return peerPhoneNumber()->uri();
break;
case Call::Role::Direction2:
return static_cast<int>(m_Direction);
break;
case Call::Role::Date:
return (int)startTimeStamp();
break;
case Call::Role::Length:
return length();
break;
case Call::Role::FormattedDate:
return QDateTime::fromTime_t(startTimeStamp()).toString();
break;
case Call::Role::HasRecording:
return hasRecording();
break;
case Call::Role::Historystate:
return static_cast<int>(historyState());
break;
case Call::Role::Filter: {
QString normStripppedC;
foreach(QChar char2,QString(static_cast<int>(historyState())+'\n'+roleData(Call::Role::Name).toString()+'\n'+
roleData(Call::Role::Number).toString()).toLower().normalized(QString::NormalizationForm_KD) ) {
if (!char2.combiningClass())
normStripppedC += char2;
}
return normStripppedC;
}
break;
case Call::Role::FuzzyDate:
return (int)m_HistoryConst;
break;
case Call::Role::IsBookmark:
return false;
break;
case Call::Role::Security:
return isSecure();
break;
case Call::Role::Department:
return ct?ct->department():QVariant();
break;
case Call::Role::Email:
return ct?ct->preferredEmail():QVariant();
break;
case Call::Role::Organisation:
return ct?ct->organization():QVariant();
break;
case Call::Role::IsConference:
return isConference();
break;
case Call::Role::Object:
return QVariant::fromValue(const_cast<Call*>(this));
break;
case Call::Role::PhoneNu:
return QVariant::fromValue(const_cast<PhoneNumber*>(peerPhoneNumber()));
break;
case Call::Role::PhotoPtr:
return QVariant::fromValue((void*)(ct?ct->photo():nullptr));
break;
case Call::Role::CallState:
return static_cast<int>(state());
break;
case Call::Role::Id:
return ((m_isConference)?confId():id());
break;
case Call::Role::StartTime:
return (int) m_pStartTimeStamp;
case Call::Role::StopTime:
return (int) m_pStopTimeStamp;
case Call::Role::IsRecording:
return isRecording();
case Call::Role::IsPresent:
return peerPhoneNumber()->isPresent();
case Call::Role::IsTracked:
return peerPhoneNumber()->isTracked();
case Call::Role::SupportPresence:
return peerPhoneNumber()->supportPresence();
case Call::Role::CategoryIcon:
return peerPhoneNumber()->category()->icon(peerPhoneNumber()->isTracked(),peerPhoneNumber()->isPresent());
case Call::Role::CallCount:
return peerPhoneNumber()->callCount();
case Call::Role::TotalSpentTime:
return peerPhoneNumber()->totalSpentTime();
case Call::Role::DropState:
return property("dropState");
break;
case Call::Role::Missed:
return m_Missed;
case Call::Role::DTMFAnimState:
return property("DTMFAnimState");
break;
case Call::Role::LastDTMFidx:
return property("latestDtmfIdx");
break;
case Call::Role::DropPosition:
return property("dropPosition");
break;
default:
break;
};
return QVariant();
}
void Call::playDTMF(const QString& str)
{
Q_NOREPLY DBus::CallManager::instance().playDTMF(str);
emit dtmfPlayed(str);
}