mirror of
https://gitlab.linphone.org/BC/public/linphone-desktop.git
synced 2026-01-26 16:28:11 +00:00
Fixes and Features : Fullscreen, crashs, contacts, messages
Fixes: * Change download URL to `https://www.linphone.org/technical-corner/linphone` * Crash on contacts list when deleting a friend and trying to load logs. * Message status behaviour : Resuming status when changing logs, cursor shapes updates, bind the `resend message` action to error icon * Synchronize settings and main presence. * QML Crashs on fullscreen Features : * Add utilities in fullscreen view (media quality, security, mutable speaker) * Show fullscreen on the current call screen * Show Display name of participant in conference * Participants in conference are mutable
This commit is contained in:
parent
f71a7c8950
commit
3031cc856a
21 changed files with 286 additions and 184 deletions
|
|
@ -615,7 +615,9 @@ void CallModel::verifyAuthenticationToken (bool verify) {
|
|||
void CallModel::updateStreams () {
|
||||
mCall->update(nullptr);
|
||||
}
|
||||
|
||||
void CallModel::toggleSpeakerMute(){
|
||||
setSpeakerMuted(!getSpeakerMuted());
|
||||
}
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
CallModel::CallEncryption CallModel::getEncryption () const {
|
||||
|
|
|
|||
|
|
@ -130,6 +130,8 @@ public:
|
|||
Q_INVOKABLE void verifyAuthenticationToken (bool verify);
|
||||
|
||||
Q_INVOKABLE void updateStreams ();
|
||||
|
||||
Q_INVOKABLE void toggleSpeakerMute();
|
||||
|
||||
signals:
|
||||
void callErrorChanged (const QString &callError);
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ static inline void fillMessageEntry (QVariantMap &dest, const shared_ptr<linphon
|
|||
if (state == linphone::ChatMessage::State::InProgress)
|
||||
dest["status"] = ChatModel::MessageStatusNotDelivered;
|
||||
else
|
||||
dest["status"] = static_cast<ChatModel::MessageStatus>(message->getState());
|
||||
dest["status"] = static_cast<ChatModel::MessageStatus>(message->getState());
|
||||
|
||||
shared_ptr<const linphone::Content> content = message->getFileTransferInformation();
|
||||
if (content) {
|
||||
|
|
@ -310,13 +310,21 @@ ChatModel::ChatModel (const QString &peerAddress, const QString &localAddress) {
|
|||
mMessageHandlers = make_shared<MessageHandlers>(this);
|
||||
|
||||
setSipAddresses(peerAddress, localAddress);
|
||||
|
||||
// Rebind lost handlers
|
||||
for(auto i = mEntries.begin() ; i != mEntries.end() ; ++i){
|
||||
if(i->first["type"] == EntryType::MessageEntry){
|
||||
shared_ptr<linphone::ChatMessage> message = static_pointer_cast<linphone::ChatMessage>(i->second);
|
||||
message->removeListener(mMessageHandlers);// Remove old listener if already exists
|
||||
message->addListener(mMessageHandlers);
|
||||
}
|
||||
}
|
||||
{
|
||||
CoreHandlers *coreHandlers = mCoreHandlers.get();
|
||||
QObject::connect(coreHandlers, &CoreHandlers::messageReceived, this, &ChatModel::handleMessageReceived);
|
||||
QObject::connect(coreHandlers, &CoreHandlers::callStateChanged, this, &ChatModel::handleCallStateChanged);
|
||||
QObject::connect(coreHandlers, &CoreHandlers::isComposingChanged, this, &ChatModel::handleIsComposingChanged);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ChatModel::~ChatModel () {
|
||||
|
|
@ -343,7 +351,7 @@ QVariant ChatModel::data (const QModelIndex &index, int role) const {
|
|||
switch (role) {
|
||||
case Roles::ChatEntry: {
|
||||
auto &data = mEntries[row].first;
|
||||
if (!data.contains("status"))
|
||||
if (data.contains("type") && data["type"]==EntryType::MessageEntry && !data.contains("content"))
|
||||
fillMessageEntry(data, static_pointer_cast<linphone::ChatMessage>(mEntries[row].second));
|
||||
return QVariant::fromValue(data);
|
||||
}
|
||||
|
|
@ -406,7 +414,7 @@ QString ChatModel::getFullLocalAddress () const {
|
|||
void ChatModel::setSipAddresses (const QString &peerAddress, const QString &localAddress) {
|
||||
shared_ptr<linphone::Core> core = CoreManager::getInstance()->getCore();
|
||||
shared_ptr<linphone::Factory> factory(linphone::Factory::get());
|
||||
|
||||
|
||||
mChatRoom = core->getChatRoom(
|
||||
factory->createAddress(Utils::appStringToCoreString(peerAddress)),
|
||||
factory->createAddress(Utils::appStringToCoreString(localAddress))
|
||||
|
|
@ -436,6 +444,7 @@ void ChatModel::setSipAddresses (const QString &peerAddress, const QString &loca
|
|||
|
||||
qInfo() << QStringLiteral("ChatModel (%1, %2) loaded in %3 milliseconds.")
|
||||
.arg(peerAddress).arg(localAddress).arg(timer.elapsed());
|
||||
|
||||
}
|
||||
|
||||
bool ChatModel::getIsRemoteComposing () const {
|
||||
|
|
@ -472,6 +481,7 @@ void ChatModel::removeAllEntries () {
|
|||
|
||||
void ChatModel::sendMessage (const QString &message) {
|
||||
shared_ptr<linphone::ChatMessage> _message = mChatRoom->createMessage(Utils::appStringToCoreString(message));
|
||||
_message->removeListener(mMessageHandlers);// Remove old listener if already exists
|
||||
_message->addListener(mMessageHandlers);
|
||||
|
||||
insertMessageAtEnd(_message);
|
||||
|
|
@ -498,6 +508,7 @@ void ChatModel::resendMessage (int id) {
|
|||
case MessageStatusFileTransferError:
|
||||
case MessageStatusNotDelivered: {
|
||||
shared_ptr<linphone::ChatMessage> message = static_pointer_cast<linphone::ChatMessage>(entry.second);
|
||||
message->removeListener(mMessageHandlers);// Remove old listener if already exists
|
||||
message->addListener(mMessageHandlers);
|
||||
message->resend();
|
||||
|
||||
|
|
@ -534,6 +545,7 @@ void ChatModel::sendFileMessage (const QString &path) {
|
|||
content->setName(Utils::appStringToCoreString( QFileInfo(file).fileName()));
|
||||
shared_ptr<linphone::ChatMessage> message = mChatRoom->createFileTransferMessage(content);
|
||||
message->getContents().front()->setFilePath(Utils::appStringToCoreString(path));
|
||||
message->removeListener(mMessageHandlers);// Remove old listener if already exists
|
||||
message->addListener(mMessageHandlers);
|
||||
|
||||
createThumbnail(message);
|
||||
|
|
@ -575,7 +587,8 @@ void ChatModel::downloadFile (int id) {
|
|||
if (!soFarSoGood) {
|
||||
qWarning() << QStringLiteral("Unable to create safe file path for: %1.").arg(id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
message->removeListener(mMessageHandlers);// Remove old listener if already exists
|
||||
message->addListener(mMessageHandlers);
|
||||
|
||||
message->getContents().front()->setFilePath(Utils::appStringToCoreString(safeFilePath));
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ namespace {
|
|||
constexpr char LinphoneDomain[] = "sip.linphone.org";
|
||||
constexpr char DefaultContactParameters[] = "message-expires=604800";
|
||||
constexpr int DefaultExpires = 3600;
|
||||
constexpr char DownloadUrl[] = "https://www.linphone.org/technical-corner/linphone/downloads";
|
||||
constexpr char DownloadUrl[] = "https://www.linphone.org/technical-corner/linphone";
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include <QVariantMap>
|
||||
|
||||
#include "components/core/CoreManager.hpp"
|
||||
#include "components/settings/AccountSettingsModel.hpp"
|
||||
|
||||
#include "OwnPresenceModel.hpp"
|
||||
|
||||
|
|
@ -29,6 +30,16 @@
|
|||
|
||||
using namespace std;
|
||||
|
||||
OwnPresenceModel::OwnPresenceModel (QObject *parent) : QObject(parent) {
|
||||
AccountSettingsModel *accountSettingsModel = CoreManager::getInstance()->getAccountSettingsModel();
|
||||
// Update status when changing the publish presence from settings
|
||||
QObject::connect(accountSettingsModel, &AccountSettingsModel::publishPresenceChanged, this, [this]() {
|
||||
Presence::PresenceStatus status = static_cast<Presence::PresenceStatus>(CoreManager::getInstance()->getCore()->getConsolidatedPresence());
|
||||
emit presenceStatusChanged(status);
|
||||
emit presenceLevelChanged(Presence::getPresenceLevel(status));
|
||||
});
|
||||
}
|
||||
|
||||
Presence::PresenceLevel OwnPresenceModel::getPresenceLevel () const {
|
||||
return Presence::getPresenceLevel(getPresenceStatus());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class OwnPresenceModel : public QObject {
|
|||
Q_PROPERTY(Presence::PresenceStatus presenceStatus READ getPresenceStatus WRITE setPresenceStatus NOTIFY presenceStatusChanged);
|
||||
|
||||
public:
|
||||
OwnPresenceModel (QObject *parent = Q_NULLPTR) : QObject(parent) {}
|
||||
OwnPresenceModel (QObject *parent = Q_NULLPTR);
|
||||
|
||||
signals:
|
||||
void presenceLevelChanged (Presence::PresenceLevel level);
|
||||
|
|
|
|||
|
|
@ -212,6 +212,7 @@ bool AccountSettingsModel::addOrUpdateProxyConfig (
|
|||
const QVariantMap &data
|
||||
) {
|
||||
Q_CHECK_PTR(proxyConfig);
|
||||
bool newPublishPresence = false;
|
||||
|
||||
proxyConfig->edit();
|
||||
|
||||
|
|
@ -249,6 +250,7 @@ bool AccountSettingsModel::addOrUpdateProxyConfig (
|
|||
proxyConfig->setContactParameters(Utils::appStringToCoreString(data["contactParams"].toString()));
|
||||
proxyConfig->setAvpfRrInterval(uint8_t(data["avpfInterval"].toInt()));
|
||||
proxyConfig->enableRegister(data["registerEnabled"].toBool());
|
||||
newPublishPresence = proxyConfig->publishEnabled() != data["publishPresence"].toBool();
|
||||
proxyConfig->enablePublish(data["publishPresence"].toBool());
|
||||
proxyConfig->setAvpfMode(data["avpfEnabled"].toBool()
|
||||
? linphone::AVPFMode::Enabled
|
||||
|
|
@ -291,7 +293,8 @@ bool AccountSettingsModel::addOrUpdateProxyConfig (
|
|||
"",
|
||||
""
|
||||
));
|
||||
|
||||
if( newPublishPresence)
|
||||
emit publishPresenceChanged();
|
||||
return addOrUpdateProxyConfig(proxyConfig);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ public:
|
|||
|
||||
signals:
|
||||
void accountSettingsUpdated ();
|
||||
void publishPresenceChanged();
|
||||
|
||||
private:
|
||||
QString getUsername () const;
|
||||
|
|
|
|||
|
|
@ -438,9 +438,8 @@ void SipAddressesModel::handleIsComposingChanged (const shared_ptr<linphone::Cha
|
|||
void SipAddressesModel::addOrUpdateSipAddress (SipAddressEntry &sipAddressEntry, ContactModel *contact) {
|
||||
const QString &sipAddress = sipAddressEntry.sipAddress;
|
||||
|
||||
if (contact)
|
||||
sipAddressEntry.contact = contact;
|
||||
else if (!sipAddressEntry.contact)
|
||||
sipAddressEntry.contact = contact;
|
||||
if (!sipAddressEntry.contact)
|
||||
qWarning() << QStringLiteral("`contact` field is empty on sip address: `%1`.").arg(sipAddress);
|
||||
|
||||
updateObservers(sipAddress, contact);
|
||||
|
|
|
|||
|
|
@ -76,6 +76,17 @@ Item {
|
|||
|
||||
hoverEnabled: true
|
||||
|
||||
MouseArea
|
||||
{
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
onPressed: mouse.accepted = false
|
||||
hoverEnabled: true
|
||||
cursorShape: containsMouse
|
||||
? Qt.PointingHandCursor
|
||||
: Qt.ArrowCursor
|
||||
}
|
||||
|
||||
height: parent.height
|
||||
width: parent.width
|
||||
|
||||
|
|
|
|||
|
|
@ -32,4 +32,14 @@ Button {
|
|||
rightPadding: SmallButtonStyle.rightPadding
|
||||
}
|
||||
hoverEnabled: true
|
||||
MouseArea
|
||||
{
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
onPressed: mouse.accepted = false
|
||||
hoverEnabled: true
|
||||
cursorShape: containsMouse
|
||||
? Qt.PointingHandCursor
|
||||
: Qt.ArrowCursor
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,8 +36,9 @@ Item {
|
|||
Layout.preferredWidth: AccountStatusStyle.presenceLevel.size
|
||||
|
||||
PresenceLevel {
|
||||
|
||||
anchors.fill: parent
|
||||
level: SettingsModel.rlsUriEnabled ? OwnPresenceModel.presenceLevel : Presence.Green
|
||||
level: OwnPresenceModel.presenceStatus===Presence.Offline?Presence.White:( SettingsModel.rlsUriEnabled ? OwnPresenceModel.presenceLevel : Presence.Green)
|
||||
visible: AccountSettingsModel.registrationState === AccountSettingsModel.RegistrationStateRegistered
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ Popup {
|
|||
|
||||
Loader {
|
||||
property string $label: qsTr('audioStatsLabel')
|
||||
property var $data: callStatistics.call.audioStats
|
||||
property var $data: callStatistics.call?callStatistics.call.audioStats:null
|
||||
|
||||
sourceComponent: media
|
||||
width: parent.width / 2
|
||||
|
|
@ -37,7 +37,7 @@ Popup {
|
|||
|
||||
Loader {
|
||||
property string $label: qsTr('videoStatsLabel')
|
||||
property var $data: callStatistics.call.videoStats
|
||||
property var $data: callStatistics.call?callStatistics.call.videoStats:null
|
||||
|
||||
sourceComponent: media
|
||||
width: parent.width / 2
|
||||
|
|
|
|||
|
|
@ -47,13 +47,12 @@ Row {
|
|||
Rectangle {
|
||||
id: rectangle
|
||||
|
||||
readonly property bool isNotDelivered: Utils.includes([
|
||||
readonly property bool isError: Utils.includes([
|
||||
ChatModel.MessageStatusFileTransferError,
|
||||
ChatModel.MessageStatusIdle,
|
||||
ChatModel.MessageStatusInProgress,
|
||||
ChatModel.MessageStatusNotDelivered
|
||||
], $chatEntry.status)
|
||||
|
||||
ChatModel.MessageStatusNotDelivered,
|
||||
], $chatEntry.status)
|
||||
readonly property bool isUploaded: $chatEntry.status === ChatModel.MessageStatusDelivered
|
||||
readonly property bool isDelivered: $chatEntry.status === ChatModel.MessageStatusDeliveredToUser
|
||||
readonly property bool isRead: $chatEntry.status === ChatModel.MessageStatusDisplayed
|
||||
|
||||
color: $chatEntry.isOutgoing
|
||||
|
|
@ -257,7 +256,7 @@ Row {
|
|||
? Qt.PointingHandCursor
|
||||
: Qt.ArrowCursor
|
||||
hoverEnabled: true
|
||||
visible: !rectangle.isNotDelivered && !$chatEntry.isOutgoing
|
||||
visible: (rectangle.isUploaded || rectangle.isRead) && !$chatEntry.isOutgoing
|
||||
|
||||
onClicked: {
|
||||
if (Utils.pointIsInItem(this, thumbnailProvider, mouse)) {
|
||||
|
|
@ -288,15 +287,20 @@ Row {
|
|||
Icon {
|
||||
anchors.centerIn: parent
|
||||
|
||||
icon: rectangle.isNotDelivered
|
||||
? 'chat_error'
|
||||
: (rectangle.isRead ? 'chat_read' : 'chat_delivered')
|
||||
icon: rectangle.isError ? 'chat_error' :
|
||||
(rectangle.isRead ? 'chat_read' :
|
||||
(rectangle.isDelivered ? 'chat_delivered' : ''))
|
||||
|
||||
iconSize: ChatStyle.entry.message.outgoing.sendIconSize
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: isNotDelivered && proxyModel.resendMessage(index)
|
||||
cursorShape: containsMouse
|
||||
? Qt.PointingHandCursor
|
||||
: Qt.ArrowCursor
|
||||
hoverEnabled: true
|
||||
visible: (rectangle.isError || $chatEntry.status === ChatModel.MessageStatusIdle) && $chatEntry.isOutgoing
|
||||
onClicked: proxyModel.resendMessage(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,28 +33,32 @@ Item {
|
|||
id: icon
|
||||
|
||||
Icon {
|
||||
readonly property bool isNotDelivered: Utils.includes([
|
||||
ChatModel.MessageStatusFileTransferError,
|
||||
ChatModel.MessageStatusIdle,
|
||||
ChatModel.MessageStatusInProgress,
|
||||
ChatModel.MessageStatusNotDelivered
|
||||
], $chatEntry.status)
|
||||
|
||||
readonly property bool isError: Utils.includes([
|
||||
ChatModel.MessageStatusFileTransferError,
|
||||
ChatModel.MessageStatusNotDelivered,
|
||||
], $chatEntry.status)
|
||||
readonly property bool isUploaded: $chatEntry.status === ChatModel.MessageStatusDelivered
|
||||
readonly property bool isDelivered: $chatEntry.status === ChatModel.MessageStatusDeliveredToUser
|
||||
readonly property bool isRead: $chatEntry.status === ChatModel.MessageStatusDisplayed
|
||||
readonly property bool isDeliveredToUser: $chatEntry.status === ChatModel.MessageStatusDeliveredToUser
|
||||
|
||||
icon: isNotDelivered
|
||||
icon: isError
|
||||
? 'chat_error'
|
||||
: (isRead ? 'chat_read' : (isDeliveredToUser ? 'chat_delivered' : ''))
|
||||
: (isRead ? 'chat_read' : (isDelivered ? 'chat_delivered' : ''))
|
||||
iconSize: ChatStyle.entry.message.outgoing.sendIconSize
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: isNotDelivered && proxyModel.resendMessage(index)
|
||||
cursorShape: containsMouse
|
||||
? Qt.PointingHandCursor
|
||||
: Qt.ArrowCursor
|
||||
hoverEnabled: true
|
||||
visible: icon.isError || $chatEntry.status === ChatModel.MessageStatusIdle
|
||||
onClicked: proxyModel.resendMessage(index)
|
||||
}
|
||||
|
||||
TooltipArea {
|
||||
text: isNotDelivered
|
||||
text: isError
|
||||
? qsTr('messageError')
|
||||
: (isRead ? qsTr('messageRead') : qsTr('messageDelivered'))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ function getTopParent (object, useFakeParent) {
|
|||
// Supported options: isString, exitHandler, properties.
|
||||
//
|
||||
// If exitHandler is used, window must implement exitStatus signal.
|
||||
function openWindow (window, parent, options) {
|
||||
function openWindow (window, parent, options, fullscreen) {
|
||||
var object = createObject(window, parent, options)
|
||||
|
||||
object.closing.connect(object.destroy.bind(object))
|
||||
|
|
@ -196,8 +196,10 @@ function openWindow (window, parent, options) {
|
|||
options.exitHandler.bind(parent)
|
||||
)
|
||||
}
|
||||
|
||||
object.show()
|
||||
if(fullscreen)
|
||||
object.showFullScreen()
|
||||
else
|
||||
object.show()
|
||||
|
||||
return object
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ Rectangle {
|
|||
|
||||
Column {
|
||||
readonly property string sipAddress: $call.peerAddress
|
||||
readonly property string fullSipAddress: $call.fullPeerAddress
|
||||
property var _sipAddressObserver : SipAddressesModel.getSipAddressObserver($call.peerAddress, $call.localAddress)
|
||||
|
||||
anchors {
|
||||
fill: parent
|
||||
|
|
@ -134,8 +134,8 @@ Rectangle {
|
|||
|
||||
horizontalTextAlignment: Text.AlignHCenter
|
||||
sipAddress: parent.sipAddress
|
||||
username: LinphoneUtils.getContactUsername(parent.fullSipAddress)
|
||||
}
|
||||
username: LinphoneUtils.getContactUsername(parent._sipAddressObserver)
|
||||
}
|
||||
IncallAvatar {
|
||||
|
||||
readonly property int size: Math.min(
|
||||
|
|
@ -172,15 +172,19 @@ Rectangle {
|
|||
leftMargin: ConferenceStyle.grid.spacing
|
||||
bottomMargin: ConferenceStyle.grid.spacing
|
||||
}
|
||||
enabled:!$call.speakerMuted
|
||||
|
||||
Timer {
|
||||
interval: 50
|
||||
repeat: true
|
||||
running: true
|
||||
|
||||
onTriggered: parent.value = $call.speakerVu
|
||||
}
|
||||
}
|
||||
MouseArea{
|
||||
anchors.fill:parent
|
||||
onClicked:$call.toggleSpeakerMute()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,26 +124,27 @@ function openCallStatistics () {
|
|||
callStatistics.open()
|
||||
}
|
||||
|
||||
function openMediaParameters () {
|
||||
function openMediaParameters (incall) {
|
||||
window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/MultimediaParameters.qml'), {
|
||||
call: incall.call
|
||||
})
|
||||
}
|
||||
|
||||
function showFullscreen () {
|
||||
function showFullscreen (position) {
|
||||
if (incall._fullscreen) {
|
||||
return
|
||||
}
|
||||
|
||||
DesktopTools.DesktopTools.screenSaverStatus = false
|
||||
incall._fullscreen = Utils.openWindow(Qt.resolvedUrl('IncallFullscreenWindow.qml'), window, {
|
||||
properties: {
|
||||
caller: incall
|
||||
caller: incall,
|
||||
x:position.x,
|
||||
y:position.y
|
||||
}
|
||||
})
|
||||
}, true)
|
||||
}
|
||||
|
||||
function updateCallQualityIcon () {
|
||||
function updateCallQualityIcon (callQuality,call) {
|
||||
var quality = call.quality
|
||||
callQuality.icon = 'call_quality_' + (
|
||||
// Note: `quality` is in the [0, 5] interval.
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ Rectangle {
|
|||
running: true
|
||||
triggeredOnStart: true
|
||||
|
||||
onTriggered: Logic.updateCallQualityIcon()
|
||||
onTriggered: Logic.updateCallQualityIcon(callQuality, call)
|
||||
}
|
||||
|
||||
CallStatistics {
|
||||
|
|
@ -203,7 +203,7 @@ Rectangle {
|
|||
icon: 'fullscreen'
|
||||
visible: incall.call.videoEnabled
|
||||
|
||||
onClicked: Logic.showFullscreen()
|
||||
onClicked: Logic.showFullscreen(contactDescription.mapToGlobal(0,0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -263,9 +263,8 @@ Rectangle {
|
|||
ZrtpTokenAuthentication {
|
||||
id: zrtp
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||
Layout.margins: CallStyle.container.margins
|
||||
Layout.preferredHeight: CallStyle.zrtpArea.height
|
||||
|
||||
call: incall.call
|
||||
visible: !call.isSecured && call.encryption !== CallModel.CallEncryptionNone
|
||||
|
|
@ -365,7 +364,7 @@ Rectangle {
|
|||
icon: 'options'
|
||||
iconSize: CallStyle.actionArea.iconSize
|
||||
|
||||
onClicked: Logic.openMediaParameters()
|
||||
onClicked: Logic.openMediaParameters(incall)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Window 2.2
|
||||
|
||||
|
|
@ -6,6 +7,7 @@ import Common 1.0
|
|||
import Common.Styles 1.0
|
||||
import DesktopTools 1.0
|
||||
import Linphone 1.0
|
||||
import LinphoneUtils 1.0
|
||||
import Utils 1.0
|
||||
|
||||
import App.Styles 1.0
|
||||
|
|
@ -15,7 +17,7 @@ import 'Incall.js' as Logic
|
|||
// =============================================================================
|
||||
|
||||
Window {
|
||||
id: window
|
||||
id: windowId
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
|
@ -29,16 +31,14 @@ Window {
|
|||
DesktopTools.screenSaverStatus = true
|
||||
|
||||
// `exit` is called by `Incall.qml`.
|
||||
// The `window` id can be null if the window was closed in this view.
|
||||
if (!window) {
|
||||
// The `windowId` id can be null if the windowId was closed in this view.
|
||||
if (!windowId) {
|
||||
return
|
||||
}
|
||||
|
||||
// It's necessary to call `showNormal` before close on MacOs
|
||||
// because the dock will be hidden forever!
|
||||
window.visible = false
|
||||
window.showNormal()
|
||||
window.close()
|
||||
if(!windowId.close() && parent)
|
||||
parent.close()
|
||||
|
||||
|
||||
if (cb) {
|
||||
cb()
|
||||
|
|
@ -46,42 +46,33 @@ Window {
|
|||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Component.onCompleted: {
|
||||
window.call = caller.call
|
||||
var show = function (visibility) {
|
||||
if (visibility === Window.Windowed) {
|
||||
window.visibilityChanged.disconnect(show)
|
||||
window.showFullScreen()
|
||||
}
|
||||
}
|
||||
|
||||
window.visibilityChanged.connect(show)
|
||||
}
|
||||
|
||||
visible: false
|
||||
|
||||
onCallChanged: if(!call) windowId.exit()
|
||||
Component.onCompleted: {
|
||||
windowId.call = caller.call
|
||||
}
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Shortcut {
|
||||
sequence: StandardKey.Close
|
||||
onActivated: window.exit()
|
||||
onActivated: windowId.exit()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Rectangle {
|
||||
id:container
|
||||
anchors.fill: parent
|
||||
color: '#000000' // Not a style.
|
||||
focus: true
|
||||
|
||||
Keys.onEscapePressed: window.exit()
|
||||
Keys.onEscapePressed: windowId.exit()
|
||||
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
|
||||
active: {
|
||||
var caller = window.caller
|
||||
var caller = windowId.caller
|
||||
return caller && !caller.cameraActivated
|
||||
}
|
||||
|
||||
|
|
@ -91,11 +82,10 @@ Window {
|
|||
id: camera
|
||||
|
||||
Camera {
|
||||
call: window.call
|
||||
call: windowId.call
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Handle mouse move / Hide buttons.
|
||||
// -------------------------------------------------------------------------
|
||||
|
|
@ -150,36 +140,57 @@ Window {
|
|||
anchors.left: parent.left
|
||||
iconSize: CallStyle.header.iconSize
|
||||
visible: !hideButtons
|
||||
|
||||
Icon {
|
||||
|
||||
ActionButton {
|
||||
id: callQuality
|
||||
|
||||
icon: 'call_quality_0'
|
||||
iconSize: parent.iconSize
|
||||
useStates: false
|
||||
|
||||
// See: http://www.linphone.org/docs/liblinphone/group__call__misc.html#ga62c7d3d08531b0cc634b797e273a0a73
|
||||
onClicked: Logic.openCallStatistics()
|
||||
|
||||
// See: http://www.linphone.org/docs/liblinphone/group__call__misc.html#ga62c7d3d08531b0cc634b797e273a0a73
|
||||
Timer {
|
||||
interval: 5000
|
||||
repeat: true
|
||||
running: true
|
||||
triggeredOnStart: true
|
||||
|
||||
onTriggered: {
|
||||
var quality = call.quality
|
||||
callQuality.icon = 'call_quality_' + (
|
||||
// Note: `quality` is in the [0, 5] interval.
|
||||
// It's necessary to map in the `call_quality_` interval. ([0, 3])
|
||||
quality >= 0 ? Math.round(quality / (5 / 3)) : 0
|
||||
)
|
||||
}
|
||||
onTriggered: Logic.updateCallQualityIcon(callQuality, windowId.call)
|
||||
}
|
||||
|
||||
CallStatistics {
|
||||
id: callStatistics
|
||||
enabled: windowId.call
|
||||
call: windowId.call
|
||||
width: container.width
|
||||
|
||||
relativeTo: callQuality
|
||||
relativeY: CallStyle.header.stats.relativeY
|
||||
|
||||
onClosed: Logic.handleCallStatisticsClosed()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ActionButton {
|
||||
icon: 'tel_keypad'
|
||||
|
||||
onClicked: telKeypad.visible = !telKeypad.visible
|
||||
}
|
||||
|
||||
ActionButton {
|
||||
id: callSecure
|
||||
|
||||
icon: windowId.call && windowId.call.isSecured ? 'call_chat_secure' : 'call_chat_unsecure'
|
||||
|
||||
onClicked: zrtp.visible = (windowId.call.encryption === CallModel.CallEncryptionZrtp)
|
||||
|
||||
TooltipArea {
|
||||
text: windowId.call?Logic.makeReadableSecuredString(windowId.call.securedString):''
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
|
@ -196,7 +207,7 @@ Window {
|
|||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
||||
visible: !window.hideButtons
|
||||
visible: !windowId.hideButtons
|
||||
|
||||
// Not a customizable style.
|
||||
color: 'white'
|
||||
|
|
@ -205,9 +216,11 @@ Window {
|
|||
|
||||
Component.onCompleted: {
|
||||
var updateDuration = function () {
|
||||
var call = window.caller.call
|
||||
text = Utils.formatElapsedTime(call.duration)
|
||||
Utils.setTimeout(elapsedTime, 1000, updateDuration)
|
||||
if(windowId.caller){
|
||||
var call = windowId.caller.call
|
||||
text = Utils.formatElapsedTime(call.duration)
|
||||
Utils.setTimeout(elapsedTime, 1000, updateDuration)
|
||||
}
|
||||
}
|
||||
|
||||
updateDuration()
|
||||
|
|
@ -236,7 +249,7 @@ Window {
|
|||
ActionSwitch {
|
||||
id: recordingSwitch
|
||||
|
||||
enabled: call.recording
|
||||
enabled: call && call.recording
|
||||
icon: 'record'
|
||||
useStates: false
|
||||
visible: SettingsModel.callRecorderEnabled
|
||||
|
|
@ -255,15 +268,30 @@ Window {
|
|||
ActionButton {
|
||||
icon: 'fullscreen'
|
||||
|
||||
onClicked: window.exit()
|
||||
onClicked: windowId.exit()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Action Buttons.
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Zrtp.
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
ZrtpTokenAuthentication {
|
||||
id: zrtp
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||
Layout.margins: CallStyle.container.margins
|
||||
call: windowId.call
|
||||
visible: call && !call.isSecured && call.encryption !== CallModel.CallEncryptionNone
|
||||
z: Constants.zPopup
|
||||
color: CallStyle.backgroundColor
|
||||
}
|
||||
Item {
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
Layout.fillWidth: true
|
||||
|
|
@ -298,7 +326,7 @@ Window {
|
|||
ActionSwitch {
|
||||
id: micro
|
||||
|
||||
enabled: !call.microMuted
|
||||
enabled: call && !call.microMuted
|
||||
icon: 'micro'
|
||||
iconSize: CallStyle.actionArea.iconSize
|
||||
|
||||
|
|
@ -324,11 +352,11 @@ Window {
|
|||
ActionSwitch {
|
||||
id: speaker
|
||||
|
||||
enabled: true
|
||||
enabled: call && !call.speakerMuted
|
||||
icon: 'speaker'
|
||||
iconSize: CallStyle.actionArea.iconSize
|
||||
|
||||
onClicked: console.log('TODO')
|
||||
onClicked: call.speakerMuted = enabled
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -336,9 +364,9 @@ Window {
|
|||
enabled: true
|
||||
icon: 'camera'
|
||||
iconSize: CallStyle.actionArea.iconSize
|
||||
updating: call.updating
|
||||
updating: call && call.updating
|
||||
|
||||
onClicked: window.exit(function () { call.videoEnabled = false })
|
||||
onClicked: windowId.exit(function () { call.videoEnabled = false })
|
||||
}
|
||||
|
||||
ActionButton {
|
||||
|
|
@ -348,7 +376,7 @@ Window {
|
|||
icon: 'options'
|
||||
iconSize: CallStyle.actionArea.iconSize
|
||||
|
||||
onClicked: Logic.openMediaParameters()
|
||||
onClicked: Logic.openMediaParameters(windowId)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -361,18 +389,18 @@ Window {
|
|||
iconSize: CallStyle.actionArea.iconSize
|
||||
|
||||
ActionSwitch {
|
||||
enabled: !call.pausedByUser
|
||||
enabled: call && !call.pausedByUser
|
||||
icon: 'pause'
|
||||
updating: call.updating
|
||||
updating: call && call.updating
|
||||
visible: SettingsModel.callPauseEnabled
|
||||
|
||||
onClicked: window.exit(function () { call.pausedByUser = enabled })
|
||||
onClicked: windowId.exit(function () { call.pausedByUser = enabled })
|
||||
}
|
||||
|
||||
ActionButton {
|
||||
icon: 'hangup'
|
||||
|
||||
onClicked: window.exit(call.terminate)
|
||||
onClicked: windowId.exit(call.terminate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -385,7 +413,7 @@ Window {
|
|||
|
||||
Loader {
|
||||
active: {
|
||||
var caller = window.caller
|
||||
var caller = windowId.caller
|
||||
return caller && !caller.cameraActivated
|
||||
}
|
||||
|
||||
|
|
@ -398,21 +426,21 @@ Window {
|
|||
property bool scale: false
|
||||
|
||||
function xPosition () {
|
||||
return window.width / 2 - width / 2
|
||||
return windowId.width / 2 - width / 2
|
||||
}
|
||||
|
||||
function yPosition () {
|
||||
return window.height - height
|
||||
return windowId.height - height
|
||||
}
|
||||
|
||||
call: window.call
|
||||
call: windowId.call
|
||||
isPreview: true
|
||||
|
||||
height: CallStyle.actionArea.userVideo.height * (scale ? 2 : 1)
|
||||
width: CallStyle.actionArea.userVideo.width * (scale ? 2 : 1)
|
||||
|
||||
DragBox {
|
||||
container: window
|
||||
container: windowId
|
||||
draggable: parent
|
||||
|
||||
xPosition: parent.xPosition
|
||||
|
|
@ -431,7 +459,7 @@ Window {
|
|||
TelKeypad {
|
||||
id: telKeypad
|
||||
|
||||
call: window.call
|
||||
call: windowId.call
|
||||
visible: false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,111 +6,118 @@ import Common 1.0
|
|||
import App.Styles 1.0
|
||||
|
||||
// =============================================================================
|
||||
|
||||
ColumnLayout {
|
||||
Rectangle{
|
||||
id: zrtp
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
property var call
|
||||
|
||||
visible: false
|
||||
color:"transparent"
|
||||
|
||||
implicitWidth: columnLayout.implicitWidth
|
||||
implicitHeight: columnLayout.implicitHeight+CallStyle.container.margins
|
||||
|
||||
radius: 10
|
||||
|
||||
ColumnLayout {
|
||||
id:columnLayout
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
visible: false
|
||||
Layout.fillWidth: true
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Main text.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
||||
text: qsTr('confirmSas')
|
||||
text: qsTr('confirmSas')
|
||||
|
||||
color: CallStyle.zrtpArea.text.colorA
|
||||
elide: Text.ElideRight
|
||||
color: CallStyle.zrtpArea.text.colorA
|
||||
elide: Text.ElideRight
|
||||
|
||||
font {
|
||||
bold: true
|
||||
pointSize: CallStyle.zrtpArea.text.pointSize
|
||||
font {
|
||||
bold: true
|
||||
pointSize: CallStyle.zrtpArea.text.pointSize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Rules.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Row {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Row {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
spacing: CallStyle.zrtpArea.text.wordsSpacing
|
||||
spacing: CallStyle.zrtpArea.text.wordsSpacing
|
||||
|
||||
Text {
|
||||
color: CallStyle.zrtpArea.text.colorA
|
||||
font.pointSize: CallStyle.zrtpArea.text.pointSize
|
||||
text: qsTr('codeA')
|
||||
}
|
||||
|
||||
Text {
|
||||
color: CallStyle.zrtpArea.text.colorB
|
||||
|
||||
font {
|
||||
bold: true
|
||||
pointSize: CallStyle.zrtpArea.text.pointSize
|
||||
Text {
|
||||
color: CallStyle.zrtpArea.text.colorA
|
||||
font.pointSize: CallStyle.zrtpArea.text.pointSize
|
||||
text: qsTr('codeA')
|
||||
}
|
||||
|
||||
text: zrtp.call.localSas
|
||||
}
|
||||
Text {
|
||||
color: CallStyle.zrtpArea.text.colorB
|
||||
|
||||
Text {
|
||||
color: CallStyle.zrtpArea.text.colorA
|
||||
font.pointSize: CallStyle.zrtpArea.text.pointSize
|
||||
text: '-'
|
||||
}
|
||||
font {
|
||||
bold: true
|
||||
pointSize: CallStyle.zrtpArea.text.pointSize
|
||||
}
|
||||
|
||||
Text {
|
||||
color: CallStyle.zrtpArea.text.colorA
|
||||
font.pointSize: CallStyle.zrtpArea.text.pointSize
|
||||
text: qsTr('codeB')
|
||||
}
|
||||
|
||||
Text {
|
||||
color: CallStyle.zrtpArea.text.colorB
|
||||
|
||||
font {
|
||||
bold: true
|
||||
pointSize: CallStyle.zrtpArea.text.pointSize
|
||||
text: zrtp.call?zrtp.call.localSas:''
|
||||
}
|
||||
|
||||
text: zrtp.call.remoteSas
|
||||
Text {
|
||||
color: CallStyle.zrtpArea.text.colorA
|
||||
font.pointSize: CallStyle.zrtpArea.text.pointSize
|
||||
text: '-'
|
||||
}
|
||||
|
||||
Text {
|
||||
color: CallStyle.zrtpArea.text.colorA
|
||||
font.pointSize: CallStyle.zrtpArea.text.pointSize
|
||||
text: qsTr('codeB')
|
||||
}
|
||||
|
||||
Text {
|
||||
color: CallStyle.zrtpArea.text.colorB
|
||||
|
||||
font {
|
||||
bold: true
|
||||
pointSize: CallStyle.zrtpArea.text.pointSize
|
||||
}
|
||||
|
||||
text: zrtp.call?zrtp.call.remoteSas:''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Buttons.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Row {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Row {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
spacing: CallStyle.zrtpArea.buttons.spacing
|
||||
spacing: CallStyle.zrtpArea.buttons.spacing
|
||||
|
||||
TextButtonA {
|
||||
text: qsTr('deny')
|
||||
onClicked: {
|
||||
zrtp.visible = false
|
||||
zrtp.call.verifyAuthenticationToken(false)
|
||||
TextButtonA {
|
||||
text: qsTr('deny')
|
||||
onClicked: {
|
||||
zrtp.visible = false
|
||||
zrtp.call.verifyAuthenticationToken(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextButtonB {
|
||||
text: qsTr('accept')
|
||||
onClicked: {
|
||||
zrtp.visible = false
|
||||
zrtp.call.verifyAuthenticationToken(true)
|
||||
TextButtonB {
|
||||
text: qsTr('accept')
|
||||
onClicked: {
|
||||
zrtp.visible = false
|
||||
zrtp.call.verifyAuthenticationToken(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue