diff --git a/Linphone/core/call/CallCore.cpp b/Linphone/core/call/CallCore.cpp index f41fccca5..3305f1e13 100644 --- a/Linphone/core/call/CallCore.cpp +++ b/Linphone/core/call/CallCore.cpp @@ -28,6 +28,59 @@ DEFINE_ABSTRACT_OBJECT(CallCore) +/***********************************************************************/ + +ZrtpStats ZrtpStats::operator=(ZrtpStats s) { + cipherAlgorithm = s.cipherAlgorithm; + keyAgreementAlgorithm = s.keyAgreementAlgorithm; + hashAlgorithm = s.hashAlgorithm; + authenticationAlgorithm = s.authenticationAlgorithm; + sasAlgorithm = s.sasAlgorithm; + isPostQuantum = s.isPostQuantum; + return *this; +} + +bool ZrtpStats::operator==(ZrtpStats s) { + return s.cipherAlgorithm == cipherAlgorithm && s.keyAgreementAlgorithm == keyAgreementAlgorithm && + s.hashAlgorithm == hashAlgorithm && s.authenticationAlgorithm == authenticationAlgorithm && + s.sasAlgorithm == sasAlgorithm && s.isPostQuantum == isPostQuantum; +} +bool ZrtpStats::operator!=(ZrtpStats s) { + return s.cipherAlgorithm != cipherAlgorithm || s.keyAgreementAlgorithm != keyAgreementAlgorithm || + s.hashAlgorithm != hashAlgorithm || s.authenticationAlgorithm != authenticationAlgorithm || + s.sasAlgorithm != sasAlgorithm || s.isPostQuantum != isPostQuantum; +} + +AudioStats AudioStats::operator=(AudioStats s) { + codec = s.codec; + bandwidth = s.bandwidth; + return *this; +} + +bool AudioStats::operator==(AudioStats s) { + return s.codec == codec && s.bandwidth == bandwidth; +} +bool AudioStats::operator!=(AudioStats s) { + return s.codec != codec || s.bandwidth != bandwidth; +} + +VideoStats VideoStats::operator=(VideoStats s) { + codec = s.codec; + bandwidth = s.bandwidth; + resolution = s.resolution; + fps = s.fps; + return *this; +} + +bool VideoStats::operator==(VideoStats s) { + return s.codec == codec && s.bandwidth == bandwidth && s.resolution == resolution && s.fps == fps; +} +bool VideoStats::operator!=(VideoStats s) { + return s.codec != codec || s.bandwidth != bandwidth || s.resolution != resolution || s.fps != fps; +} + +/***********************************************************************/ + QVariant createDeviceVariant(const QString &id, const QString &name) { QVariantMap map; map.insert("id", id); @@ -72,6 +125,16 @@ CallCore::CallCore(const std::shared_ptr &call) : QObject(nullpt mIsSecured = (mEncryption == LinphoneEnums::MediaEncryption::Zrtp && tokenVerified) || mEncryption == LinphoneEnums::MediaEncryption::Srtp || mEncryption == LinphoneEnums::MediaEncryption::Dtls; + if (mEncryption == LinphoneEnums::MediaEncryption::Zrtp) { + auto stats = call->getStats(linphone::StreamType::Audio); + if (stats) { + mZrtpStats.cipherAlgorithm = Utils::coreStringToAppString(stats->getZrtpCipherAlgo()); + mZrtpStats.keyAgreementAlgorithm = Utils::coreStringToAppString(stats->getZrtpKeyAgreementAlgo()); + mZrtpStats.hashAlgorithm = Utils::coreStringToAppString(stats->getZrtpHashAlgo()); + mZrtpStats.authenticationAlgorithm = Utils::coreStringToAppString(stats->getZrtpAuthTagAlgo()); + mZrtpStats.sasAlgorithm = Utils::coreStringToAppString(stats->getZrtpSasAlgo()); + } + } auto conference = call->getConference(); mIsConference = conference != nullptr; if (mIsConference) { @@ -158,7 +221,6 @@ void CallCore::setSelf(QSharedPointer me) { auto isMismatch = mCallModel->getZrtpCaseMismatch(); mCallModelConnection->invokeToCore([this, verified, isMismatch]() { setTokenVerified(verified); - setIsSecured(verified && !isMismatch); emit tokenVerified(); }); }); @@ -172,6 +234,9 @@ void CallCore::setSelf(QSharedPointer me) { mCallModelConnection->makeConnectToModel(&CallModel::durationChanged, [this](int duration) { mCallModelConnection->invokeToCore([this, duration]() { setDuration(duration); }); }); + mCallModelConnection->makeConnectToModel(&CallModel::qualityUpdated, [this](float quality) { + mCallModelConnection->invokeToCore([this, quality]() { setCurrentQuality(quality); }); + }); mCallModelConnection->makeConnectToModel(&CallModel::speakerVolumeGainChanged, [this](float volume) { mCallModelConnection->invokeToCore([this, volume]() { setSpeakerVolumeGain(volume); }); }); @@ -182,16 +247,11 @@ void CallCore::setSelf(QSharedPointer me) { mCallModelConnection->invokeToCore([this, volume]() { setMicrophoneVolume(volume); }); }); mCallModelConnection->makeConnectToModel( - &CallModel::stateChanged, [this](linphone::Call::State state, const std::string &message) { + &CallModel::stateChanged, + [this](std::shared_ptr call, linphone::Call::State state, const std::string &message) { mCallModelConnection->invokeToCore([this, state, message]() { setState(LinphoneEnums::fromLinphone(state), Utils::coreStringToAppString(message)); }); - }); - mCallModelConnection->makeConnectToModel(&CallModel::statusChanged, [this](linphone::Call::Status status) { - mCallModelConnection->invokeToCore([this, status]() { setStatus(LinphoneEnums::fromLinphone(status)); }); - }); - mCallModelConnection->makeConnectToModel( - &CallModel::stateChanged, [this](linphone::Call::State state, const std::string &message) { double speakerVolume = mSpeakerVolumeGain; double micVolume = mMicrophoneVolumeGain; if (state == linphone::Call::State::StreamsRunning) { @@ -210,6 +270,9 @@ void CallCore::setSelf(QSharedPointer me) { setRecordable(state == linphone::Call::State::StreamsRunning); }); }); + mCallModelConnection->makeConnectToModel(&CallModel::statusChanged, [this](linphone::Call::Status status) { + mCallModelConnection->invokeToCore([this, status]() { setStatus(LinphoneEnums::fromLinphone(status)); }); + }); mCallModelConnection->makeConnectToCore(&CallCore::lSetPaused, [this](bool paused) { mCallModelConnection->invokeToModel([this, paused]() { mCallModel->setPaused(paused); }); }); @@ -246,13 +309,18 @@ void CallCore::setSelf(QSharedPointer me) { setRemoteTokens(remoteTokens); setEncryption(encryption); setIsMismatch(isCaseMismatch); - setIsSecured( - (encryption == LinphoneEnums::MediaEncryption::Zrtp && tokenVerified && !isCaseMismatch)); // || - // encryption == LinphoneEnums::MediaEncryption::Srtp || - // encryption == LinphoneEnums::MediaEncryption::Dtls); - // TODO : change this when api available in sdk - setTokenVerified(tokenVerified); }); + auto mediaEncryption = call->getParams()->getMediaEncryption(); + if (mediaEncryption == linphone::MediaEncryption::ZRTP) { + auto stats = call->getAudioStats(); + ZrtpStats zrtpStats; + zrtpStats.cipherAlgorithm = Utils::coreStringToAppString(stats->getZrtpCipherAlgo()); + zrtpStats.keyAgreementAlgorithm = Utils::coreStringToAppString(stats->getZrtpKeyAgreementAlgo()); + zrtpStats.hashAlgorithm = Utils::coreStringToAppString(stats->getZrtpHashAlgo()); + zrtpStats.authenticationAlgorithm = Utils::coreStringToAppString(stats->getZrtpAuthTagAlgo()); + zrtpStats.sasAlgorithm = Utils::coreStringToAppString(stats->getZrtpSasAlgo()); + mCallModelConnection->invokeToCore([this, zrtpStats]() { setZrtpStats(zrtpStats); }); + } }); mCallModelConnection->makeConnectToCore(&CallCore::lSetSpeakerVolumeGain, [this](float gain) { mCallModelConnection->invokeToModel([this, gain]() { mCallModel->setSpeakerVolumeGain(gain); }); @@ -318,6 +386,52 @@ void CallCore::setSelf(QSharedPointer me) { auto core = VideoSourceDescriptorCore::create(videoSource ? videoSource->clone() : nullptr); mCallModelConnection->invokeToCore([this, core]() { setVideoSourceDescriptor(core); }); }); + mCallModelConnection->makeConnectToModel( + &CallModel::statsUpdated, + [this](const std::shared_ptr &call, const std::shared_ptr &stats) { + if (stats->getType() == linphone::StreamType::Audio) { + AudioStats audioStats; + auto playloadType = call->getParams()->getUsedAudioPayloadType(); + auto codecType = playloadType ? playloadType->getMimeType() : ""; + auto codecRate = playloadType ? playloadType->getClockRate() / 1000 : 0; + audioStats.codec = tr("Codec: %1 / %2 kHz").arg(Utils::coreStringToAppString(codecType)).arg(codecRate); + if (stats) { + audioStats.bandwidth = tr("Bande passante : %1 %2 %3 %4") + .arg("↑") + .arg(stats->getUploadBandwidth()) + .arg("↓") + .arg(stats->getDownloadBandwidth()); + } + setAudioStats(audioStats); + } else if (stats->getType() == linphone::StreamType::Video) { + VideoStats videoStats; + auto params = call->getParams(); + auto playloadType = params->getUsedAudioPayloadType(); + auto codecType = playloadType ? playloadType->getMimeType() : ""; + auto codecRate = playloadType ? playloadType->getClockRate() / 1000 : 0; + videoStats.codec = tr("Codec: %1 / %2 kHz").arg(Utils::coreStringToAppString(codecType)).arg(codecRate); + if (stats) { + videoStats.bandwidth = tr("Bande passante : %1 %2 %3 %4") + .arg("↑") + .arg(stats->getUploadBandwidth()) + .arg("↓") + .arg(stats->getDownloadBandwidth()); + } + auto sentResolution = + params->getSentVideoDefinition() ? params->getSentVideoDefinition()->getName() : ""; + auto receivedResolution = + params->getReceivedVideoDefinition() ? params->getReceivedVideoDefinition()->getName() : ""; + videoStats.resolution = tr("Définition vidéo : %1 %2 %3 %4") + .arg("↑") + .arg(Utils::coreStringToAppString(sentResolution)) + .arg("↓") + .arg(Utils::coreStringToAppString(receivedResolution)); + auto sentFps = params->getSentFramerate(); + auto receivedFps = params->getReceivedFramerate(); + videoStats.fps = tr("FPS : %1 %2 %3 %4").arg("↑").arg(sentFps).arg("↓").arg(receivedFps); + setVideoStats(videoStats); + } + }); } QString CallCore::getPeerAddress() const { @@ -378,7 +492,7 @@ void CallCore::setLastErrorMessage(const QString &message) { } } -int CallCore::getDuration() { +int CallCore::getDuration() const { return mDuration; } @@ -389,6 +503,17 @@ void CallCore::setDuration(int duration) { } } +float CallCore::getCurrentQuality() const { + return mQuality; +} + +void CallCore::setCurrentQuality(float quality) { + if (mQuality != quality) { + mQuality = quality; + emit qualityChanged(mQuality); + } +} + bool CallCore::getSpeakerMuted() const { return mSpeakerMuted; } @@ -445,17 +570,6 @@ void CallCore::setTokenVerified(bool verified) { } } -bool CallCore::isSecured() const { - return mIsSecured; -} - -void CallCore::setIsSecured(bool secured) { - if (mIsSecured != secured) { - mIsSecured = secured; - emit securityUpdated(); - } -} - bool CallCore::isMismatch() const { return mIsMismatch; } @@ -515,6 +629,10 @@ LinphoneEnums::MediaEncryption CallCore::getEncryption() const { return mEncryption; } +QString CallCore::getEncryptionString() const { + return LinphoneEnums::toString(mEncryption); +} + void CallCore::setEncryption(LinphoneEnums::MediaEncryption encryption) { if (mEncryption != encryption) { mEncryption = encryption; @@ -631,3 +749,36 @@ void CallCore::setConferenceVideoLayout(LinphoneEnums::ConferenceLayout layout) std::shared_ptr CallCore::getModel() const { return mCallModel; } + +ZrtpStats CallCore::getZrtpStats() const { + return mZrtpStats; +} + +void CallCore::setZrtpStats(ZrtpStats stats) { + if (stats != mZrtpStats) { + mZrtpStats = stats; + emit zrtpStatsChanged(); + } +} + +AudioStats CallCore::getAudioStats() const { + return mAudioStats; +} + +void CallCore::setAudioStats(AudioStats stats) { + if (stats != mAudioStats) { + mAudioStats = stats; + emit audioStatsChanged(); + } +} + +VideoStats CallCore::getVideoStats() const { + return mVideoStats; +} + +void CallCore::setVideoStats(VideoStats stats) { + if (stats != mVideoStats) { + mVideoStats = stats; + emit videoStatsChanged(); + } +} \ No newline at end of file diff --git a/Linphone/core/call/CallCore.hpp b/Linphone/core/call/CallCore.hpp index 8fc1d7316..cce46c929 100644 --- a/Linphone/core/call/CallCore.hpp +++ b/Linphone/core/call/CallCore.hpp @@ -31,6 +31,61 @@ #include #include +struct ZrtpStats { + Q_GADGET + + Q_PROPERTY(QString cipherAlgo MEMBER cipherAlgorithm) + Q_PROPERTY(QString keyAgreementAlgo MEMBER keyAgreementAlgorithm) + Q_PROPERTY(QString hashAlgo MEMBER hashAlgorithm) + Q_PROPERTY(QString authenticationAlgo MEMBER authenticationAlgorithm) + Q_PROPERTY(QString sasAlgo MEMBER sasAlgorithm) + Q_PROPERTY(bool isPostQuantum MEMBER isPostQuantum) +public: + bool isPostQuantum = false; + QString cipherAlgorithm; + QString keyAgreementAlgorithm; + QString hashAlgorithm; + QString authenticationAlgorithm; + QString sasAlgorithm; + + ZrtpStats operator=(ZrtpStats s); + bool operator==(ZrtpStats s); + bool operator!=(ZrtpStats s); +}; + +struct AudioStats { + Q_GADGET + Q_PROPERTY(QString codec MEMBER codec) + Q_PROPERTY(QString bandwidth MEMBER bandwidth) + +public: + QString codec; + QString bandwidth; + + AudioStats operator=(AudioStats s); + + bool operator==(AudioStats s); + bool operator!=(AudioStats s); +}; + +struct VideoStats { + Q_GADGET + Q_PROPERTY(QString codec MEMBER codec) + Q_PROPERTY(QString bandwidth MEMBER bandwidth) + Q_PROPERTY(QString resolution MEMBER resolution) + Q_PROPERTY(QString fps MEMBER fps) + +public: + QString codec; + QString bandwidth; + QString resolution; + QString fps; + + VideoStats operator=(VideoStats s); + bool operator==(VideoStats s); + bool operator!=(VideoStats s); +}; + class CallCore : public QObject, public AbstractObject { Q_OBJECT @@ -40,15 +95,16 @@ class CallCore : public QObject, public AbstractObject { Q_PROPERTY(LinphoneEnums::CallState state READ getState NOTIFY stateChanged) Q_PROPERTY(QString lastErrorMessage READ getLastErrorMessage NOTIFY lastErrorMessageChanged) Q_PROPERTY(int duration READ getDuration NOTIFY durationChanged) + Q_PROPERTY(int quality READ getCurrentQuality NOTIFY qualityChanged) Q_PROPERTY(bool speakerMuted READ getSpeakerMuted WRITE lSetSpeakerMuted NOTIFY speakerMutedChanged) Q_PROPERTY(bool microphoneMuted READ getMicrophoneMuted WRITE lSetMicrophoneMuted NOTIFY microphoneMutedChanged) Q_PROPERTY(bool paused READ getPaused WRITE lSetPaused NOTIFY pausedChanged) Q_PROPERTY(QString peerAddress READ getPeerAddress CONSTANT) Q_PROPERTY(QString localAddress READ getLocalAddress CONSTANT) Q_PROPERTY(bool tokenVerified READ getTokenVerified WRITE setTokenVerified NOTIFY securityUpdated) - // Q_PROPERTY(bool isSecured READ isSecured WRITE setIsSecured NOTIFY securityUpdated) Q_PROPERTY(bool isMismatch READ isMismatch WRITE setIsMismatch NOTIFY securityUpdated) Q_PROPERTY(LinphoneEnums::MediaEncryption encryption READ getEncryption NOTIFY securityUpdated) + Q_PROPERTY(QString encryptionString READ getEncryptionString NOTIFY securityUpdated) Q_PROPERTY(QString localToken READ getLocalToken WRITE setLocalToken MEMBER mLocalToken NOTIFY localTokenChanged) Q_PROPERTY(QStringList remoteTokens WRITE setRemoteTokens MEMBER mRemoteTokens NOTIFY remoteTokensChanged) Q_PROPERTY( @@ -71,6 +127,9 @@ class CallCore : public QObject, public AbstractObject { Q_PROPERTY(VideoSourceDescriptorGui *videoSourceDescriptor READ getVideoSourceDescriptorGui WRITE lSetVideoSourceDescriptor NOTIFY videoSourceDescriptorChanged) + Q_PROPERTY(ZrtpStats zrtpStats READ getZrtpStats WRITE setZrtpStats NOTIFY zrtpStatsChanged) + Q_PROPERTY(AudioStats audioStats READ getAudioStats WRITE setAudioStats NOTIFY audioStatsChanged) + Q_PROPERTY(VideoStats videoStats READ getVideoStats WRITE setVideoStats NOTIFY videoStatsChanged) public: // Should be call from model Thread. Will be automatically in App thread after initialization @@ -94,9 +153,12 @@ public: QString getLastErrorMessage() const; void setLastErrorMessage(const QString &message); - int getDuration(); + int getDuration() const; void setDuration(int duration); + float getCurrentQuality() const; + void setCurrentQuality(float quality); + bool getSpeakerMuted() const; void setSpeakerMuted(bool isMuted); @@ -109,9 +171,6 @@ public: bool getTokenVerified() const; void setTokenVerified(bool verified); - bool isSecured() const; - void setIsSecured(bool secured); - bool isMismatch() const; void setIsMismatch(bool mismatch); @@ -127,6 +186,7 @@ public: void setRemoteTokens(const QStringList &Tokens); LinphoneEnums::MediaEncryption getEncryption() const; + QString getEncryptionString() const; void setEncryption(LinphoneEnums::MediaEncryption encryption); bool getRemoteVideoEnabled() const; @@ -166,12 +226,23 @@ public: void setVideoSourceDescriptor(QSharedPointer core); std::shared_ptr getModel() const; + + ZrtpStats getZrtpStats() const; + void setZrtpStats(ZrtpStats stats); + + AudioStats getAudioStats() const; + void setAudioStats(AudioStats stats); + + VideoStats getVideoStats() const; + void setVideoStats(VideoStats stats); + signals: void statusChanged(LinphoneEnums::CallStatus status); void stateChanged(LinphoneEnums::CallState state); void dirChanged(LinphoneEnums::CallDir dir); void lastErrorMessageChanged(); void durationChanged(int duration); + void qualityChanged(float quality); void speakerMutedChanged(); void microphoneMutedChanged(); void pausedChanged(); @@ -191,6 +262,9 @@ signals: void conferenceChanged(); void conferenceVideoLayoutChanged(); void videoSourceDescriptorChanged(); + void zrtpStatsChanged(); + void audioStatsChanged(); + void videoStatsChanged(); // Linphone commands void lAccept(bool withVideo); // Accept an incoming call @@ -250,6 +324,7 @@ private: bool mIsSecured = false; bool mIsMismatch = false; int mDuration = 0; + float mQuality = 0; bool mSpeakerMuted = false; bool mMicrophoneMuted = false; bool mLocalVideoEnabled = false; @@ -266,6 +341,9 @@ private: float mMicrophoneVolume; float mMicrophoneVolumeGain; QSharedPointer> mCallModelConnection; + ZrtpStats mZrtpStats; + AudioStats mAudioStats; + VideoStats mVideoStats; DECLARE_ABSTRACT_OBJECT }; diff --git a/Linphone/data/image/cell-signal-full.svg b/Linphone/data/image/cell-signal-full.svg index 8a04f8fed..2149b9e0d 100644 --- a/Linphone/data/image/cell-signal-full.svg +++ b/Linphone/data/image/cell-signal-full.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/Linphone/data/image/cell-signal-high.svg b/Linphone/data/image/cell-signal-high.svg new file mode 100644 index 000000000..0db07907d --- /dev/null +++ b/Linphone/data/image/cell-signal-high.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Linphone/data/image/cell-signal-low.svg b/Linphone/data/image/cell-signal-low.svg index fac7f934c..dd093bcc8 100644 --- a/Linphone/data/image/cell-signal-low.svg +++ b/Linphone/data/image/cell-signal-low.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/Linphone/data/image/cell-signal-medium.svg b/Linphone/data/image/cell-signal-medium.svg new file mode 100644 index 000000000..2a986fce4 --- /dev/null +++ b/Linphone/data/image/cell-signal-medium.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Linphone/data/image/cell-signal-none.svg b/Linphone/data/image/cell-signal-none.svg new file mode 100644 index 000000000..2b1d4ba4f --- /dev/null +++ b/Linphone/data/image/cell-signal-none.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Linphone/data/image/cell-signal-slash.svg b/Linphone/data/image/cell-signal-slash.svg new file mode 100644 index 000000000..e21dab647 --- /dev/null +++ b/Linphone/data/image/cell-signal-slash.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Linphone/model/call/CallModel.cpp b/Linphone/model/call/CallModel.cpp index 9109ff0ff..8a9757755 100644 --- a/Linphone/model/call/CallModel.cpp +++ b/Linphone/model/call/CallModel.cpp @@ -35,6 +35,7 @@ CallModel::CallModel(const std::shared_ptr &call, QObject *paren mDurationTimer.setInterval(1000); mDurationTimer.setSingleShot(false); connect(&mDurationTimer, &QTimer::timeout, this, [this]() { this->durationChanged(mMonitor->getDuration()); }); + connect(&mDurationTimer, &QTimer::timeout, this, [this]() { this->qualityUpdated(mMonitor->getCurrentQuality()); }); mDurationTimer.start(); mMicroVolumeTimer.setInterval(50); @@ -302,6 +303,18 @@ void CallModel::setConference(const std::shared_ptr &confe } } +std::shared_ptr CallModel::getAudioStats() const { + return mMonitor->getAudioStats(); +} + +std::shared_ptr CallModel::getVideoStats() const { + return mMonitor->getVideoStats(); +} + +std::shared_ptr CallModel::getTextStats() const { + return mMonitor->getTextStats(); +} + LinphoneEnums::ConferenceLayout CallModel::getConferenceVideoLayout() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return LinphoneEnums::fromLinphone(mMonitor->getParams()->getConferenceVideoLayout()); @@ -416,7 +429,7 @@ void CallModel::onStateChanged(const std::shared_ptr &call, setConference(call->getConference()); updateConferenceVideoLayout(); } - emit stateChanged(state, message); + emit stateChanged(call, state, message); } void CallModel::onStatusChanged(const std::shared_ptr &call, linphone::Call::Status status) { diff --git a/Linphone/model/call/CallModel.hpp b/Linphone/model/call/CallModel.hpp index 4c6ea38c1..d245a89de 100644 --- a/Linphone/model/call/CallModel.hpp +++ b/Linphone/model/call/CallModel.hpp @@ -55,6 +55,9 @@ public: void setOutputAudioDevice(const std::shared_ptr &id); std::shared_ptr getOutputAudioDevice() const; void setConference(const std::shared_ptr &conference); + std::shared_ptr getAudioStats() const; + std::shared_ptr getVideoStats() const; + std::shared_ptr getTextStats() const; void setPaused(bool paused); void transferTo(const std::shared_ptr &address); @@ -89,6 +92,7 @@ signals: void microphoneMutedChanged(bool isMuted); void speakerMutedChanged(bool isMuted); void durationChanged(int); + void qualityUpdated(float quality); void microphoneVolumeChanged(float); void pausedChanged(bool paused); void remoteVideoEnabledChanged(bool remoteVideoEnabled); @@ -156,7 +160,8 @@ signals: void receiveMasterKeyChanged(const std::shared_ptr &call, const std::string &receiveMasterKey); void infoMessageReceived(const std::shared_ptr &call, const std::shared_ptr &message); - void stateChanged(linphone::Call::State state, const std::string &message); + void + stateChanged(const std::shared_ptr &call, linphone::Call::State state, const std::string &message); void statusChanged(linphone::Call::Status status); void dirChanged(linphone::Call::Dir dir); void statsUpdated(const std::shared_ptr &call, diff --git a/Linphone/tool/LinphoneEnums.cpp b/Linphone/tool/LinphoneEnums.cpp index bbeb8f65f..3100e1914 100644 --- a/Linphone/tool/LinphoneEnums.cpp +++ b/Linphone/tool/LinphoneEnums.cpp @@ -54,6 +54,20 @@ linphone::MediaEncryption LinphoneEnums::toLinphone(const LinphoneEnums::MediaEn LinphoneEnums::MediaEncryption LinphoneEnums::fromLinphone(const linphone::MediaEncryption &data) { return static_cast(data); } +QString LinphoneEnums::toString(LinphoneEnums::MediaEncryption encryption) { + switch (encryption) { + case LinphoneEnums::MediaEncryption::Dtls: + return "DTLS"; + case LinphoneEnums::MediaEncryption::None: + return "None"; + case LinphoneEnums::MediaEncryption::Srtp: + return "SRTP"; + case LinphoneEnums::MediaEncryption::Zrtp: + return "ZRTP"; + default: + return QString(); + } +} linphone::Friend::Capability LinphoneEnums::toLinphone(const LinphoneEnums::FriendCapability &data) { return static_cast(data); diff --git a/Linphone/tool/LinphoneEnums.hpp b/Linphone/tool/LinphoneEnums.hpp index cddb2f198..8875588b6 100644 --- a/Linphone/tool/LinphoneEnums.hpp +++ b/Linphone/tool/LinphoneEnums.hpp @@ -44,6 +44,7 @@ Q_ENUM_NS(MediaEncryption) linphone::MediaEncryption toLinphone(const LinphoneEnums::MediaEncryption &encryption); LinphoneEnums::MediaEncryption fromLinphone(const linphone::MediaEncryption &encryption); +QString toString(LinphoneEnums::MediaEncryption encryption); enum class FriendCapability { None = int(linphone::Friend::Capability::None), diff --git a/Linphone/view/App/CallsWindow.qml b/Linphone/view/App/CallsWindow.qml index 812f0d8da..0523bc1c7 100644 --- a/Linphone/view/App/CallsWindow.qml +++ b/Linphone/view/App/CallsWindow.qml @@ -116,16 +116,21 @@ AppWindow { target: call && call.core onRemoteVideoEnabledChanged: console.log("remote video enabled", call.core.remoteVideoEnabled) onSecurityUpdated: { - if (call.core.isSecured) { + if (call.core.encryption != LinphoneEnums.MediaEncryption.Zrtp || call.core.tokenVerified) { zrtpValidation.close() } else if(call.core.encryption === LinphoneEnums.MediaEncryption.Zrtp) { zrtpValidation.open() - // mainWindow.attachVirtualWindow(Utils.buildLinphoneDialogUri('ZrtpTokenAuthenticationDialog'), {call:callModel}) } } } + Component.onCompleted: { + if(call.core.encryption === LinphoneEnums.MediaEncryption.Zrtp && (!call.core.tokenVerified || call.core.isMismatch)) { + zrtpValidation.open() + } + } + Timer { id: autoCloseWindow interval: 2000 @@ -237,7 +242,7 @@ AppWindow { } } } - +/************************* CONTENT ********************************/ Rectangle { anchors.fill: parent color: DefaultStyle.grey_900 @@ -387,6 +392,10 @@ AppWindow { anchors.fill: parent hoverEnabled: true cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor + onClicked: { + rightPanel.visible = true + rightPanel.replace(encryptionPanel) + } } } Item { @@ -397,8 +406,31 @@ AppWindow { Item { Layout.fillWidth: true } - Item { - //TODO : network quality display + EffectImage { + Layout.preferredWidth: 32 * DefaultStyle.dp + Layout.preferredHeight: 32 * DefaultStyle.dp + Layout.rightMargin: 30 * DefaultStyle.dp + property int quality: mainWindow.call ? mainWindow.call.core.quality : 0 + imageSource: quality >= 4 + ? AppIcons.cellSignalFull + : quality >= 3 + ? AppIcons.cellSignalMedium + : quality >= 2 + ? AppIcons.cellSignalLow + : AppIcons.cellSignalNone + colorizationColor: DefaultStyle.grey_0 + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor + onClicked: { + if (rightPanel.visible) rightPanel.visible = false + else { + rightPanel.visible = true + rightPanel.replace(statsPanel) + } + } + } } } @@ -882,12 +914,227 @@ AppWindow { } } } + Component { + id: encryptionPanel + ColumnLayout { + Control.StackView.onActivated: { + rightPanel.headerTitleText = qsTr("Chiffrement") + } + RoundedBackgroundControl { + Layout.fillWidth: true + leftPadding: 16 * DefaultStyle.dp + rightPadding: 16 * DefaultStyle.dp + topPadding: 13 * DefaultStyle.dp + bottomPadding: 13 * DefaultStyle.dp + + Layout.topMargin: 13 * DefaultStyle.dp + Layout.leftMargin: 16 * DefaultStyle.dp + Layout.rightMargin: 16 * DefaultStyle.dp + + contentItem: ColumnLayout { + spacing: 12 * DefaultStyle.dp + Text { + text: qsTr("Chiffrement :") + Layout.alignment: Qt.AlignHCenter + font { + pixelSize: 12 * DefaultStyle.dp + weight: 700 * DefaultStyle.dp + } + } + ColumnLayout { + Layout.alignment: Qt.AlignHCenter + spacing: 7 * DefaultStyle.dp + Text { + property bool isPostQuantum: mainWindow.call.core.encryption === LinphoneEnums.MediaEncryption.Zrtp && mainWindow.call.core.zrtpStats.isPostQuantum + text: qsTr("Chiffrement du média : %1%2").arg(isPostQuantum ? "post Quantum " : "").arg(mainWindow.call.core.encryptionString) + Layout.alignment: Qt.AlignHCenter + font { + pixelSize: 12 * DefaultStyle.dp + weight: 500 * DefaultStyle.dp + } + } + ColumnLayout { + visible: mainWindow.call && mainWindow.call.core.encryption === LinphoneEnums.MediaEncryption.Zrtp + Text { + text: qsTr("Cipher algorithm : %1").arg(mainWindow.call && mainWindow.call.core.zrtpStats.cipherAlgo) + Layout.alignment: Qt.AlignHCenter + font { + pixelSize: 12 * DefaultStyle.dp + weight: 500 * DefaultStyle.dp + } + } + Text { + text: qsTr("Key agreement algorithm : %1").arg(mainWindow.call && mainWindow.call.core.zrtpStats.keyAgreementAlgo) + Layout.alignment: Qt.AlignHCenter + font { + pixelSize: 12 * DefaultStyle.dp + weight: 500 * DefaultStyle.dp + } + } + Text { + text: qsTr("Hash algorithm : %1").arg(mainWindow.call && mainWindow.call.core.zrtpStats.hashAlgo) + Layout.alignment: Qt.AlignHCenter + font { + pixelSize: 12 * DefaultStyle.dp + weight: 500 * DefaultStyle.dp + } + } + Text { + text: qsTr("Authentication algorithm : %1").arg(mainWindow.call && mainWindow.call.core.zrtpStats.authenticationAlgo) + Layout.alignment: Qt.AlignHCenter + font { + pixelSize: 12 * DefaultStyle.dp + weight: 500 * DefaultStyle.dp + } + } + Text { + text: qsTr("SAS algorithm : %1").arg(mainWindow.call && mainWindow.call.core.zrtpStats.sasAlgo) + Layout.alignment: Qt.AlignHCenter + font { + pixelSize: 12 * DefaultStyle.dp + weight: 500 * DefaultStyle.dp + } + } + } + } + } + } + Item{Layout.fillHeight: true} + Button { + visible: mainWindow.call && mainWindow.call.core.encryption === LinphoneEnums.MediaEncryption.Zrtp + Layout.fillWidth: true + text: qsTr("Validation chiffrement") + onClicked: zrtpValidation.open() + Layout.bottomMargin: 13 * DefaultStyle.dp + Layout.leftMargin: 16 * DefaultStyle.dp + Layout.rightMargin: 16 * DefaultStyle.dp + leftPadding: 20 * DefaultStyle.dp + rightPadding: 20 * DefaultStyle.dp + topPadding: 11 * DefaultStyle.dp + bottomPadding: 11 * DefaultStyle.dp + } + } + } + Component { + id: statsPanel + ColumnLayout { + spacing: 20 * DefaultStyle.dp + Control.StackView.onActivated: { + rightPanel.headerTitleText = qsTr("Statistiques") + } + RoundedBackgroundControl { + Layout.fillWidth: true + leftPadding: 16 * DefaultStyle.dp + rightPadding: 16 * DefaultStyle.dp + topPadding: 13 * DefaultStyle.dp + bottomPadding: 13 * DefaultStyle.dp + + Layout.topMargin: 13 * DefaultStyle.dp + Layout.leftMargin: 16 * DefaultStyle.dp + Layout.rightMargin: 16 * DefaultStyle.dp + + contentItem: ColumnLayout { + spacing: 12 * DefaultStyle.dp + Layout.alignment: Qt.AlignHCenter + Text { + text: qsTr("Audio") + Layout.alignment: Qt.AlignHCenter + font { + pixelSize: 12 * DefaultStyle.dp + weight: 700 * DefaultStyle.dp + } + } + ColumnLayout { + spacing: 7 * DefaultStyle.dp + Layout.alignment: Qt.AlignHCenter + Text { + text: mainWindow.call ? mainWindow.call.core.audioStats.codec : "" + Layout.alignment: Qt.AlignHCenter + font { + pixelSize: 12 * DefaultStyle.dp + weight: 500 * DefaultStyle.dp + } + } + Text { + text: mainWindow.call ? mainWindow.call.core.audioStats.bandwidth : "" + Layout.alignment: Qt.AlignHCenter + font { + pixelSize: 12 * DefaultStyle.dp + weight: 500 * DefaultStyle.dp + } + } + } + } + } + RoundedBackgroundControl { + Layout.fillWidth: true + leftPadding: 16 * DefaultStyle.dp + rightPadding: 16 * DefaultStyle.dp + topPadding: 13 * DefaultStyle.dp + bottomPadding: 13 * DefaultStyle.dp + + Layout.topMargin: 13 * DefaultStyle.dp + Layout.leftMargin: 16 * DefaultStyle.dp + Layout.rightMargin: 16 * DefaultStyle.dp + + contentItem: ColumnLayout { + spacing: 12 * DefaultStyle.dp + Layout.alignment: Qt.AlignHCenter + Text { + text: qsTr("Vidéo") + Layout.alignment: Qt.AlignHCenter + font { + pixelSize: 12 * DefaultStyle.dp + weight: 700 * DefaultStyle.dp + } + } + ColumnLayout { + spacing: 7 * DefaultStyle.dp + Layout.alignment: Qt.AlignHCenter + Text { + text: mainWindow.call ? mainWindow.call.core.videoStats.codec : "" + Layout.alignment: Qt.AlignHCenter + font { + pixelSize: 12 * DefaultStyle.dp + weight: 500 * DefaultStyle.dp + } + } + Text { + text: mainWindow.call ? mainWindow.call.core.videoStats.bandwidth : "" + Layout.alignment: Qt.AlignHCenter + font { + pixelSize: 12 * DefaultStyle.dp + weight: 500 * DefaultStyle.dp + } + } + Text { + text: mainWindow.call ? mainWindow.call.core.videoStats.resolution : "" + Layout.alignment: Qt.AlignHCenter + font { + pixelSize: 12 * DefaultStyle.dp + weight: 500 * DefaultStyle.dp + } + } + Text { + text: mainWindow.call ? mainWindow.call.core.videoStats.fps : "" + Layout.alignment: Qt.AlignHCenter + font { + pixelSize: 12 * DefaultStyle.dp + weight: 500 * DefaultStyle.dp + } + } + } + } + } + Item{Layout.fillHeight: true} + } + } } Component { id: waitingRoom WaitingRoom { id: waitingRoomIn - Layout.alignment: Qt.AlignCenter + Layout.alignment: Qt.AlignCenter onSettingsButtonCheckedChanged: { if (settingsButtonChecked) { rightPanel.visible = true diff --git a/Linphone/view/Item/Call/OngoingCallRightPanel.qml b/Linphone/view/Item/Call/OngoingCallRightPanel.qml index 9ee9a14d7..0e53f87a5 100644 --- a/Linphone/view/Item/Call/OngoingCallRightPanel.qml +++ b/Linphone/view/Item/Call/OngoingCallRightPanel.qml @@ -56,7 +56,7 @@ Control.Page { Layout.fillHeight: true Layout.alignment: Qt.AlignVCenter verticalAlignment: Text.AlignVCenter - color: mainWindow.conference ? DefaultStyle.main1_500_main : DefaultStyle.main2_700 + color: DefaultStyle.main1_500_main font { pixelSize: 16 * DefaultStyle.dp weight: 800 * DefaultStyle.dp diff --git a/Linphone/view/Item/ZrtpTokenAuthenticationDialog.qml b/Linphone/view/Item/ZrtpTokenAuthenticationDialog.qml index 4f3d5acf2..cc37302d0 100644 --- a/Linphone/view/Item/ZrtpTokenAuthenticationDialog.qml +++ b/Linphone/view/Item/ZrtpTokenAuthenticationDialog.qml @@ -28,9 +28,11 @@ Dialog { target: call && call.core onStatusChanged: if (status === CallModel.CallStatusEnded) close() onSecurityUpdated: { - if (!mainItem.isTokenVerified) { - mainItem.securityError = true - } else close() + if (mainItem.isTokenVerified) { + close() + // mainItem.securityError = true + // } else close() + } } onTokenVerified: { if (!mainItem.isTokenVerified) { @@ -58,8 +60,8 @@ Dialog { anchors.horizontalCenter: parent.horizontalCenter Item { // spacing: 14 * DefaultStyle.dp - width: childrenRect.width - height: childrenRect.height + Layout.Layout.preferredWidth: childrenRect.width + Layout.Layout.preferredHeight: childrenRect.height Layout.Layout.fillWidth: true Image { id: trustShield @@ -221,7 +223,7 @@ Dialog { hoverEnabled: true cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor onClicked: { - console.log("check token", modelData) + console.log("CHECK TOKEN", modelData) if(mainItem.call) mainItem.call.core.lCheckAuthenticationTokenSelected(modelData) } } diff --git a/Linphone/view/Style/AppIcons.qml b/Linphone/view/Style/AppIcons.qml index 6f145c47f..f2d880991 100644 --- a/Linphone/view/Style/AppIcons.qml +++ b/Linphone/view/Style/AppIcons.qml @@ -104,4 +104,9 @@ QtObject { property string detective: "image://internal/detective.svg" property string warningCircle: "image://internal/warning-circle.svg" property string fullscreen: "image://internal/fullscreen.svg" + property string cellSignalFull: "image://internal/cell-signal-full.svg" + property string cellSignalHigh: "image://internal/cell-signal-high.svg" + property string cellSignalMedium: "image://internal/cell-signal-medium.svg" + property string cellSignalLow: "image://internal/cell-signal-low.svg" + property string cellSignalNone: "image://internal/cell-signal-none.svg" }