From cfdc3069f085783d7417d0ba6937f8c491070022 Mon Sep 17 00:00:00 2001 From: Julien Wadel Date: Fri, 3 Sep 2021 12:25:54 +0200 Subject: [PATCH] - Add Attended transfer feature (contributed by Chad Attermann) - Fix crash on loading empty chat room --- linphone-app/assets/languages/en.ts | 10 + linphone-app/src/app/cli/Cli.cpp | 6 +- .../src/components/call/CallModel.cpp | 1048 +++++++++-------- .../src/components/call/CallModel.hpp | 452 +++---- .../src/components/calls/CallsListModel.cpp | 45 +- .../src/components/calls/CallsListModel.hpp | 91 +- .../chat-room/ChatRoomProxyModel.cpp | 24 +- .../ui/modules/Linphone/Calls/Calls.js | 334 +++--- .../ui/modules/Linphone/Chat/Chat.qml | 2 +- .../ui/views/App/Calls/CallsWindow.js | 137 ++- .../ui/views/App/Calls/CallsWindow.qml | 1 + .../App/Calls/Dialogs/CallSipAddress.qml | 2 +- .../views/App/Calls/Dialogs/CallTransfer.qml | 183 +-- .../ui/views/App/Main/Conversation.qml | 1 - linphone-app/ui/views/App/Main/MainWindow.qml | 4 +- 15 files changed, 1235 insertions(+), 1105 deletions(-) diff --git a/linphone-app/assets/languages/en.ts b/linphone-app/assets/languages/en.ts index 9cb9f4301..50aaa4eb6 100644 --- a/linphone-app/assets/languages/en.ts +++ b/linphone-app/assets/languages/en.ts @@ -423,6 +423,16 @@ callPause HOLD CALL + + attendedTransferComplete + 'COMPLETE ATTENDED TRANSFER' : Title button, design is in uppercase. + COMPLETE ATTENDED TRANSFER + + + attendedTransferCall + 'ATTENDED TRANSFER CALL' : Title button, design is in uppercase. + ATTENDED TRANSFER CALL + CallsWindow diff --git a/linphone-app/src/app/cli/Cli.cpp b/linphone-app/src/app/cli/Cli.cpp index 66fe7cff8..f21e5f0b0 100644 --- a/linphone-app/src/app/cli/Cli.cpp +++ b/linphone-app/src/app/cli/Cli.cpp @@ -58,7 +58,7 @@ static void cliCall (QHash &args) { app->processArguments(args); app->initContentApp(); }else - CoreManager::getInstance()->getCallsListModel()->launchAudioCall(args["sip-address"]); + CoreManager::getInstance()->getCallsListModel()->launchAudioCall(args["sip-address"], ""); } static void cliBye (QHash &args) { @@ -89,7 +89,7 @@ static void cliJoinConference (QHash &args) { } args["method"] = QStringLiteral("join-conference"); - coreManager->getCallsListModel()->launchAudioCall(sipAddress, args); + coreManager->getCallsListModel()->launchAudioCall(sipAddress, "", args); } static void cliJoinConferenceAs (QHash &args) { @@ -121,7 +121,7 @@ static void cliJoinConferenceAs (QHash &args) { } args["method"] = QStringLiteral("join-conference"); - coreManager->getCallsListModel()->launchAudioCall(toSipAddress, args); + coreManager->getCallsListModel()->launchAudioCall(toSipAddress, "", args); } static void cliInitiateConference (QHash &args) { diff --git a/linphone-app/src/components/call/CallModel.cpp b/linphone-app/src/components/call/CallModel.cpp index 06d309b35..ccbb54c45 100644 --- a/linphone-app/src/components/call/CallModel.cpp +++ b/linphone-app/src/components/call/CallModel.cpp @@ -45,45 +45,45 @@ using namespace std; namespace { - constexpr char AutoAnswerObjectName[] = "auto-answer-timer"; +constexpr char AutoAnswerObjectName[] = "auto-answer-timer"; } CallModel::CallModel (shared_ptr call){ - Q_CHECK_PTR(call); - mCall = call; - mCall->setData("call-model", *this); - - updateIsInConference(); - - CoreManager *coreManager = CoreManager::getInstance(); - - // Deal with auto-answer. - if (!isOutgoing()) { - SettingsModel *settings = coreManager->getSettingsModel(); - - if (settings->getAutoAnswerStatus()) { - QTimer *timer = new QTimer(this); - timer->setInterval(settings->getAutoAnswerDelay()); - timer->setSingleShot(true); - timer->setObjectName(AutoAnswerObjectName); - - QObject::connect(timer, &QTimer::timeout, this, &CallModel::acceptWithAutoAnswerDelay); - timer->start(); - } - } - - CoreHandlers *coreHandlers = coreManager->getHandlers().get(); - QObject::connect(coreHandlers, &CoreHandlers::callStateChanged, this, &CallModel::handleCallStateChanged ); - QObject::connect(coreHandlers, &CoreHandlers::callEncryptionChanged, this, &CallModel::handleCallEncryptionChanged ); - -// Update fields and make a search to know to who the call belong - mMagicSearch = CoreManager::getInstance()->getCore()->createMagicSearch(); - mSearch = std::make_shared(this); - QObject::connect(mSearch.get(), SIGNAL(searchReceived(std::list> )), this, SLOT(searchReceived(std::list>))); - mMagicSearch->addListener(mSearch); - - mRemoteAddress = mCall->getRemoteAddress()->clone(); - mMagicSearch->getContactListFromFilterAsync(mRemoteAddress->getUsername(),mRemoteAddress->getDomain()); + Q_CHECK_PTR(call); + mCall = call; + mCall->setData("call-model", *this); + + updateIsInConference(); + + CoreManager *coreManager = CoreManager::getInstance(); + + // Deal with auto-answer. + if (!isOutgoing()) { + SettingsModel *settings = coreManager->getSettingsModel(); + + if (settings->getAutoAnswerStatus()) { + QTimer *timer = new QTimer(this); + timer->setInterval(settings->getAutoAnswerDelay()); + timer->setSingleShot(true); + timer->setObjectName(AutoAnswerObjectName); + + QObject::connect(timer, &QTimer::timeout, this, &CallModel::acceptWithAutoAnswerDelay); + timer->start(); + } + } + + CoreHandlers *coreHandlers = coreManager->getHandlers().get(); + QObject::connect(coreHandlers, &CoreHandlers::callStateChanged, this, &CallModel::handleCallStateChanged ); + QObject::connect(coreHandlers, &CoreHandlers::callEncryptionChanged, this, &CallModel::handleCallEncryptionChanged ); + + // Update fields and make a search to know to who the call belong + mMagicSearch = CoreManager::getInstance()->getCore()->createMagicSearch(); + mSearch = std::make_shared(this); + QObject::connect(mSearch.get(), SIGNAL(searchReceived(std::list> )), this, SLOT(searchReceived(std::list>))); + mMagicSearch->addListener(mSearch); + + mRemoteAddress = mCall->getRemoteAddress()->clone(); + mMagicSearch->getContactListFromFilterAsync(mRemoteAddress->getUsername(),mRemoteAddress->getDomain()); } CallModel::~CallModel () { @@ -94,18 +94,18 @@ CallModel::~CallModel () { // ----------------------------------------------------------------------------- QString CallModel::getPeerAddress () const { - return Utils::coreStringToAppString(mRemoteAddress->asStringUriOnly()); + return Utils::coreStringToAppString(mRemoteAddress->asStringUriOnly()); } QString CallModel::getLocalAddress () const { - return Utils::coreStringToAppString(mCall->getCallLog()->getLocalAddress()->asStringUriOnly()); + return Utils::coreStringToAppString(mCall->getCallLog()->getLocalAddress()->asStringUriOnly()); } QString CallModel::getFullPeerAddress () const { - return Utils::coreStringToAppString(mRemoteAddress->asString()); + return Utils::coreStringToAppString(mRemoteAddress->asString()); } QString CallModel::getFullLocalAddress () const { - return Utils::coreStringToAppString(mCall->getCallLog()->getLocalAddress()->asString()); + return Utils::coreStringToAppString(mCall->getCallLog()->getLocalAddress()->asString()); } // ----------------------------------------------------------------------------- @@ -123,520 +123,536 @@ ChatRoomModel * CallModel::getChatRoomModel() const{ // ----------------------------------------------------------------------------- void CallModel::setRecordFile (const shared_ptr &callParams) { - callParams->setRecordFile(Utils::appStringToCoreString( - CoreManager::getInstance()->getSettingsModel()->getSavedCallsFolder() - .append(generateSavedFilename()) - .append(".mkv") - )); + callParams->setRecordFile(Utils::appStringToCoreString( + CoreManager::getInstance()->getSettingsModel()->getSavedCallsFolder() + .append(generateSavedFilename()) + .append(".mkv") + )); } void CallModel::setRecordFile (const shared_ptr &callParams, const QString &to) { - const QString from( - Utils::coreStringToAppString( - CoreManager::getInstance()->getAccountSettingsModel()->getUsedSipAddress()->getUsername() - ) - ); - - callParams->setRecordFile(Utils::appStringToCoreString( - CoreManager::getInstance()->getSettingsModel()->getSavedCallsFolder() - .append(generateSavedFilename(from, to)) - .append(".mkv") - )); + const QString from( + Utils::coreStringToAppString( + CoreManager::getInstance()->getAccountSettingsModel()->getUsedSipAddress()->getUsername() + ) + ); + + callParams->setRecordFile(Utils::appStringToCoreString( + CoreManager::getInstance()->getSettingsModel()->getSavedCallsFolder() + .append(generateSavedFilename(from, to)) + .append(".mkv") + )); } // ----------------------------------------------------------------------------- void CallModel::updateStats (const shared_ptr &callStats) { - switch (callStats->getType()) { - case linphone::StreamType::Text: - case linphone::StreamType::Unknown: - break; - - case linphone::StreamType::Audio: - updateStats(callStats, mAudioStats); - break; - case linphone::StreamType::Video: - updateStats(callStats, mVideoStats); - break; - } - - emit statsUpdated(); + switch (callStats->getType()) { + case linphone::StreamType::Text: + case linphone::StreamType::Unknown: + break; + + case linphone::StreamType::Audio: + updateStats(callStats, mAudioStats); + break; + case linphone::StreamType::Video: + updateStats(callStats, mVideoStats); + break; + } + + emit statsUpdated(); } // ----------------------------------------------------------------------------- float CallModel::getSpeakerVolumeGain () const { - return mCall->getSpeakerVolumeGain(); + return mCall->getSpeakerVolumeGain(); } void CallModel::setSpeakerVolumeGain (float volume) { - Q_ASSERT(volume >= 0.0f && volume <= 1.0f); - mCall->setSpeakerVolumeGain(volume); - emit speakerVolumeGainChanged(getSpeakerVolumeGain()); + Q_ASSERT(volume >= 0.0f && volume <= 1.0f); + mCall->setSpeakerVolumeGain(volume); + emit speakerVolumeGainChanged(getSpeakerVolumeGain()); } float CallModel::getMicroVolumeGain () const { - return mCall->getMicrophoneVolumeGain(); + return mCall->getMicrophoneVolumeGain(); } void CallModel::setMicroVolumeGain (float volume) { - Q_ASSERT(volume >= 0.0f && volume <= 1.0f); - mCall->setMicrophoneVolumeGain(volume); - emit microVolumeGainChanged(getMicroVolumeGain()); + Q_ASSERT(volume >= 0.0f && volume <= 1.0f); + mCall->setMicrophoneVolumeGain(volume); + emit microVolumeGainChanged(getMicroVolumeGain()); } // ----------------------------------------------------------------------------- void CallModel::notifyCameraFirstFrameReceived (unsigned int width, unsigned int height) { - if (mNotifyCameraFirstFrameReceived) { - mNotifyCameraFirstFrameReceived = false; - emit cameraFirstFrameReceived(width, height); - } + if (mNotifyCameraFirstFrameReceived) { + mNotifyCameraFirstFrameReceived = false; + emit cameraFirstFrameReceived(width, height); + } } // ----------------------------------------------------------------------------- void CallModel::accept () { - accept(false); + accept(false); } void CallModel::acceptWithVideo () { - accept(true); + accept(true); } void CallModel::terminate () { - CoreManager *core = CoreManager::getInstance(); - core->lockVideoRender(); - mCall->terminate(); - core->unlockVideoRender(); + CoreManager *core = CoreManager::getInstance(); + core->lockVideoRender(); + mCall->terminate(); + core->unlockVideoRender(); } // ----------------------------------------------------------------------------- void CallModel::askForTransfer () { - CoreManager::getInstance()->getCallsListModel()->askForTransfer(this); + CoreManager::getInstance()->getCallsListModel()->askForTransfer(this); +} + +void CallModel::askForAttendedTransfer () { + CoreManager::getInstance()->getCallsListModel()->askForAttendedTransfer(this); } bool CallModel::transferTo (const QString &sipAddress) { - bool status = !!mCall->transfer(Utils::appStringToCoreString(sipAddress)); - if (status) - qWarning() << QStringLiteral("Unable to transfer: `%1`.").arg(sipAddress); - return status; + bool failure = !!mCall->transferTo(Utils::interpretUrl(sipAddress)); + if (failure) + qWarning() << QStringLiteral("Unable to transfer: `%1`.").arg(sipAddress); + return !failure; } +bool CallModel::transferToAnother (const QString &peerAddress) { + qInfo() << QStringLiteral("Transferring to another: `%1`.").arg(peerAddress); + CallModel *transferCallModel = CoreManager::getInstance()->getCallsListModel()->findCallModelFromPeerAddress(peerAddress); + if (transferCallModel == nullptr) { + qWarning() << QStringLiteral("Unable to transfer to another: `%1` (peer not found)").arg(peerAddress); + return false; + } + bool failure = !!transferCallModel->mCall->transferToAnother(mCall); + if (failure) + qWarning() << QStringLiteral("Unable to transfer to another: `%1` (transfer failed)").arg(peerAddress); + return !failure; +} // ----------------------------------------------------------------------------- void CallModel::acceptVideoRequest () { - shared_ptr params = CoreManager::getInstance()->getCore()->createCallParams(mCall); - params->enableVideo(true); - - mCall->acceptUpdate(params); + shared_ptr params = CoreManager::getInstance()->getCore()->createCallParams(mCall); + params->enableVideo(true); + + mCall->acceptUpdate(params); } void CallModel::rejectVideoRequest () { - shared_ptr params = CoreManager::getInstance()->getCore()->createCallParams(mCall); - params->enableVideo(false); - - mCall->acceptUpdate(params); + shared_ptr params = CoreManager::getInstance()->getCore()->createCallParams(mCall); + params->enableVideo(false); + + mCall->acceptUpdate(params); } void CallModel::takeSnapshot () { - static QString oldName; - QString newName(generateSavedFilename().append(".jpg")); - - if (newName == oldName) { - qWarning() << QStringLiteral("Unable to take snapshot. Wait one second."); - return; - } - oldName = newName; - - qInfo() << QStringLiteral("Take snapshot of call:") << this; - - const QString filePath(CoreManager::getInstance()->getSettingsModel()->getSavedScreenshotsFolder().append(newName)); - mCall->takeVideoSnapshot(Utils::appStringToCoreString(filePath)); - App::getInstance()->getNotifier()->notifySnapshotWasTaken(filePath); + static QString oldName; + QString newName(generateSavedFilename().append(".jpg")); + + if (newName == oldName) { + qWarning() << QStringLiteral("Unable to take snapshot. Wait one second."); + return; + } + oldName = newName; + + qInfo() << QStringLiteral("Take snapshot of call:") << this; + + const QString filePath(CoreManager::getInstance()->getSettingsModel()->getSavedScreenshotsFolder().append(newName)); + mCall->takeVideoSnapshot(Utils::appStringToCoreString(filePath)); + App::getInstance()->getNotifier()->notifySnapshotWasTaken(filePath); } void CallModel::startRecording () { - if (mRecording) - return; - - qInfo() << QStringLiteral("Start recording call:") << this; - - mCall->startRecording(); - mRecording = true; - - emit recordingChanged(true); + if (mRecording) + return; + + qInfo() << QStringLiteral("Start recording call:") << this; + + mCall->startRecording(); + mRecording = true; + + emit recordingChanged(true); } void CallModel::stopRecording () { - if (!mRecording) - return; - - qInfo() << QStringLiteral("Stop recording call:") << this; - - mRecording = false; - mCall->stopRecording(); - - App::getInstance()->getNotifier()->notifyRecordingCompleted( - Utils::coreStringToAppString(mCall->getParams()->getRecordFile()) - ); - - emit recordingChanged(false); + if (!mRecording) + return; + + qInfo() << QStringLiteral("Stop recording call:") << this; + + mRecording = false; + mCall->stopRecording(); + + App::getInstance()->getNotifier()->notifyRecordingCompleted( + Utils::coreStringToAppString(mCall->getParams()->getRecordFile()) + ); + + emit recordingChanged(false); } // ----------------------------------------------------------------------------- void CallModel::handleCallEncryptionChanged (const shared_ptr &call) { - if (call == mCall) - emit securityUpdated(); + if (call == mCall) + emit securityUpdated(); } void CallModel::handleCallStateChanged (const shared_ptr &call, linphone::Call::State state) { - if (call != mCall) - return; - - updateIsInConference(); - - switch (state) { - case linphone::Call::State::Error: - case linphone::Call::State::End: - setCallErrorFromReason(call->getReason()); - stopAutoAnswerTimer(); - stopRecording(); - mPausedByRemote = false; - break; - - case linphone::Call::State::StreamsRunning: { - if (!mWasConnected && CoreManager::getInstance()->getSettingsModel()->getAutomaticallyRecordCalls()) { - startRecording(); - mWasConnected = true; - } - mPausedByRemote = false; - break; - } - case linphone::Call::State::Connected: - case linphone::Call::State::Referred: - case linphone::Call::State::Released: - mPausedByRemote = false; - break; - - case linphone::Call::State::PausedByRemote: - mNotifyCameraFirstFrameReceived = true; - mPausedByRemote = true; - break; - - case linphone::Call::State::Pausing: - mNotifyCameraFirstFrameReceived = true; - mPausedByUser = true; - break; - - case linphone::Call::State::Resuming: - mPausedByUser = false; - break; - - case linphone::Call::State::UpdatedByRemote: - if (!mCall->getCurrentParams()->videoEnabled() && mCall->getRemoteParams()->videoEnabled()) { - mCall->deferUpdate(); - emit videoRequested(); - } - break; - - case linphone::Call::State::Idle: - case linphone::Call::State::IncomingReceived: - case linphone::Call::State::OutgoingInit: - case linphone::Call::State::OutgoingProgress: - case linphone::Call::State::OutgoingRinging: - case linphone::Call::State::OutgoingEarlyMedia: - case linphone::Call::State::Paused: - case linphone::Call::State::IncomingEarlyMedia: - case linphone::Call::State::Updating: - case linphone::Call::State::EarlyUpdatedByRemote: - case linphone::Call::State::EarlyUpdating: - break; - } - - emit statusChanged(getStatus()); + if (call != mCall) + return; + + updateIsInConference(); + + switch (state) { + case linphone::Call::State::Error: + case linphone::Call::State::End: + setCallErrorFromReason(call->getReason()); + stopAutoAnswerTimer(); + stopRecording(); + mPausedByRemote = false; + break; + + case linphone::Call::State::StreamsRunning: { + if (!mWasConnected && CoreManager::getInstance()->getSettingsModel()->getAutomaticallyRecordCalls()) { + startRecording(); + mWasConnected = true; + } + mPausedByRemote = false; + break; + } + case linphone::Call::State::Connected: + case linphone::Call::State::Referred: + case linphone::Call::State::Released: + mPausedByRemote = false; + break; + + case linphone::Call::State::PausedByRemote: + mNotifyCameraFirstFrameReceived = true; + mPausedByRemote = true; + break; + + case linphone::Call::State::Pausing: + mNotifyCameraFirstFrameReceived = true; + mPausedByUser = true; + break; + + case linphone::Call::State::Resuming: + mPausedByUser = false; + break; + + case linphone::Call::State::UpdatedByRemote: + if (!mCall->getCurrentParams()->videoEnabled() && mCall->getRemoteParams()->videoEnabled()) { + mCall->deferUpdate(); + emit videoRequested(); + } + break; + + case linphone::Call::State::Idle: + case linphone::Call::State::IncomingReceived: + case linphone::Call::State::OutgoingInit: + case linphone::Call::State::OutgoingProgress: + case linphone::Call::State::OutgoingRinging: + case linphone::Call::State::OutgoingEarlyMedia: + case linphone::Call::State::Paused: + case linphone::Call::State::IncomingEarlyMedia: + case linphone::Call::State::Updating: + case linphone::Call::State::EarlyUpdatedByRemote: + case linphone::Call::State::EarlyUpdating: + break; + } + + emit statusChanged(getStatus()); } // ----------------------------------------------------------------------------- void CallModel::accept (bool withVideo) { - stopAutoAnswerTimer(); - CoreManager *coreManager = CoreManager::getInstance(); - - QQuickWindow *callsWindow = App::getInstance()->getCallsWindow(); - if (callsWindow) { - if (coreManager->getSettingsModel()->getKeepCallsWindowInBackground()) { - if (!callsWindow->isVisible()) - callsWindow->showMinimized(); - } else - App::smartShowWindow(callsWindow); - } - qApp->processEvents(); // Process GUI events before accepting in order to be synchronized with Call objects and be ready to get SDK events - shared_ptr core = coreManager->getCore(); - shared_ptr params = core->createCallParams(mCall); - params->enableVideo(withVideo); - setRecordFile(params); - - mCall->acceptWithParams(params); + stopAutoAnswerTimer(); + CoreManager *coreManager = CoreManager::getInstance(); + + QQuickWindow *callsWindow = App::getInstance()->getCallsWindow(); + if (callsWindow) { + if (coreManager->getSettingsModel()->getKeepCallsWindowInBackground()) { + if (!callsWindow->isVisible()) + callsWindow->showMinimized(); + } else + App::smartShowWindow(callsWindow); + } + qApp->processEvents(); // Process GUI events before accepting in order to be synchronized with Call objects and be ready to get SDK events + shared_ptr core = coreManager->getCore(); + shared_ptr params = core->createCallParams(mCall); + params->enableVideo(withVideo); + setRecordFile(params); + + mCall->acceptWithParams(params); } // ----------------------------------------------------------------------------- void CallModel::updateIsInConference () { - if (mIsInConference != mCall->getCurrentParams()->getLocalConferenceMode()) { - mIsInConference = !mIsInConference; - } - emit isInConferenceChanged(mIsInConference); + if (mIsInConference != mCall->getCurrentParams()->getLocalConferenceMode()) { + mIsInConference = !mIsInConference; + } + emit isInConferenceChanged(mIsInConference); } // ----------------------------------------------------------------------------- void CallModel::stopAutoAnswerTimer () const { - QTimer *timer = findChild(AutoAnswerObjectName, Qt::FindDirectChildrenOnly); - if (timer) { - timer->stop(); - timer->deleteLater(); - } + QTimer *timer = findChild(AutoAnswerObjectName, Qt::FindDirectChildrenOnly); + if (timer) { + timer->stop(); + timer->deleteLater(); + } } // ----------------------------------------------------------------------------- CallModel::CallStatus CallModel::getStatus () const { - switch (mCall->getState()) { - case linphone::Call::State::Connected: - case linphone::Call::State::StreamsRunning: - return CallStatusConnected; - - case linphone::Call::State::End: - case linphone::Call::State::Error: - case linphone::Call::State::Referred: - case linphone::Call::State::Released: - return CallStatusEnded; - - case linphone::Call::State::Paused: - case linphone::Call::State::PausedByRemote: - case linphone::Call::State::Pausing: - case linphone::Call::State::Resuming: - return CallStatusPaused; - - case linphone::Call::State::Updating: - case linphone::Call::State::UpdatedByRemote: - return mPausedByRemote ? CallStatusPaused : CallStatusConnected; - - case linphone::Call::State::EarlyUpdatedByRemote: - case linphone::Call::State::EarlyUpdating: - case linphone::Call::State::Idle: - case linphone::Call::State::IncomingEarlyMedia: - case linphone::Call::State::IncomingReceived: - case linphone::Call::State::OutgoingEarlyMedia: - case linphone::Call::State::OutgoingInit: - case linphone::Call::State::OutgoingProgress: - case linphone::Call::State::OutgoingRinging: - break; - } - - return mCall->getDir() == linphone::Call::Dir::Incoming ? CallStatusIncoming : CallStatusOutgoing; + switch (mCall->getState()) { + case linphone::Call::State::Connected: + case linphone::Call::State::StreamsRunning: + return CallStatusConnected; + + case linphone::Call::State::End: + case linphone::Call::State::Error: + case linphone::Call::State::Referred: + case linphone::Call::State::Released: + return CallStatusEnded; + + case linphone::Call::State::Paused: + case linphone::Call::State::PausedByRemote: + case linphone::Call::State::Pausing: + case linphone::Call::State::Resuming: + return CallStatusPaused; + + case linphone::Call::State::Updating: + case linphone::Call::State::UpdatedByRemote: + return mPausedByRemote ? CallStatusPaused : CallStatusConnected; + + case linphone::Call::State::EarlyUpdatedByRemote: + case linphone::Call::State::EarlyUpdating: + case linphone::Call::State::Idle: + case linphone::Call::State::IncomingEarlyMedia: + case linphone::Call::State::IncomingReceived: + case linphone::Call::State::OutgoingEarlyMedia: + case linphone::Call::State::OutgoingInit: + case linphone::Call::State::OutgoingProgress: + case linphone::Call::State::OutgoingRinging: + break; + } + + return mCall->getDir() == linphone::Call::Dir::Incoming ? CallStatusIncoming : CallStatusOutgoing; } // ----------------------------------------------------------------------------- void CallModel::acceptWithAutoAnswerDelay () { - CoreManager *coreManager = CoreManager::getInstance(); - SettingsModel *settingsModel = coreManager->getSettingsModel(); - - // Use auto-answer if activated and it's the only call. - if (settingsModel->getAutoAnswerStatus() && coreManager->getCore()->getCallsNb() == 1) { - if (mCall->getRemoteParams()->videoEnabled() && settingsModel->getAutoAnswerVideoStatus() && settingsModel->getVideoSupported()) - acceptWithVideo(); - else - accept(); - } + CoreManager *coreManager = CoreManager::getInstance(); + SettingsModel *settingsModel = coreManager->getSettingsModel(); + + // Use auto-answer if activated and it's the only call. + if (settingsModel->getAutoAnswerStatus() && coreManager->getCore()->getCallsNb() == 1) { + if (mCall->getRemoteParams()->videoEnabled() && settingsModel->getAutoAnswerVideoStatus() && settingsModel->getVideoSupported()) + acceptWithVideo(); + else + accept(); + } } // ----------------------------------------------------------------------------- QString CallModel::getCallError () const { - return mCallError; + return mCallError; } void CallModel::setCallErrorFromReason (linphone::Reason reason) { - switch (reason) { - case linphone::Reason::Declined: - mCallError = tr("callErrorDeclined"); - break; - case linphone::Reason::NotFound: - mCallError = tr("callErrorNotFound"); - break; - case linphone::Reason::Busy: - mCallError = tr("callErrorBusy"); - break; - case linphone::Reason::NotAcceptable: - mCallError = tr("callErrorNotAcceptable"); - break; - default: - break; - } - - if (!mCallError.isEmpty()) - qInfo() << QStringLiteral("Call terminated with error (%1):").arg(mCallError) << this; - - emit callErrorChanged(mCallError); + switch (reason) { + case linphone::Reason::Declined: + mCallError = tr("callErrorDeclined"); + break; + case linphone::Reason::NotFound: + mCallError = tr("callErrorNotFound"); + break; + case linphone::Reason::Busy: + mCallError = tr("callErrorBusy"); + break; + case linphone::Reason::NotAcceptable: + mCallError = tr("callErrorNotAcceptable"); + break; + default: + break; + } + + if (!mCallError.isEmpty()) + qInfo() << QStringLiteral("Call terminated with error (%1):").arg(mCallError) << this; + + emit callErrorChanged(mCallError); } // ----------------------------------------------------------------------------- int CallModel::getDuration () const { - return mCall->getDuration(); + return mCall->getDuration(); } float CallModel::getQuality () const { - return mCall->getCurrentQuality(); + return mCall->getCurrentQuality(); } // ----------------------------------------------------------------------------- float CallModel::getSpeakerVu () const { - if (mCall->getState() == linphone::Call::State::StreamsRunning) - return MediastreamerUtils::computeVu(mCall->getPlayVolume()); - return 0.0; + if (mCall->getState() == linphone::Call::State::StreamsRunning) + return MediastreamerUtils::computeVu(mCall->getPlayVolume()); + return 0.0; } float CallModel::getMicroVu () const { - if (mCall->getState() == linphone::Call::State::StreamsRunning) - return MediastreamerUtils::computeVu(mCall->getRecordVolume()); - return 0.0; + if (mCall->getState() == linphone::Call::State::StreamsRunning) + return MediastreamerUtils::computeVu(mCall->getRecordVolume()); + return 0.0; } // ----------------------------------------------------------------------------- bool CallModel::getSpeakerMuted () const { - return mCall->getSpeakerMuted(); + return mCall->getSpeakerMuted(); } void CallModel::setSpeakerMuted (bool status) { - if (status == getSpeakerMuted()) - return; - - mCall->setSpeakerMuted(status); - emit speakerMutedChanged(getSpeakerMuted()); + if (status == getSpeakerMuted()) + return; + + mCall->setSpeakerMuted(status); + emit speakerMutedChanged(getSpeakerMuted()); } // ----------------------------------------------------------------------------- bool CallModel::getMicroMuted () const { - return mCall->getMicrophoneMuted(); + return mCall->getMicrophoneMuted(); } void CallModel::setMicroMuted (bool status) { - if (status == getMicroMuted()) - return; - - mCall->setMicrophoneMuted(status); - emit microMutedChanged(getMicroMuted()); + if (status == getMicroMuted()) + return; + + mCall->setMicrophoneMuted(status); + emit microMutedChanged(getMicroMuted()); } // ----------------------------------------------------------------------------- bool CallModel::getPausedByUser () const { - return mPausedByUser; + return mPausedByUser; } void CallModel::setPausedByUser (bool status) { - switch (mCall->getState()) { - case linphone::Call::State::Connected: - case linphone::Call::State::StreamsRunning: - case linphone::Call::State::Paused: - case linphone::Call::State::PausedByRemote: - break; - default: return; - } - - if (status) { - if (!mPausedByUser) - mCall->pause(); - return; - } - - if (mPausedByUser) - mCall->resume(); + switch (mCall->getState()) { + case linphone::Call::State::Connected: + case linphone::Call::State::StreamsRunning: + case linphone::Call::State::Paused: + case linphone::Call::State::PausedByRemote: + break; + default: return; + } + + if (status) { + if (!mPausedByUser) + mCall->pause(); + return; + } + + if (mPausedByUser) + mCall->resume(); } // ----------------------------------------------------------------------------- bool CallModel::getVideoEnabled () const { - shared_ptr params = mCall->getCurrentParams(); - return params && params->videoEnabled() && getStatus() == CallStatusConnected; + shared_ptr params = mCall->getCurrentParams(); + return params && params->videoEnabled() && getStatus() == CallStatusConnected; } void CallModel::setVideoEnabled (bool status) { - shared_ptr core = CoreManager::getInstance()->getCore(); - if (!core->videoSupported()) { - qWarning() << QStringLiteral("Unable to update video call property. (Video not supported.)"); - return; - } - - switch (mCall->getState()) { - case linphone::Call::State::Connected: - case linphone::Call::State::StreamsRunning: - break; - default: return; - } - - if (status == getVideoEnabled()) - return; - - shared_ptr params = core->createCallParams(mCall); - params->enableVideo(status); - - mCall->update(params); + shared_ptr core = CoreManager::getInstance()->getCore(); + if (!core->videoSupported()) { + qWarning() << QStringLiteral("Unable to update video call property. (Video not supported.)"); + return; + } + + switch (mCall->getState()) { + case linphone::Call::State::Connected: + case linphone::Call::State::StreamsRunning: + break; + default: return; + } + + if (status == getVideoEnabled()) + return; + + shared_ptr params = core->createCallParams(mCall); + params->enableVideo(status); + + mCall->update(params); } // ----------------------------------------------------------------------------- bool CallModel::getUpdating () const { - switch (mCall->getState()) { - case linphone::Call::State::Connected: - case linphone::Call::State::StreamsRunning: - case linphone::Call::State::Paused: - case linphone::Call::State::PausedByRemote: - return false; - - default: - break; - } - - return true; + switch (mCall->getState()) { + case linphone::Call::State::Connected: + case linphone::Call::State::StreamsRunning: + case linphone::Call::State::Paused: + case linphone::Call::State::PausedByRemote: + return false; + + default: + break; + } + + return true; } bool CallModel::getRecording () const { - return mRecording; + return mRecording; } // ----------------------------------------------------------------------------- void CallModel::sendDtmf (const QString &dtmf) { - const char key = dtmf.constData()[0].toLatin1(); - qInfo() << QStringLiteral("Send dtmf: `%1`.").arg(key); - mCall->sendDtmf(key); - CoreManager::getInstance()->getCore()->playDtmf(key, DtmfSoundDelay); + const char key = dtmf.constData()[0].toLatin1(); + qInfo() << QStringLiteral("Send dtmf: `%1`.").arg(key); + mCall->sendDtmf(key); + CoreManager::getInstance()->getCore()->playDtmf(key, DtmfSoundDelay); } // ----------------------------------------------------------------------------- void CallModel::verifyAuthenticationToken (bool verify) { - mCall->setAuthenticationTokenVerified(verify); - emit securityUpdated(); + mCall->setAuthenticationTokenVerified(verify); + emit securityUpdated(); } // ----------------------------------------------------------------------------- void CallModel::updateStreams () { - mCall->update(nullptr); + mCall->update(nullptr); } void CallModel::toggleSpeakerMute(){ - setSpeakerMuted(!getSpeakerMuted()); + setSpeakerMuted(!getSpeakerMuted()); } // ----------------------------------------------------------------------------- @@ -687,181 +703,201 @@ void CallModel::setRemoteDisplayName(const std::string& name){ } emit fullPeerAddressChanged(); } + +QString CallModel::getTransferAddress () const { + return mTransferAddress; +} + +void CallModel::setTransferAddress (const QString &transferAddress) { + mTransferAddress = transferAddress; + emit transferAddressChanged(mTransferAddress); +} + +void CallModel::prepareTransfert(shared_ptr call, const QString& transfertAddress){ + if( call && transfertAddress != ""){ + CallModel * model = &call->getData("call-model"); + model->setTransferAddress(transfertAddress); + } +} + +std::shared_ptr CallModel::getRemoteAddress()const{ + return mRemoteAddress; +} // ----------------------------------------------------------------------------- CallModel::CallEncryption CallModel::getEncryption () const { - return static_cast(mCall->getCurrentParams()->getMediaEncryption()); + return static_cast(mCall->getCurrentParams()->getMediaEncryption()); } bool CallModel::isSecured () const { - shared_ptr params = mCall->getCurrentParams(); - linphone::MediaEncryption encryption = params->getMediaEncryption(); - return ( - encryption == linphone::MediaEncryption::ZRTP && mCall->getAuthenticationTokenVerified() - ) || encryption == linphone::MediaEncryption::SRTP || encryption == linphone::MediaEncryption::DTLS; + shared_ptr params = mCall->getCurrentParams(); + linphone::MediaEncryption encryption = params->getMediaEncryption(); + return ( + encryption == linphone::MediaEncryption::ZRTP && mCall->getAuthenticationTokenVerified() + ) || encryption == linphone::MediaEncryption::SRTP || encryption == linphone::MediaEncryption::DTLS; } // ----------------------------------------------------------------------------- QString CallModel::getLocalSas () const { - QString token = Utils::coreStringToAppString(mCall->getAuthenticationToken()); - return mCall->getDir() == linphone::Call::Dir::Incoming ? token.left(2).toUpper() : token.right(2).toUpper(); + QString token = Utils::coreStringToAppString(mCall->getAuthenticationToken()); + return mCall->getDir() == linphone::Call::Dir::Incoming ? token.left(2).toUpper() : token.right(2).toUpper(); } QString CallModel::getRemoteSas () const { - QString token = Utils::coreStringToAppString(mCall->getAuthenticationToken()); - return mCall->getDir() != linphone::Call::Dir::Incoming ? token.left(2).toUpper() : token.right(2).toUpper(); + QString token = Utils::coreStringToAppString(mCall->getAuthenticationToken()); + return mCall->getDir() != linphone::Call::Dir::Incoming ? token.left(2).toUpper() : token.right(2).toUpper(); } // ----------------------------------------------------------------------------- QString CallModel::getSecuredString () const { - switch (mCall->getCurrentParams()->getMediaEncryption()) { - case linphone::MediaEncryption::SRTP: - return QStringLiteral("SRTP"); - case linphone::MediaEncryption::ZRTP: - return QStringLiteral("ZRTP"); - case linphone::MediaEncryption::DTLS: - return QStringLiteral("DTLS"); - case linphone::MediaEncryption::None: - break; - } - - return QString(""); + switch (mCall->getCurrentParams()->getMediaEncryption()) { + case linphone::MediaEncryption::SRTP: + return QStringLiteral("SRTP"); + case linphone::MediaEncryption::ZRTP: + return QStringLiteral("ZRTP"); + case linphone::MediaEncryption::DTLS: + return QStringLiteral("DTLS"); + case linphone::MediaEncryption::None: + break; + } + + return QString(""); } // ----------------------------------------------------------------------------- QVariantList CallModel::getAudioStats () const { - return mAudioStats; + return mAudioStats; } QVariantList CallModel::getVideoStats () const { - return mVideoStats; + return mVideoStats; } // ----------------------------------------------------------------------------- static inline QVariantMap createStat (const QString &key, const QString &value) { - QVariantMap m; - m["key"] = key; - m["value"] = value; - return m; + QVariantMap m; + m["key"] = key; + m["value"] = value; + return m; } void CallModel::updateStats (const shared_ptr &callStats, QVariantList &statsList) { - shared_ptr params = mCall->getCurrentParams(); - shared_ptr payloadType; - - switch (callStats->getType()) { - case linphone::StreamType::Audio: - payloadType = params->getUsedAudioPayloadType(); - break; - case linphone::StreamType::Video: - payloadType = params->getUsedVideoPayloadType(); - break; - default: - return; - } - - QString family; - switch (callStats->getIpFamilyOfRemote()) { - case linphone::AddressFamily::Inet: - family = QStringLiteral("IPv4"); - break; - case linphone::AddressFamily::Inet6: - family = QStringLiteral("IPv6"); - break; - default: - family = QStringLiteral("Unknown"); - break; - } - - statsList.clear(); - - statsList << createStat(tr("callStatsCodec"), payloadType - ? QStringLiteral("%1 / %2kHz").arg(Utils::coreStringToAppString(payloadType->getMimeType())).arg(payloadType->getClockRate() / 1000) - : QString("")); - statsList << createStat(tr("callStatsUploadBandwidth"), QStringLiteral("%1 kbits/s").arg(int(callStats->getUploadBandwidth()))); - statsList << createStat(tr("callStatsDownloadBandwidth"), QStringLiteral("%1 kbits/s").arg(int(callStats->getDownloadBandwidth()))); - statsList << createStat(tr("callStatsIceState"), iceStateToString(callStats->getIceState())); - statsList << createStat(tr("callStatsIpFamily"), family); - statsList << createStat(tr("callStatsSenderLossRate"), QStringLiteral("%1 %").arg(static_cast(callStats->getSenderLossRate()))); - statsList << createStat(tr("callStatsReceiverLossRate"), QStringLiteral("%1 %").arg(static_cast(callStats->getReceiverLossRate()))); - - switch (callStats->getType()) { - case linphone::StreamType::Audio: - statsList << createStat(tr("callStatsJitterBuffer"), QStringLiteral("%1 ms").arg(callStats->getJitterBufferSizeMs())); - break; - case linphone::StreamType::Video: { - statsList << createStat(tr("callStatsEstimatedDownloadBandwidth"), QStringLiteral("%1 kbits/s").arg(int(callStats->getEstimatedDownloadBandwidth()))); - const QString sentVideoDefinitionName = Utils::coreStringToAppString(params->getSentVideoDefinition()->getName()); - const QString sentVideoDefinition = QStringLiteral("%1x%2") - .arg(params->getSentVideoDefinition()->getWidth()) - .arg(params->getSentVideoDefinition()->getHeight()); - - statsList << createStat(tr("callStatsSentVideoDefinition"), sentVideoDefinition == sentVideoDefinitionName - ? sentVideoDefinition - : QStringLiteral("%1 (%2)").arg(sentVideoDefinition).arg(sentVideoDefinitionName)); - - const QString receivedVideoDefinitionName = Utils::coreStringToAppString(params->getReceivedVideoDefinition()->getName()); - const QString receivedVideoDefinition = QString("%1x%2") - .arg(params->getReceivedVideoDefinition()->getWidth()) - .arg(params->getReceivedVideoDefinition()->getHeight()); - - statsList << createStat(tr("callStatsReceivedVideoDefinition"), receivedVideoDefinition == receivedVideoDefinitionName - ? receivedVideoDefinition - : QString("%1 (%2)").arg(receivedVideoDefinition).arg(receivedVideoDefinitionName)); - - statsList << createStat(tr("callStatsReceivedFramerate"), QStringLiteral("%1 FPS").arg(static_cast(params->getReceivedFramerate()))); - statsList << createStat(tr("callStatsSentFramerate"), QStringLiteral("%1 FPS").arg(static_cast(params->getSentFramerate()))); - } break; - - default: - break; - } + shared_ptr params = mCall->getCurrentParams(); + shared_ptr payloadType; + + switch (callStats->getType()) { + case linphone::StreamType::Audio: + payloadType = params->getUsedAudioPayloadType(); + break; + case linphone::StreamType::Video: + payloadType = params->getUsedVideoPayloadType(); + break; + default: + return; + } + + QString family; + switch (callStats->getIpFamilyOfRemote()) { + case linphone::AddressFamily::Inet: + family = QStringLiteral("IPv4"); + break; + case linphone::AddressFamily::Inet6: + family = QStringLiteral("IPv6"); + break; + default: + family = QStringLiteral("Unknown"); + break; + } + + statsList.clear(); + + statsList << createStat(tr("callStatsCodec"), payloadType + ? QStringLiteral("%1 / %2kHz").arg(Utils::coreStringToAppString(payloadType->getMimeType())).arg(payloadType->getClockRate() / 1000) + : QString("")); + statsList << createStat(tr("callStatsUploadBandwidth"), QStringLiteral("%1 kbits/s").arg(int(callStats->getUploadBandwidth()))); + statsList << createStat(tr("callStatsDownloadBandwidth"), QStringLiteral("%1 kbits/s").arg(int(callStats->getDownloadBandwidth()))); + statsList << createStat(tr("callStatsIceState"), iceStateToString(callStats->getIceState())); + statsList << createStat(tr("callStatsIpFamily"), family); + statsList << createStat(tr("callStatsSenderLossRate"), QStringLiteral("%1 %").arg(static_cast(callStats->getSenderLossRate()))); + statsList << createStat(tr("callStatsReceiverLossRate"), QStringLiteral("%1 %").arg(static_cast(callStats->getReceiverLossRate()))); + + switch (callStats->getType()) { + case linphone::StreamType::Audio: + statsList << createStat(tr("callStatsJitterBuffer"), QStringLiteral("%1 ms").arg(callStats->getJitterBufferSizeMs())); + break; + case linphone::StreamType::Video: { + statsList << createStat(tr("callStatsEstimatedDownloadBandwidth"), QStringLiteral("%1 kbits/s").arg(int(callStats->getEstimatedDownloadBandwidth()))); + const QString sentVideoDefinitionName = Utils::coreStringToAppString(params->getSentVideoDefinition()->getName()); + const QString sentVideoDefinition = QStringLiteral("%1x%2") + .arg(params->getSentVideoDefinition()->getWidth()) + .arg(params->getSentVideoDefinition()->getHeight()); + + statsList << createStat(tr("callStatsSentVideoDefinition"), sentVideoDefinition == sentVideoDefinitionName + ? sentVideoDefinition + : QStringLiteral("%1 (%2)").arg(sentVideoDefinition).arg(sentVideoDefinitionName)); + + const QString receivedVideoDefinitionName = Utils::coreStringToAppString(params->getReceivedVideoDefinition()->getName()); + const QString receivedVideoDefinition = QString("%1x%2") + .arg(params->getReceivedVideoDefinition()->getWidth()) + .arg(params->getReceivedVideoDefinition()->getHeight()); + + statsList << createStat(tr("callStatsReceivedVideoDefinition"), receivedVideoDefinition == receivedVideoDefinitionName + ? receivedVideoDefinition + : QString("%1 (%2)").arg(receivedVideoDefinition).arg(receivedVideoDefinitionName)); + + statsList << createStat(tr("callStatsReceivedFramerate"), QStringLiteral("%1 FPS").arg(static_cast(params->getReceivedFramerate()))); + statsList << createStat(tr("callStatsSentFramerate"), QStringLiteral("%1 FPS").arg(static_cast(params->getSentFramerate()))); + } break; + + default: + break; + } } // ----------------------------------------------------------------------------- QString CallModel::iceStateToString (linphone::IceState state) const { - switch (state) { - case linphone::IceState::NotActivated: - return tr("iceStateNotActivated"); - case linphone::IceState::Failed: - return tr("iceStateFailed"); - case linphone::IceState::InProgress: - return tr("iceStateInProgress"); - case linphone::IceState::ReflexiveConnection: - return tr("iceStateReflexiveConnection"); - case linphone::IceState::HostConnection: - return tr("iceStateHostConnection"); - case linphone::IceState::RelayConnection: - return tr("iceStateRelayConnection"); - } - - return tr("iceStateInvalid"); + switch (state) { + case linphone::IceState::NotActivated: + return tr("iceStateNotActivated"); + case linphone::IceState::Failed: + return tr("iceStateFailed"); + case linphone::IceState::InProgress: + return tr("iceStateInProgress"); + case linphone::IceState::ReflexiveConnection: + return tr("iceStateReflexiveConnection"); + case linphone::IceState::HostConnection: + return tr("iceStateHostConnection"); + case linphone::IceState::RelayConnection: + return tr("iceStateRelayConnection"); + } + + return tr("iceStateInvalid"); } // ----------------------------------------------------------------------------- QString CallModel::generateSavedFilename () const { - const shared_ptr callLog(mCall->getCallLog()); - return generateSavedFilename( - Utils::coreStringToAppString(callLog->getFromAddress()->getUsername()), - Utils::coreStringToAppString(callLog->getToAddress()->getUsername()) - ); + const shared_ptr callLog(mCall->getCallLog()); + return generateSavedFilename( + Utils::coreStringToAppString(callLog->getFromAddress()->getUsername()), + Utils::coreStringToAppString(callLog->getToAddress()->getUsername()) + ); } QString CallModel::generateSavedFilename (const QString &from, const QString &to) { - auto escape = [](const QString &str) { - constexpr char ReservedCharacters[] = "<>:\"/\\|\\?\\*"; - static QRegularExpression regexp(ReservedCharacters); - return QString(str).replace(regexp, ""); - }; - - return QStringLiteral("%1_%2_%3") - .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss")) - .arg(escape(from)) - .arg(escape(to)); + auto escape = [](const QString &str) { + constexpr char ReservedCharacters[] = "<>:\"/\\|\\?\\*"; + static QRegularExpression regexp(ReservedCharacters); + return QString(str).replace(regexp, ""); + }; + + return QStringLiteral("%1_%2_%3") + .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss")) + .arg(escape(from)) + .arg(escape(to)); } diff --git a/linphone-app/src/components/call/CallModel.hpp b/linphone-app/src/components/call/CallModel.hpp index 064bd9521..7ee9de087 100644 --- a/linphone-app/src/components/call/CallModel.hpp +++ b/linphone-app/src/components/call/CallModel.hpp @@ -30,237 +30,243 @@ class ContactModel; class ChatRoomModel; class CallModel : public QObject { - Q_OBJECT; - - Q_PROPERTY(QString peerAddress READ getPeerAddress CONSTANT); - Q_PROPERTY(QString localAddress READ getLocalAddress CONSTANT); - Q_PROPERTY(QString fullPeerAddress READ getFullPeerAddress NOTIFY fullPeerAddressChanged); - Q_PROPERTY(QString fullLocalAddress READ getFullLocalAddress CONSTANT); + Q_OBJECT; + + Q_PROPERTY(QString peerAddress READ getPeerAddress CONSTANT); + Q_PROPERTY(QString localAddress READ getLocalAddress CONSTANT); + Q_PROPERTY(QString fullPeerAddress READ getFullPeerAddress NOTIFY fullPeerAddressChanged); + Q_PROPERTY(QString fullLocalAddress READ getFullLocalAddress CONSTANT); Q_PROPERTY(ContactModel *contactModel READ getContactModel CONSTANT ) Q_PROPERTY(ChatRoomModel * chatRoomModel READ getChatRoomModel CONSTANT) - /* - Q_PROPERTY(QString sipAddress READ getFullPeerAddress NOTIFY fullPeerAddressChanged) - Q_PROPERTY(QString username READ getUsername NOTIFY usernameChanged) - Q_PROPERTY(QString avatar READ getAvatar NOTIFY avatarChanged) - Q_PROPERTY(int presenceStatus READ getPresenceStatus NOTIFY presenceStatusChanged)*/ - - Q_PROPERTY(CallStatus status READ getStatus NOTIFY statusChanged); - Q_PROPERTY(QString callError READ getCallError NOTIFY callErrorChanged); - - Q_PROPERTY(bool isOutgoing READ isOutgoing CONSTANT); - - Q_PROPERTY(bool isInConference READ isInConference NOTIFY isInConferenceChanged); - - Q_PROPERTY(int duration READ getDuration CONSTANT); // Constants but called with a timer in qml. - Q_PROPERTY(float quality READ getQuality CONSTANT); - Q_PROPERTY(float speakerVu READ getSpeakerVu CONSTANT); - Q_PROPERTY(float microVu READ getMicroVu CONSTANT); - - Q_PROPERTY(bool speakerMuted READ getSpeakerMuted WRITE setSpeakerMuted NOTIFY speakerMutedChanged); - Q_PROPERTY(bool microMuted READ getMicroMuted WRITE setMicroMuted NOTIFY microMutedChanged); - - Q_PROPERTY(bool pausedByUser READ getPausedByUser WRITE setPausedByUser NOTIFY statusChanged); - Q_PROPERTY(bool videoEnabled READ getVideoEnabled WRITE setVideoEnabled NOTIFY statusChanged); - Q_PROPERTY(bool updating READ getUpdating NOTIFY statusChanged) - - Q_PROPERTY(bool recording READ getRecording NOTIFY recordingChanged); - - Q_PROPERTY(QVariantList audioStats READ getAudioStats NOTIFY statsUpdated); - Q_PROPERTY(QVariantList videoStats READ getVideoStats NOTIFY statsUpdated); - - Q_PROPERTY(CallEncryption encryption READ getEncryption NOTIFY securityUpdated); - Q_PROPERTY(bool isSecured READ isSecured NOTIFY securityUpdated); - Q_PROPERTY(QString localSas READ getLocalSas NOTIFY securityUpdated); - Q_PROPERTY(QString remoteSas READ getRemoteSas NOTIFY securityUpdated); - Q_PROPERTY(QString securedString READ getSecuredString NOTIFY securityUpdated); - - Q_PROPERTY(float speakerVolumeGain READ getSpeakerVolumeGain WRITE setSpeakerVolumeGain NOTIFY speakerVolumeGainChanged); - Q_PROPERTY(float microVolumeGain READ getMicroVolumeGain WRITE setMicroVolumeGain NOTIFY microVolumeGainChanged); - - - + Q_PROPERTY(CallStatus status READ getStatus NOTIFY statusChanged); + Q_PROPERTY(QString callError READ getCallError NOTIFY callErrorChanged); + + Q_PROPERTY(bool isOutgoing READ isOutgoing CONSTANT); + + Q_PROPERTY(bool isInConference READ isInConference NOTIFY isInConferenceChanged); + + Q_PROPERTY(int duration READ getDuration CONSTANT); // Constants but called with a timer in qml. + Q_PROPERTY(float quality READ getQuality CONSTANT); + Q_PROPERTY(float speakerVu READ getSpeakerVu CONSTANT); + Q_PROPERTY(float microVu READ getMicroVu CONSTANT); + + Q_PROPERTY(bool speakerMuted READ getSpeakerMuted WRITE setSpeakerMuted NOTIFY speakerMutedChanged); + Q_PROPERTY(bool microMuted READ getMicroMuted WRITE setMicroMuted NOTIFY microMutedChanged); + + Q_PROPERTY(float speakerVolumeGain READ getSpeakerVolumeGain WRITE setSpeakerVolumeGain NOTIFY speakerVolumeGainChanged); + Q_PROPERTY(float microVolumeGain READ getMicroVolumeGain WRITE setMicroVolumeGain NOTIFY microVolumeGainChanged); + + Q_PROPERTY(bool pausedByUser READ getPausedByUser WRITE setPausedByUser NOTIFY statusChanged); + Q_PROPERTY(bool videoEnabled READ getVideoEnabled WRITE setVideoEnabled NOTIFY statusChanged); + Q_PROPERTY(bool updating READ getUpdating NOTIFY statusChanged) + + Q_PROPERTY(bool recording READ getRecording NOTIFY recordingChanged); + + Q_PROPERTY(QVariantList audioStats READ getAudioStats NOTIFY statsUpdated); + Q_PROPERTY(QVariantList videoStats READ getVideoStats NOTIFY statsUpdated); + + Q_PROPERTY(CallEncryption encryption READ getEncryption NOTIFY securityUpdated); + Q_PROPERTY(bool isSecured READ isSecured NOTIFY securityUpdated); + Q_PROPERTY(QString localSas READ getLocalSas NOTIFY securityUpdated); + Q_PROPERTY(QString remoteSas READ getRemoteSas NOTIFY securityUpdated); + Q_PROPERTY(QString securedString READ getSecuredString NOTIFY securityUpdated); + + Q_PROPERTY(QString transferAddress READ getTransferAddress WRITE setTransferAddress NOTIFY transferAddressChanged); + + + public: - enum CallStatus { - CallStatusConnected, - CallStatusEnded, - CallStatusIdle, - CallStatusIncoming, - CallStatusOutgoing, - CallStatusPaused - }; - Q_ENUM(CallStatus); - - enum CallEncryption { - CallEncryptionNone = int(linphone::MediaEncryption::None), - CallEncryptionDtls = int(linphone::MediaEncryption::DTLS), - CallEncryptionSrtp = int(linphone::MediaEncryption::SRTP), - CallEncryptionZrtp = int(linphone::MediaEncryption::ZRTP) - }; - Q_ENUM(CallEncryption); - - CallModel (std::shared_ptr call); - ~CallModel (); - - std::shared_ptr getCall () const { - return mCall; - } - - QString getPeerAddress () const; - QString getLocalAddress () const; - QString getFullPeerAddress () const; - QString getFullLocalAddress () const; - - ContactModel *getContactModel() const; - - ChatRoomModel * getChatRoomModel() const; - - bool isInConference () const { - return mIsInConference; - } - - void setRecordFile (const std::shared_ptr &callParams); - static void setRecordFile (const std::shared_ptr &callParams, const QString &to); - - void updateStats (const std::shared_ptr &callStats); - - void notifyCameraFirstFrameReceived (unsigned int width, unsigned int height); - - Q_INVOKABLE void accept (); - Q_INVOKABLE void acceptWithVideo (); - Q_INVOKABLE void terminate (); - - Q_INVOKABLE void askForTransfer (); - Q_INVOKABLE bool transferTo (const QString &sipAddress); - - Q_INVOKABLE void acceptVideoRequest (); - Q_INVOKABLE void rejectVideoRequest (); - - Q_INVOKABLE void takeSnapshot (); - - Q_INVOKABLE void startRecording (); - Q_INVOKABLE void stopRecording (); - - Q_INVOKABLE void sendDtmf (const QString &dtmf); - - Q_INVOKABLE void verifyAuthenticationToken (bool verify); - - Q_INVOKABLE void updateStreams (); - - Q_INVOKABLE void toggleSpeakerMute(); - - void setRemoteDisplayName(const std::string& name); - - static constexpr int DtmfSoundDelay = 200; - - std::shared_ptr mCall; - std::shared_ptr mRemoteAddress; - std::shared_ptr mMagicSearch; - + enum CallStatus { + CallStatusConnected, + CallStatusEnded, + CallStatusIdle, + CallStatusIncoming, + CallStatusOutgoing, + CallStatusPaused + }; + Q_ENUM(CallStatus); + + enum CallEncryption { + CallEncryptionNone = int(linphone::MediaEncryption::None), + CallEncryptionDtls = int(linphone::MediaEncryption::DTLS), + CallEncryptionSrtp = int(linphone::MediaEncryption::SRTP), + CallEncryptionZrtp = int(linphone::MediaEncryption::ZRTP) + }; + Q_ENUM(CallEncryption); + + CallModel (std::shared_ptr call); + ~CallModel (); + + std::shared_ptr getCall () const { + return mCall; + } + + QString getPeerAddress () const; + QString getLocalAddress () const; + QString getFullPeerAddress () const; + QString getFullLocalAddress () const; + + ContactModel *getContactModel() const; + + ChatRoomModel * getChatRoomModel() const; + + bool isInConference () const { + return mIsInConference; + } + + void setRecordFile (const std::shared_ptr &callParams); + static void setRecordFile (const std::shared_ptr &callParams, const QString &to); + + void updateStats (const std::shared_ptr &callStats); + + void notifyCameraFirstFrameReceived (unsigned int width, unsigned int height); + + Q_INVOKABLE void accept (); + Q_INVOKABLE void acceptWithVideo (); + Q_INVOKABLE void terminate (); + + Q_INVOKABLE void askForTransfer (); + Q_INVOKABLE void askForAttendedTransfer (); + Q_INVOKABLE bool transferTo (const QString &sipAddress); + Q_INVOKABLE bool transferToAnother (const QString &peerAddress); + + Q_INVOKABLE void acceptVideoRequest (); + Q_INVOKABLE void rejectVideoRequest (); + + Q_INVOKABLE void takeSnapshot (); + + Q_INVOKABLE void startRecording (); + Q_INVOKABLE void stopRecording (); + + Q_INVOKABLE void sendDtmf (const QString &dtmf); + + Q_INVOKABLE void verifyAuthenticationToken (bool verify); + + Q_INVOKABLE void updateStreams (); + + Q_INVOKABLE void toggleSpeakerMute(); + + void setRemoteDisplayName(const std::string& name); + + QString getTransferAddress () const; + void setTransferAddress (const QString &transferAddress); + static void prepareTransfert(std::shared_ptr call, const QString& transfertAddress); + + std::shared_ptr getRemoteAddress()const; + + static constexpr int DtmfSoundDelay = 200; + + std::shared_ptr mCall; + std::shared_ptr mRemoteAddress; + std::shared_ptr mMagicSearch; + public slots: -// Set remote display name when a search has been done - void searchReceived(std::list> results); - void callEnded(); - + // Set remote display name when a search has been done + void searchReceived(std::list> results); + void callEnded(); + signals: - void callErrorChanged (const QString &callError); - void isInConferenceChanged (bool status); - void speakerMutedChanged (bool status); - void microMutedChanged (bool status); - void recordingChanged (bool status); - void statsUpdated (); - void statusChanged (CallStatus status); - void videoRequested (); - void securityUpdated (); - void speakerVolumeGainChanged (float volume); - void microVolumeGainChanged (float volume); - - void cameraFirstFrameReceived (unsigned int width, unsigned int height); - - void fullPeerAddressChanged(); - + void callErrorChanged (const QString &callError); + void isInConferenceChanged (bool status); + void speakerMutedChanged (bool status); + void microMutedChanged (bool status); + void recordingChanged (bool status); + void statsUpdated (); + void statusChanged (CallStatus status); + void videoRequested (); + void securityUpdated (); + void speakerVolumeGainChanged (float volume); + void microVolumeGainChanged (float volume); + + void cameraFirstFrameReceived (unsigned int width, unsigned int height); + + void fullPeerAddressChanged(); + void transferAddressChanged (const QString &transferAddress); + private: - void handleCallEncryptionChanged (const std::shared_ptr &call); - void handleCallStateChanged (const std::shared_ptr &call, linphone::Call::State state); - - void accept (bool withVideo); - - void stopAutoAnswerTimer () const; - - CallStatus getStatus () const; - - bool isOutgoing () const { - return mCall->getDir() == linphone::Call::Dir::Outgoing; - } - - void updateIsInConference (); - - void acceptWithAutoAnswerDelay (); - - QString getCallError () const; - void setCallErrorFromReason (linphone::Reason reason); - - int getDuration () const; - float getQuality () const; - float getMicroVu () const; - float getSpeakerVu () const; - - bool getSpeakerMuted () const; - void setSpeakerMuted (bool status); - - bool getMicroMuted () const; - void setMicroMuted (bool status); - - bool getPausedByUser () const; - void setPausedByUser (bool status); - - bool getVideoEnabled () const; - void setVideoEnabled (bool status); - - bool getUpdating () const; - - bool getRecording () const; - - CallEncryption getEncryption () const; - bool isSecured () const; - - QString getLocalSas () const; - QString getRemoteSas () const; - - QString getSecuredString () const; - - QVariantList getAudioStats () const; - QVariantList getVideoStats () const; - void updateStats (const std::shared_ptr &callStats, QVariantList &statsList); - - QString iceStateToString (linphone::IceState state) const; - - float getSpeakerVolumeGain () const; - void setSpeakerVolumeGain (float volume); - - float getMicroVolumeGain () const; - void setMicroVolumeGain (float volume); - - QString generateSavedFilename () const; - - static QString generateSavedFilename (const QString &from, const QString &to); - - bool mIsInConference = false; - - bool mPausedByRemote = false; - bool mPausedByUser = false; - bool mRecording = false; - - bool mWasConnected = false; - - bool mNotifyCameraFirstFrameReceived = true; - - QString mCallError; - - QVariantList mAudioStats; - QVariantList mVideoStats; - std::shared_ptr mSearch; + void handleCallEncryptionChanged (const std::shared_ptr &call); + void handleCallStateChanged (const std::shared_ptr &call, linphone::Call::State state); + + void accept (bool withVideo); + + void stopAutoAnswerTimer () const; + + CallStatus getStatus () const; + + bool isOutgoing () const { + return mCall->getDir() == linphone::Call::Dir::Outgoing; + } + + void updateIsInConference (); + + void acceptWithAutoAnswerDelay (); + + QString getCallError () const; + void setCallErrorFromReason (linphone::Reason reason); + + int getDuration () const; + float getQuality () const; + float getMicroVu () const; + float getSpeakerVu () const; + + bool getSpeakerMuted () const; + void setSpeakerMuted (bool status); + + bool getMicroMuted () const; + void setMicroMuted (bool status); + + bool getPausedByUser () const; + void setPausedByUser (bool status); + + bool getVideoEnabled () const; + void setVideoEnabled (bool status); + + bool getUpdating () const; + + bool getRecording () const; + + CallEncryption getEncryption () const; + bool isSecured () const; + + QString getLocalSas () const; + QString getRemoteSas () const; + + QString getSecuredString () const; + + QVariantList getAudioStats () const; + QVariantList getVideoStats () const; + void updateStats (const std::shared_ptr &callStats, QVariantList &statsList); + + QString iceStateToString (linphone::IceState state) const; + + float getSpeakerVolumeGain () const; + void setSpeakerVolumeGain (float volume); + + float getMicroVolumeGain () const; + void setMicroVolumeGain (float volume); + + QString generateSavedFilename () const; + + static QString generateSavedFilename (const QString &from, const QString &to); + + bool mIsInConference = false; + + bool mPausedByRemote = false; + bool mPausedByUser = false; + bool mRecording = false; + + bool mWasConnected = false; + + bool mNotifyCameraFirstFrameReceived = true; + + QString mCallError; + + QVariantList mAudioStats; + QVariantList mVideoStats; + std::shared_ptr mSearch; + QString mTransferAddress; }; #endif // CALL_MODEL_H_ diff --git a/linphone-app/src/components/calls/CallsListModel.cpp b/linphone-app/src/components/calls/CallsListModel.cpp index e4f969160..83cf410a9 100644 --- a/linphone-app/src/components/calls/CallsListModel.cpp +++ b/linphone-app/src/components/calls/CallsListModel.cpp @@ -93,15 +93,27 @@ QVariant CallsListModel::data (const QModelIndex &index, int role) const { return QVariant(); } +CallModel *CallsListModel::findCallModelFromPeerAddress (const QString &peerAddress) const { + std::shared_ptr address = Utils::interpretUrl(peerAddress); + auto it = find_if(mList.begin(), mList.end(), [address](CallModel *callModel) { + return callModel->getRemoteAddress()->weakEqual(address); + }); + return it != mList.end() ? *it : nullptr; +} + // ----------------------------------------------------------------------------- void CallsListModel::askForTransfer (CallModel *callModel) { emit callTransferAsked(callModel); } +void CallsListModel::askForAttendedTransfer (CallModel *callModel) { + emit callAttendedTransferAsked(callModel); +} + // ----------------------------------------------------------------------------- -void CallsListModel::launchAudioCall (const QString &sipAddress, const QHash &headers) const { +void CallsListModel::launchAudioCall (const QString &sipAddress, const QString& prepareTransfertAddress, const QHash &headers) const { CoreManager::getInstance()->getTimelineListModel()->mAutoSelectAfterCreation = true; shared_ptr core = CoreManager::getInstance()->getCore(); @@ -122,23 +134,23 @@ void CallsListModel::launchAudioCall (const QString &sipAddress, const QHash currentProxyConfig = core->getDefaultProxyConfig(); if(currentProxyConfig){ if(!CoreManager::getInstance()->getSettingsModel()->getWaitRegistrationForCall() || currentProxyConfig->getState() == linphone::RegistrationState::Ok) - core->inviteAddressWithParams(address, params); + CallModel::prepareTransfert(core->inviteAddressWithParams(address, params), prepareTransfertAddress); else{ QObject * context = new QObject(); QObject::connect(CoreManager::getInstance()->getHandlers().get(), &CoreHandlers::registrationStateChanged,context, - [address,core,params,currentProxyConfig, context](const std::shared_ptr &proxyConfig, linphone::RegistrationState state) mutable { + [address,core,params,currentProxyConfig,prepareTransfertAddress, context](const std::shared_ptr &proxyConfig, linphone::RegistrationState state) mutable { if(context && proxyConfig==currentProxyConfig && state==linphone::RegistrationState::Ok){ + CallModel::prepareTransfert(core->inviteAddressWithParams(address, params), prepareTransfertAddress); delete context; context = nullptr; - core->inviteAddressWithParams(address, params); } }); } }else - core->inviteAddressWithParams(address, params); + CallModel::prepareTransfert(core->inviteAddressWithParams(address, params), prepareTransfertAddress); } -void CallsListModel::launchSecureAudioCall (const QString &sipAddress, LinphoneEnums::MediaEncryption encryption, const QHash &headers) const { +void CallsListModel::launchSecureAudioCall (const QString &sipAddress, LinphoneEnums::MediaEncryption encryption, const QHash &headers, const QString& prepareTransfertAddress) const { CoreManager::getInstance()->getTimelineListModel()->mAutoSelectAfterCreation = true; shared_ptr core = CoreManager::getInstance()->getCore(); @@ -160,28 +172,28 @@ void CallsListModel::launchSecureAudioCall (const QString &sipAddress, LinphoneE params->setMediaEncryption(LinphoneEnums::toLinphone(encryption)); if(currentProxyConfig){ if(!CoreManager::getInstance()->getSettingsModel()->getWaitRegistrationForCall() || currentProxyConfig->getState() == linphone::RegistrationState::Ok) - core->inviteAddressWithParams(address, params); + CallModel::prepareTransfert(core->inviteAddressWithParams(address, params), prepareTransfertAddress); else{ QObject * context = new QObject(); QObject::connect(CoreManager::getInstance()->getHandlers().get(), &CoreHandlers::registrationStateChanged,context, - [address,core,params,currentProxyConfig, context](const std::shared_ptr &proxyConfig, linphone::RegistrationState state) mutable { + [address,core,params,currentProxyConfig,prepareTransfertAddress, context](const std::shared_ptr &proxyConfig, linphone::RegistrationState state) mutable { if(context && proxyConfig==currentProxyConfig && state==linphone::RegistrationState::Ok){ + CallModel::prepareTransfert(core->inviteAddressWithParams(address, params), prepareTransfertAddress); delete context; context = nullptr; - core->inviteAddressWithParams(address, params); } }); } }else - core->inviteAddressWithParams(address, params); + CallModel::prepareTransfert(core->inviteAddressWithParams(address, params), prepareTransfertAddress); } -void CallsListModel::launchVideoCall (const QString &sipAddress) const { +void CallsListModel::launchVideoCall (const QString &sipAddress, const QString& prepareTransfertAddress) const { CoreManager::getInstance()->getTimelineListModel()->mAutoSelectAfterCreation = true; shared_ptr core = CoreManager::getInstance()->getCore(); if (!core->videoSupported()) { qWarning() << QStringLiteral("Unable to launch video call. (Video not supported.) Launching audio call..."); - launchAudioCall(sipAddress); + launchAudioCall(sipAddress, prepareTransfertAddress, {}); return; } @@ -193,7 +205,7 @@ void CallsListModel::launchVideoCall (const QString &sipAddress) const { params->enableVideo(true); params->setProxyConfig(core->getDefaultProxyConfig()); CallModel::setRecordFile(params, Utils::coreStringToAppString(address->getUsername())); - core->inviteAddressWithParams(address, params); + CallModel::prepareTransfert(core->inviteAddressWithParams(address, params), prepareTransfertAddress); } ChatRoomModel* CallsListModel::launchSecureChat (const QString &sipAddress) const { @@ -249,7 +261,7 @@ ChatRoomModel* CallsListModel::createChat (const QString &participantAddress) co params->setBackend(linphone::ChatRoomBackend::Basic); std::shared_ptr chatRoom = core->createChatRoom(params, localAddress, participants); - + if( chatRoom != nullptr){ auto timelineList = CoreManager::getInstance()->getTimelineListModel(); auto timeline = timelineList->getTimeline(chatRoom, true); @@ -315,7 +327,7 @@ QVariantMap CallsListModel::createChatRoom(const QString& subject, const int& se address = Utils::interpretUrl(participant); } if( address) - chatRoomParticipants.push_back( address ); + chatRoomParticipants.push_back( address ); } auto proxy = core->getDefaultProxyConfig(); params->enableEncryption(securityLevel>0); @@ -344,7 +356,7 @@ QVariantMap CallsListModel::createChatRoom(const QString& subject, const int& se chatRoom = core->createChatRoom(params, localAddress, chatRoomParticipants); if(chatRoom != nullptr && admins.size() > 0) ChatRoomInitializer::setAdminsAsync(params->getSubject(), params->getBackend(), params->groupEnabled(), admins ); - timeline = timelineList->getTimeline(chatRoom, false); + timeline = timelineList->getTimeline(chatRoom, false); }else{ if(admins.size() > 0){ ChatRoomInitializer::setAdminsSync(chatRoom, admins); @@ -391,6 +403,7 @@ void CallsListModel::terminateCall (const QString& sipAddress) const{ } } } + // ----------------------------------------------------------------------------- static void joinConference (const shared_ptr &call) { diff --git a/linphone-app/src/components/calls/CallsListModel.hpp b/linphone-app/src/components/calls/CallsListModel.hpp index c4c31af17..6e1fa5210 100644 --- a/linphone-app/src/components/calls/CallsListModel.hpp +++ b/linphone-app/src/components/calls/CallsListModel.hpp @@ -33,53 +33,56 @@ class ChatRoomModel; class CoreHandlers; class CallsListModel : public QAbstractListModel { - Q_OBJECT - + Q_OBJECT + public: - CallsListModel (QObject *parent = Q_NULLPTR); - - int rowCount (const QModelIndex &index = QModelIndex()) const override; - - QHash roleNames () const override; - QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override; - - void askForTransfer (CallModel *callModel); - - Q_INVOKABLE void launchAudioCall (const QString &sipAddress, const QHash &headers = {}) const; - Q_INVOKABLE void launchSecureAudioCall (const QString &sipAddress, LinphoneEnums::MediaEncryption encryption, const QHash &headers = {}) const; - Q_INVOKABLE void launchVideoCall (const QString &sipAddress) const; - Q_INVOKABLE ChatRoomModel* launchSecureChat (const QString &sipAddress) const; - Q_INVOKABLE QVariantMap launchChat(const QString &sipAddress, const int& securityLevel) const; - Q_INVOKABLE ChatRoomModel* createChat (const QString &participantAddress) const; - Q_INVOKABLE ChatRoomModel* createChat (const CallModel * ) const; - Q_INVOKABLE bool createSecureChat (const QString& subject, const QString &participantAddress) const; - - Q_INVOKABLE QVariantMap createChatRoom(const QString& subject, const int& securityLevel, const QVariantList& participants, const bool& selectAfterCreation) const; - - Q_INVOKABLE int getRunningCallsNumber () const; - - Q_INVOKABLE void terminateAllCalls () const; - Q_INVOKABLE void terminateCall (const QString& sipAddress) const; - + CallsListModel (QObject *parent = Q_NULLPTR); + + int rowCount (const QModelIndex &index = QModelIndex()) const override; + + QHash roleNames () const override; + QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override; + CallModel *findCallModelFromPeerAddress (const QString &peerAddress) const; + + void askForTransfer (CallModel *callModel); + void askForAttendedTransfer (CallModel *callModel); + + Q_INVOKABLE void launchAudioCall (const QString &sipAddress, const QString& prepareTransfertAddress = "", const QHash &headers = {}) const; + Q_INVOKABLE void launchSecureAudioCall (const QString &sipAddress, LinphoneEnums::MediaEncryption encryption, const QHash &headers = {}, const QString& prepareTransfertAddress = "") const; + Q_INVOKABLE void launchVideoCall (const QString &sipAddress, const QString& prepareTransfertAddress = "") const; + Q_INVOKABLE ChatRoomModel* launchSecureChat (const QString &sipAddress) const; + Q_INVOKABLE QVariantMap launchChat(const QString &sipAddress, const int& securityLevel) const; + Q_INVOKABLE ChatRoomModel* createChat (const QString &participantAddress) const; + Q_INVOKABLE ChatRoomModel* createChat (const CallModel * ) const; + Q_INVOKABLE bool createSecureChat (const QString& subject, const QString &participantAddress) const; + + Q_INVOKABLE QVariantMap createChatRoom(const QString& subject, const int& securityLevel, const QVariantList& participants, const bool& selectAfterCreation) const; + + Q_INVOKABLE int getRunningCallsNumber () const; + + Q_INVOKABLE void terminateAllCalls () const; + Q_INVOKABLE void terminateCall (const QString& sipAddress) const; + signals: - void callRunning (int index, CallModel *callModel); - void callTransferAsked (CallModel *callModel); - - void callMissed (CallModel *callModel); - + void callRunning (int index, CallModel *callModel); + void callTransferAsked (CallModel *callModel); + void callAttendedTransferAsked (CallModel *callModel); + + void callMissed (CallModel *callModel); + private: - bool removeRow (int row, const QModelIndex &parent = QModelIndex()); - bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override; - - void handleCallStateChanged (const std::shared_ptr &call, linphone::Call::State state); - - void addCall (const std::shared_ptr &call); - void removeCall (const std::shared_ptr &call); - void removeCallCb (CallModel *callModel); - - QList mList; - - std::shared_ptr mCoreHandlers; + bool removeRow (int row, const QModelIndex &parent = QModelIndex()); + bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override; + + void handleCallStateChanged (const std::shared_ptr &call, linphone::Call::State state); + + void addCall (const std::shared_ptr &call); + void removeCall (const std::shared_ptr &call); + void removeCallCb (CallModel *callModel); + + QList mList; + + std::shared_ptr mCoreHandlers; }; #endif // CALLS_LIST_MODEL_H_ diff --git a/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp b/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp index e9270cae2..492e0f5cf 100644 --- a/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp +++ b/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp @@ -139,17 +139,19 @@ void ChatRoomProxyModel::compose (const QString& text) { // ----------------------------------------------------------------------------- void ChatRoomProxyModel::loadMoreEntries () { - int count = rowCount(); - int parentCount = sourceModel()->rowCount(); - if (count == mMaxDisplayedEntries) - mMaxDisplayedEntries += EntriesChunkSize; - - if (count + 10 >= parentCount) // Magic number : try to load more entries if near to max event count - mChatRoomModel->loadMoreEntries(); - invalidateFilter(); - count = rowCount() - count; - if (count > 0) - emit moreEntriesLoaded(count); + if(mChatRoomModel ) { + int count = rowCount(); + int parentCount = sourceModel()->rowCount(); + if (count == mMaxDisplayedEntries) + mMaxDisplayedEntries += EntriesChunkSize; + + if (count + 10 >= parentCount) // Magic number : try to load more entries if near to max event count + mChatRoomModel->loadMoreEntries(); + invalidateFilter(); + count = rowCount() - count; + if (count > 0) + emit moreEntriesLoaded(count); + } } void ChatRoomProxyModel::setEntryTypeFilter (int type) { diff --git a/linphone-app/ui/modules/Linphone/Calls/Calls.js b/linphone-app/ui/modules/Linphone/Calls/Calls.js index d942a8fe0..d2c9bb95f 100644 --- a/linphone-app/ui/modules/Linphone/Calls/Calls.js +++ b/linphone-app/ui/modules/Linphone/Calls/Calls.js @@ -30,123 +30,155 @@ // ----------------------------------------------------------------------------- function getParams (call) { - if (!call) { - return - } - - var CallModel = Linphone.CallModel - var status = call.status - - if (status === CallModel.CallStatusConnected) { - var optActions = [] - if (Linphone.SettingsModel.callPauseEnabled) { - optActions.push({ - handler: (function () { call.pausedByUser = true }), - name: qsTr('callPause') - }) - } - - return { - actions: optActions.concat([{ - handler: call.askForTransfer, - name: qsTr('transferCall') - }, { - handler: call.terminate, - name: qsTr('terminateCall') - }]), - component: callActions, - string: 'connected' - } - } - - if (status === CallModel.CallStatusEnded) { - return { - string: 'ended' - } - } - - if (status === CallModel.CallStatusIncoming) { - var optActions = [] - if (Linphone.SettingsModel.videoSupported) { - optActions.push({ - handler: call.acceptWithVideo, - name: qsTr('acceptVideoCall') - }) - } - - return { - actions: [{ - handler: (function () { call.accept() }), - name: qsTr('acceptAudioCall') - }].concat(optActions).concat([{ - handler: call.terminate, - name: qsTr('terminateCall') - }]), - component: callActions, - string: 'incoming' - } - } - - if (status === CallModel.CallStatusOutgoing) { - return { - component: callAction, - handler: call.terminate, - icon: 'hangup', - string: 'outgoing' - } - } - - if (status === CallModel.CallStatusPaused) { - var optActions = [] - if (call.pausedByUser) { - optActions.push({ - handler: (function () { call.pausedByUser = false }), - name: qsTr('resumeCall') - }) - } else if (Linphone.SettingsModel.callPauseEnabled) { - optActions.push({ - handler: (function () { call.pausedByUser = true }), - name: qsTr('callPause') - }) - } - - return { - actions: optActions.concat([{ - handler: call.askForTransfer, - name: qsTr('transferCall') - }, { - handler: call.terminate, - name: qsTr('terminateCall') - }]), - component: callActions, - string: 'paused' - } - } + if (!call) { + return + } + + var CallModel = Linphone.CallModel + var status = call.status + + if (status === CallModel.CallStatusConnected) { + var optActions = [] + if (Linphone.SettingsModel.callPauseEnabled) { + optActions.push({ + handler: (function () { call.pausedByUser = true }), + name: qsTr('callPause') + }) + } + + if (call.transferAddress !== '') { + optActions.push({ + handler: call.askForTransfer, + //: 'COMPLETE ATTENDED TRANSFER' : Title button, design is in uppercase. + name: qsTr('attendedTransferComplete') + }) + } + else { + optActions.push({ + handler: call.askForTransfer, + name: qsTr('transferCall') + }) + optActions.push({ + handler: call.askForAttendedTransfer, + //: 'ATTENDED TRANSFER CALL' : Title button, design is in uppercase. + name: qsTr('attendedTransferCall') + }) + } + + return { + actions: optActions.concat([{ + handler: call.terminate, + name: qsTr('terminateCall') + }]), + component: callActions, + string: 'connected' + } + } + + if (status === CallModel.CallStatusEnded) { + return { + string: 'ended' + } + } + + if (status === CallModel.CallStatusIncoming) { + var optActions = [] + if (Linphone.SettingsModel.videoSupported) { + optActions.push({ + handler: call.acceptWithVideo, + name: qsTr('acceptVideoCall') + }) + } + + return { + actions: [{ + handler: (function () { call.accept() }), + name: qsTr('acceptAudioCall') + }].concat(optActions).concat([{ + handler: call.terminate, + name: qsTr('terminateCall') + }]), + component: callActions, + string: 'incoming' + } + } + + if (status === CallModel.CallStatusOutgoing) { + return { + component: callAction, + handler: call.terminate, + icon: 'hangup', + string: 'outgoing' + } + } + + if (status === CallModel.CallStatusPaused) { + var optActions = [] + if (call.pausedByUser) { + optActions.push({ + handler: (function () { call.pausedByUser = false }), + name: qsTr('resumeCall') + }) + } else if (Linphone.SettingsModel.callPauseEnabled) { + optActions.push({ + handler: (function () { call.pausedByUser = true }), + name: qsTr('callPause') + }) + } + + if (call.transferAddress !== '') { + optActions.push({ + handler: call.askForTransfer, + //: 'COMPLETE ATTENDED TRANSFER' : Title button, design is in uppercase. + name: qsTr('attendedTransferComplete') + }) + } + else { + optActions.push({ + handler: call.askForTransfer, + name: qsTr('transferCall') + }) + optActions.push({ + handler: call.askForAttendedTransfer, + //: 'ATTENDED TRANSFER CALL' : Title button, design is in uppercase. + name: qsTr('attendedTransferCall') + }) + } + + return { + actions: optActions.concat([{ + handler: call.terminate, + name: qsTr('terminateCall') + }]), + component: callActions, + string: 'paused' + } + } } function updateSelectedCall (call, index) { - calls._selectedCall = call - if (index != null) { - calls.currentIndex = index - } + calls._selectedCall = call + if (index != null) { + calls.currentIndex = index + } } function resetSelectedCall () { - updateSelectedCall(null, -1) + updateSelectedCall(null, -1) } function setIndexWithCall (call) { - var count = calls.count - var model = calls.model - - for (var i = 0; i < count; i++) { - if (call === model.data(model.index(i, 0))) { - updateSelectedCall(call, i) - return - } - } - - updateSelectedCall(call, -1) + var count = calls.count + var model = calls.model + + for (var i = 0; i < count; i++) { + if (call === model.data(model.index(i, 0))) { + updateSelectedCall(call, i) + return + } + } + + updateSelectedCall(call, -1) } // ----------------------------------------------------------------------------- @@ -154,23 +186,23 @@ function setIndexWithCall (call) { // ----------------------------------------------------------------------------- function handleCountChanged (count) { - if (count === 0) { - return - } - - var call = calls._selectedCall - - if (call == null) { - if (calls.conferenceModel.count > 0) { - return - } - - var model = calls.model - var index = count - 1 - updateSelectedCall(model.data(model.index(index, 0)), index) - } else { - setIndexWithCall(call) - } + if (count === 0) { + return + } + + var call = calls._selectedCall + + if (call == null) { + if (calls.conferenceModel.count > 0) { + return + } + + var model = calls.model + var index = count - 1 + updateSelectedCall(model.data(model.index(index, 0)), index) + } else { + setIndexWithCall(call) + } } // ----------------------------------------------------------------------------- @@ -178,36 +210,36 @@ function handleCountChanged (count) { // ----------------------------------------------------------------------------- function handleCallRunning (call) { - if (!call.isInConference) { - setIndexWithCall(call) - } + if (!call.isInConference) { + setIndexWithCall(call) + } } function handleRowsAboutToBeRemoved (_, first, last) { - var index = calls.currentIndex - - if (index >= first && index <= last) { - resetSelectedCall() - } + var index = calls.currentIndex + + if (index >= first && index <= last) { + resetSelectedCall() + } } function handleRowsInserted (_, first, last) { - // The last inserted outgoing element become the selected call. - var model = calls.model - for (var index = last; index >= first; index--) { - var call = model.data(model.index(index, 0)) - - if (call.isOutgoing && !call.isInConference) { - updateSelectedCall(call) - return - } - } - - // First received call. - if (first === 0 && model.rowCount() === 1) { - var call = model.data(model.index(0, 0)) - if (!call.isInConference) { - updateSelectedCall(model.data(model.index(0, 0))) - } - } + // The last inserted outgoing element become the selected call. + var model = calls.model + for (var index = last; index >= first; index--) { + var call = model.data(model.index(index, 0)) + + if (call.isOutgoing && !call.isInConference) { + updateSelectedCall(call) + return + } + } + + // First received call. + if (first === 0 && model.rowCount() === 1) { + var call = model.data(model.index(0, 0)) + if (!call.isInConference) { + updateSelectedCall(model.data(model.index(0, 0))) + } + } } diff --git a/linphone-app/ui/modules/Linphone/Chat/Chat.qml b/linphone-app/ui/modules/Linphone/Chat/Chat.qml index 97677b362..0d3210f28 100644 --- a/linphone-app/ui/modules/Linphone/Chat/Chat.qml +++ b/linphone-app/ui/modules/Linphone/Chat/Chat.qml @@ -245,7 +245,7 @@ Rectangle { borderColor: ChatStyle.sendArea.border.color topWidth: ChatStyle.sendArea.border.width - visible: SettingsModel.chatEnabled && !proxyModel.chatRoomModel.hasBeenLeft + visible: SettingsModel.chatEnabled && proxyModel.chatRoomModel && !proxyModel.chatRoomModel.hasBeenLeft DroppableTextArea { id: textArea diff --git a/linphone-app/ui/views/App/Calls/CallsWindow.js b/linphone-app/ui/views/App/Calls/CallsWindow.js index 825b96359..d8210f3e6 100644 --- a/linphone-app/ui/views/App/Calls/CallsWindow.js +++ b/linphone-app/ui/views/App/Calls/CallsWindow.js @@ -28,86 +28,109 @@ // ============================================================================= function handleClosing (close) { - var callsList = Linphone.CallsListModel - - window.detachVirtualWindow() - - if (callsList.getRunningCallsNumber() === 0) { - return - } - - window.attachVirtualWindow(Utils.buildDialogUri('ConfirmDialog'), { - descriptionText: qsTr('acceptClosingDescription') - }, function (status) { - if (status) { - callsList.terminateAllCalls() - window.close() - } - }) - - close.accepted = false + var callsList = Linphone.CallsListModel + + window.detachVirtualWindow() + + if (callsList.getRunningCallsNumber() === 0) { + return + } + + window.attachVirtualWindow(Utils.buildDialogUri('ConfirmDialog'), { + descriptionText: qsTr('acceptClosingDescription') + }, function (status) { + if (status) { + callsList.terminateAllCalls() + window.close() + } + }) + + close.accepted = false } // ----------------------------------------------------------------------------- function openCallSipAddress () { - window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/CallSipAddress.qml')) + window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/CallSipAddress.qml')) } function openConferenceManager (params, exitHandler) { - window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/ConferenceManager.qml'), params, exitHandler) + window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/ConferenceManager.qml'), params, exitHandler) } // ----------------------------------------------------------------------------- // Used to get Component based from Call Status function getContent () { - var call = window.call - if (call == null) { - return conference - } - - var status = call.status - if (status == null) { - return calls.conferenceModel.count > 0 ? conference : null - } - - var CallModel = Linphone.CallModel - if (status === CallModel.CallStatusIncoming) { - return incomingCall - } - - if (status === CallModel.CallStatusOutgoing) { - return outgoingCall - } - - if (status === CallModel.CallStatusEnded) { - return endedCall - } - - return incall + var call = window.call + if (call == null) { + return conference + } + + var status = call.status + if (status == null) { + return calls.conferenceModel.count > 0 ? conference : null + } + + var CallModel = Linphone.CallModel + if (status === CallModel.CallStatusIncoming) { + return incomingCall + } + + if (status === CallModel.CallStatusOutgoing) { + return outgoingCall + } + + if (status === CallModel.CallStatusEnded) { + return endedCall + } + + return incall } // ----------------------------------------------------------------------------- function handleCallTransferAsked (call) { - if (!call) { - return - } + if (!call) { + return + } + + if (call.transferAddress !== '') { + console.debug('Attended transfer to call ' + call.transferAddress) + call.transferToAnother(call.transferAddress) + return + } + + window.detachVirtualWindow() + window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/CallTransfer.qml'), { + call: call, + attended: false + }) +} - window.detachVirtualWindow() - window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/CallTransfer.qml'), { - call: call - }) +function handleCallAttendedTransferAsked (call) { + if (!call) { + return + } + if (call.transferAddress !== '') { + console.debug('Attended transfer to call ' + call.transferAddress) + call.transferToAnother(call.transferAddress) + return + } + window.detachVirtualWindow() + window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/CallTransfer.qml'), { + call: call, + attended: true + }) } function windowMustBeClosed () { - return Linphone.CallsListModel.rowCount() === 0 && !window.virtualWindowVisible + return Linphone.CallsListModel.rowCount() === 0 && !window.virtualWindowVisible } function tryToCloseWindow () { - if (windowMustBeClosed()) { - // Workaround, it's necessary to use a timeout because at last call termination - // a segfault is emit in `QOpenGLContext::functions() const ()`. - Utils.setTimeout(window, 0, function () { windowMustBeClosed() && window.close() }) - } + if (windowMustBeClosed()) { + // Workaround, it's necessary to use a timeout because at last call termination + // a segfault is emit in `QOpenGLContext::functions() const ()`. + Utils.setTimeout(window, 0, function () { windowMustBeClosed() && window.close() }) + } } diff --git a/linphone-app/ui/views/App/Calls/CallsWindow.qml b/linphone-app/ui/views/App/Calls/CallsWindow.qml index 146719414..be7de2bc4 100644 --- a/linphone-app/ui/views/App/Calls/CallsWindow.qml +++ b/linphone-app/ui/views/App/Calls/CallsWindow.qml @@ -253,6 +253,7 @@ Window { Connections { target: CallsListModel onCallTransferAsked: Logic.handleCallTransferAsked(callModel) + onCallAttendedTransferAsked: Logic.handleCallAttendedTransferAsked(callModel) onRowsRemoved: Logic.tryToCloseWindow() } } diff --git a/linphone-app/ui/views/App/Calls/Dialogs/CallSipAddress.qml b/linphone-app/ui/views/App/Calls/Dialogs/CallSipAddress.qml index 98c19aaeb..1916e3a1e 100644 --- a/linphone-app/ui/views/App/Calls/Dialogs/CallSipAddress.qml +++ b/linphone-app/ui/views/App/Calls/Dialogs/CallSipAddress.qml @@ -72,7 +72,7 @@ DialogPlus { secure:0, visible:true, handler: function (entry) { - CallsListModel.launchAudioCall(entry.sipAddress) + CallsListModel.launchAudioCall(entry.sipAddress, "") exit(1) } }] diff --git a/linphone-app/ui/views/App/Calls/Dialogs/CallTransfer.qml b/linphone-app/ui/views/App/Calls/Dialogs/CallTransfer.qml index 549e2cc27..8e1b14703 100644 --- a/linphone-app/ui/views/App/Calls/Dialogs/CallTransfer.qml +++ b/linphone-app/ui/views/App/Calls/Dialogs/CallTransfer.qml @@ -9,93 +9,98 @@ import App.Styles 1.0 // ============================================================================= DialogPlus { - id: callTransfer - - // --------------------------------------------------------------------------- - - property var call - - // --------------------------------------------------------------------------- - - buttons: [ - TextButtonA { - text: qsTr('cancel') - - onClicked: exit(0) - } - ] - - buttonsAlignment: Qt.AlignCenter - descriptionText: qsTr('callTransferDescription') - - height: CallTransferStyle.height + 30 - width: CallTransferStyle.width - - onCallChanged: !call && exit(0) - - // --------------------------------------------------------------------------- - - ColumnLayout { - anchors.fill: parent - spacing: 0 - - // ------------------------------------------------------------------------- - // Contact. - // ------------------------------------------------------------------------- - - Contact { - Layout.fillWidth: true - - entry: SipAddressesModel.getSipAddressObserver(call ? call.fullPeerAddress : '', call ? call.fullLocalAddress : '') - } - - // ------------------------------------------------------------------------- - // Address selector. - // ------------------------------------------------------------------------- - - Item { - Layout.fillHeight: true - Layout.fillWidth: true - - ColumnLayout { - anchors.fill: parent - spacing: CallTransferStyle.spacing - - TextField { - id: filter - - Layout.fillWidth: true - - icon: 'search' - - onTextChanged: sipAddressesModel.setFilter(text) - } - - ScrollableListViewField { - Layout.fillHeight: true - Layout.fillWidth: true - - SipAddressesView { - anchors.fill: parent - - actions: [{ - icon: 'transfer', - handler: function (entry) { - callTransfer.call.transferTo(entry.sipAddress) - exit(1) - } - }] - - genSipAddress: filter.text - - model: SearchSipAddressesModel { - id: sipAddressesModel - } - - onEntryClicked: actions[0].handler(entry) - } - } - } - } - } + id: callTransfer + + // --------------------------------------------------------------------------- + + property var call + property bool attended: false + + // --------------------------------------------------------------------------- + + buttons: [ + TextButtonA { + text: qsTr('cancel') + + onClicked: exit(0) + } + ] + + buttonsAlignment: Qt.AlignCenter + descriptionText: qsTr('callTransferDescription') + + height: CallTransferStyle.height + 30 + width: CallTransferStyle.width + + onCallChanged: !call && exit(0) + + // --------------------------------------------------------------------------- + + ColumnLayout { + anchors.fill: parent + spacing: 0 + + // ------------------------------------------------------------------------- + // Contact. + // ------------------------------------------------------------------------- + + Contact { + Layout.fillWidth: true + + entry: SipAddressesModel.getSipAddressObserver(call ? call.fullPeerAddress : '', call ? call.fullLocalAddress : '') + } + + // ------------------------------------------------------------------------- + // Address selector. + // ------------------------------------------------------------------------- + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + + ColumnLayout { + anchors.fill: parent + spacing: CallTransferStyle.spacing + + TextField { + id: filter + + Layout.fillWidth: true + + icon: 'search' + + onTextChanged: sipAddressesModel.setFilter(text) + } + + ScrollableListViewField { + Layout.fillHeight: true + Layout.fillWidth: true + + SipAddressesView { + anchors.fill: parent + + actions: [{ + icon: 'transfer', + handler: function (entry) { + if (attended) { + var call = CallsListModel.launchAudioCall(entry.sipAddress, callTransfer.call.peerAddress) + } else { + callTransfer.call.transferTo(entry.sipAddress) + } + exit(1) + } + }] + + genSipAddress: filter.text + + model: SearchSipAddressesModel { + id: sipAddressesModel + } + + onEntryClicked: actions[0].handler(entry) + } + } + } + } + } } diff --git a/linphone-app/ui/views/App/Main/Conversation.qml b/linphone-app/ui/views/App/Main/Conversation.qml index 54ee317cf..f4cfb9ba7 100644 --- a/linphone-app/ui/views/App/Main/Conversation.qml +++ b/linphone-app/ui/views/App/Main/Conversation.qml @@ -259,7 +259,6 @@ ColumnLayout { icon: 'group_chat' visible: SettingsModel.outgoingCallsEnabled && conversation.haveMoreThanOneParticipants && conversation.haveLessThanMinParticipantsForCall && !conversation.hasBeenLeft - //onClicked: CallsListModel.launchAudioCall(conversation.chatRoomModel) onClicked: Logic.openConferenceManager({chatRoomModel:conversation.chatRoomModel, autoCall:true}) TooltipArea { //: "Call all chat room's participants" : tooltip on a button for calling all participant in the current chat room diff --git a/linphone-app/ui/views/App/Main/MainWindow.qml b/linphone-app/ui/views/App/Main/MainWindow.qml index 4dcf69a77..9f82a79be 100644 --- a/linphone-app/ui/views/App/Main/MainWindow.qml +++ b/linphone-app/ui/views/App/Main/MainWindow.qml @@ -182,10 +182,10 @@ ApplicationWindow { } } - onLaunchCall: CallsListModel.launchAudioCall(sipAddress) + onLaunchCall: CallsListModel.launchAudioCall(sipAddress, "") onLaunchChat: CallsListModel.launchChat( sipAddress,0 ) onLaunchSecureChat: CallsListModel.launchChat( sipAddress,1 ) - onLaunchVideoCall: CallsListModel.launchVideoCall(sipAddress) + onLaunchVideoCall: CallsListModel.launchVideoCall(sipAddress, "") }