- 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.
- Forbid to calibrate echo cancellation while being in call because it is not supported.
This commit is contained in:
Julien Wadel 2024-03-11 16:46:31 +01:00
parent 761f04d474
commit d69a68c5ca
12 changed files with 130 additions and 12 deletions

View file

@ -24,6 +24,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- '[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

View file

@ -2026,7 +2026,7 @@
<message>
<source>checkForUpdates</source>
<extracomment>&apos;Check for updates&apos; : Item menu for checking updates</extracomment>
<translation type="unfinished"></translation>
<translation type="unfinished"></translation>
</message>
<message>
<source>recordings</source>
@ -2056,7 +2056,7 @@
<message>
<source>checkForUpdates</source>
<extracomment>&apos;Check for updates&apos; : Item menu for checking updates</extracomment>
<translation type="unfinished"></translation>
<translation type="unfinished"></translation>
</message>
<message>
<source>recordings</source>

View file

@ -501,6 +501,55 @@ void CallsListModel::terminateCall (const QString& sipAddress) const{
}
}
QSharedPointer<CallModel> CallsListModel::getLastCall(bool incoming) const{
QSharedPointer<CallModel> lastCall = nullptr;
time_t lastTime = 0;
auto item = mList.rbegin();
while(item != mList.rend() && !lastCall) {
auto call = item->objectCast<CallModel>();
if(!incoming || call->getStatus() == CallModel::CallStatusIncoming) {
lastCall = call;
}
}
return lastCall;
}
QSharedPointer<CallModel> CallsListModel::getCallModel(const std::shared_ptr<linphone::Call> &call) const{
QSharedPointer<CallModel> callModel;
if(call) {
auto itCall = std::find_if(mList.begin(), mList.end(), [call](const QSharedPointer<QObject> &item){
auto c = item.objectCast<CallModel>();
return c && c->getCall() == call;
});
if( itCall != mList.end())
callModel = itCall->objectCast<CallModel>();
}
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<std::shared_ptr<linphone::CallLog>> CallsListModel::getCallHistory(const QString& peerAddress, const QString& localAddress){
std::shared_ptr<linphone::Address> cleanedPeerAddress = Utils::interpretUrl(Utils::cleanSipAddress(peerAddress));
std::shared_ptr<linphone::Address> cleanedLocalAddress = Utils::interpretUrl(Utils::cleanSipAddress(localAddress));

View file

@ -68,6 +68,13 @@ public:
Q_INVOKABLE void terminateAllCalls () const;
Q_INVOKABLE void terminateCall (const QString& sipAddress) const;
// Call commands
QSharedPointer<CallModel> getLastCall(bool incoming) const;
QSharedPointer<CallModel> getCallModel(const std::shared_ptr<linphone::Call> &call) const;
Q_INVOKABLE void acceptLastIncomingCall(bool video);
Q_INVOKABLE void terminateLastCall();
Q_INVOKABLE void toggleMuteSpeaker();
Q_INVOKABLE void toggleMuteMicrophone();
static std::list<std::shared_ptr<linphone::CallLog>> getCallHistory(const QString& peerAddress, const QString& localAddress);

View file

@ -558,6 +558,10 @@ void SettingsModel::startEchoCancellerCalibration(){
CoreManager::getInstance()->getCore()->startEchoCancellerCalibration();
}
int SettingsModel::getEchoCancellationCalibration()const {
return CoreManager::getInstance()->getCore()->getEchoCancellationCalibration();
}
// -----------------------------------------------------------------------------
bool SettingsModel::getShowAudioCodecs () const {
@ -2104,6 +2108,7 @@ void SettingsModel::handleCallStateChanged(const shared_ptr<linphone::Call> &, 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;

View file

@ -85,6 +85,7 @@ class SettingsModel : public QObject {
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)
@ -369,6 +370,7 @@ public:
bool getEchoCancellationEnabled () const;
void setEchoCancellationEnabled (bool status);
int getEchoCancellationCalibration()const;
Q_INVOKABLE void startEchoCancellerCalibration();
@ -780,6 +782,7 @@ signals:
void echoCancellationEnabledChanged (bool status);
void echoCancellationStatus(int status, int msDelay);
void echoCancellationCalibrationChanged();
void showAudioCodecsChanged (bool status);

View file

@ -4,8 +4,9 @@ import Common 1.0
import Common.Styles 1.0
Quick.MouseArea {
cursorShape: containsMouse
? Qt.PointingHandCursor
: Qt.ArrowCursor
hoverEnabled: true
property int hoveredCursor: Qt.PointingHandCursor
cursorShape: containsMouse
? hoveredCursor
: Qt.ArrowCursor
hoverEnabled: true
}

View file

@ -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 {

View file

@ -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 {

View file

@ -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
}
}

View file

@ -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 {

View file

@ -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')