diff --git a/CHANGELOG.md b/CHANGELOG.md
index 46bc5ad4b..f0cd45c3e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- '[ui] logs_max_size' : option to set the max size of one log file.
+- '[ui] notification_origin' : option to specify where to display notifications (only supported: 0=bottom-right and 1=top-right).
+- '[ui] systray_notification_blink' : option to activate/deactivate the blinking systray on unread notifications.
+- '[ui] systray_notification_global' : option to display notification number from all accounts or only selected.
+- '[ui] systray_notification_filtered' : option to filter the notification number (not count if chat room is muted).
+- Keyboard shortcuts:
+ * 'Ctrl+Shift+W' (or V): accept with video the last incoming call.
+ * 'Ctrl+Shift+A': accept without video the last incoming call.
+ * 'Ctrl+Shift+D': terminate the last call.
+ * 'Ctrl+Shift+E': Enable/disable echo cancellation.
+ * 'Ctrl+Shift+L': Unmute/Mute speaker.
+ * 'Ctrl+Shift+M': Unmute/Mute microphone.
+- Request application focus when hovering a call notification.
## 5.2.1 - 2024-02-01
diff --git a/linphone-app/assets/languages/zh_CN.ts b/linphone-app/assets/languages/zh_CN.ts
index 60beba189..d37e5130d 100644
--- a/linphone-app/assets/languages/zh_CN.ts
+++ b/linphone-app/assets/languages/zh_CN.ts
@@ -2076,7 +2076,7 @@
checkForUpdates
'Check for updates' : Item menu for checking updates
-
+ 检测软件更新
recordings
@@ -2106,7 +2106,7 @@
checkForUpdates
'Check for updates' : Item menu for checking updates
-
+ 检测软件更新
recordings
diff --git a/linphone-app/src/components/calls/CallsListModel.cpp b/linphone-app/src/components/calls/CallsListModel.cpp
index f11abb91f..f52572d3a 100644
--- a/linphone-app/src/components/calls/CallsListModel.cpp
+++ b/linphone-app/src/components/calls/CallsListModel.cpp
@@ -501,6 +501,55 @@ void CallsListModel::terminateCall (const QString& sipAddress) const{
}
}
+QSharedPointer CallsListModel::getLastCall(bool incoming) const{
+ QSharedPointer lastCall = nullptr;
+ time_t lastTime = 0;
+ auto item = mList.rbegin();
+ while(item != mList.rend() && !lastCall) {
+ auto call = item->objectCast();
+ if(!incoming || call->getStatus() == CallModel::CallStatusIncoming) {
+ lastCall = call;
+ }
+ }
+ return lastCall;
+}
+
+QSharedPointer CallsListModel::getCallModel(const std::shared_ptr &call) const{
+ QSharedPointer callModel;
+ if(call) {
+ auto itCall = std::find_if(mList.begin(), mList.end(), [call](const QSharedPointer &item){
+ auto c = item.objectCast();
+ return c && c->getCall() == call;
+ });
+ if( itCall != mList.end())
+ callModel = itCall->objectCast();
+ }
+ return callModel;
+}
+
+void CallsListModel::acceptLastIncomingCall(bool video) {
+ auto call = getLastCall(true);
+ if(call) {
+ if(video) call->acceptWithVideo();
+ else call->accept();
+ }
+}
+
+void CallsListModel::terminateLastCall() {
+ auto call = getLastCall(false); // Allow to terminate last outgoing call
+ if(call) call->terminate();
+}
+
+void CallsListModel::toggleMuteSpeaker() {
+ auto currentCall = getCallModel(CoreManager::getInstance()->getCore()->getCurrentCall());
+ if(currentCall) currentCall->setSpeakerMuted(!currentCall->getSpeakerMuted());
+}
+
+void CallsListModel::toggleMuteMicrophone() {
+ auto currentCall = getCallModel(CoreManager::getInstance()->getCore()->getCurrentCall());
+ if(currentCall) currentCall->setMicroMuted(!currentCall->getMicroMuted());
+}
+
std::list> CallsListModel::getCallHistory(const QString& peerAddress, const QString& localAddress){
std::shared_ptr cleanedPeerAddress = Utils::interpretUrl(Utils::cleanSipAddress(peerAddress));
std::shared_ptr cleanedLocalAddress = Utils::interpretUrl(Utils::cleanSipAddress(localAddress));
diff --git a/linphone-app/src/components/calls/CallsListModel.hpp b/linphone-app/src/components/calls/CallsListModel.hpp
index 5e5562c6e..81444a014 100644
--- a/linphone-app/src/components/calls/CallsListModel.hpp
+++ b/linphone-app/src/components/calls/CallsListModel.hpp
@@ -68,6 +68,13 @@ public:
Q_INVOKABLE void terminateAllCalls () const;
Q_INVOKABLE void terminateCall (const QString& sipAddress) const;
+// Call commands
+ QSharedPointer getLastCall(bool incoming) const;
+ QSharedPointer getCallModel(const std::shared_ptr &call) const;
+ Q_INVOKABLE void acceptLastIncomingCall(bool video);
+ Q_INVOKABLE void terminateLastCall();
+ Q_INVOKABLE void toggleMuteSpeaker();
+ Q_INVOKABLE void toggleMuteMicrophone();
static std::list> getCallHistory(const QString& peerAddress, const QString& localAddress);
diff --git a/linphone-app/src/components/settings/SettingsModel.cpp b/linphone-app/src/components/settings/SettingsModel.cpp
index f7041fdf6..ef5fbbc78 100644
--- a/linphone-app/src/components/settings/SettingsModel.cpp
+++ b/linphone-app/src/components/settings/SettingsModel.cpp
@@ -552,6 +552,10 @@ void SettingsModel::setEchoCancellationEnabled(bool status) {
void SettingsModel::startEchoCancellerCalibration() {
CoreManager::getInstance()->getCore()->startEchoCancellerCalibration();
}
+
+int SettingsModel::getEchoCancellationCalibration()const {
+ return CoreManager::getInstance()->getCore()->getEchoCancellationCalibration();
+}
// -----------------------------------------------------------------------------
bool SettingsModel::getShowAudioCodecs() const {
@@ -2051,6 +2055,7 @@ void SettingsModel::handleCallStateChanged(const shared_ptr &, l
}
void SettingsModel::handleEcCalibrationResult(linphone::EcCalibratorStatus status, int delayMs) {
emit echoCancellationStatus((int)status, delayMs);
+ emit echoCancellationCalibrationChanged();
}
bool SettingsModel::getIsInCall() const {
return CoreManager::getInstance()->getCore()->getCallsNb() != 0;
diff --git a/linphone-app/src/components/settings/SettingsModel.hpp b/linphone-app/src/components/settings/SettingsModel.hpp
index 742a33a02..2a9e035b5 100644
--- a/linphone-app/src/components/settings/SettingsModel.hpp
+++ b/linphone-app/src/components/settings/SettingsModel.hpp
@@ -93,10 +93,10 @@ class SettingsModel : public QObject {
Q_PROPERTY(QString ringerDevice READ getRingerDevice WRITE setRingerDevice NOTIFY ringerDeviceChanged)
Q_PROPERTY(QString ringPath READ getRingPath WRITE setRingPath NOTIFY ringPathChanged)
-
Q_PROPERTY(bool echoCancellationEnabled READ getEchoCancellationEnabled WRITE setEchoCancellationEnabled NOTIFY
echoCancellationEnabledChanged)
-
+ Q_PROPERTY(
+ int echoCancellationCalibration READ getEchoCancellationCalibration NOTIFY echoCancellationCalibrationChanged)
Q_PROPERTY(bool showAudioCodecs READ getShowAudioCodecs WRITE setShowAudioCodecs NOTIFY showAudioCodecsChanged)
// Video. --------------------------------------------------------------------
@@ -372,7 +372,7 @@ public:
Q_INVOKABLE void startCaptureGraph();
Q_INVOKABLE void stopCaptureGraph();
- ;
+
Q_INVOKABLE void stopCaptureGraphs();
Q_INVOKABLE void resetCaptureGraph();
void createCaptureGraph();
@@ -406,6 +406,7 @@ public:
bool getEchoCancellationEnabled() const;
void setEchoCancellationEnabled(bool status);
+ int getEchoCancellationCalibration() const;
Q_INVOKABLE void startEchoCancellerCalibration();
@@ -813,9 +814,9 @@ signals:
void echoCancellationEnabledChanged(bool status);
void echoCancellationStatus(int status, int msDelay);
+ void echoCancellationCalibrationChanged();
void showAudioCodecsChanged(bool status);
-
// Video. --------------------------------------------------------------------
void videoEnabledChanged();
void videoAvailableChanged();
diff --git a/linphone-app/ui/modules/Common/Form/MouseArea.qml b/linphone-app/ui/modules/Common/Form/MouseArea.qml
index 7ded9e4c5..17ccab5e2 100644
--- a/linphone-app/ui/modules/Common/Form/MouseArea.qml
+++ b/linphone-app/ui/modules/Common/Form/MouseArea.qml
@@ -4,9 +4,10 @@ import Common 1.0
import Common.Styles 1.0
Quick.MouseArea {
+ property int hoveredCursor: Qt.PointingHandCursor
property bool interactive: true
- cursorShape: containsMouse && interactive
- ? Qt.PointingHandCursor
+ cursorShape: containsMouse && interactive
+ ? hoveredCursor
: Qt.ArrowCursor
- hoverEnabled: interactive
+ hoverEnabled: interactive
}
diff --git a/linphone-app/ui/modules/Linphone/Notifications/Notification.qml b/linphone-app/ui/modules/Linphone/Notifications/Notification.qml
index 5f4826395..5ae87a7f5 100644
--- a/linphone-app/ui/modules/Linphone/Notifications/Notification.qml
+++ b/linphone-app/ui/modules/Linphone/Notifications/Notification.qml
@@ -7,7 +7,7 @@ import Linphone.Styles 1.0
DesktopPopup {
id: notification
-
+ property bool selected: false
property alias icon: iconSign.icon
property var notificationData: ({
timelineModel : null
@@ -35,8 +35,8 @@ DesktopPopup {
width: NotificationStyle.width
border {
- color: NotificationStyle.border.colorModel.color
- width: NotificationStyle.border.width
+ color: notification.selected ? NotificationStyle.selectedBorder.colorModel.color : NotificationStyle.border.colorModel.color
+ width: notification.selected ? NotificationStyle.selectedBorder.width : NotificationStyle.border.width
}
Item {
diff --git a/linphone-app/ui/modules/Linphone/Notifications/NotificationReceivedCall.qml b/linphone-app/ui/modules/Linphone/Notifications/NotificationReceivedCall.qml
index 30bcacd66..dfb6c368c 100644
--- a/linphone-app/ui/modules/Linphone/Notifications/NotificationReceivedCall.qml
+++ b/linphone-app/ui/modules/Linphone/Notifications/NotificationReceivedCall.qml
@@ -17,7 +17,15 @@ Notification {
readonly property var call: notificationData && notificationData.call
// ---------------------------------------------------------------------------
-
+
+ selected: focusArea.containsMouse
+ onSelectedChanged: { if(selected) Window.window.requestActivate()}
+ MouseArea{
+ id: focusArea
+ anchors.fill: parent
+ acceptedButtons: Qt.NoButton
+ hoveredCursor: Qt.ArrowCursor
+ }
Loader {
active: Boolean(notification.call)
anchors {
diff --git a/linphone-app/ui/modules/Linphone/Styles/Notifications/NotificationStyle.qml b/linphone-app/ui/modules/Linphone/Styles/Notifications/NotificationStyle.qml
index 001f7a0bb..b3dbb68e9 100644
--- a/linphone-app/ui/modules/Linphone/Styles/Notifications/NotificationStyle.qml
+++ b/linphone-app/ui/modules/Linphone/Styles/Notifications/NotificationStyle.qml
@@ -16,4 +16,8 @@ QtObject {
property var colorModel: ColorsList.add(sectionName+'_border', 'n')
property int width: 1
}
+ property QtObject selectedBorder: QtObject {
+ property var colorModel: ColorsList.add(sectionName+'_selected_border', 'i')
+ property int width: 1
+ }
}
diff --git a/linphone-app/ui/views/App/Main/MainWindow.qml b/linphone-app/ui/views/App/Main/MainWindow.qml
index fa957ea23..c65bdf0db 100644
--- a/linphone-app/ui/views/App/Main/MainWindow.qml
+++ b/linphone-app/ui/views/App/Main/MainWindow.qml
@@ -72,9 +72,42 @@ ApplicationWindow {
}
Shortcut {
+ context: Qt.ApplicationShortcut
sequence: StandardKey.Close
onActivated: window.hide()
}
+ Shortcut {
+ context: Qt.ApplicationShortcut
+ sequence: ['Ctrl+Shift+W', 'Ctrl+Shift+V']
+ onActivated: CallsListModel.acceptLastIncomingCall(true)
+ }
+ Shortcut {
+ context: Qt.ApplicationShortcut
+ sequence: 'Ctrl+Shift+A'
+ onActivated: CallsListModel.acceptLastIncomingCall(false)
+ }
+ Shortcut {
+ sequence: 'Ctrl+Shift+D'
+ context: Qt.ApplicationShortcut
+ onActivated: CallsListModel.terminateLastCall(true)
+ }
+ Shortcut {
+ sequence: 'Ctrl+Shift+E'
+ context: Qt.ApplicationShortcut
+ onActivated: {// startEchoCancellerCalibration is unsupported while being in call.
+ SettingsModel.echoCancellationEnabled = !SettingsModel.echoCancellationEnabled;
+ }
+ }
+ Shortcut {
+ sequence: 'Ctrl+Shift+L'
+ context: Qt.ApplicationShortcut
+ onActivated: CallsListModel.toggleMuteSpeaker()
+ }
+ Shortcut {
+ sequence: 'Ctrl+Shift+M'
+ context: Qt.ApplicationShortcut
+ onActivated: CallsListModel.toggleMuteMicrophone()
+ }
// ---------------------------------------------------------------------------
Loader {
diff --git a/linphone-app/ui/views/App/Settings/SettingsAudio.qml b/linphone-app/ui/views/App/Settings/SettingsAudio.qml
index 8e1d8829b..b091aa8ce 100644
--- a/linphone-app/ui/views/App/Settings/SettingsAudio.qml
+++ b/linphone-app/ui/views/App/Settings/SettingsAudio.qml
@@ -254,7 +254,7 @@ TabContainer {
}
Text{
id:echoCalibrationStatus
- text: ''
+ text: SettingsModel.echoCancellationCalibration > 0 ? qsTr("calibratingEchoCancellationDone").replace('%1', SettingsModel.echoCancellationCalibration) : ''
Layout.fillWidth:true
height:parent.height
verticalAlignment: Text.AlignVCenter
@@ -262,7 +262,7 @@ TabContainer {
}
TextButtonB {
id: echoCalibration
- enabled: SettingsModel.echoCancellationEnabled
+ enabled: SettingsModel.echoCancellationEnabled && !SettingsModel.isInCall
text: qsTr('echoCancellationCalibrationLabel')